dioxus_animate/
lib.rs

1use std::{rc::Rc, time::Duration};
2
3use dioxus::{prelude::*, web::WebEventExt};
4
5#[derive(Clone, PartialEq)]
6pub enum Operation {
7    Add(String),
8    Remove(String),
9    Group(Vec<Operation>),
10}
11
12fn operate(patient: &web_sys::Element, operation: &Operation) {
13    match operation {
14        Operation::Add(class) => {
15            let classes = class.split_whitespace().collect::<Vec<&str>>();
16
17            for class in classes {
18                let _ = patient.class_list().add_1(class);
19            }
20        }
21        Operation::Remove(class) => {
22            let classes = class.split_whitespace().collect::<Vec<&str>>();
23
24            for class in classes {
25                let _ = patient.class_list().remove_1(class);
26            }
27        },
28        Operation::Group(operations) => {
29            for operation in operations {
30                operate(patient, operation);
31            }
32        }
33    }
34}
35
36#[macro_export]
37macro_rules! use_animate {
38    ( $($input:tt)* ) => {{
39        let operations = use_signal(|| {
40            let mut ops = Vec::new();
41            $crate::__parse_animations!(ops, $($input)*);
42            ops
43        });
44
45        $crate::_use_animate(operations.into())
46    }};
47}
48
49#[macro_export]
50macro_rules! __parse_animations {
51    ($ops:ident, $t:expr => add($s:literal), $($rest:tt)*) => {
52        $ops.push(($t, $crate::Operation::Add($s.to_string())));
53        $crate::__parse_animations!($ops, $($rest)*);
54    };
55
56    ($ops:ident, $t:expr => remove($s:literal), $($rest:tt)*) => {
57        $ops.push(($t, $crate::Operation::Remove($s.to_string())));
58        $crate::__parse_animations!($ops, $($rest)*);
59    };
60
61    ($ops:ident, $t:expr => ($($inner:tt)*), $($rest:tt)*) => {
62        $ops.push(($t, {
63            let mut group_ops = Vec::new();
64            $crate::__expand_inner!(group_ops, $($inner)*);
65            $crate::Operation::Group(group_ops)
66        }));
67        $crate::__parse_animations!($ops, $($rest)*);
68    };
69
70    ($ops:ident, $t:expr => add($s:literal)) => {
71        $ops.push(($t, $crate::Operation::Add($s.to_string())));
72    };
73
74    ($ops:ident, $t:expr => remove($s:literal)) => {
75        $ops.push(($t, $crate::Operation::Remove($s.to_string())));
76    };
77
78    ($ops:ident, $t:expr => ($($inner:tt)*)) => {
79        $ops.push(($t, {
80            let mut group_ops = Vec::new();
81            $crate::__expand_inner!(group_ops, $($inner)*);
82            $crate::Operation::Group(group_ops)
83        }));
84    };
85
86    ($ops:ident,) => {};
87}
88
89#[macro_export]
90macro_rules! __expand_inner {
91    ( $ops:ident, add($s:literal); $($rest:tt)* ) => {
92        $ops.push($crate::Operation::Add($s.to_string()));
93        $crate::__expand_inner!($ops, $($rest)*);
94    };
95    ( $ops:ident, remove($s:literal); $($rest:tt)* ) => {
96        $ops.push($crate::Operation::Remove($s.to_string()));
97        $crate::__expand_inner!($ops, $($rest)*);
98    };
99    ( $ops:ident, add($s:literal) ) => {
100        $ops.push($crate::Operation::Add($s.to_string()));
101    };
102    ( $ops:ident, remove($s:literal) ) => {
103        $ops.push($crate::Operation::Remove($s.to_string()));
104    };
105    ( $ops:ident, ) => {};
106}
107
108pub struct UseAnimate {
109    ops: Vec<(u64, Operation)>
110}
111
112impl UseAnimate {
113    pub fn start(&self, patient: ReadOnlySignal<Option<Rc<MountedData>>>) {
114        let ops = self.ops.clone();
115
116        spawn(async move {
117            let Some(element) = patient() else {
118                return;
119            };
120
121            let mut time_elapsed = 0;
122
123            for (duration, operation) in ops.into_iter() {
124                let interval = duration - time_elapsed;
125
126                dioxus_time::sleep(Duration::from_millis(interval)).await;
127                time_elapsed += interval;
128
129                let web_element = element.as_web_event();
130                operate(&web_element, &operation);
131            }
132        });
133    }
134}
135
136pub fn _use_animate(ops: ReadOnlySignal<Vec<(u64, Operation)>>) -> UseAnimate {
137    UseAnimate {
138        ops: ops()
139    }
140}
141
142pub mod prelude {
143    #[macro_use]
144    pub use crate::use_animate;
145}