use core::fmt;
use crate::css::Css;
use crate::data_type::Time;
use crate::data_type_ext::EasingFunction;
use crate::keyword::{
AnimationDirection, AnimationFillMode, AnimationIterationCount, AnimationPlayState,
};
use crate::to_css::ToCss;
#[derive(Clone, Debug, PartialEq)]
pub struct Animation {
pub name: String,
pub duration: Option<Time>,
pub timing: Option<EasingFunction>,
pub delay: Option<Time>,
pub iteration_count: Option<AnimationIterationCount>,
pub direction: Option<AnimationDirection>,
pub fill_mode: Option<AnimationFillMode>,
pub play_state: Option<AnimationPlayState>,
}
impl Animation {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
duration: None,
timing: None,
delay: None,
iteration_count: None,
direction: None,
fill_mode: None,
play_state: None,
}
}
pub fn duration(mut self, d: Time) -> Self {
self.duration = Some(d);
self
}
pub fn timing(mut self, t: EasingFunction) -> Self {
self.timing = Some(t);
self
}
pub fn delay(mut self, d: Time) -> Self {
self.delay = Some(d);
self
}
pub fn iteration_count(mut self, c: AnimationIterationCount) -> Self {
self.iteration_count = Some(c);
self
}
pub fn direction(mut self, d: AnimationDirection) -> Self {
self.direction = Some(d);
self
}
pub fn fill_mode(mut self, f: AnimationFillMode) -> Self {
self.fill_mode = Some(f);
self
}
pub fn play_state(mut self, p: AnimationPlayState) -> Self {
self.play_state = Some(p);
self
}
}
impl ToCss for Animation {
fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
dest.write_str(&self.name)?;
if let Some(d) = &self.duration {
dest.write_char(' ')?;
d.to_css(dest)?;
}
if let Some(t) = &self.timing {
dest.write_char(' ')?;
t.to_css(dest)?;
}
if let Some(d) = &self.delay {
dest.write_char(' ')?;
d.to_css(dest)?;
}
if let Some(c) = &self.iteration_count {
dest.write_char(' ')?;
c.to_css(dest)?;
}
if let Some(d) = &self.direction {
dest.write_char(' ')?;
d.to_css(dest)?;
}
if let Some(f) = &self.fill_mode {
dest.write_char(' ')?;
f.to_css(dest)?;
}
if let Some(p) = &self.play_state {
dest.write_char(' ')?;
p.to_css(dest)?;
}
Ok(())
}
}
impl Css {
pub fn animation(self, a: Animation) -> Self {
self.push("animation", a)
}
pub fn animations(self, anims: impl IntoIterator<Item = Animation>) -> Self {
let mut s = String::new();
for (i, a) in anims.into_iter().enumerate() {
if i > 0 {
s.push_str(", ");
}
let _ = a.to_css(&mut s);
}
self.push_raw("animation", s)
}
}
#[cfg(test)]
mod tests {
use crate::data_type_ext::EasingFunction;
use crate::ext::*;
use crate::keyword::*;
use crate::Css;
use super::*;
#[test]
fn animation_name_only() {
let s = Css::new().animation(Animation::new("spin"));
assert_eq!(s.to_string(), "animation: spin;");
}
#[test]
fn animation_full_shorthand() {
let s = Css::new().animation(
Animation::new("spin")
.duration(1.s())
.timing(EasingFunction::Linear)
.delay(100.ms())
.iteration_count(AnimationIterationCount::Infinite)
.direction(AnimationDirection::Alternate)
.fill_mode(AnimationFillMode::Forwards)
.play_state(AnimationPlayState::Running),
);
assert_eq!(
s.to_string(),
"animation: spin 1s linear 100ms infinite alternate forwards running;"
);
}
#[test]
fn animations_multiple() {
let s = Css::new().animations([
Animation::new("fade").duration(300.ms()),
Animation::new("slide").duration(500.ms()).delay(100.ms()),
]);
assert_eq!(s.to_string(), "animation: fade 300ms, slide 500ms 100ms;");
}
}