From 86f61ff3f64856ddd2d6e558b4e95e73f4bf67ff Mon Sep 17 00:00:00 2001 From: JMARyA Date: Thu, 16 Jan 2025 18:22:52 +0100 Subject: [PATCH] update --- examples/ui.rs | 14 +- src/htmx.rs | 11 +- src/ui/mod.rs | 7 +- src/ui/primitives/cursor.rs | 124 +++++++++++++ src/ui/primitives/flex.rs | 319 +++++++++++++++++++++++++++++++- src/ui/primitives/height.rs | 30 ++- src/ui/primitives/link.rs | 1 + src/ui/primitives/mod.rs | 5 +- src/ui/primitives/shadow.rs | 76 +++++++- src/ui/primitives/space.rs | 2 + src/ui/primitives/text.rs | 190 ++++++++++++++++++- src/ui/primitives/visibility.rs | 54 ++++++ src/ui/primitives/width.rs | 24 ++- src/ui/primitives/zindex.rs | 82 ++++++++ src/ui/wrapper/hover.rs | 20 +- 15 files changed, 918 insertions(+), 41 deletions(-) create mode 100644 src/ui/primitives/cursor.rs create mode 100644 src/ui/primitives/visibility.rs create mode 100644 src/ui/primitives/zindex.rs diff --git a/examples/ui.rs b/examples/ui.rs index 8353b95..e52b8db 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -15,12 +15,16 @@ pub async fn index_page(ctx: RequestContext) -> StringResponse { h1 { "Hello World!" }; (Hover( - Padding(Text("").color(&Gray::_400)).x(ScreenValue::_10), - Link("/test", Text("Hello")).hx_get("/test").hx_get("/test").hx_trigger( - Event::on_load().delay("2s") - .and(Event::on_revealed()) + Cursor::NorthEastResize.on( + Padding(Text("").color(&Gray::_400)).x(ScreenValue::_10) ) - )) + ).on( + Link("/test", Text("Hello")).hx_get("/test").hx_get("/test").hx_trigger( + Event::on_load().delay("2s") + .and(Event::on_revealed()) + ) + ) + ) (content) diff --git a/src/htmx.rs b/src/htmx.rs index 6271666..950e3ca 100644 --- a/src/htmx.rs +++ b/src/htmx.rs @@ -1,8 +1,11 @@ +use crate::request::assets::DataResponse; use rocket::get; -use crate::request::{StringResponse, respond_script}; - #[get("/assets/htmx.min.js")] -pub fn htmx_script_route() -> StringResponse { - respond_script(include_str!("htmx.min.js").to_string()) +pub fn htmx_script_route() -> DataResponse { + DataResponse::new( + include_str!("htmx.min.js").as_bytes().to_vec(), + "application/javascript".to_string(), + Some(60 * 60 * 24 * 3), + ) } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b769b24..0afe03a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -15,17 +15,18 @@ pub mod components; // Preludes pub mod prelude { pub use super::color::*; - pub use super::primitives::Nothing; pub use super::primitives::Context; + pub use super::primitives::Nothing; pub use super::primitives::Side; pub use super::primitives::Size; pub use super::primitives::aspect::Aspect; pub use super::primitives::background::Background; pub use super::primitives::container::Container; + pub use super::primitives::cursor::Cursor; pub use super::primitives::div::Div; pub use super::primitives::flex::{Flex, Justify}; pub use super::primitives::header::Header; - pub use super::primitives::height::Height; + pub use super::primitives::height::HeightWidget; pub use super::primitives::image::Image; pub use super::primitives::link::Link; pub use super::primitives::margin::Margin; @@ -36,7 +37,9 @@ pub mod prelude { pub use super::primitives::sized::Sized; pub use super::primitives::space::{ScreenValue, SpaceBetween}; pub use super::primitives::text::{Paragraph, Span, Text}; + pub use super::primitives::visibility::Visibility; pub use super::primitives::width::Width; + pub use super::primitives::zindex::ZIndex; pub use super::wrapper::Hover; } diff --git a/src/ui/primitives/cursor.rs b/src/ui/primitives/cursor.rs new file mode 100644 index 0000000..1ab521e --- /dev/null +++ b/src/ui/primitives/cursor.rs @@ -0,0 +1,124 @@ +use crate::ui::UIWidget; +use maud::{Markup, Render, html}; + +pub enum Cursor { + Auto, + Default, + Pointer, + Wait, + Text, + Move, + Help, + NotAllowed, + None, + ContextMenu, + Progress, + Cell, + Crosshair, + VerticalText, + Alias, + Copy, + NoDrop, + Grab, + Grabbing, + AllScroll, + ColResize, + RowResize, + NorthResize, + EastResize, + SouthResize, + WestResize, + NorthEastResize, + NorthWestResize, + SouthEastResize, + SouthWestResize, + EastWestResize, + NorthSouthResize, + NorthEastSouthWestResize, + NorthWestSouthEastResize, + ZoomIn, + ZoomOut, +} + +impl Cursor { + pub fn on(self, inner: T) -> CursorWidget { + CursorWidget(self, Box::new(inner)) + } +} + +pub struct CursorWidget(Cursor, Box); + +impl Render for CursorWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for CursorWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = match self.0 { + Cursor::Auto => "cursor-auto", + Cursor::Default => "cursor-default", + Cursor::Pointer => "cursor-pointer", + Cursor::Wait => "cursor-wait", + Cursor::Text => "cursor-text", + Cursor::Move => "cursor-move", + Cursor::Help => "cursor-help", + Cursor::NotAllowed => "cursor-not-allowed", + Cursor::None => "cursor-none", + Cursor::ContextMenu => "cursor-context-menu", + Cursor::Progress => "cursor-progress", + Cursor::Cell => "cursor-cell", + Cursor::Crosshair => "cursor-crosshair", + Cursor::VerticalText => "cursor-vertical-text", + Cursor::Alias => "cursor-alias", + Cursor::Copy => "cursor-copy", + Cursor::NoDrop => "cursor-no-drop", + Cursor::Grab => "cursor-grab", + Cursor::Grabbing => "cursor-grabbing", + Cursor::AllScroll => "cursor-all-scroll", + Cursor::ColResize => "cursor-col-resize", + Cursor::RowResize => "cursor-row-resize", + Cursor::NorthResize => "cursor-n-resize", + Cursor::EastResize => "cursor-e-resize", + Cursor::SouthResize => "cursor-s-resize", + Cursor::WestResize => "cursor-w-resize", + Cursor::NorthEastResize => "cursor-ne-resize", + Cursor::NorthWestResize => "cursor-nw-resize", + Cursor::SouthEastResize => "cursor-se-resize", + Cursor::SouthWestResize => "cursor-sw-resize", + Cursor::EastWestResize => "cursor-ew-resize", + Cursor::NorthSouthResize => "cursor-ns-resize", + Cursor::NorthEastSouthWestResize => "cursor-nesw-resize", + Cursor::NorthWestSouthEastResize => "cursor-nwse-resize", + Cursor::ZoomIn => "cursor-zoom-in", + Cursor::ZoomOut => "cursor-zoom-out", + }; + + 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) + } + } + } + } +} diff --git a/src/ui/primitives/flex.rs b/src/ui/primitives/flex.rs index 17c9030..b662362 100644 --- a/src/ui/primitives/flex.rs +++ b/src/ui/primitives/flex.rs @@ -1,14 +1,22 @@ use crate::ui::UIWidget; 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) } pub enum Justify { + Normal, + Start, + End, Center, Between, + Around, + Evenly, + Stretch, } pub struct FlexWidget(Box, Vec, bool); @@ -33,11 +41,29 @@ impl FlexWidget { self } + #[must_use] + pub fn direction(mut self, direction: Direction) -> Self { + self.1.push(format!("flex-{}", direction.to_value())); + 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(mut self, value: Justify) -> Self { let class = match value { - Justify::Center => "justify-center".to_owned(), - Justify::Between => "justify-between".to_owned(), + 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); @@ -51,10 +77,56 @@ impl FlexWidget { } #[must_use] - pub fn gap(mut self, amount: u32) -> Self { - self.1.push(format!("gap-{amount}")); + 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 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 { @@ -88,3 +160,242 @@ impl UIWidget for FlexWidget { } } } + +#[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) + } + } + } + } +} diff --git a/src/ui/primitives/height.rs b/src/ui/primitives/height.rs index 788575e..b465d22 100644 --- a/src/ui/primitives/height.rs +++ b/src/ui/primitives/height.rs @@ -4,24 +4,44 @@ use maud::{Markup, Render, html}; use super::space::ScreenValue; #[allow(non_snake_case)] -pub fn Width(size: ScreenValue, inner: T) -> Height { - Height(Box::new(inner), size) +pub fn Height(size: ScreenValue, inner: T) -> HeightWidget { + HeightWidget(Box::new(inner), size, 0) } -pub struct Height(Box, ScreenValue); +#[allow(non_snake_case)] +pub fn MinHeight(size: ScreenValue, inner: T) -> HeightWidget { + HeightWidget(Box::new(inner), size, 1) +} -impl Render for Height { +#[allow(non_snake_case)] +pub fn MaxHeight(size: ScreenValue, inner: T) -> HeightWidget { + HeightWidget(Box::new(inner), size, 2) +} + +pub struct HeightWidget(Box, ScreenValue, u8); + +impl Render for HeightWidget { fn render(&self) -> Markup { self.render_with_class("") } } -impl UIWidget for Height { +impl UIWidget for HeightWidget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { + match self.2 { + 1 => { + return vec![format!("min-h-{}", self.1.to_value())]; + } + 2 => { + return vec![format!("max-h-{}", self.1.to_value())]; + } + _ => {} + } + vec![format!("h-{}", self.1.to_value())] } diff --git a/src/ui/primitives/link.rs b/src/ui/primitives/link.rs index e175d2f..3b89130 100644 --- a/src/ui/primitives/link.rs +++ b/src/ui/primitives/link.rs @@ -69,6 +69,7 @@ impl LinkWidget { /// Enable HTMX link capabilities #[must_use] pub fn use_htmx(self) -> Self { + // todo : investigate htmx attrs let url = self.1.clone(); self.hx_get(&url) .hx_target(Selector::Query("#main_content".to_string())) diff --git a/src/ui/primitives/mod.rs b/src/ui/primitives/mod.rs index 2595030..9ffee18 100644 --- a/src/ui/primitives/mod.rs +++ b/src/ui/primitives/mod.rs @@ -5,6 +5,7 @@ use super::UIWidget; pub mod aspect; pub mod background; pub mod container; +pub mod cursor; pub mod div; pub mod flex; pub mod header; @@ -19,7 +20,9 @@ pub mod shadow; pub mod sized; pub mod space; pub mod text; +pub mod visibility; pub mod width; +pub mod zindex; #[allow(non_snake_case)] #[must_use] @@ -30,7 +33,7 @@ pub fn Nothing() -> PreEscaped { #[allow(non_snake_case)] #[must_use] /// Create a new inheritance context -/// +/// /// This acts as a hard barrier for inheritance. /// This allows you to embed Widgets without them interacting with the rest of the tree. pub fn Context(inner: T) -> PreEscaped { diff --git a/src/ui/primitives/shadow.rs b/src/ui/primitives/shadow.rs index da6386d..92f2323 100644 --- a/src/ui/primitives/shadow.rs +++ b/src/ui/primitives/shadow.rs @@ -31,10 +31,6 @@ impl Shadow { pub fn _2xl(inner: T) -> Self { Self(Box::new(inner), "2xl".to_owned()) } - - pub fn inner(inner: T) -> Self { - Self(Box::new(inner), "inner".to_owned()) - } } impl Render for Shadow { @@ -76,3 +72,75 @@ impl UIWidget for Shadow { } } } + +pub struct DropShadow(Box, String); + +impl DropShadow { + pub fn small(inner: T) -> Self { + Self(Box::new(inner), "sm".to_owned()) + } + + pub fn regular(inner: T) -> Self { + Self(Box::new(inner), String::new()) + } + + pub fn medium(inner: T) -> Self { + Self(Box::new(inner), "md".to_owned()) + } + + pub fn large(inner: T) -> Self { + Self(Box::new(inner), "lg".to_owned()) + } + + pub fn none(inner: T) -> Self { + Self(Box::new(inner), "none".to_owned()) + } + + pub fn xl(inner: T) -> Self { + Self(Box::new(inner), "xl".to_owned()) + } + + pub fn _2xl(inner: T) -> Self { + Self(Box::new(inner), "2xl".to_owned()) + } +} + +impl Render for DropShadow { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for DropShadow { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + if self.1.is_empty() { + vec!["drop-shadow".to_string()] + } else { + vec![format!("drop-shadow-{}", self.1)] + } + } + + 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.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()) + } + } + } + } +} diff --git a/src/ui/primitives/space.rs b/src/ui/primitives/space.rs index c1ad83c..2888980 100644 --- a/src/ui/primitives/space.rs +++ b/src/ui/primitives/space.rs @@ -70,6 +70,7 @@ impl UIWidget for SpaceBetweenWidget { } #[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy)] pub enum ScreenValue { _0, _0p5, @@ -165,6 +166,7 @@ impl ScreenValue { } } +#[derive(Debug, Clone, Copy)] pub enum Fraction { _1on2, _1on3, diff --git a/src/ui/primitives/text.rs b/src/ui/primitives/text.rs index 47003b1..39ca05d 100644 --- a/src/ui/primitives/text.rs +++ b/src/ui/primitives/text.rs @@ -8,8 +8,10 @@ pub fn Text(txt: &str) -> TextWidget { TextWidget { inner: None, txt: txt.to_string(), + family: String::new(), font: String::new(), color: String::new(), + style: Vec::new(), size: String::new(), span: false, } @@ -21,8 +23,10 @@ pub fn Paragraph(inner: T) -> TextWidget { TextWidget { inner: Some(Box::new(inner)), font: String::new(), + family: String::new(), color: String::new(), txt: String::new(), + style: Vec::new(), size: String::new(), span: false, } @@ -35,7 +39,9 @@ pub fn Span(txt: &str) -> TextWidget { TextWidget { inner: None, txt: txt.to_string(), + family: String::new(), font: String::new(), + style: Vec::new(), color: String::new(), size: String::new(), span: true, @@ -45,6 +51,8 @@ pub fn Span(txt: &str) -> TextWidget { pub struct TextWidget { inner: Option>, txt: String, + family: String, + style: Vec, font: String, color: String, size: String, @@ -52,6 +60,41 @@ pub struct TextWidget { } impl TextWidget { + // Weight + + #[must_use] + pub fn thin(mut self) -> Self { + self.font = "font-thin".to_string(); + self + } + + #[must_use] + pub fn extralight(mut self) -> Self { + self.font = "font-extralight".to_string(); + self + } + + #[must_use] + pub fn light(mut self) -> Self { + self.font = "font-light".to_string(); + self + } + + #[must_use] + pub fn normal(mut self) -> Self { + self.font = "font-normal".to_string(); + self + } + + /// Turn `Text` medium. + /// + /// Adds the class `font-medium` + #[must_use] + pub fn medium(mut self) -> Self { + self.font = "font-medium".to_string(); + self + } + /// Turn `Text` semibold. /// /// Adds the class `font-semibold` @@ -70,12 +113,83 @@ impl TextWidget { self } - /// Turn `Text` medium. - /// - /// Adds the class `font-medium` #[must_use] - pub fn medium(mut self) -> Self { - self.font = "font-medium".to_string(); + pub fn extrabold(mut self) -> Self { + self.font = "font-extrabold".to_string(); + self + } + + #[must_use] + pub fn weight_black(mut self) -> Self { + self.font = "font-black".to_string(); + self + } + + // Styles + + #[must_use] + pub fn italic(mut self, apply: bool) -> Self { + if apply { + self.style.push("italic".to_string()); + } else { + self.style.push("not-italic".to_string()) + } + self + } + + #[must_use] + pub fn number_style(mut self, s: NumberStyle) -> Self { + self.style.push(s.to_value().to_string()); + self + } + + // Sizes + + #[must_use] + pub fn _9xl(mut self) -> Self { + self.size = "text-9xl".to_string(); + self + } + + #[must_use] + pub fn _8xl(mut self) -> Self { + self.size = "text-8xl".to_string(); + self + } + + #[must_use] + pub fn _7xl(mut self) -> Self { + self.size = "text-7xl".to_string(); + self + } + + #[must_use] + pub fn _6xl(mut self) -> Self { + self.size = "text-6xl".to_string(); + self + } + + #[must_use] + pub fn _5xl(mut self) -> Self { + self.size = "text-5xl".to_string(); + self + } + + #[must_use] + pub fn _4xl(mut self) -> Self { + self.size = "text-4xl".to_string(); + self + } + + #[must_use] + pub fn large(mut self) -> Self { + self.size = "text-lg".to_string(); + self + } + + #[must_use] + pub fn base_size(mut self) -> Self { + self.size = "text-base".to_string(); self } @@ -115,6 +229,17 @@ impl TextWidget { self } + /// Turn `Text` size to x small. + /// + /// Adds the class `text-xs` + #[must_use] + pub fn xs(mut self) -> Self { + self.size = "text-xs".to_string(); + self + } + + // Text Color + #[must_use] pub fn color(mut self, color: &T) -> Self { self.color = format!("text-{}", color.color_class()); @@ -132,6 +257,26 @@ impl TextWidget { self.color = "text-white".to_string(); self } + + // Font Family + + #[must_use] + pub fn sans(mut self) -> Self { + self.family = "font-sans".to_string(); + self + } + + #[must_use] + pub fn serif(mut self) -> Self { + self.family = "font-serif".to_string(); + self + } + + #[must_use] + pub fn mono(mut self) -> Self { + self.family = "font-mono".to_string(); + self + } } impl Render for TextWidget { @@ -146,7 +291,12 @@ impl UIWidget for TextWidget { } fn base_class(&self) -> Vec { - vec![self.color.clone(), self.font.clone(), self.size.clone()] + vec![ + self.color.clone(), + self.font.clone(), + self.size.clone(), + self.family.clone(), + ] } fn extended_class(&self) -> Vec { @@ -179,3 +329,31 @@ impl UIWidget for TextWidget { } } } + +pub enum NumberStyle { + Normal, + Ordinal, + SlashedZero, + OldStyle, + Lining, + Proportional, + Tabular, + DiagonalFractions, + StackedFractions, +} + +impl NumberStyle { + pub const fn to_value(&self) -> &str { + match self { + NumberStyle::Normal => "normal-nums", + NumberStyle::Ordinal => "ordinal", + NumberStyle::SlashedZero => "slashed-zero", + NumberStyle::OldStyle => "oldstyle-nums", + NumberStyle::Lining => "lining-nums", + NumberStyle::Proportional => "proportional-nums", + NumberStyle::Tabular => "tabular-nums", + NumberStyle::DiagonalFractions => "diagonal-fractions", + NumberStyle::StackedFractions => "stacked-fractions", + } + } +} diff --git a/src/ui/primitives/visibility.rs b/src/ui/primitives/visibility.rs new file mode 100644 index 0000000..3c4d5cd --- /dev/null +++ b/src/ui/primitives/visibility.rs @@ -0,0 +1,54 @@ +use crate::ui::UIWidget; +use maud::{Markup, Render, html}; + +pub struct Visibility(Box, String); + +impl Visibility { + pub fn visible(inner: T) -> Self { + Self(Box::new(inner), "visible".to_string()) + } + + pub fn hidden(inner: T) -> Self { + Self(Box::new(inner), "invisible".to_string()) + } + + pub fn collapsed(inner: T) -> Self { + Self(Box::new(inner), "collapse".to_string()) + } +} + +impl Render for Visibility { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for Visibility { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + vec![self.1.clone()] + } + + 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.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()) + } + } + } + } +} diff --git a/src/ui/primitives/width.rs b/src/ui/primitives/width.rs index 90416d5..807e0a0 100644 --- a/src/ui/primitives/width.rs +++ b/src/ui/primitives/width.rs @@ -5,10 +5,20 @@ use super::space::ScreenValue; #[allow(non_snake_case)] pub fn Width(size: ScreenValue, inner: T) -> WidthWidget { - WidthWidget(Box::new(inner), size) + WidthWidget(Box::new(inner), size, 0) } -pub struct WidthWidget(Box, ScreenValue); +#[allow(non_snake_case)] +pub fn MinWidth(size: ScreenValue, inner: T) -> WidthWidget { + WidthWidget(Box::new(inner), size, 1) +} + +#[allow(non_snake_case)] +pub fn MaxWidth(size: ScreenValue, inner: T) -> WidthWidget { + WidthWidget(Box::new(inner), size, 2) +} + +pub struct WidthWidget(Box, ScreenValue, u8); impl Render for WidthWidget { fn render(&self) -> Markup { @@ -22,6 +32,16 @@ impl UIWidget for WidthWidget { } fn base_class(&self) -> Vec { + match self.2 { + 1 => { + return vec![format!("min-w-{}", self.1.to_value())]; + } + 2 => { + return vec![format!("max-w-{}", self.1.to_value())]; + } + _ => {} + } + vec![format!("w-{}", self.1.to_value())] } diff --git a/src/ui/primitives/zindex.rs b/src/ui/primitives/zindex.rs new file mode 100644 index 0000000..2ddf0b4 --- /dev/null +++ b/src/ui/primitives/zindex.rs @@ -0,0 +1,82 @@ +use maud::{Markup, Render, html}; + +use crate::ui::UIWidget; + +pub struct ZIndex(Box, u8); + +impl Render for ZIndex { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl ZIndex { + pub fn auto(inner: T) -> Self { + Self(Box::new(inner), 0) + } + + pub fn zero(inner: T) -> Self { + Self(Box::new(inner), 1) + } + + pub fn one(inner: T) -> Self { + Self(Box::new(inner), 2) + } + + pub fn two(inner: T) -> Self { + Self(Box::new(inner), 3) + } + + pub fn three(inner: T) -> Self { + Self(Box::new(inner), 4) + } + + pub fn four(inner: T) -> Self { + Self(Box::new(inner), 5) + } + + pub fn five(inner: T) -> Self { + Self(Box::new(inner), 6) + } +} + +impl UIWidget for ZIndex { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = match self.1 { + 0 => "z-auto", + 1 => "z-0", + 2 => "z-10", + 3 => "z-20", + 4 => "z-30", + 5 => "z-40", + 6 => "z-50", + _ => "z-auto", + }; + + vec![class.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, class: &str) -> Markup { + if self.0.as_ref().can_inherit() { + 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()) + } + } + } + } +} diff --git a/src/ui/wrapper/hover.rs b/src/ui/wrapper/hover.rs index 870ddc4..7996497 100644 --- a/src/ui/wrapper/hover.rs +++ b/src/ui/wrapper/hover.rs @@ -3,11 +3,11 @@ use maud::{Markup, Render, html}; use crate::ui::UIWidget; #[allow(non_snake_case)] -pub fn Hover(inherit: I, inner: T) -> HoverWrapper { - HoverWrapper(Box::new(inner), Box::new(inherit)) +pub fn Hover(inherit: I) -> HoverWrapper { + HoverWrapper(None, Box::new(inherit)) } -pub struct HoverWrapper(Box, Box); +pub struct HoverWrapper(Option>, Box); impl HoverWrapper { fn hovered_class(&self) -> Vec { @@ -18,6 +18,11 @@ impl HoverWrapper { .map(|x| format!("hover:{x}")) .collect::>() } + + pub fn on(mut self, inner: T) -> Self { + self.0 = Some(Box::new(inner)); + self + } } impl Render for HoverWrapper { @@ -36,20 +41,19 @@ impl UIWidget for HoverWrapper { } fn extended_class(&self) -> Vec { - let mut ret = self.base_class(); - ret.extend_from_slice(&self.0.extended_class()); - ret + self.base_class() } fn render_with_class(&self, class: &str) -> Markup { - if self.0.as_ref().can_inherit() { + if self.0.as_ref().unwrap().can_inherit() { self.0 .as_ref() + .unwrap() .render_with_class(&format!("{} {class}", self.hovered_class().join(" "))) } else { html! { div class=(format!("{} {class}", self.hovered_class().join(" "))) { - (self.0.as_ref()) + (self.0.as_ref().unwrap()) } } }