rustyle_css/components/
button.rs1use super::{ComponentStyle, Size, State, Variant};
6use crate::css::{Color, Radius, Spacing};
7use crate::tokens::{BorderTokens, ColorTokens, SpacingTokens};
8
9#[derive(Clone, Debug)]
11pub struct ButtonStyle {
12 pub variant: Variant,
13 pub size: Size,
14 pub state: State,
15 pub tokens: Option<ButtonTokens>,
16}
17
18#[derive(Clone, Debug)]
20pub struct ButtonTokens {
21 pub colors: ColorTokens,
22 pub spacing: SpacingTokens,
23 pub borders: BorderTokens,
24}
25
26impl ButtonStyle {
27 pub fn new(variant: Variant, size: Size) -> Self {
29 Self {
30 variant,
31 size,
32 state: State::Default,
33 tokens: None,
34 }
35 }
36
37 pub fn state(mut self, state: State) -> Self {
39 self.state = state;
40 self
41 }
42
43 pub fn tokens(mut self, tokens: ButtonTokens) -> Self {
45 self.tokens = Some(tokens);
46 self
47 }
48
49 fn background_color(&self) -> Color {
51 let default_colors = ColorTokens::default();
52 let colors = self
53 .tokens
54 .as_ref()
55 .map(|t| &t.colors)
56 .unwrap_or(&default_colors);
57
58 match (&self.variant, &self.state) {
59 (Variant::Primary, State::Default) => colors.primary.c500.clone(),
60 (Variant::Primary, State::Hover) => colors.primary.c600.clone(),
61 (Variant::Primary, State::Active) => colors.primary.c700.clone(),
62 (Variant::Primary, State::Disabled) => colors.secondary.c300.clone(),
63 (Variant::Secondary, State::Default) => colors.secondary.c500.clone(),
64 (Variant::Secondary, State::Hover) => colors.secondary.c600.clone(),
65 (Variant::Secondary, State::Active) => colors.secondary.c700.clone(),
66 (Variant::Secondary, State::Disabled) => colors.secondary.c300.clone(),
67 (Variant::Success, _) => colors.semantic.success.clone(),
68 (Variant::Error, _) => colors.semantic.error.clone(),
69 (Variant::Warning, _) => colors.semantic.warning.clone(),
70 (Variant::Info, _) => colors.semantic.info.clone(),
71 _ => colors.primary.c500.clone(),
72 }
73 }
74
75 fn text_color(&self) -> Color {
77 let default_colors = ColorTokens::default();
78 let colors = self
79 .tokens
80 .as_ref()
81 .map(|t| &t.colors)
82 .unwrap_or(&default_colors);
83
84 match &self.variant {
85 Variant::Primary | Variant::Secondary => colors.text.inverse.clone(),
86 _ => colors.text.primary.clone(),
87 }
88 }
89
90 fn padding(&self) -> Spacing {
92 let default_spacing = SpacingTokens::default();
93 let spacing = self
94 .tokens
95 .as_ref()
96 .map(|t| &t.spacing)
97 .unwrap_or(&default_spacing);
98
99 match &self.size {
100 Size::Small => Spacing::vh(spacing.sm.clone(), spacing.md.clone()),
101 Size::Medium => Spacing::vh(spacing.md.clone(), spacing.lg.clone()),
102 Size::Large => Spacing::vh(spacing.lg.clone(), spacing.xl.clone()),
103 Size::ExtraLarge => Spacing::vh(spacing.xl.clone(), spacing.xl2.clone()),
104 }
105 }
106
107 fn border_radius(&self) -> Radius {
109 let default_borders = BorderTokens::default();
110 let borders = self
111 .tokens
112 .as_ref()
113 .map(|t| &t.borders)
114 .unwrap_or(&default_borders);
115
116 match &self.size {
117 Size::Small => Radius::all(borders.radius.sm.clone()),
118 Size::Medium => Radius::all(borders.radius.base.clone()),
119 Size::Large => Radius::all(borders.radius.lg.clone()),
120 Size::ExtraLarge => Radius::all(borders.radius.xl.clone()),
121 }
122 }
123}
124
125impl ComponentStyle for ButtonStyle {
126 fn to_css(&self) -> String {
127 let mut css = String::new();
128
129 css.push_str(&format!(
130 "background-color: {}; ",
131 self.background_color().to_css()
132 ));
133 css.push_str(&format!("color: {}; ", self.text_color().to_css()));
134 css.push_str(&format!("padding: {}; ", self.padding().to_css()));
135 css.push_str(&format!(
136 "border-radius: {}; ",
137 self.border_radius().to_css()
138 ));
139 css.push_str("border: none; ");
140 css.push_str("cursor: pointer; ");
141 css.push_str("font-weight: 500; ");
142 css.push_str("transition: all 0.2s ease; ");
143
144 if self.state == State::Disabled {
145 css.push_str("opacity: 0.6; ");
146 css.push_str("cursor: not-allowed; ");
147 }
148
149 css
150 }
151
152 fn class_name(&self) -> &str {
153 "rustyle-button"
154 }
155}
156
157impl Default for ButtonStyle {
158 fn default() -> Self {
159 Self::new(Variant::Primary, Size::Medium)
160 }
161}