css-style 0.9.0

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

/// ```
/// use css_style::{Style, Color, unit::em};
/// use palette::rgb::Rgb;
///
/// Style::default()
///     // for single shadow
///     .and_box_shadow(|conf| {
///         conf.x(em(0.4))
///             .y(em(-0.8))
///             // we can pass Rgb, Rgba, Hsl, Hsla
///             .color(Rgb::new(0.5, 0.1, 0.1))
///             // or we can use HTML colors
///             .color(Color::BlueViolet)
///             // shadow blur radius
///             .blur(em(1.0))
///             // spread radius
///             .spread(em(2.0))
///             // inset shadow
///             .inset()
///     })
///     // for multiple shadows
///     .and_box_shadow(|conf| {
///         conf
///             .push(|conf| {
///                 conf.x(em(1.0))
///                     .y(em(2.0))
///                     .color(Color::DimGray)
///             })
///             .push(|conf| {
///                 conf.x(em(-2.0))
///                     .y(em(-4.0))
///                     .color(Color::Red)
///             })
///     });
/// ```
#[derive(Clone, Debug, PartialEq, From, Display)]
pub enum BoxShadow {
    One(ShadowValue),
    #[display(
        fmt = "{}",
        "_0.iter().map(|s| s.to_string()).collect::<Vec<_>>().join(\", \")"
    )]
    Multiple(Vec<ShadowValue>),
    #[display(fmt = "initial")]
    Initial,
    #[display(fmt = "inherit")]
    Inherit,
    #[display(fmt = "none")]
    None,
    #[display(fmt = "unset")]
    Unset,
}

impl Default for BoxShadow {
    fn default() -> Self {
        BoxShadow::Multiple(vec![])
    }
}

impl StyleUpdater for BoxShadow {
    fn update_style(self, style: Style) -> Style {
        style.insert("box-shadow", self)
    }
}

impl BoxShadow {
    fn shadow(mut self, conf: impl FnOnce(ShadowValue) -> ShadowValue) -> Self {
        self = match self {
            Self::One(shadow) => Self::One(conf(shadow)),
            Self::Multiple(shadows) => Self::One(conf(
                shadows
                    .into_iter()
                    .next()
                    .unwrap_or_else(ShadowValue::default),
            )),
            _ => Self::One(conf(ShadowValue::default())),
        };
        self
    }

    pub fn new() -> Self {
        BoxShadow::Multiple(vec![])
    }

    pub fn x(self, val: impl Into<Length>) -> Self {
        self.shadow(|sh| sh.x(val))
    }

    pub fn y(self, val: impl Into<Length>) -> Self {
        self.shadow(|sh| sh.y(val))
    }

    pub fn blur(self, val: impl Into<Length>) -> Self {
        self.shadow(|sh| sh.blur(val))
    }

    pub fn try_blur(self, val: Option<impl Into<Length>>) -> Self {
        self.shadow(|sh| sh.try_blur(val))
    }

    pub fn spread(self, val: impl Into<Length>) -> Self {
        self.shadow(|sh| sh.spread(val))
    }

    pub fn try_spread(self, val: Option<impl Into<Length>>) -> Self {
        self.shadow(|sh| sh.try_spread(val))
    }

    pub fn color(self, val: impl Into<Color>) -> Self {
        self.shadow(|sh| sh.color(val))
    }

    pub fn try_color(self, val: Option<impl Into<Color>>) -> Self {
        self.shadow(|sh| sh.try_color(val))
    }

    pub fn inset(self) -> Self {
        self.shadow(|sh| sh.inset())
    }

    pub fn outset(self) -> Self {
        self.shadow(|sh| sh.outset())
    }

    pub fn push(mut self, get_val: impl FnOnce(ShadowValue) -> ShadowValue) -> Self {
        let val = get_val(ShadowValue::default());
        self = match self {
            Self::Multiple(mut vec) => {
                vec.push(val);
                Self::Multiple(vec)
            }
            _ => Self::Multiple(vec![val]),
        };
        self
    }
}

#[derive(Rich, Clone, Debug, PartialEq)]
pub struct ShadowValue {
    #[rich(write)]
    x: Length,
    #[rich(write)]
    y: Length,
    #[rich(write, write(option))]
    blur: Option<Length>,
    #[rich(write, write(option))]
    spread: Option<Length>,
    #[rich(write, write(option))]
    color: Option<Color>,
    #[rich(value_fns = { inset = true, outset = false })]
    inset: bool,
}

impl fmt::Display for ShadowValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.inset {
            write!(f, "inset ")?;
        }

        write!(f, "{} {}", self.x, self.y)?;

        match (self.blur.as_ref(), self.spread.as_ref()) {
            (Some(blur), Some(spread)) => write!(f, " {} {}", blur, spread)?,
            (Some(blur), None) => write!(f, " {}", blur)?,
            // if there was no blur specified then use 0px
            (None, Some(spread)) => write!(f, " {} {}", px(0), spread)?,
            (None, None) => {}
        };

        if let Some(color) = self.color {
            write!(f, " {}", color)?;
        }

        Ok(())
    }
}

impl Default for ShadowValue {
    fn default() -> Self {
        Self {
            x: px(0),
            y: px(0),
            blur: None,
            spread: None,
            color: None,
            inset: false,
        }
    }
}