1use dioxus::prelude::*;
2
3#[derive(Clone, Copy, PartialEq, Debug, Default)]
4pub enum CardShadow {
5 None,
6 Sm,
7 #[default]
8 Md,
9 Lg,
10 Xl,
11}
12
13impl CardShadow {
14 pub fn scss_class(&self) -> &'static str {
15 match self {
16 CardShadow::None => "sd-card--shadow-none",
17 CardShadow::Sm => "sd-card--shadow-sm",
18 CardShadow::Md => "sd-card--shadow-md",
19 CardShadow::Lg => "sd-card--shadow-lg",
20 CardShadow::Xl => "sd-card--shadow-xl",
21 }
22 }
23}
24
25#[derive(Clone, Copy, PartialEq, Debug, Default)]
26pub enum CardVariant {
27 #[default]
28 Default,
29 Elevated,
30 Interactive,
31 Metric,
32}
33
34impl CardVariant {
35 pub fn scss_class(&self) -> &'static str {
36 match self {
37 CardVariant::Default => "",
38 CardVariant::Elevated => "sd-card--elevated",
39 CardVariant::Interactive => "sd-card--interactive",
40 CardVariant::Metric => "sd-card--metric",
41 }
42 }
43}
44
45#[component]
46pub fn Card(
47 #[props(default)] title: Option<String>,
48 #[props(default)] subtitle: Option<String>,
49 #[props(default)] variant: CardVariant,
50 #[props(default)] shadow: CardShadow,
51 #[props(default)] hover_glow: bool,
52 #[props(default)] glow: Option<bool>,
53 #[props(default = false)] overflow_hidden: bool,
54 #[props(default)] class: String,
55 #[props(default)] footer: Option<Element>,
56 children: Element,
57) -> Element {
58 let variant_class = variant.scss_class();
59 let shadow_class = shadow.scss_class();
60 let overflow_class = if overflow_hidden {
61 "sd-card--overflow-hidden"
62 } else {
63 ""
64 };
65
66 #[cfg(feature = "theme")]
67 let theme_val = crate::theme::use_theme()();
68 #[cfg(feature = "theme")]
69 let radius_class = theme_val.radius_scale.md();
70 #[cfg(feature = "theme")]
71 let base_override = theme_val.resolve_component_base("card").unwrap_or("");
72 #[cfg(feature = "theme")]
73 let class_modifiers = theme_val.resolve_class_modifiers("card", &class);
74 #[cfg(feature = "theme")]
75 let glow_class = match glow {
76 Some(true) => match &theme_val.primary_color {
77 crate::theme::PrimaryColor::Violet => "sd-card--glow-violet".to_string(),
78 crate::theme::PrimaryColor::Obsidian => "sd-card--glow-obsidian".to_string(),
79 crate::theme::PrimaryColor::Crimson => "sd-card--glow-crimson".to_string(),
80 crate::theme::PrimaryColor::Cyan => "sd-card--glow-cyan".to_string(),
81 crate::theme::PrimaryColor::Emerald => "sd-card--glow-emerald".to_string(),
82 crate::theme::PrimaryColor::Custom { .. } => {
83 format!("sd-card--glow-{}", theme_val.primary_color.css_prefix())
84 }
85 },
86 Some(false) => "".to_string(),
87 None => {
88 if hover_glow
89 || (theme_val.glow_mode != crate::theme::GlowMode::Off
90 && variant == CardVariant::Interactive)
91 {
92 theme_val.resolve_elevation_class("card", None)
93 } else {
94 "".to_string()
95 }
96 }
97 };
98
99 #[cfg(not(feature = "theme"))]
100 let base_override = "";
101 #[cfg(not(feature = "theme"))]
102 let class_modifiers = String::new();
103 #[cfg(not(feature = "theme"))]
104 let radius_class = "";
105 #[cfg(not(feature = "theme"))]
106 let glow_class = if hover_glow || glow == Some(true) {
107 "sd-card--hover-glow".to_string()
108 } else {
109 "".to_string()
110 };
111
112 rsx! {
113 div {
114 class: "sd-card {base_override} {variant_class} {shadow_class} {radius_class} {glow_class} {overflow_class} {class_modifiers} {class}",
115 if title.is_some() || subtitle.is_some() {
116 div { class: "sd-card__header",
117 if let Some(t) = title {
118 h3 { class: "sd-card__title", "{t}" }
119 }
120 if let Some(s) = subtitle {
121 p { class: "sd-card__subtitle", "{s}" }
122 }
123 }
124 }
125 div { class: "sd-card__body",
126 {children}
127 }
128 if let Some(f) = footer {
129 div { class: "sd-card__footer",
130 {f}
131 }
132 }
133 }
134 }
135}