use crate::{innerlude::*, scope_context::SuspenseLocation};
#[allow(non_camel_case_types)]
pub struct SuspenseBoundaryProps {
fallback: Callback<SuspenseContext, Element>,
children: Element,
}
impl Clone for SuspenseBoundaryProps {
fn clone(&self) -> Self {
Self {
fallback: self.fallback,
children: self.children.clone(),
}
}
}
impl SuspenseBoundaryProps {
#[allow(dead_code, clippy::type_complexity)]
fn builder() -> SuspenseBoundaryPropsBuilder<((), ())> {
SuspenseBoundaryPropsBuilder {
owner: Owner::default(),
fields: ((), ()),
_phantom: ::core::default::Default::default(),
}
}
}
#[must_use]
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub struct SuspenseBoundaryPropsBuilder<TypedBuilderFields> {
owner: Owner,
fields: TypedBuilderFields,
_phantom: (),
}
impl Properties for SuspenseBoundaryProps
where
Self: Clone,
{
type Builder = SuspenseBoundaryPropsBuilder<((), ())>;
fn builder() -> Self::Builder {
SuspenseBoundaryProps::builder()
}
fn memoize(&mut self, new: &Self) -> bool {
let equal = self == new;
self.fallback.__point_to(&new.fallback);
if !equal {
let new_clone = new.clone();
self.children = new_clone.children;
}
equal
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub trait SuspenseBoundaryPropsBuilder_Optional<T> {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
}
impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for () {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
default()
}
}
impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for (T,) {
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
self.0
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
#[allow(clippy::type_complexity)]
pub fn fallback<__Marker>(
self,
fallback: impl SuperInto<Callback<SuspenseContext, Element>, __Marker>,
) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
let fallback = (with_owner(self.owner.clone(), move || {
SuperInto::super_into(fallback)
}),);
let (_, children) = self.fields;
SuspenseBoundaryPropsBuilder {
owner: self.owner,
fields: (fallback, children),
_phantom: self._phantom,
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__children> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
#[deprecated(note = "Repeated field fallback")]
#[allow(clippy::type_complexity)]
pub fn fallback(
self,
_: SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback,
) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
self
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, ())> {
#[allow(clippy::type_complexity)]
pub fn children(
self,
children: Element,
) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
let children = (children,);
let (fallback, _) = self.fields;
SuspenseBoundaryPropsBuilder {
owner: self.owner,
fields: (fallback, children),
_phantom: self._phantom,
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_children {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
#[deprecated(note = "Repeated field children")]
#[allow(clippy::type_complexity)]
pub fn children(
self,
_: SuspenseBoundaryPropsBuilder_Error_Repeated_field_children,
) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
self
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub enum SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs, clippy::panic)]
impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
#[deprecated(note = "Missing required field fallback")]
pub fn build(
self,
_: SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback,
) -> SuspenseBoundaryProps {
panic!()
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
pub struct SuspenseBoundaryPropsWithOwner {
inner: SuspenseBoundaryProps,
owner: Owner,
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl ::core::clone::Clone for SuspenseBoundaryPropsWithOwner {
#[inline]
fn clone(&self) -> SuspenseBoundaryPropsWithOwner {
SuspenseBoundaryPropsWithOwner {
inner: ::core::clone::Clone::clone(&self.inner),
owner: ::core::clone::Clone::clone(&self.owner),
}
}
}
impl PartialEq for SuspenseBoundaryPropsWithOwner {
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl SuspenseBoundaryPropsWithOwner {
pub fn into_vcomponent<M: 'static>(
self,
render_fn: impl ComponentFunction<SuspenseBoundaryProps, M>,
) -> VComponent {
let component_name = std::any::type_name_of_val(&render_fn);
VComponent::new(
move |wrapper: Self| render_fn.rebuild(wrapper.inner),
self,
component_name,
)
}
}
impl Properties for SuspenseBoundaryPropsWithOwner {
type Builder = ();
fn builder() -> Self::Builder {
unreachable!()
}
fn memoize(&mut self, new: &Self) -> bool {
self.inner.memoize(&new.inner)
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__children: SuspenseBoundaryPropsBuilder_Optional<Element>>
SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)>
{
pub fn build(self) -> SuspenseBoundaryPropsWithOwner {
let (fallback, children) = self.fields;
let fallback = fallback.0;
let children = SuspenseBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
SuspenseBoundaryPropsWithOwner {
inner: SuspenseBoundaryProps { fallback, children },
owner: self.owner,
}
}
}
#[automatically_derived]
#[allow(non_camel_case_types)]
impl ::core::cmp::PartialEq for SuspenseBoundaryProps {
#[inline]
fn eq(&self, other: &SuspenseBoundaryProps) -> bool {
self.fallback == other.fallback && self.children == other.children
}
}
#[allow(non_snake_case)]
pub fn SuspenseBoundary(mut __props: SuspenseBoundaryProps) -> Element {
unreachable!("SuspenseBoundary should not be called directly")
}
#[allow(non_snake_case)]
#[doc(hidden)]
mod SuspenseBoundary_completions {
#[doc(hidden)]
#[allow(non_camel_case_types)]
pub enum Component {
SuspenseBoundary {},
}
}
use generational_box::Owner;
#[allow(unused)]
pub use SuspenseBoundary_completions::Component::SuspenseBoundary;
impl SuspenseBoundaryProps {
pub(crate) fn downcast_from_props(props: &mut dyn AnyProps) -> Option<&mut Self> {
let inner: Option<&mut SuspenseBoundaryPropsWithOwner> = props.props_mut().downcast_mut();
inner.map(|inner| &mut inner.inner)
}
pub(crate) fn create<M: WriteMutations>(
mount: MountId,
idx: usize,
component: &VComponent,
parent: Option<ElementRef>,
dom: &mut VirtualDom,
to: Option<&mut M>,
) -> usize {
let mut scope_id = ScopeId(dom.get_mounted_dyn_node(mount, idx));
if scope_id.is_placeholder() {
{
let suspense_context = SuspenseContext::new();
let suspense_boundary_location =
crate::scope_context::SuspenseLocation::SuspenseBoundary(
suspense_context.clone(),
);
dom.runtime
.clone()
.with_suspense_location(suspense_boundary_location, || {
let scope_state = dom
.new_scope(component.props.duplicate(), component.name)
.state();
suspense_context.mount(scope_state.id);
scope_id = scope_state.id;
});
}
dom.set_mounted_dyn_node(mount, idx, scope_id.0);
}
dom.runtime.clone().with_scope_on_stack(scope_id, || {
let scope_state = &mut dom.scopes[scope_id.0];
let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
let suspense_context =
SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
.unwrap();
let children = props.children.clone();
suspense_context.under_suspense_boundary(&dom.runtime(), || {
children.as_vnode().create(dom, parent, None::<&mut M>);
});
let scope_state = &mut dom.scopes[scope_id.0];
let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
props.children.clone_from(&children);
let scope_state = &mut dom.scopes[scope_id.0];
let suspense_context = scope_state
.state()
.suspense_location()
.suspense_context()
.unwrap()
.clone();
let nodes_created = if !suspense_context.suspended_futures().is_empty() {
let (node, nodes_created) =
suspense_context.in_suspense_placeholder(&dom.runtime(), || {
let scope_state = &mut dom.scopes[scope_id.0];
let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
let suspense_context =
SuspenseContext::downcast_suspense_boundary_from_scope(
&dom.runtime,
scope_id,
)
.unwrap();
suspense_context.set_suspended_nodes(children.into());
let suspense_placeholder = props.fallback.call(suspense_context);
let nodes_created = suspense_placeholder.as_vnode().create(dom, parent, to);
(suspense_placeholder, nodes_created)
});
let scope_state = &mut dom.scopes[scope_id.0];
scope_state.last_rendered_node = Some(node);
nodes_created
} else {
debug_assert!(children.as_vnode().mount.get().mounted());
let nodes_created = suspense_context
.under_suspense_boundary(&dom.runtime(), || {
children.as_vnode().create(dom, parent, to)
});
let scope_state = &mut dom.scopes[scope_id.0];
scope_state.last_rendered_node = Some(children);
let suspense_context =
SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
.unwrap();
suspense_context.take_suspended_nodes();
mark_suspense_resolved(dom, scope_id);
nodes_created
};
nodes_created
})
}
#[doc(hidden)]
pub fn resolve_suspense<M: WriteMutations>(
scope_id: ScopeId,
dom: &mut VirtualDom,
to: &mut M,
only_write_templates: impl FnOnce(&mut M),
replace_with: usize,
) {
dom.runtime.clone().with_scope_on_stack(scope_id, || {
let _runtime = RuntimeGuard::new(dom.runtime());
let Some(scope_state) = dom.scopes.get_mut(scope_id.0) else {
return;
};
let suspense_context = scope_state
.state()
.suspense_location()
.suspense_context()
.unwrap()
.clone();
suspense_context.inner.suspended_tasks.borrow_mut().clear();
let currently_rendered = scope_state.last_rendered_node.as_ref().unwrap().clone();
let mount = currently_rendered.as_vnode().mount.get();
let parent = {
let mounts = dom.runtime.mounts.borrow();
mounts
.get(mount.0)
.expect("suspense placeholder is not mounted")
.parent
};
let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
let children = props.children.clone();
let suspense_context =
SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
.unwrap();
let suspended = suspense_context.take_suspended_nodes();
if let Some(node) = suspended {
node.remove_node(&mut *dom, None::<&mut M>, None);
}
currently_rendered
.as_vnode()
.remove_node(&mut *dom, Some(to), Some(replace_with));
only_write_templates(to);
children.as_vnode().mount.take();
suspense_context.under_suspense_boundary(&dom.runtime(), || {
children.as_vnode().create(dom, parent, Some(to));
});
let scope_state = &mut dom.scopes[scope_id.0];
let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
props.children.clone_from(&children);
scope_state.last_rendered_node = Some(children);
})
}
pub(crate) fn diff<M: WriteMutations>(
scope_id: ScopeId,
dom: &mut VirtualDom,
to: Option<&mut M>,
) {
dom.runtime.clone().with_scope_on_stack(scope_id, || {
let scope = &mut dom.scopes[scope_id.0];
let myself = Self::downcast_from_props(&mut *scope.props)
.unwrap()
.clone();
let last_rendered_node = scope.last_rendered_node.as_ref().unwrap().clone();
let Self {
fallback, children, ..
} = myself;
let suspense_context = scope.state().suspense_boundary().unwrap().clone();
let suspended_nodes = suspense_context.suspended_nodes();
let suspended = !suspense_context.suspended_futures().is_empty();
match (suspended_nodes, suspended) {
(Some(suspended_nodes), true) => {
let new_suspended_nodes: VNode = children.into();
let new_placeholder =
suspense_context.in_suspense_placeholder(&dom.runtime(), || {
let old_placeholder = last_rendered_node;
let new_placeholder = fallback.call(suspense_context.clone());
old_placeholder.as_vnode().diff_node(
new_placeholder.as_vnode(),
dom,
to,
);
new_placeholder
});
dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
suspense_context.under_suspense_boundary(&dom.runtime(), || {
suspended_nodes.diff_node(&new_suspended_nodes, dom, None::<&mut M>);
});
let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
&dom.runtime,
scope_id,
)
.unwrap();
suspense_context.set_suspended_nodes(new_suspended_nodes);
}
(None, false) => {
let old_children = last_rendered_node;
let new_children = children;
suspense_context.under_suspense_boundary(&dom.runtime(), || {
old_children
.as_vnode()
.diff_node(new_children.as_vnode(), dom, to);
});
dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
}
(None, true) => {
let old_children = last_rendered_node.as_vnode();
let new_children: VNode = children.into();
let new_placeholder = fallback.call(suspense_context.clone());
let mount = old_children.mount.get();
let parent = dom.get_mounted_parent(mount);
suspense_context.in_suspense_placeholder(&dom.runtime(), || {
old_children.move_node_to_background(
std::slice::from_ref(new_placeholder.as_vnode()),
parent,
dom,
to,
);
});
suspense_context.under_suspense_boundary(&dom.runtime(), || {
old_children.diff_node(&new_children, dom, None::<&mut M>);
});
dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
&dom.runtime,
scope_id,
)
.unwrap();
suspense_context.set_suspended_nodes(new_children);
un_resolve_suspense(dom, scope_id);
}
(Some(_), false) => {
let old_suspended_nodes = suspense_context.take_suspended_nodes().unwrap();
let old_placeholder = last_rendered_node;
let new_children = children;
suspense_context.under_suspense_boundary(&dom.runtime(), || {
old_suspended_nodes.diff_node(new_children.as_vnode(), dom, None::<&mut M>);
let mount = old_placeholder.as_vnode().mount.get();
let parent = dom.get_mounted_parent(mount);
old_placeholder.as_vnode().replace(
std::slice::from_ref(new_children.as_vnode()),
parent,
dom,
to,
);
});
dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
mark_suspense_resolved(dom, scope_id);
}
}
})
}
}
fn mark_suspense_resolved(dom: &mut VirtualDom, scope_id: ScopeId) {
dom.resolved_scopes.push(scope_id);
}
fn un_resolve_suspense(dom: &mut VirtualDom, scope_id: ScopeId) {
dom.resolved_scopes.retain(|&id| id != scope_id);
}
impl SuspenseContext {
pub fn under_suspense_boundary<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
runtime.with_suspense_location(SuspenseLocation::UnderSuspense(self.clone()), f)
}
pub fn in_suspense_placeholder<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
runtime.with_suspense_location(SuspenseLocation::InSuspensePlaceholder(self.clone()), f)
}
pub fn downcast_suspense_boundary_from_scope(
runtime: &Runtime,
scope_id: ScopeId,
) -> Option<Self> {
runtime
.get_state(scope_id)
.and_then(|scope| scope.suspense_boundary())
}
pub(crate) fn remove_suspended_nodes<M: WriteMutations>(
dom: &mut VirtualDom,
scope_id: ScopeId,
destroy_component_state: bool,
) {
let Some(scope) = Self::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
else {
return;
};
if let Some(node) = scope.take_suspended_nodes() {
node.remove_node_inner(dom, None::<&mut M>, destroy_component_state, None)
}
}
}