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_val = crate::theme::use_theme()();
46 #[cfg(feature = "theme")]
47 let active_color_class = match &theme_val.primary_color {
48 crate::theme::PrimaryColor::Violet => "sd-checkbox__box--checked".to_string(),
49 crate::theme::PrimaryColor::Obsidian => "sd-checkbox__box--checked-obsidian".to_string(),
50 crate::theme::PrimaryColor::Crimson => "sd-checkbox__box--checked-crimson".to_string(),
51 crate::theme::PrimaryColor::Cyan => "sd-checkbox__box--checked-cyan".to_string(),
52 crate::theme::PrimaryColor::Emerald => "sd-checkbox__box--checked-emerald".to_string(),
53 crate::theme::PrimaryColor::Custom { .. } => {
54 format!("sd-checkbox__box--checked-{}", theme_val.primary_color.css_prefix())
55 }
56 };
57 #[cfg(feature = "theme")]
58 let glow_class = if is_checked || indeterminate {
59 theme_val.resolve_elevation_class("checkbox", glow)
60 } else {
61 String::new()
62 };
63 #[cfg(feature = "theme")]
64 let base_override = theme_val.resolve_component_base("checkbox").unwrap_or("");
65 #[cfg(feature = "theme")]
66 let class_modifiers = theme_val.resolve_class_modifiers("checkbox", &class);
67
68 #[cfg(not(feature = "theme"))]
69 let base_override = "";
70 #[cfg(not(feature = "theme"))]
71 let class_modifiers = String::new();
72 #[cfg(not(feature = "theme"))]
73 let active_color_class = "sd-checkbox__box--checked".to_string();
74 #[cfg(not(feature = "theme"))]
75 let glow_class = if glow == Some(true) || (glow.is_none() && (is_checked || indeterminate)) {
76 "sd-checkbox__box--glow".to_string()
77 } else {
78 String::new()
79 };
80
81 let checked_class = if is_checked || indeterminate {
82 active_color_class.as_str()
83 } else {
84 ""
85 };
86
87 let error_class = if error.is_some() {
88 "sd-checkbox__box--error"
89 } else {
90 ""
91 };
92
93 let box_size = size.scss_class();
94 let is_disabled = disabled;
95 let disabled_class = if is_disabled {
96 "sd-checkbox--disabled"
97 } else {
98 ""
99 };
100
101 rsx! {
102 div {
103 class: "sd-checkbox {base_override} {disabled_class} {class_modifiers} {class}",
104 onclick: move |_| {
105 if !is_disabled {
106 let next = !*checked.read();
107 *checked.write() = next;
108 }
109 },
110 div {
111 class: "sd-checkbox__box {box_size} {checked_class} {glow_class} {error_class}",
112 if indeterminate {
113 span { style: "display: block; width: 10px; height: 2px; background: currentColor; border-radius: 9999px;" }
114 } else if is_checked {
115 Icon { kind: IconKind::Check, size: size.icon_size() }
116 }
117 }
118 if label.is_some() || description.is_some() || error.is_some() {
119 div { class: "sd-checkbox__label",
120 if let Some(lbl) = label {
121 span { class: "sd-checkbox__title", "{lbl}" }
122 }
123 if let Some(desc) = description {
124 span { class: "sd-checkbox__description", "{desc}" }
125 }
126 if let Some(err) = error {
127 span { class: "sd-checkbox__error-text", "{err}" }
128 }
129 }
130 }
131 }
132 }
133}