Skip to main content

whisker_css/shorthand/
transition.rs

1//! `transition` shorthand — bundles the four transition longhands
2//! into one declaration. Multiple transitions are comma-separated.
3
4use core::fmt;
5
6use crate::css::Css;
7use crate::data_type::Time;
8use crate::data_type_ext::EasingFunction;
9use crate::keyword::TransitionPropertyKind;
10use crate::to_css::ToCss;
11
12/// One transition layer.
13#[derive(Clone, Debug, PartialEq)]
14pub struct Transition {
15    /// Which property to transition.
16    pub property: TransitionPropertyKind,
17    /// Duration of the transition.
18    pub duration: Option<Time>,
19    /// Timing function.
20    pub timing: Option<EasingFunction>,
21    /// Delay before the transition starts.
22    pub delay: Option<Time>,
23}
24
25impl Transition {
26    /// Start with a property to transition.
27    pub fn new(property: TransitionPropertyKind) -> Self {
28        Self {
29            property,
30            duration: None,
31            timing: None,
32            delay: None,
33        }
34    }
35
36    /// Set duration.
37    pub fn duration(mut self, d: Time) -> Self {
38        self.duration = Some(d);
39        self
40    }
41
42    /// Set timing function.
43    pub fn timing(mut self, t: EasingFunction) -> Self {
44        self.timing = Some(t);
45        self
46    }
47
48    /// Set delay.
49    pub fn delay(mut self, d: Time) -> Self {
50        self.delay = Some(d);
51        self
52    }
53}
54
55impl ToCss for Transition {
56    fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
57        self.property.to_css(dest)?;
58        if let Some(d) = &self.duration {
59            dest.write_char(' ')?;
60            d.to_css(dest)?;
61        }
62        if let Some(t) = &self.timing {
63            dest.write_char(' ')?;
64            t.to_css(dest)?;
65        }
66        if let Some(d) = &self.delay {
67            dest.write_char(' ')?;
68            d.to_css(dest)?;
69        }
70        Ok(())
71    }
72}
73
74impl Css {
75    /// Sets the `transition` shorthand for a single transition.
76    /// <https://lynxjs.org/api/css/properties/transition>
77    pub fn transition(self, t: Transition) -> Self {
78        self.push("transition", t)
79    }
80
81    /// Sets the `transition` shorthand for multiple comma-separated
82    /// transitions.
83    pub fn transitions(self, ts: impl IntoIterator<Item = Transition>) -> Self {
84        let mut s = String::new();
85        for (i, t) in ts.into_iter().enumerate() {
86            if i > 0 {
87                s.push_str(", ");
88            }
89            let _ = t.to_css(&mut s);
90        }
91        self.push_raw("transition", s)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::data_type_ext::EasingFunction;
98    use crate::ext::*;
99    use crate::keyword::TransitionPropertyKind;
100    use crate::Css;
101
102    use super::*;
103
104    #[test]
105    fn transition_property_only() {
106        let s = Css::new().transition(Transition::new(TransitionPropertyKind::All));
107        assert_eq!(s.to_string(), "transition: all;");
108    }
109
110    #[test]
111    fn transition_property_duration_timing_delay() {
112        let s = Css::new().transition(
113            Transition::new(TransitionPropertyKind::name("opacity"))
114                .duration(300.ms())
115                .timing(EasingFunction::EaseInOut)
116                .delay(100.ms()),
117        );
118        assert_eq!(
119            s.to_string(),
120            "transition: opacity 300ms ease-in-out 100ms;"
121        );
122    }
123
124    #[test]
125    fn transitions_multiple_layers() {
126        let s = Css::new().transitions([
127            Transition::new(TransitionPropertyKind::name("opacity")).duration(300.ms()),
128            Transition::new(TransitionPropertyKind::name("transform"))
129                .duration(500.ms())
130                .delay(100.ms()),
131        ]);
132        assert_eq!(
133            s.to_string(),
134            "transition: opacity 300ms, transform 500ms 100ms;"
135        );
136    }
137}