css_style/
border.rs

1use crate::{color::Color, unit::*, StyleUpdater};
2use derive_rich::Rich;
3
4/// ```
5/// use css_style::{prelude::*, unit::px, color, border::BorderStyle};
6///
7/// style()
8///     .and_border(|conf| {
9///         conf.solid() // or .style(BorderStyle::Solid)
10///             .width(px(2))
11///             .color(color::named::DIMGRAY)
12///             .radius(px(4))
13///     });
14/// ```
15// TODO: add shadow
16#[derive(Rich, Clone, Debug, PartialEq, Default)]
17pub struct Border {
18    #[rich(write(rename = left), write(style = compose))]
19    pub left: Side,
20    #[rich(write(rename = top), write(style = compose))]
21    pub top: Side,
22    #[rich(write(rename = right), write(style = compose))]
23    pub right: Side,
24    #[rich(write(rename = bottom), write(style = compose))]
25    pub bottom: Side,
26    #[rich(write(rename = top_left), write(option, rename = try_top_left))]
27    pub top_left: Option<Radius>,
28    #[rich(write(rename = top_right), write(option, rename = try_top_right))]
29    pub top_right: Option<Radius>,
30    #[rich(write(rename = bottom_left), write(option, rename = try_bottom_left))]
31    pub bottom_left: Option<Radius>,
32    #[rich(write(rename = bottom_right), write(option, rename = try_bottom_right))]
33    pub bottom_right: Option<Radius>,
34}
35
36impl StyleUpdater for Border {
37    fn update_style(self, style: crate::Style) -> crate::Style {
38        style
39            // left side
40            .try_insert("border-left-color", self.left.color)
41            .try_insert("border-left-width", self.left.width)
42            .try_insert("border-left-style", self.left.style)
43            // top side
44            .try_insert("border-top-color", self.top.color)
45            .try_insert("border-top-width", self.top.width)
46            .try_insert("border-top-style", self.top.style)
47            // right side
48            .try_insert("border-right-color", self.right.color)
49            .try_insert("border-right-width", self.right.width)
50            .try_insert("border-right-style", self.right.style)
51            // bottom side
52            .try_insert("border-bottom-color", self.bottom.color)
53            .try_insert("border-bottom-width", self.bottom.width)
54            .try_insert("border-bottom-style", self.bottom.style)
55            // radius
56            .try_insert("border-top-left-radius", self.top_left)
57            .try_insert("border-top-right-radius", self.top_right)
58            .try_insert("border-bottom-left-radius", self.bottom_left)
59            .try_insert("border-bottom-right-radius", self.bottom_right)
60    }
61}
62
63impl<T: Into<Color>> From<T> for Border {
64    fn from(source: T) -> Self {
65        Self::default().color(source.into())
66    }
67}
68
69impl From<Option<Border>> for Border {
70    fn from(source: Option<Border>) -> Self {
71        match source {
72            Some(border) => border,
73            None => Border::default().none(),
74        }
75    }
76}
77
78impl From<Radius> for Border {
79    fn from(source: Radius) -> Self {
80        Self::default().radius(source)
81    }
82}
83
84macro_rules! sides_style_shortcut_functions {
85    ( $( $fn:ident() $(,)? )* ) => {
86        $(
87            pub fn $fn(self) -> Self {
88                self.all_side(|side| side.$fn())
89            }
90        )*
91    }
92}
93
94impl Border {
95    pub fn all_side(self, value: impl Fn(Side) -> Side + Clone) -> Self {
96        self.and_left(value.clone())
97            .and_top(value.clone())
98            .and_right(value.clone())
99            .and_bottom(value)
100    }
101
102    pub fn style(self, style: impl Into<BorderStyle>) -> Self {
103        let style = style.into();
104        self.all_side(|side| side.style(style))
105    }
106
107    pub fn width(self, width: impl Into<Width>) -> Self {
108        let width = width.into();
109        self.all_side(|side| side.width(width.clone()))
110    }
111
112    pub fn color(self, color: impl Into<Color>) -> Self {
113        let color = color.into();
114        self.all_side(|side| side.color(color))
115    }
116
117    pub fn transparent(self) -> Self {
118        self.color(Color::Transparent)
119    }
120
121    pub fn radius(self, rad: impl Into<Radius>) -> Self {
122        let rad = rad.into();
123        self.top_left(rad.clone())
124            .top_right(rad.clone())
125            .bottom_left(rad.clone())
126            .bottom_right(rad)
127    }
128
129    pub fn top_radius(self, rad: impl Into<Radius>) -> Self {
130        let rad = rad.into();
131        self.top_left(rad.clone()).top_right(rad.clone())
132    }
133
134    pub fn right_radius(self, rad: impl Into<Radius>) -> Self {
135        let rad = rad.into();
136        self.bottom_right(rad.clone()).top_right(rad.clone())
137    }
138
139    pub fn bottom_radius(self, rad: impl Into<Radius>) -> Self {
140        let rad = rad.into();
141        self.bottom_right(rad.clone()).bottom_left(rad.clone())
142    }
143
144    pub fn left_radius(self, rad: impl Into<Radius>) -> Self {
145        let rad = rad.into();
146        self.bottom_left(rad.clone()).top_left(rad.clone())
147    }
148
149    sides_style_shortcut_functions! {
150        none(), hidden(), dotted(), dashed(), solid(), double(),
151        groove(), ridge(), inset(), outset(),
152    }
153}
154
155#[derive(Rich, Clone, Debug, PartialEq, From, Default)]
156pub struct Side {
157    #[rich(write(rename = style), write(option, rename = try_style), value_fns = {
158        none = BorderStyle::None,
159        hidden = BorderStyle::Hidden,
160        dotted = BorderStyle::Dotted,
161        dashed = BorderStyle::Dashed,
162        solid = BorderStyle::Solid,
163        double = BorderStyle::Double,
164        groove = BorderStyle::Groove,
165        ridge = BorderStyle::Ridge,
166        inset = BorderStyle::Inset,
167        outset = BorderStyle::Outset,
168        initial_style = BorderStyle::Initial,
169        inherit_style = BorderStyle::Inherit,
170    })]
171    pub style: Option<BorderStyle>,
172    #[rich(write(rename = width), write(option, rename = try_width), value_fns = {
173        thick = Width::Thick,
174        thin = Width::Thin,
175        medium = Width::Medium,
176        initial_width = Width::Initial,
177        inherit_width = Width::Inherit,
178    })]
179    pub width: Option<Width>,
180    #[rich(write(rename = color), write(option, rename = try_color))]
181    pub color: Option<Color>,
182}
183
184#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
185pub enum BorderStyle {
186    #[display(fmt = "none")]
187    None,
188    #[display(fmt = "hidden")]
189    Hidden,
190    #[display(fmt = "dotted")]
191    Dotted,
192    #[display(fmt = "dashed")]
193    Dashed,
194    #[display(fmt = "solid")]
195    Solid,
196    #[display(fmt = "double")]
197    Double,
198    #[display(fmt = "groove")]
199    Groove,
200    #[display(fmt = "ridge")]
201    Ridge,
202    #[display(fmt = "inset")]
203    Inset,
204    #[display(fmt = "outset")]
205    Outset,
206    #[display(fmt = "initial")]
207    Initial,
208    #[display(fmt = "inherit")]
209    Inherit,
210}
211
212#[derive(Clone, Debug, PartialEq, Display, From)]
213pub enum Width {
214    Length(Length),
215    #[display(fmt = "thin")]
216    Thin,
217    #[display(fmt = "medium")]
218    Medium,
219    #[display(fmt = "thick")]
220    Thick,
221    #[display(fmt = "initial")]
222    Initial,
223    #[display(fmt = "inherit")]
224    Inherit,
225}
226
227#[derive(Clone, Debug, PartialEq, Display, From)]
228pub enum Radius {
229    #[from]
230    Length(Length),
231    #[from(forward)]
232    Percent(Percent),
233    #[display(fmt = "initial")]
234    Initial,
235    #[display(fmt = "inherit")]
236    Inherit,
237}