rustyle_css/components/
alert.rs

1//! Alert component styles
2//!
3//! Provides type-safe alert styling with variants and sizes.
4
5use super::{ComponentStyle, Size, Variant};
6use crate::css::{Color, Radius, Spacing};
7use crate::tokens::{BorderTokens, ColorTokens, SpacingTokens};
8
9/// Alert style configuration
10#[derive(Clone, Debug)]
11pub struct AlertStyle {
12    pub variant: Variant,
13    pub size: Size,
14    pub tokens: Option<AlertTokens>,
15}
16
17/// Alert design tokens
18#[derive(Clone, Debug)]
19pub struct AlertTokens {
20    pub colors: ColorTokens,
21    pub spacing: SpacingTokens,
22    pub borders: BorderTokens,
23}
24
25impl AlertStyle {
26    /// Create a new alert style
27    pub fn new(variant: Variant, size: Size) -> Self {
28        Self {
29            variant,
30            size,
31            tokens: None,
32        }
33    }
34
35    /// Set custom tokens
36    pub fn tokens(mut self, tokens: AlertTokens) -> Self {
37        self.tokens = Some(tokens);
38        self
39    }
40
41    fn background_color(&self) -> Color {
42        let default_colors = ColorTokens::default();
43        let colors = self
44            .tokens
45            .as_ref()
46            .map(|t| &t.colors)
47            .unwrap_or(&default_colors);
48
49        match &self.variant {
50            Variant::Primary => colors.primary.c50.clone(),
51            Variant::Secondary => colors.secondary.c50.clone(),
52            Variant::Success => colors.semantic.success.clone(),
53            Variant::Error => colors.semantic.error.clone(),
54            Variant::Warning => colors.semantic.warning.clone(),
55            Variant::Info => colors.semantic.info.clone(),
56        }
57    }
58
59    fn border_color(&self) -> Color {
60        let default_colors = ColorTokens::default();
61        let colors = self
62            .tokens
63            .as_ref()
64            .map(|t| &t.colors)
65            .unwrap_or(&default_colors);
66
67        match &self.variant {
68            Variant::Primary => colors.primary.c200.clone(),
69            Variant::Secondary => colors.secondary.c200.clone(),
70            Variant::Success => colors.semantic.success.clone(),
71            Variant::Error => colors.semantic.error.clone(),
72            Variant::Warning => colors.semantic.warning.clone(),
73            Variant::Info => colors.semantic.info.clone(),
74        }
75    }
76
77    fn text_color(&self) -> Color {
78        let default_colors = ColorTokens::default();
79        let colors = self
80            .tokens
81            .as_ref()
82            .map(|t| &t.colors)
83            .unwrap_or(&default_colors);
84
85        match &self.variant {
86            Variant::Primary => colors.primary.c900.clone(),
87            Variant::Secondary => colors.secondary.c900.clone(),
88            Variant::Success | Variant::Error | Variant::Warning | Variant::Info => {
89                colors.text.inverse.clone()
90            }
91        }
92    }
93
94    fn padding(&self) -> Spacing {
95        let default_spacing = SpacingTokens::default();
96        let spacing = self
97            .tokens
98            .as_ref()
99            .map(|t| &t.spacing)
100            .unwrap_or(&default_spacing);
101
102        match &self.size {
103            Size::Small => Spacing::all(spacing.sm.clone()),
104            Size::Medium => Spacing::all(spacing.md.clone()),
105            Size::Large => Spacing::all(spacing.lg.clone()),
106            Size::ExtraLarge => Spacing::all(spacing.xl.clone()),
107        }
108    }
109
110    fn border_radius(&self) -> Radius {
111        let default_borders = BorderTokens::default();
112        let borders = self
113            .tokens
114            .as_ref()
115            .map(|t| &t.borders)
116            .unwrap_or(&default_borders);
117
118        match &self.size {
119            Size::Small => Radius::all(borders.radius.sm.clone()),
120            Size::Medium => Radius::all(borders.radius.base.clone()),
121            Size::Large => Radius::all(borders.radius.lg.clone()),
122            Size::ExtraLarge => Radius::all(borders.radius.xl.clone()),
123        }
124    }
125}
126
127impl ComponentStyle for AlertStyle {
128    fn to_css(&self) -> String {
129        let mut css = String::new();
130
131        css.push_str(&format!(
132            "background-color: {}; ",
133            self.background_color().to_css()
134        ));
135        css.push_str(&format!(
136            "border: 1px solid {}; ",
137            self.border_color().to_css()
138        ));
139        css.push_str(&format!("border-left-width: 4px; "));
140        css.push_str(&format!("color: {}; ", self.text_color().to_css()));
141        css.push_str(&format!("padding: {}; ", self.padding().to_css()));
142        css.push_str(&format!(
143            "border-radius: {}; ",
144            self.border_radius().to_css()
145        ));
146        css.push_str("display: flex; ");
147        css.push_str("align-items: flex-start; ");
148        css.push_str("gap: 0.75rem; ");
149        css.push_str("margin-bottom: 1rem; ");
150
151        css
152    }
153
154    fn class_name(&self) -> &str {
155        "rustyle-alert"
156    }
157}
158
159impl Default for AlertStyle {
160    fn default() -> Self {
161        Self::new(Variant::Info, Size::Medium)
162    }
163}