Skip to main content

shadow_design/
card.rs

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}