Skip to main content

tablo_ui/components/primitives/
checkbox.rs

1// SYNC: topcoat-ui-registry@0.10.0 sha256:e6ffefb0bf8890492b98ef8fb64385544ce9a55f5ecf505ff40e64caef259385 — do not hand-edit. Sync via `cargo xtask sync-topcoat-ui` (ADR-0007).
2use topcoat::{
3    Result,
4    icon::{icon, iconify::iconify_icon},
5    view::{Attributes, StaticClass, View, attributes, class, component, view},
6};
7
8/// Classes for the native checkbox input and its checked state.
9const CHECKBOX: StaticClass = class!(
10    "peer size-4 shrink-0 appearance-none rounded-[4px] border border-border \
11     bg-background transition-colors outline-none \
12     checked:border-primary checked:bg-primary \
13     focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 \
14     focus-visible:ring-offset-background disabled:pointer-events-none",
15);
16
17/// A styled native checkbox.
18///
19/// Pass input attributes and event handlers through `attrs`. Classes apply to the
20/// wrapper, while other attributes go on the `<input>`. Use `checked` for the initial
21/// state. The indeterminate state requires setting a DOM property and has no custom
22/// styling.
23///
24/// ```ignore
25/// view! {
26///     <div class="flex items-center gap-2">
27///         checkbox(attrs: attributes! { id="terms" name="terms" checked="" })
28///         label(attrs: attributes! { for="terms" }, "Accept terms")
29///     </div>
30/// }
31/// ```
32#[component]
33pub async fn checkbox(#[default] mut attrs: Attributes) -> Result<impl View> {
34    // The checkmark cannot be drawn by the `<input>` itself, which renders no
35    // children or pseudo-elements: it is a sibling icon overlaid on the
36    // control, revealed by the input's `peer` state while checked.
37    Ok(view! {
38        <span
39            class=(class!(
40                "peer relative inline-flex shrink-0 has-[:disabled]:opacity-50",
41                attrs.remove("class"),
42            ))
43        >
44            <input type="checkbox" class=(CHECKBOX) (attrs)>
45            icon(
46                data: iconify_icon!("lucide:check"),
47                attrs: attributes! {
48                    class="pointer-events-none absolute inset-0 m-auto size-3.5 \
49                        text-primary-foreground opacity-0 peer-checked:opacity-100"
50                }
51            )
52        </span>
53    })
54}