css-style 0.8.0

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

/// ```
/// use css_style::{css, Style, Color, unit::em};
///
/// Style::default()
///     .and_background(|conf| {
///         conf.image("/bg/fullpage.png")
///             .color(Color::White)
///             .scroll()
///     });
/// ```
#[derive(Rich, Clone, Debug, PartialEq, Default)]
pub struct Background {
    #[rich(write)]
    pub color: Option<Color>,
    // TODO: support multiple images
    #[rich(write, value_fns = { empty = css::None })]
    pub image: Option<Image>,
    #[rich(value_fns = {
        repeat_x = css::RepeatX,
        repeat_y = css::RepeatY,
        repeat = css::Repeat,
        repeat_with_space = css::Space,
        repeat_round = css::Round,
        no_repeat = css::NoRepeat,
        initial_repeat = css::Initial,
        inherit_repeat = css::Inherit,
    })]
    pub repeat: Option<Repeat>,
    #[rich(write(rename = attachment), value_fns = {
        scroll = css::Scroll,
        fixed = css::Fixed,
        local = css::Local,
        initial_attachment = css::Initial,
        inherit_attachment = css::Inherit,
    })]
    pub attachment: Option<Attachment>,
    #[rich(write(rename = position), value_fns = {
        left_top = (Horizontal::from(css::Left), css::Top.into()),
        center_top = (Horizontal::from(css::Center), css::Top.into()),
        right_top = (Horizontal::from(css::Right), css::Top.into()),
        left_center = (Horizontal::from(css::Left), css::Center.into()),
        center = (Horizontal::from(css::Center), css::Center.into()),
        right_center = (Horizontal::from(css::Right), css::Center.into()),
        left_bottom = (Horizontal::from(css::Left), css::Bottom.into()),
        center_bottom = (Horizontal::from(css::Center), css::Bottom.into()),
        right_bottom = (Horizontal::from(css::Right), css::Bottom.into()),
    })]
    pub position: Option<Position>,
    #[rich(write(rename = clip), value_fns = {
        fill_under_border = css::BorderBox,
        fill_inside_border = css::PaddingBox,
        fill_under_content = css::ContentBox,
    })]
    pub clip: Option<Clip>,
    #[rich(write(rename = origin), value_fns = {
        image_fill_under_border = css::BorderBox,
        image_inside_border = css::PaddingBox,
        image_under_content = css::ContentBox,
    })]
    pub origin: Option<Origin>,
    #[rich(write(rename = size), value_fns = {
        full = (1.0, 1.0),
        half = (0.5, 0.5),
        quarter = (0.25, 0.25),
        auto_size = css::Auto,
        cover = css::Cover,
        contain = css::Contain,
    })]
    pub size: Option<Size>,
}

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

impl StyleUpdater for Background {
    fn update_style(self, style: Style) -> Style {
        style
            .try_insert("background-color", self.color)
            .try_insert("background-image", self.image.as_ref())
            .try_insert("background-repeat", self.repeat)
            .try_insert("background-attachment", self.attachment)
            .try_insert("background-position", self.position)
    }
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Image {
    #[from]
    None(css::None),
    #[display(fmt = "url({})", _0)]
    #[from(forward)]
    Url(String),
}

#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum Repeat {
    RepeatX(css::RepeatX),
    RepeatY(css::RepeatY),
    Repeat(css::Repeat),
    Space(css::Space),
    Round(css::Round),
    NoRepeat(css::NoRepeat),
    Initial(css::Initial),
    Inherit(css::Inherit),
}

#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum Attachment {
    Scroll(css::Scroll),
    Fixed(css::Fixed),
    Local(css::Local),
    Initial(css::Initial),
    Inherit(css::Inherit),
}

fn display_helper(v: &Option<impl ToString>) -> String {
    v.as_ref()
        .map_or("".into(), |s| format!(" {}", s.to_string()))
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Horizontal {
    #[from]
    Center(css::Center),
    #[display(fmt = "{}{}", _0, "display_helper(_1)")]
    Left(css::Left, Option<Length>),
    #[display(fmt = "{}{}", _0, "display_helper(_1)")]
    Right(css::Right, Option<Length>),
}

impl From<css::Left> for Horizontal {
    fn from(source: css::Left) -> Self {
        Self::Left(source, None)
    }
}

impl From<css::Right> for Horizontal {
    fn from(source: css::Right) -> Self {
        Self::Right(source, None)
    }
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Vertical {
    #[from]
    Center(css::Center),
    #[display(fmt = "{}{}", _0, "display_helper(_1)")]
    Top(css::Top, Option<Length>),
    #[display(fmt = "{}{}", _0, "display_helper(_1)")]
    Bottom(css::Bottom, Option<Length>),
}

impl From<css::Top> for Vertical {
    fn from(source: css::Top) -> Self {
        Self::Top(source, None)
    }
}

impl From<css::Bottom> for Vertical {
    fn from(source: css::Bottom) -> Self {
        Self::Bottom(source, None)
    }
}

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Position {
    #[display(fmt = "{} {}", _0, _1)]
    #[from]
    Percent(Percent, Percent),
    #[display(fmt = "{} {}", _0, _1)]
    #[from]
    Placement(Horizontal, Vertical),
}

impl From<(f32, f32)> for Position {
    fn from((hor, ver): (f32, f32)) -> Self {
        Position::Percent(hor.into(), ver.into())
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum Box {
    BorderBox(css::BorderBox),
    PaddingBox(css::PaddingBox),
    ContentBox(css::ContentBox),
    Initial(css::Initial),
    Inherit(css::Inherit),
}

pub type Clip = Box;
pub type Origin = Box;

#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Size {
    #[display(fmt = "{} {}", _0, _1)]
    WidthHeight(LengthPercent, LengthPercent),
    Auto(css::Auto),
    Cover(css::Cover),
    Contain(css::Contain),
    Initial(css::Initial),
}

impl From<(f32, f32)> for Size {
    fn from((width, height): (f32, f32)) -> Self {
        Self::WidthHeight(width.into(), height.into())
    }
}