1use dioxus::prelude::*;
2use super::size::Size;
3
4#[derive(Clone, Copy, PartialEq)]
5pub enum InputType {
6 Text,
7 Email,
8 Password,
9 Number,
10 Tel,
11 Url,
12 Search,
13 Date,
14 Time,
15 DateTime,
16 Month,
17 Week,
18 Color,
19 File,
20 Hidden,
21 Range,
22}
23
24impl Into<&'static str> for InputType {
25 fn into(self) -> &'static str {
26 match self {
27 InputType::Text => "text",
28 InputType::Email => "email",
29 InputType::Password => "password",
30 InputType::Number => "number",
31 InputType::Tel => "tel",
32 InputType::Url => "url",
33 InputType::Search => "search",
34 InputType::Date => "date",
35 InputType::Time => "time",
36 InputType::DateTime => "datetime-local",
37 InputType::Month => "month",
38 InputType::Week => "week",
39 InputType::Color => "color",
40 InputType::File => "file",
41 InputType::Hidden => "hidden",
42 InputType::Range => "range",
43 }
44 }
45}
46
47#[derive(Clone, Props, PartialEq)]
48pub struct InputProps {
49 #[props(optional)]
50 id: String,
51 #[props(optional, default = "".to_string())]
52 class: String,
53 #[props(optional, default = InputType::Text)]
54 input_type: InputType,
55 #[props(optional, default = Size::Normal)]
56 size: Size,
57 #[props(optional, default = "".to_string())]
58 placeholder: String,
59 #[props(optional, default = "".to_string())]
60 value: String,
61 #[props(optional, default = false)]
62 disabled: bool,
63 #[props(optional, default = false)]
64 readonly: bool,
65 #[props(optional, default = false)]
66 plaintext: bool,
67 #[props(optional, default = None)]
68 min: Option<String>,
69 #[props(optional, default = None)]
70 max: Option<String>,
71 #[props(optional, default = None)]
72 step: Option<String>,
73 #[props(optional, default = None)]
74 pattern: Option<String>,
75 #[props(optional)]
76 oninput: EventHandler<FormEvent>,
77 #[props(optional)]
78 onchange: EventHandler<FormEvent>,
79 #[props(optional)]
80 onfocus: EventHandler<FocusEvent>,
81 #[props(optional)]
82 onblur: EventHandler<FocusEvent>,
83}
84
85#[component]
86pub fn Input(props: InputProps) -> Element {
87 let mut class_list = if props.plaintext {
88 vec!["form-control-plaintext".to_string()]
89 } else {
90 vec!["form-control".to_string()]
91 };
92
93 let size: &str = props.size.into();
94 if props.size != Size::Normal && !props.plaintext {
95 class_list.push(format!("form-control-{}", size));
96 }
97
98 if !props.class.is_empty() {
99 class_list.push(props.class.clone());
100 }
101
102 let class_list = class_list.join(" ");
103 let input_type: &str = props.input_type.into();
104
105 rsx! {
106 input {
107 id: props.id,
108 r#type: input_type,
109 class: class_list,
110 placeholder: props.placeholder,
111 value: props.value,
112 disabled: props.disabled,
113 readonly: props.readonly,
114 min: props.min,
115 max: props.max,
116 step: props.step,
117 pattern: props.pattern,
118 oninput: props.oninput,
119 onchange: props.onchange,
120 onfocus: props.onfocus,
121 onblur: props.onblur,
122 }
123 }
124}
125
126#[derive(Clone, Props, PartialEq)]
127pub struct TextareaProps {
128 #[props(optional)]
129 id: String,
130 #[props(optional, default = "".to_string())]
131 class: String,
132 #[props(optional, default = Size::Normal)]
133 size: Size,
134 #[props(optional, default = "".to_string())]
135 placeholder: String,
136 #[props(optional, default = "".to_string())]
137 value: String,
138 #[props(optional, default = false)]
139 disabled: bool,
140 #[props(optional, default = false)]
141 readonly: bool,
142 #[props(optional, default = None)]
143 rows: Option<u32>,
144 #[props(optional, default = None)]
145 cols: Option<u32>,
146 #[props(optional)]
147 oninput: EventHandler<FormEvent>,
148 #[props(optional)]
149 onchange: EventHandler<FormEvent>,
150 #[props(optional)]
151 onfocus: EventHandler<FocusEvent>,
152 #[props(optional)]
153 onblur: EventHandler<FocusEvent>,
154}
155
156#[component]
157pub fn Textarea(props: TextareaProps) -> Element {
158 let mut class_list = vec!["form-control".to_string()];
159
160 let size: &str = props.size.into();
161 if props.size != Size::Normal {
162 class_list.push(format!("form-control-{}", size));
163 }
164
165 if !props.class.is_empty() {
166 class_list.push(props.class.clone());
167 }
168
169 let class_list = class_list.join(" ");
170
171 rsx! {
172 textarea {
173 id: props.id,
174 class: class_list,
175 placeholder: props.placeholder,
176 value: props.value,
177 disabled: props.disabled,
178 readonly: props.readonly,
179 rows: props.rows,
180 cols: props.cols,
181 oninput: props.oninput,
182 onchange: props.onchange,
183 onfocus: props.onfocus,
184 onblur: props.onblur,
185 }
186 }
187}
188
189#[derive(Clone, Props, PartialEq)]
190pub struct SelectProps {
191 #[props(optional)]
192 id: String,
193 #[props(optional, default = "".to_string())]
194 class: String,
195 #[props(optional, default = Size::Normal)]
196 size: Size,
197 #[props(optional, default = "".to_string())]
198 value: String,
199 #[props(optional, default = false)]
200 disabled: bool,
201 #[props(optional, default = false)]
202 multiple: bool,
203 #[props(optional)]
204 onchange: EventHandler<FormEvent>,
205 children: Element,
206}
207
208#[component]
209pub fn Select(props: SelectProps) -> Element {
210 let mut class_list = vec!["form-select".to_string()];
211
212 let size: &str = props.size.into();
213 if props.size != Size::Normal {
214 class_list.push(format!("form-select-{}", size));
215 }
216
217 if !props.class.is_empty() {
218 class_list.push(props.class.clone());
219 }
220
221 let class_list = class_list.join(" ");
222
223 rsx! {
224 select {
225 id: props.id,
226 class: class_list,
227 value: props.value,
228 disabled: props.disabled,
229 multiple: props.multiple,
230 onchange: props.onchange,
231 {props.children}
232 }
233 }
234}
235
236#[derive(Clone, Props, PartialEq)]
237pub struct CheckboxProps {
238 #[props(optional)]
239 id: String,
240 #[props(optional, default = "".to_string())]
241 class: String,
242 #[props(optional, default = "".to_string())]
243 value: String,
244 #[props(optional, default = false)]
245 checked: bool,
246 #[props(optional, default = false)]
247 disabled: bool,
248 #[props(optional, default = false)]
249 inline: bool,
250 #[props(optional, default = false)]
251 switch: bool,
252 #[props(optional)]
253 onchange: EventHandler<FormEvent>,
254 #[props(optional, default = None)]
255 label: Option<String>,
256}
257
258#[component]
259pub fn Checkbox(props: CheckboxProps) -> Element {
260 let input_class = vec!["form-check-input".to_string()];
261
262 let mut container_class = if props.switch {
263 vec!["form-check".to_string(), "form-switch".to_string()]
264 } else {
265 vec!["form-check".to_string()]
266 };
267
268 if props.inline {
269 container_class.push("form-check-inline".to_string());
270 }
271
272 if !props.class.is_empty() {
273 container_class.push(props.class.clone());
274 }
275
276 let input_class = input_class.join(" ");
277 let container_class = container_class.join(" ");
278 let label_id = format!("{}-label", &props.id);
279
280 rsx! {
281 div {
282 class: container_class,
283 input {
284 id: props.id.clone(),
285 r#type: "checkbox",
286 class: input_class,
287 value: props.value,
288 checked: props.checked,
289 disabled: props.disabled,
290 onchange: props.onchange,
291 }
292 if let Some(label_text) = props.label {
293 label {
294 id: label_id,
295 class: "form-check-label",
296 r#for: props.id.clone(),
297 "{label_text}"
298 }
299 }
300 }
301 }
302}
303
304#[derive(Clone, Props, PartialEq)]
305pub struct RadioProps {
306 #[props(optional)]
307 id: String,
308 #[props(optional, default = "".to_string())]
309 class: String,
310 #[props(optional, default = "".to_string())]
311 name: String,
312 #[props(optional, default = "".to_string())]
313 value: String,
314 #[props(optional, default = false)]
315 checked: bool,
316 #[props(optional, default = false)]
317 disabled: bool,
318 #[props(optional, default = false)]
319 inline: bool,
320 #[props(optional)]
321 onchange: EventHandler<FormEvent>,
322 #[props(optional, default = None)]
323 label: Option<String>,
324}
325
326#[component]
327pub fn Radio(props: RadioProps) -> Element {
328 let input_class = "form-check-input";
329
330 let mut container_class = vec!["form-check".to_string()];
331
332 if props.inline {
333 container_class.push("form-check-inline".to_string());
334 }
335
336 if !props.class.is_empty() {
337 container_class.push(props.class.clone());
338 }
339
340 let container_class = container_class.join(" ");
341 let label_id = format!("{}-label", &props.id);
342
343 rsx! {
344 div {
345 class: container_class,
346 input {
347 id: props.id.clone(),
348 r#type: "radio",
349 class: input_class,
350 name: props.name,
351 value: props.value,
352 checked: props.checked,
353 disabled: props.disabled,
354 onchange: props.onchange,
355 }
356 if let Some(label_text) = props.label {
357 label {
358 id: label_id,
359 class: "form-check-label",
360 r#for: props.id.clone(),
361 "{label_text}"
362 }
363 }
364 }
365 }
366}