Skip to main content

tablo_ui/components/primitives/
switch.rs

1// SYNC: topcoat-ui-registry@0.11.0 sha256:ce9a0c7be3ac7187f0935c193bec82f8587953c8a85fdbaf29c08d9593365c36 — do not hand-edit. Sync via `cargo xtask sync-topcoat-ui` (ADR-0007).
2use topcoat::{
3    Result,
4    view::{Attributes, StaticClass, View, class, component, view},
5};
6
7/// Classes for the checkbox input that forms the switch track.
8const SWITCH: StaticClass = class!(
9    "peer h-4.5 w-8 shrink-0 appearance-none rounded-full",
10    "bg-foreground/20 shadow-xs transition-colors outline-none checked:bg-primary",
11    "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
12    "focus-visible:ring-offset-background disabled:pointer-events-none",
13);
14
15/// Classes that position the thumb at either end of the track according to the checked
16/// state.
17const THUMB: StaticClass = class!(
18    "pointer-events-none absolute top-1/2 left-0.5 size-3.5 -translate-y-1/2",
19    "rounded-full bg-background shadow-xs transition-transform peer-checked:translate-x-3.5",
20);
21
22/// An on/off control for a setting.
23///
24/// Uses a native checkbox with the `switch` role. Pass `checked` in `attrs` for the
25/// initial state. Classes apply to the wrapper, while other attributes and event
26/// handlers go on the `<input>`.
27///
28/// ```ignore
29/// view! {
30///     <div class="flex items-center gap-2">
31///         let airplane_mode = element_id(cx);
32///         switch(attrs: attributes! { id=(&airplane_mode) checked="" })
33///         label(attrs: attributes! { for=(&airplane_mode) }, "Airplane mode")
34///     </div>
35/// }
36/// ```
37#[component]
38pub async fn switch(#[default] mut attrs: Attributes) -> Result<impl View> {
39    // The thumb cannot be drawn by the `<input>` itself, which renders no
40    // children or pseudo-elements: it is a sibling overlaid on the track,
41    // slid to the far end by the input's `peer` state while checked.
42    Ok(view! {
43        <span
44            class=(class!(
45                "peer relative inline-flex shrink-0 has-[:disabled]:opacity-50",
46                attrs.remove("class"),
47            ))
48        >
49            <input type="checkbox" role="switch" class=(SWITCH) (attrs)>
50            <span class=(THUMB)></span>
51        </span>
52    })
53}