Skip to content

Commit ba3d26a

Browse files
committed
support setting and editing queryparams with a new generic HMSxn component
1 parent f427c98 commit ba3d26a

File tree

4 files changed

+217
-21
lines changed

4 files changed

+217
-21
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ colored_json = "3"
2929
# TODO: gate the desktop only dependencies behind a feature gate
3030
dioxus = { git = "https://github.com/DioxusLabs/dioxus/" }
3131
dioxus-desktop = { git = "https://github.com/DioxusLabs/dioxus/" }
32+
dioxus-logger = "0.4.1"
3233
# dioxus-web = { git = "https://github.com/DioxusLabs/dioxus/" }
3334
# dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus/" }
3435
# dioxus-hot-reload = "0.1.1"

Dioxus.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ asset_dir = "public"
1616
[web.app]
1717

1818
# HTML title tag content
19-
title = "dioxus | ⛺"
19+
title = "testkit | ⛺"
2020

2121
[web.watcher]
2222

src/app.rs

+210-15
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
#![allow(non_snake_case)]
22

33
use dioxus::prelude::*;
4+
use dioxus_desktop::tao::keyboard::Key;
5+
use std::{collections::HashMap, mem::swap};
46

57
pub fn app_init() {
68
dioxus_desktop::launch_cfg(
79
app,
810
dioxus_desktop::Config::default()
9-
.with_window(dioxus_desktop::WindowBuilder::new().with_maximized(true)),
11+
.with_window(dioxus_desktop::WindowBuilder::new().with_maximized(true))
12+
.with_background_color((0, 0, 0, 1)).with_custom_head(r#"
13+
<style>
14+
html{background-color:black}
15+
</style>
16+
<script src="https://cdn.tailwindcss.com"></script>
17+
<script src="https://kit.fontawesome.com/67b52e9a3b.js" crossorigin="anonymous"></script>
18+
"#.to_string()),
1019
);
1120
}
1221

1322
pub fn app(cx: Scope) -> Element {
1423
let _contenteditableRef = use_ref(cx, || "Please press - or ? for help");
1524
let ctxmenu = use_state(cx, || false);
16-
let _ctxmenu_class = if *ctxmenu.get() { "" } else { "hidden" };
17-
1825
let _selectAction = |_action: &str| {};
1926

2027
cx.render(rsx! {
21-
// script { src: "https://cdn.tailwindcss.com" }
22-
script { src: "https://cdn.tailwindcss.com" }
23-
script { src: "https://kit.fontawesome.com/67b52e9a3b.js", crossorigin: "anonymous" }
24-
2528
datalist { id: "methods-list",
2629
option { value: "GET" }
2730
option { value: "POST" }
@@ -44,8 +47,32 @@ pub fn app(cx: Scope) -> Element {
4447
})
4548
}
4649

50+
#[derive(Default, Clone)]
51+
struct RequestStep {
52+
queryparams: Option<Vec<(String, String)>>,
53+
headers: Option<HashMap<String, String>>,
54+
body: Option<ReqBody>,
55+
}
56+
57+
#[derive(Default, Clone)]
58+
enum ReqBody {
59+
#[default]
60+
None,
61+
Raw(String),
62+
FormData(HashMap<String, String>),
63+
}
64+
4765
fn RequestElement(cx: Scope) -> Element {
4866
let toggle_sxn_option = use_state(cx, || false);
67+
let req_obj = use_ref(cx, RequestStep::default);
68+
69+
let add_section = |p: &str| match p {
70+
"q" => req_obj
71+
.with_mut(|ro| ro.queryparams = ro.queryparams.clone().or(Some(Default::default()))),
72+
"h" => req_obj.with_mut(|ro| ro.headers = ro.headers.clone().or(Some(HashMap::new()))),
73+
"b" => req_obj.with_mut(|ro| ro.body = ro.body.clone().or(Some(ReqBody::default()))),
74+
_ => (),
75+
};
4976

5077
cx.render(rsx! {
5178
div { class: "pl-4 border-l-[.5px] border-gray-600 pt-2 pb-4 space-y-2",
@@ -65,9 +92,22 @@ fn RequestElement(cx: Scope) -> Element {
6592
}
6693
}
6794
},
68-
QueryParamSxn {},
69-
HeadersSxn {},
70-
BodySxn {},
95+
req_obj.with(|req_objj|{
96+
if let Some(queryparams) = req_objj.queryparams.clone() {
97+
rsx!( HMSxn {datalist:queryparams, req_o: req_obj, title:"QueryParams", field:"q"})
98+
} else {rsx!( div {})}
99+
}),
100+
req_obj.with(|req_objj|{
101+
if let Some(_headers) = &req_objj.headers {
102+
rsx!(HeadersSxn {})
103+
} else {rsx!( div {})}
104+
}),
105+
req_obj.with(|req_objj|{
106+
if let Some(_body) = &req_objj.body{
107+
rsx!(BodySxn {})
108+
}else {rsx!( div {})}
109+
110+
}),
71111
div { class: " relative",
72112
a {
73113
class: " cursor-pointer p-2 inline-block",
@@ -76,38 +116,193 @@ fn RequestElement(cx: Scope) -> Element {
76116
}
77117
if *toggle_sxn_option.get() {
78118
rsx!{div { class: "flex flex-col gap-3 flex-1 absolute left-0 bg-stone-900 p-3 border-[.5px] border-gray-100 shadow-md rounded",
79-
a {class: "cursor-pointer", i {class: "fa-solid fa-plus"}, " Query Parameter"},
80-
a {class: "cursor-pointer",i {class: "fa-solid fa-plus"}, " Headers"},
81-
a {class: "cursor-pointer",i {class: "fa-solid fa-plus"}, " Body"},
82-
a {class: "cursor-pointer",i {class: "fa-solid fa-plus"}, " Tests"},
119+
a {class: "cursor-pointer", onclick: move |_|add_section("q"),i {class: "fa-solid fa-plus"}, " Query Parameter"},
120+
a {class: "cursor-pointer", onclick: move |_|add_section("h"),i {class: "fa-solid fa-plus"}, " Headers"},
121+
a {class: "cursor-pointer", onclick: move |_|add_section("b"),i {class: "fa-solid fa-plus"}, " Body"},
122+
a {class: "cursor-pointer", onclick: move |_|add_section("t"),i {class: "fa-solid fa-plus"}, " Tests"},
83123
}}
84124
}
85125
}
86126
}
87127
})
88128
}
89129

90-
fn QueryParamSxn(cx: Scope) -> Element {
130+
#[inline_props]
131+
fn QueryParamSxn<'a>(
132+
cx: Scope<'a>,
133+
req_o: &'a UseRef<RequestStep>,
134+
queryparams: Vec<(String, String)>,
135+
) -> Element {
136+
let new_key = use_state(cx, || "".to_string());
137+
let new_value = use_state(cx, || "".to_string());
91138
cx.render(rsx! {
92139
div { class: "pl-4 space-x-3",
93140
div { class: "flex items-center",
94141
a { class: " cursor-pointer p-2", i { class: "fa-solid fa-chevron-down" } }
95142
span { class: "flex gap-3 flex-1", "QueryParams"}
96143
}
144+
for (i, (k, v)) in queryparams.iter().enumerate() {
145+
div { class: "pl-8 flex",
146+
input {
147+
r#type: "text",
148+
name: "key",
149+
placeholder: "key",
150+
oninput:move |e| {
151+
let mut p = queryparams.clone();
152+
p[i] = (e.value.clone(), v.to_string());
153+
req_o.write().queryparams = Some((p).to_vec());
154+
},
155+
value: "{k}",
156+
class: "bg-transparent border-b-[.5px] border-gray-100 p-1 w-64"
157+
}
158+
span { class: "shrink inline-block px-2", "=" }
159+
input {
160+
r#type: "text",
161+
name: "value",
162+
placeholder: "value",
163+
oninput:move |e| {
164+
let mut p = queryparams.clone();
165+
p[i] = (k.to_string(), e.value.clone());
166+
req_o.write().queryparams = Some((p).to_vec());
167+
},
168+
value: "{v}",
169+
class: "flex-1 bg-transparent border-b-[.5px] border-gray-100 p-1 "
170+
}
171+
}
172+
},
173+
div { class: "pl-8 flex",
174+
input {
175+
r#type: "text",
176+
name: "key",
177+
placeholder: "key",
178+
value: "{new_key.get()}",
179+
oninput:move |e| {
180+
new_key.set(e.value.clone())
181+
},
182+
class: "bg-transparent border-b-[.5px] border-gray-100 p-1 w-64"
183+
}
184+
span { class: "shrink inline-block px-2", "=" }
185+
input {
186+
r#type: "text",
187+
name: "value",
188+
placeholder: "value",
189+
value: "{new_value.get()}",
190+
oninput:move |e| {
191+
new_value.set(e.value.clone())
192+
},
193+
onkeyup: move |e| {
194+
if (e.key == "Enter") {
195+
let mut p = queryparams.clone();
196+
p.push((new_key.get().to_string(), new_value.get().to_string()));
197+
new_value.set("".to_string());
198+
new_key.set("".to_string());
199+
req_o.write().queryparams = Some((p).to_vec());
200+
201+
}
202+
},
203+
class: "flex-1 bg-transparent border-b-[.5px] border-gray-100 p-1 "
204+
}
205+
a { class: " cursor-pointer p-2", onclick: |_| {
206+
let mut p = queryparams.clone();
207+
p.push((new_key.get().to_string(), new_value.get().to_string()));
208+
new_value.set("".to_string());
209+
new_key.set("".to_string());
210+
req_o.write().queryparams = Some((p).to_vec());
211+
}, i { class: "fa-solid fa-person-walking-arrow-loop-left" } }
212+
}
213+
}
214+
})
215+
}
216+
217+
#[inline_props]
218+
fn HMSxn<'a>(
219+
cx: Scope<'a>,
220+
req_o: &'a UseRef<RequestStep>,
221+
title: &'a str,
222+
datalist: Vec<(String, String)>,
223+
field: &'a str,
224+
) -> Element {
225+
let new_key = use_state(cx, || "".to_string());
226+
let new_value = use_state(cx, || "".to_string());
227+
let overwrite_reqo = |p: Vec<(String, String)>| {
228+
match *field {
229+
"q" => req_o.write().queryparams = Some(p),
230+
_ => (),
231+
};
232+
};
233+
cx.render(rsx! {
234+
div { class: "pl-4 space-x-3",
235+
div { class: "flex items-center",
236+
a { class: " cursor-pointer p-2", i { class: "fa-solid fa-chevron-down" } }
237+
span { class: "flex gap-3 flex-1", *title}
238+
}
239+
for (i, (k, v)) in datalist.iter().enumerate() {
240+
div { class: "pl-8 flex",
241+
input {
242+
r#type: "text",
243+
name: "key",
244+
placeholder: "key",
245+
oninput:move |e| {
246+
let mut p = datalist.clone();
247+
p[i] = (e.value.clone(), v.to_string());
248+
overwrite_reqo((p).to_vec());
249+
},
250+
value: "{k}",
251+
class: "bg-transparent border-b-[.5px] border-gray-100 p-1 w-64"
252+
}
253+
span { class: "shrink inline-block px-2", "=" }
254+
input {
255+
r#type: "text",
256+
name: "value",
257+
placeholder: "value",
258+
oninput:move |e| {
259+
let mut p = datalist.clone();
260+
p[i] = (k.to_string(), e.value.clone());
261+
overwrite_reqo((p).to_vec());
262+
},
263+
value: "{v}",
264+
class: "flex-1 bg-transparent border-b-[.5px] border-gray-100 p-1 "
265+
}
266+
}
267+
},
97268
div { class: "pl-8 flex",
98269
input {
99270
r#type: "text",
100271
name: "key",
101272
placeholder: "key",
273+
value: "{new_key.get()}",
274+
oninput:move |e| {
275+
new_key.set(e.value.clone())
276+
},
102277
class: "bg-transparent border-b-[.5px] border-gray-100 p-1 w-64"
103278
}
104279
span { class: "shrink inline-block px-2", "=" }
105280
input {
106281
r#type: "text",
107282
name: "value",
108283
placeholder: "value",
284+
value: "{new_value.get()}",
285+
oninput:move |e| {
286+
new_value.set(e.value.clone())
287+
},
288+
onkeyup: move |e| {
289+
if e.key().to_string() == "Enter" {
290+
let mut p = datalist.clone();
291+
p.push((new_key.get().to_string(), new_value.get().to_string()));
292+
new_value.set("".to_string());
293+
new_key.set("".to_string());
294+
overwrite_reqo((p).to_vec());
295+
}
296+
},
109297
class: "flex-1 bg-transparent border-b-[.5px] border-gray-100 p-1 "
110298
}
299+
a { class: " cursor-pointer p-2", onclick: move |_| {
300+
let mut p = datalist.clone();
301+
p.push((new_key.get().to_string(), new_value.get().to_string()));
302+
new_value.set("".to_string());
303+
new_key.set("".to_string());
304+
overwrite_reqo((p).to_vec());
305+
}, i { class: "fa-solid fa-person-walking-arrow-loop-left" } }
111306
}
112307
}
113308
})

src/main.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
#![feature(extend_one)]
2+
13
mod base_cli;
24
mod base_request;
35
use base_cli::Commands;
46
use base_request::TestContext;
57
extern crate dotenv;
8+
use clap::Parser;
69
use dotenv::dotenv;
710
use env_logger::Builder;
8-
use std::env;
9-
use std::str::FromStr;
10-
use clap::Parser;
1111
use log::LevelFilter;
12-
use std::{fs, path::PathBuf, str::FromStr};
12+
use std::{env, fs, path::PathBuf, str::FromStr};
1313

14+
use std::io::Write;
1415
mod app;
1516
extern crate log;
1617

@@ -19,7 +20,6 @@ async fn main() {
1920
dotenv().ok();
2021
let cli_instance = base_cli::Cli::parse();
2122

22-
2323
let mut builder = env_logger::Builder::from_default_env();
2424
builder
2525
.format_timestamp(None)

0 commit comments

Comments
 (0)