/ libs / primitives / src / switch.rs
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  }