oxiui_theme/
anim_tokens.rs1use std::collections::HashMap;
8
9#[derive(Clone, Debug, PartialEq)]
11pub enum EasingKind {
12 Linear,
14 EaseIn,
16 EaseOut,
18 EaseInOut,
20 CubicBezier(f32, f32, f32, f32),
22}
23
24#[derive(Clone, Debug, PartialEq)]
26pub struct TransitionSpec {
27 pub duration_ms: u64,
29 pub delay_ms: u64,
31 pub easing: EasingKind,
33}
34
35#[derive(Clone, Debug, PartialEq)]
37pub struct AnimationKeyframe {
38 pub offset: f32,
40 pub props: HashMap<String, String>,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum FillMode {
47 None,
49 Forwards,
51 Backwards,
53 Both,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub enum IterationCount {
60 Count(u32),
62 Infinite,
64}
65
66#[derive(Clone, Debug, PartialEq)]
68pub struct AnimationSpec {
69 pub keyframes: Vec<AnimationKeyframe>,
71 pub duration_ms: u64,
73 pub fill_mode: FillMode,
75 pub iteration_count: IterationCount,
77}
78
79pub fn fade_in() -> TransitionSpec {
83 TransitionSpec {
84 duration_ms: 150,
85 delay_ms: 0,
86 easing: EasingKind::EaseInOut,
87 }
88}
89
90pub fn slide_in() -> TransitionSpec {
92 TransitionSpec {
93 duration_ms: 200,
94 delay_ms: 0,
95 easing: EasingKind::EaseOut,
96 }
97}
98
99pub fn scale_up() -> TransitionSpec {
101 TransitionSpec {
102 duration_ms: 150,
103 delay_ms: 0,
104 easing: EasingKind::EaseInOut,
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn fade_in_preset_present() {
114 let t = fade_in();
115 assert!(t.duration_ms > 0, "fade_in must have a positive duration");
116 assert_eq!(t.easing, EasingKind::EaseInOut);
117 }
118
119 #[test]
120 fn slide_in_preset_present() {
121 let t = slide_in();
122 assert!(t.duration_ms > 0, "slide_in must have a positive duration");
123 assert_eq!(t.easing, EasingKind::EaseOut);
124 }
125
126 #[test]
127 fn scale_up_preset_present() {
128 let t = scale_up();
129 assert!(t.duration_ms > 0, "scale_up must have a positive duration");
130 assert_eq!(t.easing, EasingKind::EaseInOut);
131 }
132
133 #[test]
134 fn easing_kind_cubic_bezier_stores_values() {
135 let e = EasingKind::CubicBezier(0.25, 0.1, 0.25, 1.0);
136 if let EasingKind::CubicBezier(x1, y1, x2, y2) = e {
137 assert_eq!(x1, 0.25);
138 assert_eq!(y1, 0.1);
139 assert_eq!(x2, 0.25);
140 assert_eq!(y2, 1.0);
141 } else {
142 panic!("unexpected variant");
143 }
144 }
145
146 #[test]
147 fn animation_spec_builds() {
148 let mut props = HashMap::new();
149 props.insert("opacity".to_string(), "0".to_string());
150 let spec = AnimationSpec {
151 keyframes: vec![
152 AnimationKeyframe {
153 offset: 0.0,
154 props: props.clone(),
155 },
156 AnimationKeyframe {
157 offset: 1.0,
158 props: {
159 let mut p = HashMap::new();
160 p.insert("opacity".to_string(), "1".to_string());
161 p
162 },
163 },
164 ],
165 duration_ms: 300,
166 fill_mode: FillMode::Forwards,
167 iteration_count: IterationCount::Count(1),
168 };
169 assert_eq!(spec.keyframes.len(), 2);
170 assert_eq!(spec.duration_ms, 300);
171 }
172
173 #[test]
174 fn iteration_count_infinite() {
175 let ic = IterationCount::Infinite;
176 assert_eq!(ic, IterationCount::Infinite);
177 }
178}