171 lines
4.7 KiB
Rust
171 lines
4.7 KiB
Rust
//! Style your widgets.
|
|
use crate::{bumpalo, Align, Background, Color, Length, Padding};
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
/// A CSS rule of a VDOM node.
|
|
#[derive(Debug)]
|
|
pub enum Rule {
|
|
/// Container with vertical distribution
|
|
Column,
|
|
|
|
/// Container with horizonal distribution
|
|
Row,
|
|
|
|
/// Spacing between elements
|
|
Spacing(u16),
|
|
}
|
|
|
|
impl Rule {
|
|
/// Returns the class name of the [`Rule`].
|
|
pub fn class<'a>(&self) -> String {
|
|
match self {
|
|
Rule::Column => String::from("c"),
|
|
Rule::Row => String::from("r"),
|
|
Rule::Spacing(spacing) => format!("s-{}", spacing),
|
|
}
|
|
}
|
|
|
|
/// Returns the declaration of the [`Rule`].
|
|
pub fn declaration<'a>(&self, bump: &'a bumpalo::Bump) -> &'a str {
|
|
let class = self.class();
|
|
|
|
match self {
|
|
Rule::Column => {
|
|
let body = "{ display: flex; flex-direction: column; }";
|
|
|
|
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
|
}
|
|
Rule::Row => {
|
|
let body = "{ display: flex; flex-direction: row; }";
|
|
|
|
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
|
}
|
|
Rule::Spacing(spacing) => bumpalo::format!(
|
|
in bump,
|
|
".c.{} > * {{ margin-bottom: {}px }} \
|
|
.r.{} > * {{ margin-right: {}px }} \
|
|
.c.{} > *:last-child {{ margin-bottom: 0 }} \
|
|
.r.{} > *:last-child {{ margin-right: 0 }}",
|
|
class,
|
|
spacing,
|
|
class,
|
|
spacing,
|
|
class,
|
|
class
|
|
)
|
|
.into_bump_str(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A cascading style sheet.
|
|
#[derive(Debug)]
|
|
pub struct Css<'a> {
|
|
rules: BTreeMap<String, &'a str>,
|
|
}
|
|
|
|
impl<'a> Css<'a> {
|
|
/// Creates an empty [`Css`].
|
|
pub fn new() -> Self {
|
|
Css {
|
|
rules: BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Inserts the [`Rule`] in the [`Css`], if it was not previously
|
|
/// inserted.
|
|
///
|
|
/// It returns the class name of the provided [`Rule`].
|
|
pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String {
|
|
let class = rule.class();
|
|
|
|
if !self.rules.contains_key(&class) {
|
|
let _ = self.rules.insert(class.clone(), rule.declaration(bump));
|
|
}
|
|
|
|
class
|
|
}
|
|
|
|
/// Produces the VDOM node of the [`Css`].
|
|
pub fn node(self, bump: &'a bumpalo::Bump) -> dodrio::Node<'a> {
|
|
use dodrio::builder::*;
|
|
|
|
let mut declarations = bumpalo::collections::Vec::new_in(bump);
|
|
|
|
declarations.push(text("html { height: 100% }"));
|
|
declarations.push(text(
|
|
"body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }",
|
|
));
|
|
declarations.push(text("* { margin: 0; padding: 0 }"));
|
|
declarations.push(text(
|
|
"button { border: none; cursor: pointer; outline: none }",
|
|
));
|
|
|
|
for declaration in self.rules.values() {
|
|
declarations.push(text(*declaration));
|
|
}
|
|
|
|
style(bump).children(declarations).finish()
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given [`Length`].
|
|
pub fn length(length: Length) -> String {
|
|
match length {
|
|
Length::Shrink => String::from("auto"),
|
|
Length::Units(px) => format!("{}px", px),
|
|
Length::Fill | Length::FillPortion(_) => String::from("100%"),
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given maximum length in units.
|
|
pub fn max_length(units: u32) -> String {
|
|
use std::u32;
|
|
|
|
if units == u32::MAX {
|
|
String::from("initial")
|
|
} else {
|
|
format!("{}px", units)
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given minimum length in units.
|
|
pub fn min_length(units: u32) -> String {
|
|
if units == 0 {
|
|
String::from("initial")
|
|
} else {
|
|
format!("{}px", units)
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given [`Color`].
|
|
pub fn color(Color { r, g, b, a }: Color) -> String {
|
|
format!("rgba({}, {}, {}, {})", 255.0 * r, 255.0 * g, 255.0 * b, a)
|
|
}
|
|
|
|
/// Returns the style value for the given [`Background`].
|
|
pub fn background(background: Background) -> String {
|
|
match background {
|
|
Background::Color(c) => color(c),
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given [`Align`].
|
|
pub fn align(align: Align) -> &'static str {
|
|
match align {
|
|
Align::Start => "flex-start",
|
|
Align::Center => "center",
|
|
Align::End => "flex-end",
|
|
}
|
|
}
|
|
|
|
/// Returns the style value for the given [`Padding`].
|
|
///
|
|
/// [`Padding`]: struct.Padding.html
|
|
pub fn padding(padding: Padding) -> String {
|
|
format!(
|
|
"{}px {}px {}px {}px",
|
|
padding.top, padding.right, padding.bottom, padding.left
|
|
)
|
|
}
|