use leptos_dom::{Fragment, IntoView, View};
use leptos_macro::component;
use leptos_reactive::{use_context, Scope, SignalSetter, SuspenseContext};
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
#[cfg_attr(
any(debug_assertions, feature = "ssr"),
tracing::instrument(level = "info", skip_all)
)]
#[component(transparent)]
pub fn Transition<F, E>(
cx: Scope,
fallback: F,
#[prop(optional)]
set_pending: Option<SignalSetter<bool>>,
children: Box<dyn Fn(Scope) -> Fragment>,
) -> impl IntoView
where
F: Fn() -> E + 'static,
E: IntoView,
{
let prev_children = Rc::new(RefCell::new(None::<Vec<View>>));
let first_run = Rc::new(std::cell::Cell::new(true));
let child_runs = Cell::new(0);
crate::Suspense(
cx,
crate::SuspenseProps::builder()
.fallback({
let prev_child = Rc::clone(&prev_children);
let first_run = Rc::clone(&first_run);
move || {
let suspense_context = use_context::<SuspenseContext>(cx)
.expect("there to be a SuspenseContext");
let is_first_run =
is_first_run(&first_run, &suspense_context);
first_run.set(is_first_run);
if let Some(set_pending) = &set_pending {
set_pending.set(true);
}
if let Some(prev_children) = &*prev_child.borrow() {
if is_first_run {
fallback().into_view(cx)
} else {
prev_children.clone().into_view(cx)
}
} else {
fallback().into_view(cx)
}
}
})
.children(Box::new(move |cx| {
let frag = children(cx);
let suspense_context = use_context::<SuspenseContext>(cx)
.expect("there to be a SuspenseContext");
if cfg!(feature = "hydrate") || !first_run.get() {
*prev_children.borrow_mut() = Some(frag.nodes.clone());
}
if is_first_run(&first_run, &suspense_context) {
let has_local_only = suspense_context.has_local_only()
|| cfg!(feature = "csr");
if !has_local_only || child_runs.get() > 0 {
first_run.set(false);
}
}
child_runs.set(child_runs.get() + 1);
if let Some(set_pending) = &set_pending {
set_pending.set(false);
}
frag
}))
.build(),
)
}
fn is_first_run(
first_run: &Rc<Cell<bool>>,
suspense_context: &SuspenseContext,
) -> bool {
if cfg!(feature = "csr") {
false
} else {
match (
first_run.get(),
cfg!(feature = "hydrate"),
suspense_context.has_local_only(),
) {
(false, _, _) => false,
(_, false, false) => false,
(_, false, true) => true,
(_, true, _) => true,
}
}
}