Skip to main content

shadow_design/
checkbox.rs

1use crate::icon::{Icon, IconKind};
2use dioxus::prelude::*;
3
4#[derive(Clone, Copy, PartialEq, Debug, Default)]
5pub enum CheckboxSize {
6    Sm,
7    #[default]
8    Md,
9    Lg,
10}
11
12impl CheckboxSize {
13    pub fn scss_class(&self) -> &'static str {
14        match self {
15            CheckboxSize::Sm => "sd-checkbox__box--sm",
16            CheckboxSize::Md => "sd-checkbox__box--md",
17            CheckboxSize::Lg => "sd-checkbox__box--lg",
18        }
19    }
20
21    pub fn icon_size(&self) -> u32 {
22        match self {
23            CheckboxSize::Sm => 11,
24            CheckboxSize::Md => 13,
25            CheckboxSize::Lg => 16,
26        }
27    }
28}
29
30#[component]
31pub fn Checkbox(
32    checked: Signal<bool>,
33    #[props(default)] label: Option<String>,
34    #[props(default)] description: Option<String>,
35    #[props(default)] disabled: bool,
36    #[props(default)] error: Option<String>,
37    #[props(default)] indeterminate: bool,
38    #[props(default)] size: CheckboxSize,
39    #[props(default)] glow: Option<bool>,
40    #[props(default)] class: String,
41) -> Element {
42    let is_checked = *checked.read();
43
44    #[cfg(feature = "theme")]
45    let theme = crate::theme::use_theme();
46    #[cfg(feature = "theme")]
47    let active_color_class = match theme().primary_color {
48        crate::theme::PrimaryColor::Violet => "sd-checkbox__box--checked",
49        crate::theme::PrimaryColor::Obsidian => "sd-checkbox__box--checked-obsidian",
50        crate::theme::PrimaryColor::Crimson => "sd-checkbox__box--checked-crimson",
51        crate::theme::PrimaryColor::Cyan => "sd-checkbox__box--checked-cyan",
52        crate::theme::PrimaryColor::Emerald => "sd-checkbox__box--checked-emerald",
53    };
54    #[cfg(feature = "theme")]
55    let glow_class = match glow {
56        Some(true) => "sd-checkbox__box--glow",
57        Some(false) => "",
58        None => {
59            if theme().glow_mode != crate::theme::GlowMode::Off && (is_checked || indeterminate) {
60                "sd-checkbox__box--glow"
61            } else {
62                ""
63            }
64        }
65    };
66
67    #[cfg(not(feature = "theme"))]
68    let active_color_class = "sd-checkbox__box--checked";
69    #[cfg(not(feature = "theme"))]
70    let glow_class = if is_checked || indeterminate {
71        "sd-checkbox__box--glow"
72    } else {
73        ""
74    };
75
76    let checked_class = if is_checked || indeterminate {
77        active_color_class
78    } else {
79        ""
80    };
81
82    let error_class = if error.is_some() {
83        "sd-checkbox__box--error"
84    } else {
85        ""
86    };
87
88    let box_size = size.scss_class();
89    let is_disabled = disabled;
90    let disabled_class = if is_disabled {
91        "sd-checkbox--disabled"
92    } else {
93        ""
94    };
95
96    rsx! {
97        div {
98            class: "sd-checkbox {disabled_class} {class}",
99            onclick: move |_| {
100                if !is_disabled {
101                    let next = !*checked.read();
102                    *checked.write() = next;
103                }
104            },
105            div {
106                class: "sd-checkbox__box {box_size} {checked_class} {glow_class} {error_class}",
107                if indeterminate {
108                    span { style: "display: block; width: 10px; height: 2px; background: currentColor; border-radius: 9999px;" }
109                } else if is_checked {
110                    Icon { kind: IconKind::Check, size: size.icon_size() }
111                }
112            }
113            if label.is_some() || description.is_some() || error.is_some() {
114                div { class: "sd-checkbox__label",
115                    if let Some(lbl) = label {
116                        span { class: "sd-checkbox__title", "{lbl}" }
117                    }
118                    if let Some(desc) = description {
119                        span { class: "sd-checkbox__description", "{desc}" }
120                    }
121                    if let Some(err) = error {
122                        span { class: "sd-checkbox__error-text", "{err}" }
123                    }
124                }
125            }
126        }
127    }
128}