use dioxus::prelude::*;
use wasm_bindgen::JsValue;
use crate::utils;
#[component]
pub fn Transition(
children: Element,
id: ReadOnlySignal<String>,
kind: ReadOnlySignal<String>,
duration: u32,
#[props(default)]
ignore_first: bool,
) -> Element {
#[cfg(all(feature = "builtins", not(feature = "ssr")))]
utils::int::inject_default_stylesheet();
let mut backup_element: Signal<Option<Element>> = use_signal(|| None);
let mut slot_element: Signal<Option<Element>> = use_signal(|| None);
let mut first_run = use_signal(|| true);
let root_element = use_callback(move |_: ()| {
let id = id();
element_by_id!(&id)
});
let backup_slot_element = use_callback(move |_: ()| {
let slot_element = slot_element();
backup_element.set(slot_element);
});
let wipe_backup_element = use_callback(move |_: ()| {
backup_element.set(None);
});
let record_the_run = use_callback(move |_: ()| {
let is_it_first_run = first_run();
if !is_it_first_run {
return;
}
first_run.set(false);
});
let hidden_class = use_memo(move || format!("{kind}-transition-hidden"));
let run_class = use_memo(move || format!("{kind}-transition-activating"));
let duration_ms = use_memo(move || format!("{duration}ms"));
let mut activate = use_future(move || async move {
if let Some(element) = root_element(()) {
let should_be_hidden = !first_run() || !ignore_first;
if should_be_hidden {
let hidden_class = hidden_class();
element.class_list().add_1(&hidden_class)?;
}
}
utils::dom::request_animation_frame().await;
let Some(element) = root_element(()) else {
return Ok(());
};
let run_class = run_class();
let duration_ms = duration_ms();
animate!(element => using &run_class => for &duration_ms => forwards);
wait_for!(duration).await;
backup_slot_element(());
record_the_run(());
Ok::<(), JsValue>(())
});
let mut deactivate = use_future(move || async move {
utils::dom::request_animation_frame().await;
let Some(element) = root_element(()) else {
return Ok(());
};
let run_class = run_class();
let duration_ms = duration_ms();
animate!(element => using &run_class => for &duration_ms => backwards);
wait_for!(duration).await;
wipe_backup_element(());
Ok::<(), JsValue>(())
});
let when_children_change = use_reactive((&children,), move |(children,)| {
let maybe_is_slot_placeholder = {
let children = children.clone();
utils::dioxus::maybe_is_element_placeholder(children)
};
let Some(is_slot_placeholder) = maybe_is_slot_placeholder else {
return;
};
if is_slot_placeholder {
deactivate.restart();
} else {
let indeed_children = Some(children.clone());
slot_element.set(indeed_children);
activate.restart();
}
});
use_effect(when_children_change);
rsx! {
Fragment {
{ backup_element().unwrap_or(children) }
}
}
}