dioxus_transition/
components.rs1use dioxus::prelude::*;
2use wasm_bindgen::JsValue;
3
4use crate::utils;
5
6#[component]
10pub fn Transition(
11 children: Element,
13
14 id: ReadSignal<String>,
16
17 kind: ReadSignal<String>,
23
24 duration: u32,
26
27 #[props(default)]
30 ignore_first: bool,
31) -> Element {
32 #[cfg(all(feature = "builtins", not(feature = "ssr")))]
33 utils::int::inject_default_stylesheet();
34
35 let mut backup_element: Signal<Option<Element>> = use_signal(|| None);
36 let mut slot_element: Signal<Option<Element>> = use_signal(|| None);
37 let mut first_run = use_signal(|| true);
38
39 let root_element = use_callback(move |_: ()| {
40 let id = id();
41 element_by_id!(&id)
42 });
43
44 let backup_slot_element = use_callback(move |_: ()| {
45 let slot_element = slot_element();
46 backup_element.set(slot_element);
47 });
48
49 let wipe_backup_element = use_callback(move |_: ()| {
50 backup_element.set(None);
51 });
52
53 let record_the_run = use_callback(move |_: ()| {
54 let is_it_first_run = first_run();
55
56 if !is_it_first_run {
57 return;
58 }
59
60 first_run.set(false);
61 });
62
63 let hidden_class = use_memo(move || format!("{kind}-transition-hidden"));
64 let run_class = use_memo(move || format!("{kind}-transition-activating"));
65 let duration_ms = use_memo(move || format!("{duration}ms"));
66
67 let mut activate = use_future(move || async move {
68 if let Some(element) = root_element(()) {
69 let should_be_hidden = !first_run() || !ignore_first;
70
71 if should_be_hidden {
72 let hidden_class = hidden_class();
73 element.class_list().add_1(&hidden_class)?;
74 }
75 }
76
77 utils::dom::request_animation_frame().await;
78
79 let Some(element) = root_element(()) else {
80 return Ok(());
81 };
82
83 let run_class = run_class();
84 let duration_ms = duration_ms();
85
86 animate!(element => using &run_class => for &duration_ms => forwards);
87 wait_for!(duration).await;
88
89 backup_slot_element(());
90 record_the_run(());
91
92 Ok::<(), JsValue>(())
93 });
94
95 let mut deactivate = use_future(move || async move {
96 utils::dom::request_animation_frame().await;
97
98 let Some(element) = root_element(()) else {
99 return Ok(());
100 };
101
102 let run_class = run_class();
103 let duration_ms = duration_ms();
104
105 animate!(element => using &run_class => for &duration_ms => backwards);
106 wait_for!(duration).await;
107
108 wipe_backup_element(());
109
110 Ok::<(), JsValue>(())
111 });
112
113 let when_children_change = use_reactive((&children,), move |(children,)| {
114 let maybe_is_slot_placeholder = {
115 let children = children.clone();
116 utils::dioxus::maybe_is_element_placeholder(children)
117 };
118
119 let Some(is_slot_placeholder) = maybe_is_slot_placeholder else {
120 return;
121 };
122
123 if is_slot_placeholder {
124 deactivate.restart();
125 } else {
126 let indeed_children = Some(children.clone());
127
128 slot_element.set(indeed_children);
129 activate.restart();
130 }
131 });
132
133 use_effect(when_children_change);
134
135 rsx! {
136 Fragment {
137 { backup_element().unwrap_or(children) }
138 }
139 }
140}