Skip to main content

dioxus_element_plug/components/
checkbox.rs

1use dioxus::prelude::*;
2
3/// Checkbox size variants
4#[derive(Clone, PartialEq)]
5pub enum CheckboxSize {
6    Large,
7    Default,
8    Small,
9}
10
11impl CheckboxSize {
12    pub fn as_class(&self) -> &'static str {
13        match self {
14            CheckboxSize::Large => "el-checkbox--large",
15            CheckboxSize::Default => "",
16            CheckboxSize::Small => "el-checkbox--small",
17        }
18    }
19}
20
21/// Checkbox props
22#[derive(Props, Clone, PartialEq)]
23pub struct CheckboxProps {
24    /// Checkbox label/content
25    #[props(default)]
26    pub children: Option<Element>,
27
28    /// Label text (used when no children)
29    #[props(default)]
30    pub label: Option<String>,
31
32    /// Whether the checkbox is checked
33    #[props(default = false)]
34    pub model_value: bool,
35
36    /// Whether the checkbox is disabled
37    #[props(default = false)]
38    pub disabled: bool,
39
40    /// Whether the checkbox is indeterminate
41    #[props(default = false)]
42    pub indeterminate: bool,
43
44    /// Checkbox size
45    #[props(default = CheckboxSize::Default)]
46    pub size: CheckboxSize,
47
48    /// Checkbox value (used in checkbox group)
49    #[props(default)]
50    pub value: Option<String>,
51
52    /// Checkbox name attribute
53    #[props(default)]
54    pub name: Option<String>,
55
56    /// Change event handler
57    #[props(default)]
58    pub on_change: Option<EventHandler<bool>>,
59
60    /// Additional CSS classes
61    #[props(default)]
62    pub class: Option<String>,
63
64    /// Inline styles
65    #[props(default)]
66    pub style: Option<String>,
67}
68
69/// Checkbox component for selecting options
70///
71/// ## Example
72///
73/// ```rust,ignore
74/// rsx! {
75///     Checkbox {
76///         model_value: true,
77///         label: Some("Accept terms".to_string()),
78///         on_change: move |v| println!("Checked: {}", v),
79///     }
80/// }
81/// ```
82#[component]
83pub fn Checkbox(props: CheckboxProps) -> Element {
84    let mut class_names = vec!["el-checkbox".to_string()];
85
86    let size_class = props.size.as_class();
87    if !size_class.is_empty() {
88        class_names.push(size_class.to_string());
89    }
90
91    if props.model_value {
92        class_names.push("is-checked".to_string());
93    }
94
95    if props.disabled {
96        class_names.push("is-disabled".to_string());
97    }
98
99    if props.indeterminate {
100        class_names.push("is-indeterminate".to_string());
101    }
102
103    if let Some(ref custom_class) = props.class {
104        class_names.push(custom_class.clone());
105    }
106
107    let class_string = class_names.join(" ");
108    let style_string = props.style.clone().unwrap_or_default();
109
110    rsx! {
111        label {
112            class: "{class_string}",
113            style: "{style_string}",
114            role: "checkbox",
115            aria_checked: "{props.model_value}",
116            span {
117                class: "el-checkbox__input",
118                if props.model_value || props.indeterminate {
119                    span {
120                        class: "el-checkbox__inner",
121                        if props.indeterminate {
122                            i { class: "el-checkbox__indeterminate" }
123                        }
124                    }
125                } else {
126                    span { class: "el-checkbox__inner" }
127                }
128                if let Some(ref name) = props.name {
129                    input {
130                        r#type: "checkbox",
131                        class: "el-checkbox__original",
132                        name: "{name}",
133                        checked: props.model_value,
134                        onchange: move |_| {
135                            if !props.disabled {
136                                if let Some(handler) = props.on_change {
137                                    handler.call(!props.model_value);
138                                }
139                            }
140                        },
141                    }
142                }
143            }
144            span {
145                class: "el-checkbox__label",
146                onclick: move |_| {
147                    if !props.disabled {
148                        if let Some(handler) = props.on_change {
149                            handler.call(!props.model_value);
150                        }
151                    }
152                },
153                if let Some(ref label) = props.label {
154                    "{label}"
155                }
156                {props.children}
157            }
158        }
159    }
160}
161
162/// CheckboxGroup props
163#[derive(Props, Clone, PartialEq)]
164pub struct CheckboxGroupProps {
165    /// Checkbox group content
166    #[props(default)]
167    pub children: Element,
168
169    /// Whether the group is disabled
170    #[props(default = false)]
171    pub disabled: bool,
172
173    /// Minimum checked items
174    #[props(default)]
175    pub min: Option<u32>,
176
177    /// Maximum checked items
178    #[props(default)]
179    pub max: Option<u32>,
180
181    /// Group name attribute
182    #[props(default)]
183    pub name: Option<String>,
184
185    /// Change event handler
186    #[props(default)]
187    pub on_change: Option<EventHandler<Vec<String>>>,
188
189    /// Additional CSS classes
190    #[props(default)]
191    pub class: Option<String>,
192
193    /// Inline styles
194    #[props(default)]
195    pub style: Option<String>,
196}
197
198/// CheckboxGroup component for grouping multiple checkboxes
199#[component]
200pub fn CheckboxGroup(props: CheckboxGroupProps) -> Element {
201    let mut class_names = vec!["el-checkbox-group".to_string()];
202
203    if props.disabled {
204        class_names.push("is-disabled".to_string());
205    }
206
207    if let Some(ref custom_class) = props.class {
208        class_names.push(custom_class.clone());
209    }
210
211    let class_string = class_names.join(" ");
212    let style_string = props.style.clone().unwrap_or_default();
213
214    rsx! {
215        div {
216            class: "{class_string}",
217            style: "{style_string}",
218            role: "group",
219            {props.children}
220        }
221    }
222}
223
224/// CheckboxButton props (same as Checkbox but with button styling)
225#[derive(Props, Clone, PartialEq)]
226pub struct CheckboxButtonProps {
227    /// Checkbox button content
228    #[props(default)]
229    pub children: Option<Element>,
230
231    /// Label text
232    #[props(default)]
233    pub label: Option<String>,
234
235    /// Whether checked
236    #[props(default = false)]
237    pub model_value: bool,
238
239    /// Whether disabled
240    #[props(default = false)]
241    pub disabled: bool,
242
243    /// Change event handler
244    #[props(default)]
245    pub on_change: Option<EventHandler<bool>>,
246
247    /// Additional CSS classes
248    #[props(default)]
249    pub class: Option<String>,
250
251    /// Inline styles
252    #[props(default)]
253    pub style: Option<String>,
254}
255
256/// CheckboxButton component - checkbox styled as a button
257#[component]
258pub fn CheckboxButton(props: CheckboxButtonProps) -> Element {
259    let mut class_names = vec!["el-checkbox-button".to_string()];
260
261    if props.model_value {
262        class_names.push("is-checked".to_string());
263    }
264
265    if props.disabled {
266        class_names.push("is-disabled".to_string());
267    }
268
269    if let Some(ref custom_class) = props.class {
270        class_names.push(custom_class.clone());
271    }
272
273    let class_string = class_names.join(" ");
274    let style_string = props.style.clone().unwrap_or_default();
275
276    rsx! {
277        label {
278            class: "{class_string}",
279            style: "{style_string}",
280            span {
281                class: "el-checkbox-button__inner",
282                onclick: move |_| {
283                    if !props.disabled {
284                        if let Some(handler) = props.on_change {
285                            handler.call(!props.model_value);
286                        }
287                    }
288                },
289                if let Some(ref label) = props.label {
290                    "{label}"
291                }
292                {props.children}
293            }
294        }
295    }
296}