Skip to main content

dioxus_element_plug/components/
switch.rs

1use dioxus::prelude::*;
2
3/// Switch size variants
4#[derive(Clone, PartialEq)]
5pub enum SwitchSize {
6    Large,
7    Default,
8    Small,
9}
10
11impl SwitchSize {
12    pub fn as_class(&self) -> &'static str {
13        match self {
14            SwitchSize::Large => "el-switch--large",
15            SwitchSize::Default => "",
16            SwitchSize::Small => "el-switch--small",
17        }
18    }
19}
20
21/// Switch props
22#[derive(Props, Clone, PartialEq)]
23pub struct SwitchProps {
24    /// Whether the switch is active
25    #[props(default = false)]
26    pub model_value: bool,
27
28    /// Whether the switch is disabled
29    #[props(default = false)]
30    pub disabled: bool,
31
32    /// Whether the switch is in loading state
33    #[props(default = false)]
34    pub loading: bool,
35
36    /// Switch size
37    #[props(default = SwitchSize::Default)]
38    pub size: SwitchSize,
39
40    /// Switch width (e.g., "40px")
41    #[props(default)]
42    pub width: Option<String>,
43
44    /// Whether to show text inside the dot
45    #[props(default = false)]
46    pub inline_prompt: bool,
47
48    /// Text displayed when in "on" state
49    #[props(default)]
50    pub active_text: Option<String>,
51
52    /// Text displayed when in "off" state
53    #[props(default)]
54    pub inactive_text: Option<String>,
55
56    /// Icon CSS class when in "on" state
57    #[props(default)]
58    pub active_icon: Option<String>,
59
60    /// Icon CSS class when in "off" state
61    #[props(default)]
62    pub inactive_icon: Option<String>,
63
64    /// Input name attribute
65    #[props(default)]
66    pub name: Option<String>,
67
68    /// Change event handler (receives new value)
69    #[props(default)]
70    pub on_change: Option<EventHandler<bool>>,
71
72    /// Additional CSS classes
73    #[props(default)]
74    pub class: Option<String>,
75
76    /// Inline styles
77    #[props(default)]
78    pub style: Option<String>,
79}
80
81/// Switch component for toggling between two states
82///
83/// ## Example
84///
85/// ```rust,ignore
86/// rsx! {
87///     Switch {
88///         model_value: true,
89///         active_text: Some("ON".to_string()),
90///         inactive_text: Some("OFF".to_string()),
91///         on_change: move |val| println!("Switch: {}", val),
92///     }
93/// }
94/// ```
95#[component]
96pub fn Switch(props: SwitchProps) -> Element {
97    let mut class_names = vec!["el-switch".to_string()];
98
99    let size_class = props.size.as_class();
100    if !size_class.is_empty() {
101        class_names.push(size_class.to_string());
102    }
103
104    if props.model_value {
105        class_names.push("is-checked".to_string());
106    }
107
108    if props.disabled {
109        class_names.push("is-disabled".to_string());
110    }
111
112    if props.loading {
113        class_names.push("is-loading".to_string());
114    }
115
116    if let Some(ref custom_class) = props.class {
117        class_names.push(custom_class.clone());
118    }
119
120    let class_string = class_names.join(" ");
121
122    let mut style_parts = vec![props.style.clone().unwrap_or_default()];
123    if let Some(ref width) = props.width {
124        style_parts.push(format!("width: {};", width));
125    }
126    let style_string = style_parts.join("");
127
128    rsx! {
129        div {
130            class: "{class_string}",
131            style: "{style_string}",
132            role: "switch",
133            aria_checked: "{props.model_value}",
134            onclick: move |_| {
135                if !props.disabled && !props.loading {
136                    if let Some(handler) = props.on_change {
137                        handler.call(!props.model_value);
138                    }
139                }
140            },
141            div {
142                class: "el-switch__core",
143                if props.inline_prompt {
144                    div {
145                        class: "el-switch__inner",
146                        if props.model_value {
147                            if let Some(ref text) = props.active_text {
148                                span { class: "el-switch__text", "{text}" }
149                            } else if let Some(ref icon) = props.active_icon {
150                                i { class: "{icon}" }
151                            }
152                        } else {
153                            if let Some(ref text) = props.inactive_text {
154                                span { class: "el-switch__text", "{text}" }
155                            } else if let Some(ref icon) = props.inactive_icon {
156                                i { class: "{icon}" }
157                            }
158                        }
159                    }
160                }
161                div {
162                    class: "el-switch__action",
163                    if props.loading {
164                        i { class: "el-icon-loading" }
165                    }
166                }
167            }
168            if !props.inline_prompt {
169                if props.model_value {
170                    if let Some(ref text) = props.active_text {
171                        span {
172                            class: "el-switch__label el-switch__label--left is-active",
173                            "{text}"
174                        }
175                    }
176                } else {
177                    if let Some(ref text) = props.inactive_text {
178                        span {
179                            class: "el-switch__label el-switch__label--right is-active",
180                            "{text}"
181                        }
182                    }
183                }
184            }
185            if let Some(ref name) = props.name {
186                input {
187                    r#type: "hidden",
188                    name: "{name}",
189                    value: "{props.model_value}",
190                }
191            }
192        }
193    }
194}