From bcb69805ef5c71c566bf3297d1c7fa769125847f Mon Sep 17 00:00:00 2001 From: JMARyA Date: Thu, 16 Jan 2025 20:24:01 +0100 Subject: [PATCH] add animation --- src/ui/mod.rs | 7 +- src/ui/primitives/animation.rs | 204 +++++++++++++++++++++++++++++++++ src/ui/primitives/mod.rs | 1 + src/ui/wrapper/hover.rs | 7 +- 4 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 src/ui/primitives/animation.rs diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0afe03a..524c0e8 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -19,14 +19,15 @@ pub mod prelude { pub use super::primitives::Nothing; pub use super::primitives::Side; pub use super::primitives::Size; + 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::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::flex::{Flex, FlexBasis, FlexGrow, Justify}; pub use super::primitives::header::Header; - pub use super::primitives::height::HeightWidget; + pub use super::primitives::height::{Height, MaxHeight, MinHeight}; pub use super::primitives::image::Image; pub use super::primitives::link::Link; pub use super::primitives::margin::Margin; @@ -38,7 +39,7 @@ pub mod prelude { 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::width::{MaxWidth, MinWidth, Width}; pub use super::primitives::zindex::ZIndex; pub use super::wrapper::Hover; } diff --git a/src/ui/primitives/animation.rs b/src/ui/primitives/animation.rs new file mode 100644 index 0000000..d95dd24 --- /dev/null +++ b/src/ui/primitives/animation.rs @@ -0,0 +1,204 @@ +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, +} + +impl AnimatedWidget { + pub fn scope(mut self, scope: Scope) -> Self { + self.scope = scope; + self + } + + pub fn timing(mut self, timing: Timing) -> Self { + self.timing = Some(timing); + self + } + + pub fn delay(mut self, delay: Delay) -> Self { + self.delay = Some(delay); + self + } + + pub fn duration(mut self, duration: Duration) -> Self { + self.duration = Some(duration); + self + } + + pub fn animate(mut self, animation: Animation) -> Self { + self.animation = Some(animation); + self + } +} + +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", + } + } +} diff --git a/src/ui/primitives/mod.rs b/src/ui/primitives/mod.rs index 9ffee18..d99d291 100644 --- a/src/ui/primitives/mod.rs +++ b/src/ui/primitives/mod.rs @@ -2,6 +2,7 @@ use maud::{PreEscaped, html}; use super::UIWidget; +pub mod animation; pub mod aspect; pub mod background; pub mod container; diff --git a/src/ui/wrapper/hover.rs b/src/ui/wrapper/hover.rs index 7996497..b6d069f 100644 --- a/src/ui/wrapper/hover.rs +++ b/src/ui/wrapper/hover.rs @@ -41,7 +41,12 @@ impl UIWidget for HoverWrapper { } fn extended_class(&self) -> Vec { - self.base_class() + let mut ret = self.base_class(); + if let Some(inner) = &self.0 { + ret.extend_from_slice(&inner.extended_class()); + } + + ret } fn render_with_class(&self, class: &str) -> Markup {