css-style 0.14.1

Typed CSS Style
Documentation
use crate::{color::Color, unit::*, StyleUpdater};
use derive_rich::Rich;

/// ```
/// use css_style::{prelude::*, unit::px, color, border::BorderStyle};
///
/// style()
///     .and_border(|conf| {
///         conf.solid() // or .style(BorderStyle::Solid)
///             .width(px(2))
///             .color(color::named::DIMGRAY)
///             .radius(px(4))
///     });
/// ```
// TODO: add shadow
#[derive(Rich, Clone, Debug, PartialEq, Default)]
pub struct Border {
    #[rich(write(rename = left), write(style = compose))]
    pub left: Side,
    #[rich(write(rename = top), write(style = compose))]
    pub top: Side,
    #[rich(write(rename = right), write(style = compose))]
    pub right: Side,
    #[rich(write(rename = bottom), write(style = compose))]
    pub bottom: Side,
    #[rich(write(rename = top_left), write(option, rename = try_top_left))]
    pub top_left: Option<Radius>,
    #[rich(write(rename = top_right), write(option, rename = try_top_right))]
    pub top_right: Option<Radius>,
    #[rich(write(rename = bottom_left), write(option, rename = try_bottom_left))]
    pub bottom_left: Option<Radius>,
    #[rich(write(rename = bottom_right), write(option, rename = try_bottom_right))]
    pub bottom_right: Option<Radius>,
}

impl StyleUpdater for Border {
    fn update_style(self, style: crate::Style) -> crate::Style {
        style
            // left side
            .try_insert("border-left-color", self.left.color)
            .try_insert("border-left-width", self.left.width)
            .try_insert("border-left-style", self.left.style)
            // top side
            .try_insert("border-top-color", self.top.color)
            .try_insert("border-top-width", self.top.width)
            .try_insert("border-top-style", self.top.style)
            // right side
            .try_insert("border-right-color", self.right.color)
            .try_insert("border-right-width", self.right.width)
            .try_insert("border-right-style", self.right.style)
            // bottom side
            .try_insert("border-bottom-color", self.bottom.color)
            .try_insert("border-bottom-width", self.bottom.width)
            .try_insert("border-bottom-style", self.bottom.style)
            // radius
            .try_insert("border-top-left-radius", self.top_left)
            .try_insert("border-top-right-radius", self.top_right)
            .try_insert("border-bottom-left-radius", self.bottom_left)
            .try_insert("border-bottom-right-radius", self.bottom_right)
    }
}

impl<T: Into<Color>> From<T> for Border {
    fn from(source: T) -> Self {
        Self::default().color(source.into())
    }
}

impl From<Option<Border>> for Border {
    fn from(source: Option<Border>) -> Self {
        match source {
            Some(border) => border,
            None => Border::default().none(),
        }
    }
}

impl From<Radius> for Border {
    fn from(source: Radius) -> Self {
        Self::default().radius(source)
    }
}

macro_rules! sides_style_shortcut_functions {
    ( $( $fn:ident() $(,)? )* ) => {
        $(
            pub fn $fn(self) -> Self {
                self.all_side(|side| side.$fn())
            }
        )*
    }
}

impl Border {
    pub fn all_side(self, value: impl Fn(Side) -> Side + Clone) -> Self {
        self.and_left(value.clone())
            .and_top(value.clone())
            .and_right(value.clone())
            .and_bottom(value)
    }

    pub fn style(self, style: impl Into<BorderStyle>) -> Self {
        let style = style.into();
        self.all_side(|side| side.style(style))
    }

    pub fn width(self, width: impl Into<Width>) -> Self {
        let width = width.into();
        self.all_side(|side| side.width(width.clone()))
    }

    pub fn color(self, color: impl Into<Color>) -> Self {
        let color = color.into();
        self.all_side(|side| side.color(color))
    }

    pub fn transparent(self) -> Self {
        self.color(Color::Transparent)
    }

    pub fn radius(self, rad: impl Into<Radius>) -> Self {
        let rad = rad.into();
        self.top_left(rad.clone())
            .top_right(rad.clone())
            .bottom_left(rad.clone())
            .bottom_right(rad)
    }

    pub fn top_radius(self, rad: impl Into<Radius>) -> Self {
        let rad = rad.into();
        self.top_left(rad.clone()).top_right(rad.clone())
    }

    pub fn right_radius(self, rad: impl Into<Radius>) -> Self {
        let rad = rad.into();
        self.bottom_right(rad.clone()).top_right(rad.clone())
    }

    pub fn bottom_radius(self, rad: impl Into<Radius>) -> Self {
        let rad = rad.into();
        self.bottom_right(rad.clone()).bottom_left(rad.clone())
    }

    pub fn left_radius(self, rad: impl Into<Radius>) -> Self {
        let rad = rad.into();
        self.bottom_left(rad.clone()).top_left(rad.clone())
    }

    sides_style_shortcut_functions! {
        none(), hidden(), dotted(), dashed(), solid(), double(),
        groove(), ridge(), inset(), outset(),
    }
}

#[derive(Rich, Clone, Debug, PartialEq, From, Default)]
pub struct Side {
    #[rich(write(rename = style), write(option, rename = try_style), value_fns = {
        none = BorderStyle::None,
        hidden = BorderStyle::Hidden,
        dotted = BorderStyle::Dotted,
        dashed = BorderStyle::Dashed,
        solid = BorderStyle::Solid,
        double = BorderStyle::Double,
        groove = BorderStyle::Groove,
        ridge = BorderStyle::Ridge,
        inset = BorderStyle::Inset,
        outset = BorderStyle::Outset,
        initial_style = BorderStyle::Initial,
        inherit_style = BorderStyle::Inherit,
    })]
    pub style: Option<BorderStyle>,
    #[rich(write(rename = width), write(option, rename = try_width), value_fns = {
        thick = Width::Thick,
        thin = Width::Thin,
        medium = Width::Medium,
        initial_width = Width::Initial,
        inherit_width = Width::Inherit,
    })]
    pub width: Option<Width>,
    #[rich(write(rename = color), write(option, rename = try_color))]
    pub color: Option<Color>,
}

#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum BorderStyle {
    #[display(fmt = "none")]
    None,
    #[display(fmt = "hidden")]
    Hidden,
    #[display(fmt = "dotted")]
    Dotted,
    #[display(fmt = "dashed")]
    Dashed,
    #[display(fmt = "solid")]
    Solid,
    #[display(fmt = "double")]
    Double,
    #[display(fmt = "groove")]
    Groove,
    #[display(fmt = "ridge")]
    Ridge,
    #[display(fmt = "inset")]
    Inset,
    #[display(fmt = "outset")]
    Outset,
    #[display(fmt = "initial")]
    Initial,
    #[display(fmt = "inherit")]
    Inherit,
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Width {
    Length(Length),
    #[display(fmt = "thin")]
    Thin,
    #[display(fmt = "medium")]
    Medium,
    #[display(fmt = "thick")]
    Thick,
    #[display(fmt = "initial")]
    Initial,
    #[display(fmt = "inherit")]
    Inherit,
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Radius {
    #[from]
    Length(Length),
    #[from(forward)]
    Percent(Percent),
    #[display(fmt = "initial")]
    Initial,
    #[display(fmt = "inherit")]
    Inherit,
}