1use crate::{
2 unit::{sec, Ms, Sec},
3 Style, StyleUpdater,
4};
5use derive_rich::Rich;
6use indexmap::IndexMap;
7use std::{borrow::Cow, fmt};
8
9#[derive(Clone, Debug, PartialEq, From, Display)]
22pub enum Transition {
23 #[from(forward)]
24 One(TransitionValue),
25 #[display(
26 fmt = "{}",
27 "_0.iter().map(|(prop, trans)| format!(\"{} {}\", prop, trans)).collect::<Vec<_>>().join(\", \")"
28 )]
29 Multiple(IndexMap<Cow<'static, str>, TransitionValue>),
30 #[display(fmt = "initial")]
31 Initial,
32 #[display(fmt = "inherit")]
33 Inherit,
34 #[display(fmt = "none")]
35 None,
36 #[display(fmt = "unset")]
37 Unset,
38}
39
40impl Default for Transition {
41 fn default() -> Self {
42 Transition::Multiple(IndexMap::default())
43 }
44}
45
46impl StyleUpdater for Transition {
47 fn update_style(self, style: Style) -> Style {
48 style.insert("transition", self)
49 }
50}
51
52impl Transition {
53 fn transition(mut self, conf: impl FnOnce(TransitionValue) -> TransitionValue) -> Self {
54 self = match self {
55 Self::One(val) => Self::One(conf(val)),
56 _ => Self::One(conf(TransitionValue::default())),
57 };
58 self
59 }
60
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 pub fn duration(self, val: impl Into<Duration>) -> Self {
66 self.transition(|t| t.duration(val))
67 }
68
69 pub fn timing_function(self, val: impl Into<TimingFunction>) -> Self {
70 self.transition(|t| t.timing_function(val))
71 }
72
73 pub fn try_timing_function(self, val: Option<impl Into<TimingFunction>>) -> Self {
74 self.transition(|t| t.try_timing_function(val))
75 }
76
77 pub fn ease(self) -> Self {
78 self.transition(|t| t.ease())
79 }
80
81 pub fn linear(self) -> Self {
82 self.transition(|t| t.linear())
83 }
84
85 pub fn ease_in(self) -> Self {
86 self.transition(|t| t.ease_in())
87 }
88
89 pub fn ease_out(self) -> Self {
90 self.transition(|t| t.ease_out())
91 }
92
93 pub fn ease_in_out(self) -> Self {
94 self.transition(|t| t.ease_in_out())
95 }
96
97 pub fn step_start(self) -> Self {
98 self.transition(|t| t.step_start())
99 }
100
101 pub fn step_end(self) -> Self {
102 self.transition(|t| t.step_end())
103 }
104
105 pub fn steps(self, intervals: usize, pos: impl Into<StepsPos>) -> Self {
106 self.transition(|t| t.steps(intervals, pos))
107 }
108
109 pub fn cubic_bezier(self, n1: f32, n2: f32, n3: f32, n4: f32) -> Self {
110 self.transition(|t| t.cubic_bezier(n1, n2, n3, n4))
111 }
112
113 pub fn delay(self, val: impl Into<Delay>) -> Self {
114 self.transition(|t| t.delay(val))
115 }
116
117 pub fn try_delay(self, val: Option<impl Into<Delay>>) -> Self {
118 self.transition(|t| t.try_delay(val))
119 }
120
121 #[allow(clippy::should_implement_trait)]
122 pub fn insert(
123 mut self,
124 property: impl Into<Cow<'static, str>>,
125 get_val: impl FnOnce(TransitionValue) -> TransitionValue,
126 ) -> Self {
127 let val = get_val(TransitionValue::default());
128 self = match self {
129 Self::Multiple(mut map) => {
130 map.insert(property.into(), val);
131 Self::Multiple(map)
132 }
133 _ => {
134 let mut map = IndexMap::default();
135 map.insert(property.into(), val);
136 Self::Multiple(map)
137 }
138 };
139 self
140 }
141
142 pub fn foreach(
143 mut self,
144 properties: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
145 f: impl FnOnce(TransitionValue) -> TransitionValue + Clone,
146 ) -> Self {
147 for prop in properties.into_iter() {
148 self = self.insert(prop.into(), f.clone());
149 }
150 self
151 }
152}
153
154#[derive(Rich, Clone, Debug, PartialEq, From)]
155pub struct TransitionValue {
156 #[rich(write)]
157 pub duration: Duration,
158 #[rich(write(rename = timing_function), write(option, rename = try_timing_function), value_fns = {
159 ease = TimingFunction::Ease,
160 linear = TimingFunction::Linear,
161 ease_in = TimingFunction::EaseIn,
162 ease_out = TimingFunction::EaseOut,
163 ease_in_out = TimingFunction::EaseInOut,
164 step_start = TimingFunction::StepStart,
165 step_end = TimingFunction::StepEnd,
166 })]
167 pub timing_function: Option<TimingFunction>,
168 #[rich(write, write(option))]
169 pub delay: Option<Delay>,
170}
171
172impl fmt::Display for TransitionValue {
173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174 write!(f, "{}", self.duration)?;
175
176 if let Some(timing_fn) = self.timing_function {
177 write!(f, " {}", timing_fn)?;
178 }
179
180 if let Some(delay) = self.delay {
181 write!(f, " {}", delay)?;
182 }
183
184 Ok(())
185 }
186}
187
188impl Default for TransitionValue {
189 fn default() -> Self {
190 Self::new(Duration::Unset)
191 }
192}
193
194impl TransitionValue {
195 pub fn new(duration: impl Into<Duration>) -> Self {
196 Self {
197 duration: duration.into(),
198 timing_function: None,
199 delay: None,
200 }
201 }
202
203 pub fn steps(mut self, intervals: usize, pos: impl Into<StepsPos>) -> Self {
204 self.timing_function = Some(TimingFunction::Steps(intervals, pos.into()));
205 self
206 }
207
208 pub fn cubic_bezier(mut self, n1: f32, n2: f32, n3: f32, n4: f32) -> Self {
209 self.timing_function = Some(TimingFunction::CubicBezier(n1, n2, n3, n4));
210 self
211 }
212}
213
214impl<T> From<T> for TransitionValue
215where
216 T: Into<Duration>,
217{
218 fn from(source: T) -> Self {
219 TransitionValue::new(source)
220 }
221}
222
223#[derive(Clone, Debug, Copy, PartialEq, Display, From)]
224pub enum TimingFunction {
225 #[display(fmt = "ease")]
226 Ease,
227 #[display(fmt = "linear")]
228 Linear,
229 #[display(fmt = "ease-in")]
230 EaseIn,
231 #[display(fmt = "ease-out")]
232 EaseOut,
233 #[display(fmt = "ease-in-out")]
234 EaseInOut,
235 #[display(fmt = "step-start")]
236 StepStart,
237 #[display(fmt = "step-end")]
238 StepEnd,
239 #[display(fmt = "steps({}, {})", _0, _1)]
240 Steps(usize, StepsPos),
241 #[display(fmt = "cubic-bezier({}, {}, {}, {})", _0, _1, _2, _3)]
242 CubicBezier(f32, f32, f32, f32),
243 #[display(fmt = "initial")]
244 Initial,
245 #[display(fmt = "inherit")]
246 Inherit,
247}
248
249#[derive(Clone, Debug, Copy, PartialEq, Display, From)]
250pub enum StepsPos {
251 #[display(fmt = "start")]
252 Start,
253 #[display(fmt = "end")]
254 End,
255}
256
257#[derive(Clone, Debug, Copy, PartialEq, Display, From)]
258pub enum Duration {
259 #[display(fmt = "initial")]
260 Initial,
261 #[display(fmt = "inherit")]
262 Inherit,
263 #[display(fmt = "unset")]
264 Unset,
265 Ms(Ms),
266 Sec(Sec),
267}
268
269impl From<std::time::Duration> for Duration {
270 fn from(source: std::time::Duration) -> Self {
271 sec(source.as_secs_f32()).into()
272 }
273}
274
275pub type Delay = Duration;