diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 9b06d13..bb8a803 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -23,7 +23,9 @@ pub mod prelude { pub use super::primitives::animation::{Animated, Animation, Delay, Duration, Scope, Timing}; pub use super::primitives::aspect::Aspect; pub use super::primitives::background::Background; - pub use super::primitives::border::{Border, BorderSide, BorderSize, BorderStyle}; + pub use super::primitives::border::{ + Border, BorderSide, BorderSize, BorderStyle, Outline, OutlineStyle, Ring, + }; pub use super::primitives::container::Container; pub use super::primitives::cursor::Cursor; pub use super::primitives::div::Div; diff --git a/src/ui/primitives/border.rs b/src/ui/primitives/border.rs index 12bfd56..c7e14c3 100644 --- a/src/ui/primitives/border.rs +++ b/src/ui/primitives/border.rs @@ -164,3 +164,234 @@ impl UIWidget for BorderWidget { } } } + +pub enum OutlineStyle { + Solid, + Dashed, + Dotted, + Double, + None, +} + +impl OutlineStyle { + pub const fn to_value(&self) -> &str { + match self { + OutlineStyle::Solid => "outline", + OutlineStyle::Dashed => "outline-dashed", + OutlineStyle::Dotted => "outline-dotted", + OutlineStyle::Double => "outline-double", + OutlineStyle::None => "outline-none", + } + } +} + +#[allow(non_snake_case)] +pub fn Outline(width: u32, inner: T) -> OutlineWidget { + OutlineWidget(Box::new(inner), width, None, None, 0) +} + +pub struct OutlineWidget( + Box, + u32, + Option>, + Option, + u32, +); + +impl OutlineWidget { + #[must_use] + pub const fn offset(mut self, offset: u32) -> Self { + self.4 = offset; + self + } + + #[must_use] + pub const fn style(mut self, style: OutlineStyle) -> Self { + self.3 = Some(style); + self + } + + #[must_use] + pub fn color(mut self, color: C) -> Self { + self.2 = Some(Box::new(color)); + self + } +} + +impl Render for OutlineWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for OutlineWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = match self.1 { + 0 => "outline-0", + 1 => "outline-1", + 2 => "outline-2", + 4 => "outline-4", + 8 => "outline-8", + _ => &format!("outline-[{}px]", self.1), + }; + + let mut ret = vec![class.to_string()]; + + if let Some(color) = &self.2 { + ret.push(format!("outline-{}", color.color_class())); + } + + if let Some(style) = &self.3 { + ret.push(style.to_value().to_string()); + } + + ret.push(match self.4 { + 0 => "outline-offset-0".to_string(), + 1 => "outline-offset-1".to_string(), + 2 => "outline-offset-2".to_string(), + 4 => "outline-offset-4".to_string(), + 8 => "outline-offset-8".to_string(), + _ => format!("outline-offset-[{}px]", self.4), + }); + + ret + } + + 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()) + } + } + } + } +} + +#[allow(non_snake_case)] +pub fn Ring(width: u32, inner: T) -> RingWidget { + RingWidget(Box::new(inner), width, None, false, 0, None) +} + +pub struct RingWidget( + // Inner + Box, + // Size + u32, + // Color + Option>, + // Inset + bool, + // Offset Width + u32, + // Offset Color + Option>, +); + +impl RingWidget { + #[must_use] + pub const fn inset(mut self) -> Self { + self.3 = true; + self + } + + #[must_use] + pub const fn offset_width(mut self, offset: u32) -> Self { + self.4 = offset; + self + } + + #[must_use] + pub fn offset_color(mut self, color: C) -> Self { + self.5 = Some(Box::new(color)); + self + } + + #[must_use] + pub fn color(mut self, color: C) -> Self { + self.2 = Some(Box::new(color)); + self + } +} + +impl Render for RingWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for RingWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = match self.1 { + 0 => "ring-0", + 1 => "ring-1", + 2 => "ring-2", + 4 => "ring-4", + 8 => "ring-8", + _ => &format!("ring-[{}px]", self.1), + }; + + let mut ret = vec![class.to_string()]; + + if let Some(color) = &self.2 { + ret.push(format!("ring-{}", color.color_class())); + } + + if self.3 { + ret.push("ring-inset".to_string()); + } + + ret.push(match self.4 { + 0 => "ring-offset-0".to_string(), + 1 => "ring-offset-1".to_string(), + 2 => "ring-offset-2".to_string(), + 4 => "ring-offset-4".to_string(), + 8 => "ring-offset-8".to_string(), + _ => format!("ring-offset-[{}px]", self.4), + }); + + if let Some(color) = &self.5 { + ret.push(format!("ring-offset-{}", color.color_class())); + } + + ret + } + + 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()) + } + } + } + } +}