shadow_design/
checkbox.rs1use 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}