Skip to main content

euv_ui/component/switch/view/
struct.rs

1use super::*;
2
3/// Props for the `euv_switch` component.
4///
5/// Defines the strongly-typed interface for a toggle switch. The checked
6/// state is owned by the caller through a `Signal<bool>` so the switch can
7/// participate in two-way binding with the rest of the page.
8#[derive(Clone, CustomDebug, Data, Default, New)]
9pub struct EuvSwitchProps {
10    /// The unique identifier for the switch element.
11    #[get(type(copy))]
12    pub id: &'static str,
13    /// The HTML name attribute shared by switches in the same form group.
14    #[get(type(copy))]
15    pub name: &'static str,
16    /// The boolean signal bound to the switch checked state.
17    #[get(type(copy))]
18    pub checked: Signal<bool>,
19    /// The label text displayed next to the switch.
20    #[get(type(copy))]
21    pub label: &'static str,
22    /// Whether the switch is disabled.
23    #[get(type(copy))]
24    pub disabled: Signal<bool>,
25}
26
27/// The reactive state handle for the `euv_switch` component.
28///
29/// Registered against the current hook context slot via
30/// [`HookContext::use_hook`], so every render at the same hook index
31/// observes the same `Signal<bool>` and the switch keeps its state
32/// across re-renders.
33#[derive(Clone, Copy, CustomDebug, Data, Default)]
34pub struct EuvSwitchState {
35    /// The signal holding the switch checked state.
36    #[get(type(copy))]
37    pub checked: Signal<bool>,
38}
39
40impl EuvSwitchState {
41    /// Creates a state handle for the given checked signal.
42    ///
43    /// The handle is a thin wrapper: it never owns the signal, it only
44    /// gives the view a place to hang [`Self::toggle`] and
45    /// [`Self::on_toggle`] without threading bare signals around.
46    ///
47    /// # Arguments
48    ///
49    /// - `Signal<bool>` - The checked-state signal to wrap.
50    ///
51    /// # Returns
52    ///
53    /// - `EuvSwitchState` - The state handle.
54    pub fn new(checked: Signal<bool>) -> Self {
55        Self { checked }
56    }
57
58    /// Flips the checked state of the switch.
59    ///
60    /// The click handler is a no-op while the switch is disabled, so a
61    /// disabled switch can never be flipped by a stray click.
62    pub fn toggle(&self) {
63        let current: bool = self.get_checked().get();
64        self.get_checked().set(!current);
65    }
66
67    /// Builds a click handler that toggles the switch.
68    ///
69    /// # Arguments
70    ///
71    /// - `Signal<bool>` - The disabled signal consulted on every click.
72    ///
73    /// # Returns
74    ///
75    /// - `Option<Rc<dyn Fn(Event)>>` - A click handler toggling the
76    ///   checked signal, or ignoring the event while disabled.
77    pub fn on_toggle(&self, disabled: Signal<bool>) -> Option<Rc<dyn Fn(Event)>> {
78        let state: EuvSwitchState = *self;
79        Some(Rc::new(move |_: Event| {
80            if disabled.get() {
81                return;
82            }
83            state.toggle();
84        }))
85    }
86}
87
88/// Obtains the switch state handle registered against the current hook
89/// context slot.
90///
91/// Behaves like [`HookContext::use_hook`] — the same handle is returned
92/// on every render at the same hook index, so the captured signal
93/// survives re-renders. The factory is used directly when no hook
94/// context is active (e.g. when called outside a render cycle).
95///
96/// # Arguments
97///
98/// - `Signal<bool>` - The checked-state signal owned by the caller.
99///
100/// # Returns
101///
102/// - `EuvSwitchState` - The switch state handle.
103pub fn use_euv_switch_state(checked: Signal<bool>) -> EuvSwitchState {
104    HookContext::use_hook(move || EuvSwitchState::new(checked))
105}