1use dioxus::prelude::*;
2
3use crate::types::Size;
4
5#[derive(Clone, PartialEq, Props)]
15pub struct FormGroupProps {
16 #[props(default)]
18 pub label: String,
19 #[props(default)]
21 pub class: String,
22 pub children: Element,
24}
25
26#[component]
27pub fn FormGroup(props: FormGroupProps) -> Element {
28 let full_class = if props.class.is_empty() {
29 "mb-3".to_string()
30 } else {
31 format!("mb-3 {}", props.class)
32 };
33
34 rsx! {
35 div { class: "{full_class}",
36 if !props.label.is_empty() {
37 label { class: "form-label", "{props.label}" }
38 }
39 {props.children}
40 }
41 }
42}
43
44#[derive(Clone, PartialEq, Props)]
53pub struct InputProps {
54 #[props(default = "text".to_string())]
56 pub r#type: String,
57 #[props(default)]
59 pub value: String,
60 #[props(default)]
62 pub placeholder: String,
63 #[props(default)]
65 pub size: Size,
66 #[props(default)]
68 pub disabled: bool,
69 #[props(default)]
71 pub readonly: bool,
72 #[props(default)]
74 pub oninput: Option<EventHandler<FormEvent>>,
75 #[props(default)]
77 pub class: String,
78}
79
80#[component]
81pub fn Input(props: InputProps) -> Element {
82 let size_class = match props.size {
83 Size::Md => String::new(),
84 s => format!(" form-control-{s}"),
85 };
86
87 let full_class = if props.class.is_empty() {
88 format!("form-control{size_class}")
89 } else {
90 format!("form-control{size_class} {}", props.class)
91 };
92
93 rsx! {
94 input {
95 class: "{full_class}",
96 r#type: "{props.r#type}",
97 value: "{props.value}",
98 placeholder: "{props.placeholder}",
99 disabled: props.disabled,
100 readonly: props.readonly,
101 oninput: move |evt| {
102 if let Some(handler) = &props.oninput {
103 handler.call(evt);
104 }
105 },
106 }
107 }
108}
109
110#[derive(Clone, PartialEq, Props)]
121pub struct SelectProps {
122 #[props(default)]
124 pub value: String,
125 #[props(default)]
127 pub size: Size,
128 #[props(default)]
130 pub disabled: bool,
131 #[props(default)]
133 pub onchange: Option<EventHandler<FormEvent>>,
134 #[props(default)]
136 pub class: String,
137 pub children: Element,
139}
140
141#[component]
142pub fn Select(props: SelectProps) -> Element {
143 let size_class = match props.size {
144 Size::Md => String::new(),
145 s => format!(" form-select-{s}"),
146 };
147
148 let full_class = if props.class.is_empty() {
149 format!("form-select{size_class}")
150 } else {
151 format!("form-select{size_class} {}", props.class)
152 };
153
154 rsx! {
155 select {
156 class: "{full_class}",
157 value: "{props.value}",
158 disabled: props.disabled,
159 onchange: move |evt| {
160 if let Some(handler) = &props.onchange {
161 handler.call(evt);
162 }
163 },
164 {props.children}
165 }
166 }
167}
168
169#[derive(Clone, PartialEq, Props)]
177pub struct TextareaProps {
178 #[props(default)]
180 pub value: String,
181 #[props(default = 3)]
183 pub rows: u32,
184 #[props(default)]
186 pub placeholder: String,
187 #[props(default)]
189 pub disabled: bool,
190 #[props(default)]
192 pub readonly: bool,
193 #[props(default)]
195 pub oninput: Option<EventHandler<FormEvent>>,
196 #[props(default)]
198 pub class: String,
199}
200
201#[component]
202pub fn Textarea(props: TextareaProps) -> Element {
203 let full_class = if props.class.is_empty() {
204 "form-control".to_string()
205 } else {
206 format!("form-control {}", props.class)
207 };
208
209 rsx! {
210 textarea {
211 class: "{full_class}",
212 rows: "{props.rows}",
213 placeholder: "{props.placeholder}",
214 disabled: props.disabled,
215 readonly: props.readonly,
216 value: "{props.value}",
217 oninput: move |evt| {
218 if let Some(handler) = &props.oninput {
219 handler.call(evt);
220 }
221 },
222 }
223 }
224}
225
226#[derive(Clone, PartialEq, Props)]
234pub struct CheckboxProps {
235 #[props(default)]
237 pub checked: bool,
238 #[props(default)]
240 pub label: String,
241 #[props(default)]
243 pub disabled: bool,
244 #[props(default)]
246 pub onchange: Option<EventHandler<FormEvent>>,
247 #[props(default)]
249 pub class: String,
250}
251
252#[component]
253pub fn Checkbox(props: CheckboxProps) -> Element {
254 let full_class = if props.class.is_empty() {
255 "form-check".to_string()
256 } else {
257 format!("form-check {}", props.class)
258 };
259
260 rsx! {
261 div { class: "{full_class}",
262 input {
263 class: "form-check-input",
264 r#type: "checkbox",
265 checked: props.checked,
266 disabled: props.disabled,
267 onchange: move |evt| {
268 if let Some(handler) = &props.onchange {
269 handler.call(evt);
270 }
271 },
272 }
273 if !props.label.is_empty() {
274 label { class: "form-check-label", "{props.label}" }
275 }
276 }
277 }
278}
279
280#[derive(Clone, PartialEq, Props)]
289pub struct RadioProps {
290 pub name: String,
292 #[props(default)]
294 pub checked: bool,
295 #[props(default)]
297 pub label: String,
298 #[props(default)]
300 pub disabled: bool,
301 #[props(default)]
303 pub onchange: Option<EventHandler<FormEvent>>,
304 #[props(default)]
306 pub class: String,
307}
308
309#[component]
310pub fn Radio(props: RadioProps) -> Element {
311 let full_class = if props.class.is_empty() {
312 "form-check".to_string()
313 } else {
314 format!("form-check {}", props.class)
315 };
316
317 rsx! {
318 div { class: "{full_class}",
319 input {
320 class: "form-check-input",
321 r#type: "radio",
322 name: "{props.name}",
323 checked: props.checked,
324 disabled: props.disabled,
325 onchange: move |evt| {
326 if let Some(handler) = &props.onchange {
327 handler.call(evt);
328 }
329 },
330 }
331 if !props.label.is_empty() {
332 label { class: "form-check-label", "{props.label}" }
333 }
334 }
335 }
336}