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 = crate::theme::use_theme();
68    #[cfg(feature = "theme")]
69    let radius_class = theme().radius_scale.md();
70    #[cfg(feature = "theme")]
71    let glow_class = match glow {
72        Some(true) => match theme().primary_color {
73            crate::theme::PrimaryColor::Violet => "sd-card--glow-violet",
74            crate::theme::PrimaryColor::Obsidian => "sd-card--glow-obsidian",
75            crate::theme::PrimaryColor::Crimson => "sd-card--glow-crimson",
76            crate::theme::PrimaryColor::Cyan => "sd-card--glow-cyan",
77            crate::theme::PrimaryColor::Emerald => "sd-card--glow-emerald",
78        },
79        Some(false) => "",
80        None => {
81            if hover_glow
82                || (theme().glow_mode != crate::theme::GlowMode::Off
83                    && variant == CardVariant::Interactive)
84            {
85                "sd-card--hover-glow"
86            } else {
87                ""
88            }
89        }
90    };
91
92    #[cfg(not(feature = "theme"))]
93    let radius_class = "";
94    #[cfg(not(feature = "theme"))]
95    let glow_class = if hover_glow || glow == Some(true) {
96        "sd-card--hover-glow"
97    } else {
98        ""
99    };
100
101    rsx! {
102        div {
103            class: "sd-card {variant_class} {shadow_class} {radius_class} {glow_class} {overflow_class} {class}",
104            if title.is_some() || subtitle.is_some() {
105                div { class: "sd-card__header",
106                    if let Some(t) = title {
107                        h3 { class: "sd-card__title", "{t}" }
108                    }
109                    if let Some(s) = subtitle {
110                        p { class: "sd-card__subtitle", "{s}" }
111                    }
112                }
113            }
114            div { class: "sd-card__body",
115                {children}
116            }
117            if let Some(f) = footer {
118                div { class: "sd-card__footer",
119                    {f}
120                }
121            }
122        }
123    }
124}