use maud::{Markup, Render, html}; use crate::ui::UIWidget; #[allow(non_snake_case)] pub fn Animated(inner: T) -> AnimatedWidget { AnimatedWidget { inner: Box::new(inner), scope: Scope::Normal, timing: None, delay: None, duration: None, animation: None, } } pub struct AnimatedWidget { inner: Box, scope: Scope, timing: Option, delay: Option, duration: Option, animation: Option, } macro_rules! setter { ($fnname:ident, $varname:ident, $vartype:ident, $internal_var:ident) => { #[must_use] pub fn $fnname(mut self, $varname: $vartype) -> Self { self.$internal_var = $varname; self } }; } macro_rules! setter_opt { ($fnname:ident, $varname:ident, $vartype:ident, $internal_var:ident) => { #[must_use] pub fn $fnname(mut self, $varname: $vartype) -> Self { self.$internal_var = Some($varname); self } }; } impl AnimatedWidget { setter!(scope, scope, Scope, scope); setter_opt!(timing, timing, Timing, timing); setter_opt!(delay, delay, Delay, delay); setter_opt!(duration, duration, Duration, duration); setter_opt!(animate, animation, Animation, animation); } impl Render for AnimatedWidget { fn render(&self) -> Markup { self.render_with_class("") } } impl UIWidget for AnimatedWidget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { let mut ret = vec![self.scope.to_value().to_string()]; if let Some(timing) = &self.timing { ret.push(timing.to_value().to_owned()); } if let Some(delay) = &self.delay { ret.push(delay.to_value()); } if let Some(duration) = &self.duration { ret.push(duration.to_value()); } if let Some(anim) = &self.animation { ret.push(anim.to_value().to_owned()); } ret } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.inner.extended_class()); c } fn render_with_class(&self, class: &str) -> Markup { if self.inner.as_ref().can_inherit() { self.inner .as_ref() .render_with_class(&format!("{} {class}", self.base_class().join(" "))) } else { html! { div class=(format!("{} {class}", self.base_class().join(" "))) { (self.inner.as_ref()) } } } } } pub enum Scope { None, All, Normal, Colors, Opacity, Shadow, Transform, } impl Scope { pub fn to_value(&self) -> &str { match self { Scope::None => "transition-none", Scope::All => "transition-all", Scope::Normal => "transition", Scope::Colors => "transition-colors", Scope::Opacity => "transition-opacity", Scope::Shadow => "transition-shadow", Scope::Transform => "transition-transform", } } } macro_rules! num_opt { ($name:ident, $id:literal) => { pub enum $name { Custom(String), _0, _75, _100, _150, _200, _300, _500, _700, _1000, } impl $name { pub fn to_value(&self) -> String { match self { Self::Custom(s) => format!("{}-[{s}]", $id), Self::_0 => concat!($id, "-0").to_string(), Self::_75 => concat!($id, "-75").to_string(), Self::_100 => concat!($id, "-100").to_string(), Self::_150 => concat!($id, "-150").to_string(), Self::_200 => concat!($id, "-200").to_string(), Self::_300 => concat!($id, "-300").to_string(), Self::_500 => concat!($id, "-500").to_string(), Self::_700 => concat!($id, "-700").to_string(), Self::_1000 => concat!($id, "-1000").to_string(), } } } }; } num_opt!(Duration, "duration"); num_opt!(Delay, "delay"); pub enum Timing { EaseLinear, EaseIn, EaseOut, EaseInOut, } impl Timing { pub const fn to_value(&self) -> &str { match self { Timing::EaseLinear => "ease-linear", Timing::EaseIn => "ease-in", Timing::EaseOut => "ease-out", Timing::EaseInOut => "ease-in-out", } } } pub enum Animation { None, Spin, Ping, Pulse, Bounce, } impl Animation { pub fn to_value(&self) -> &str { match self { Animation::None => "animate-none", Animation::Spin => "animate-spin", Animation::Ping => "animate-ping", Animation::Pulse => "animate-pulse", Animation::Bounce => "animate-bounce", } } }