switch.rs
1 //! Defines the [`Switch`] component and its sub-components. 2 3 use crate::use_controlled; 4 use dioxus::prelude::*; 5 6 /// The props for the [`Switch`] component. 7 #[derive(Props, Clone, PartialEq)] 8 pub struct SwitchProps { 9 /// The controlled checked state of the switch. 10 pub checked: ReadSignal<Option<bool>>, 11 12 /// The default checked state when uncontrolled. 13 #[props(default = false)] 14 pub default_checked: bool, 15 16 /// Whether the switch is disabled. 17 #[props(default = ReadSignal::new(Signal::new(false)))] 18 pub disabled: ReadSignal<bool>, 19 20 /// Whether the switch is required in a form. 21 #[props(default)] 22 pub required: ReadSignal<bool>, 23 24 /// The name attribute for form submission. 25 #[props(default)] 26 pub name: ReadSignal<String>, 27 28 /// The value attribute for form submission. 29 #[props(default = ReadSignal::new(Signal::new(String::from("on"))))] 30 pub value: ReadSignal<String>, 31 32 /// Callback fired when the checked state changes. 33 #[props(default)] 34 pub on_checked_change: Callback<bool>, 35 36 /// Additional attributes to apply to the switch element. 37 #[props(extends = GlobalAttributes)] 38 pub attributes: Vec<Attribute>, 39 40 /// The children of the switch component. 41 pub children: Element, 42 } 43 44 /// # Switch 45 /// 46 /// The `Switch` component is a toggle control that allows users to switch a state on or off. 47 /// 48 /// ## Example 49 /// 50 /// ```rust 51 /// use dioxus::prelude::*; 52 /// use dioxus_primitives::switch::{Switch, SwitchThumb}; 53 /// #[component] 54 /// fn Demo() -> Element { 55 /// let mut checked = use_signal(|| false); 56 /// rsx! { 57 /// Switch { 58 /// checked: checked(), 59 /// aria_label: "Switch Demo", 60 /// SwitchThumb {} 61 /// } 62 /// } 63 /// } 64 /// ``` 65 /// 66 /// ## Styling 67 /// 68 /// The [`Switch`] component defines the following data attributes you can use to control styling: 69 /// - `data-state`: Indicates the state of the switch. Values are `checked` or `unchecked`. 70 /// - `data-disabled`: Indicates if the switch is disabled. Values are `true` or `false`. 71 #[component] 72 pub fn Switch(props: SwitchProps) -> Element { 73 let (checked, set_checked) = use_controlled( 74 props.checked, 75 props.default_checked, 76 props.on_checked_change, 77 ); 78 79 rsx! { 80 button { 81 type: "button", 82 role: "switch", 83 value: props.value, 84 aria_checked: checked, 85 aria_required: props.required, 86 disabled: props.disabled, 87 "data-state": if checked() { "checked" } else { "unchecked" }, 88 // Only add data-disabled when actually disabled 89 "data-disabled": if (props.disabled)() { "true" } else { "false" }, 90 91 onclick: move |_| { 92 let new_checked = !checked(); 93 set_checked.call(new_checked); 94 }, 95 96 // Switches should only toggle on Space, not Enter 97 onkeydown: move |e| { 98 if e.key() == Key::Enter { 99 e.prevent_default(); 100 } 101 }, 102 103 ..props.attributes, 104 {props.children} 105 } 106 107 // Hidden input for form submission 108 input { 109 type: "checkbox", 110 aria_hidden: true, 111 tabindex: -1, 112 name: props.name, 113 value: props.value, 114 checked, 115 disabled: props.disabled, 116 style: "transform: translateX(-100%); position: absolute; pointer-events: none; opacity: 0; margin: 0; width: 0; height: 0;", 117 } 118 } 119 } 120 121 /// The props for the [`SwitchThumb`] component. 122 #[derive(Props, Clone, PartialEq)] 123 pub struct SwitchThumbProps { 124 /// Additional attributes to apply to the thumb element. 125 #[props(extends = GlobalAttributes)] 126 pub attributes: Vec<Attribute>, 127 /// The children of the thumb component. 128 pub children: Element, 129 } 130 131 /// # SwitchThumb 132 /// 133 /// The `SwitchThumb` component represents the visual thumb indicator that moves when the switch is toggled. 134 /// 135 /// This must be used inside a [`Switch`] component. 136 /// 137 /// ## Example 138 /// 139 /// ```rust 140 /// 141 /// use dioxus::prelude::*; 142 /// use dioxus_primitives::switch::{Switch, SwitchThumb}; 143 /// #[component] 144 /// fn Demo() -> Element { 145 /// let mut checked = use_signal(|| false); 146 /// rsx! { 147 /// Switch { 148 /// checked: checked(), 149 /// aria_label: "Switch Demo", 150 /// SwitchThumb {} 151 /// } 152 /// } 153 /// } 154 /// ``` 155 #[component] 156 pub fn SwitchThumb(props: SwitchThumbProps) -> Element { 157 rsx! { 158 span { ..props.attributes, {props.children} } 159 } 160 }