Skip to main content

dioxus_bootstrap/
input.rs

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}