use crate::ui::{UIWidget, color::UIColor}; use maud::{Markup, Render, html}; use super::space::{Fraction, ScreenValue}; #[allow(non_snake_case)] pub fn Flex(inner: T) -> FlexWidget { FlexWidget(Box::new(inner), vec![], false, None) } pub enum Justify { Normal, Start, End, Center, Between, Around, Evenly, Stretch, } pub struct FlexWidget(Box, Vec, bool, Option); impl Render for FlexWidget { fn render(&self) -> Markup { self.render_with_class("") } } impl FlexWidget { #[must_use] pub fn full_center(mut self) -> Self { self.1.push("items-center".to_owned()); self.1.push("justify-center".to_owned()); self } #[must_use] pub const fn group(mut self) -> Self { self.2 = true; self } #[must_use] pub fn divide_style(mut self, style: DivideStyle) -> Self { self.1.push(style.to_value().to_string()); self } #[must_use] pub fn divide_color(mut self, color: C) -> Self { self.1.push(format!("divide-{}", color.color_class())); self } #[must_use] pub fn divide_x(mut self, width: DivideWidth) -> Self { let reversed = self .3 .as_ref() .map(|x| match x { Direction::Row => false, Direction::RowReverse => true, Direction::Column => false, Direction::ColumnReverse => true, }) .unwrap_or_default(); self.1.push(format!("divide-x-{}", width.to_value())); if reversed { self.1.push("divide-x-reverse".to_string()); } self } #[must_use] pub fn divide_y(mut self, width: DivideWidth) -> Self { let reversed = self .3 .as_ref() .map(|x| match x { Direction::Row => false, Direction::RowReverse => true, Direction::Column => false, Direction::ColumnReverse => true, }) .unwrap_or_default(); self.1.push(format!("divide-y-{}", width.to_value())); if reversed { self.1.push("divide-y-reverse".to_string()); } self } #[must_use] pub fn direction(mut self, direction: Direction) -> Self { self.3 = Some(direction); self } #[must_use] pub fn wrap(mut self, wrap: Wrap) -> Self { self.1.push(format!("flex-{}", wrap.to_value())); self } #[must_use] pub fn justify_items(mut self, justify: JustifyItems) -> Self { self.1.push(justify.to_value().to_string()); self } #[must_use] pub fn align_content(mut self, align: AlignContent) -> Self { self.1.push(align.to_value().to_string()); self } #[must_use] pub fn align_items(mut self, align: AlignItems) -> Self { self.1.push(align.to_value().to_string()); self } #[must_use] pub fn justify(mut self, value: Justify) -> Self { let class = match value { Justify::Center => "justify-center".to_string(), Justify::Between => "justify-between".to_string(), Justify::Normal => "justify-normal".to_string(), Justify::Start => "justify-start".to_string(), Justify::End => "justify-end".to_string(), Justify::Around => "justify-around".to_string(), Justify::Evenly => "justify-evenly".to_string(), Justify::Stretch => "justify-stretch".to_string(), }; self.1.push(class); self } #[must_use] pub fn items_center(mut self) -> Self { self.1.push("items-center".to_owned()); self } #[must_use] pub fn gap(mut self, amount: ScreenValue) -> Self { self.1.push(format!("gap-{}", amount.to_value())); self } #[must_use] pub fn gap_x(mut self, amount: ScreenValue) -> Self { self.1.push(format!("gap-x-{}", amount.to_value())); self } #[must_use] pub fn gap_y(mut self, amount: ScreenValue) -> Self { self.1.push(format!("gap-y-{}", amount.to_value())); self } } pub enum DivideWidth { Custom(u64), _0, _2, _4, _8, } impl DivideWidth { pub fn to_value(&self) -> String { match self { DivideWidth::Custom(s) => format!("[{s}px]"), DivideWidth::_0 => "0".to_string(), DivideWidth::_2 => "2".to_string(), DivideWidth::_4 => "4".to_string(), DivideWidth::_8 => "8".to_string(), } } } pub enum Direction { Row, RowReverse, Column, ColumnReverse, } impl Direction { pub const fn to_value(&self) -> &str { match self { Direction::Row => "row", Direction::RowReverse => "row-reverse", Direction::Column => "col", Direction::ColumnReverse => "col-reverse", } } } pub enum Wrap { Wrap, Reverse, NoWrap, } impl Wrap { pub const fn to_value(&self) -> &str { match self { Wrap::Wrap => "wrap", Wrap::Reverse => "wrap-reverse", Wrap::NoWrap => "nowrap", } } } impl UIWidget for FlexWidget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { let mut res = vec!["flex".to_string()]; if let Some(direction) = &self.3 { res.push(format!("flex-{}", direction.to_value())); } res.extend_from_slice(&self.1); res } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.0.extended_class()); c } fn render_with_class(&self, class: &str) -> Markup { if self.0.as_ref().can_inherit() && !self.2 { self.0 .as_ref() .render_with_class(&format!("{} {class}", self.base_class().join(" "))) } else { html! { div class=(format!("{} {class}", self.base_class().join(" "))) { (self.0.as_ref()) } } } } } #[derive(Debug, Clone, Copy)] pub enum Either { Right(R), Left(L), } impl Either { pub fn map(self, lf: X, rf: Y) -> U where X: FnOnce(L) -> U, Y: FnOnce(R) -> U, { match self { Either::Right(r) => rf(r), Either::Left(l) => lf(l), } } } impl From for Either { fn from(value: ScreenValue) -> Self { Self::Right(value) } } impl From for Either { fn from(value: Fraction) -> Self { Self::Left(value) } } #[allow(non_snake_case)] pub fn FlexBasis( inner: T, value: Either, ) -> FlexBasisWidget { FlexBasisWidget(Box::new(inner), value) } pub struct FlexBasisWidget(Box, Either); impl Render for FlexBasisWidget { fn render(&self) -> Markup { self.render_with_class("") } } impl UIWidget for FlexBasisWidget { fn can_inherit(&self) -> bool { false } fn base_class(&self) -> Vec { vec![format!( "basis-{}", self.1 .clone() .map(|x| x.to_value().to_string(), |x| x.to_value().to_string()) )] } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.0.extended_class()); c } fn render_with_class(&self, _: &str) -> Markup { if self.0.as_ref().can_inherit() { self.0 .as_ref() .render_with_class(&format!("{}", self.base_class().join(" "))) } else { html! { div class=(format!("{}", self.base_class().join(" "))) { (self.0.as_ref()) } } } } } #[allow(non_snake_case)] pub fn FlexGrow(strategy: Strategy, inner: T) -> FlexGrowWidget { FlexGrowWidget(strategy, Box::new(inner)) } pub struct FlexGrowWidget(Strategy, Box); impl Render for FlexGrowWidget { fn render(&self) -> Markup { self.render_with_class("") } } impl UIWidget for FlexGrowWidget { fn can_inherit(&self) -> bool { false } fn base_class(&self) -> Vec { vec![self.0.to_value().to_string()] } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.1.extended_class()); c } fn render_with_class(&self, _: &str) -> Markup { if self.1.as_ref().can_inherit() { self.1 .as_ref() .render_with_class(&format!("{}", self.base_class().join(" "))) } else { html! { div class=(format!("{}", self.base_class().join(" "))) { (self.1.as_ref()) } } } } } pub enum Strategy { /// Allow a flex item to shrink but not grow, taking into account its initial size. Initial, /// Allow a flex item to grow and shrink as needed, ignoring its initial size. Expand, /// Allow a flex item to grow and shrink, taking into account its initial size. Auto, /// Prevent a flex item from growing or shrinking. None, /// Allow a flex item to grow to fill any available space. Grow, /// Prevent a flex item from growing. NoGrow, /// Allow a flex item to shrink if needed. Shrink, /// Prevent a flex item from shrinking. NoShrink, } impl Strategy { pub fn to_value(&self) -> &str { match self { Self::Initial => "flex-initial", Self::Expand => "flex-1", Self::Auto => "flex-auto", Self::None => "flex-none", Self::Grow => "grow", Self::NoGrow => "grow-0", Self::Shrink => "shrink", Self::NoShrink => "shrink-0", } } } pub enum Order { _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, First, Last, None, } impl Order { pub fn on(self, inner: T) -> OrderWidget { OrderWidget(self, Box::new(inner)) } } pub struct OrderWidget(Order, Box); impl Render for OrderWidget { fn render(&self) -> Markup { self.render_with_class("") } } impl UIWidget for OrderWidget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { let class = match self.0 { Order::_1 => "order-1", Order::_2 => "order-2", Order::_3 => "order-3", Order::_4 => "order-4", Order::_5 => "order-5", Order::_6 => "order-6", Order::_7 => "order-7", Order::_8 => "order-8", Order::_9 => "order-9", Order::_10 => "order-10", Order::_11 => "order-11", Order::_12 => "order-12", Order::First => "order-first", Order::Last => "order-last", Order::None => "order-none", }; vec![class.to_string()] } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.1.extended_class()); c } fn render_with_class(&self, class: &str) -> Markup { let inner = &self.1; if inner.can_inherit() { inner.render_with_class(&format!("{} {class}", self.base_class().join(" "))) } else { html! { div class=(format!("{} {class}", self.base_class().join(" "))) { (inner) } } } } } pub enum DivideStyle { Solid, Dashed, Dotted, Double, None, } impl DivideStyle { pub const fn to_value(&self) -> &str { match self { DivideStyle::Solid => "divide-solid", DivideStyle::Dashed => "divide-dashed", DivideStyle::Dotted => "divide-dotted", DivideStyle::Double => "divide-double", DivideStyle::None => "divide-none", } } } pub enum JustifyItems { Start, End, Center, Stretch, } impl JustifyItems { pub const fn to_value(&self) -> &str { match self { JustifyItems::Start => "justify-items-start", JustifyItems::End => "justify-items-end", JustifyItems::Center => "justify-items-center", JustifyItems::Stretch => "justify-items-stretch", } } } pub enum AlignContent { Normal, Center, Start, End, Between, Around, Evenly, Baseline, Stretch, } impl AlignContent { pub const fn to_value(&self) -> &str { match self { AlignContent::Normal => "content-normal", AlignContent::Center => "content-center", AlignContent::Start => "content-start", AlignContent::End => "content-end", AlignContent::Between => "content-between", AlignContent::Around => "content-around", AlignContent::Evenly => "content-evenly", AlignContent::Baseline => "content-baseline", AlignContent::Stretch => "content-stretch", } } } pub enum AlignItems { Start, End, Center, Baseline, Stretch, } impl AlignItems { pub const fn to_value(&self) -> &str { match self { AlignItems::Start => "items-start", AlignItems::End => "items-end", AlignItems::Center => "items-center", AlignItems::Baseline => "items-baseline", AlignItems::Stretch => "items-stretch", } } }