1use crate::{innerlude::*, scope_context::SuspenseLocation};
2
3#[allow(non_camel_case_types)]
5pub struct SuspenseBoundaryProps {
6 fallback: Callback<SuspenseContext, Element>,
7 children: Element,
9}
10
11impl Clone for SuspenseBoundaryProps {
12 fn clone(&self) -> Self {
13 Self {
14 fallback: self.fallback,
15 children: self.children.clone(),
16 }
17 }
18}
19
20impl SuspenseBoundaryProps {
21 #[allow(dead_code, clippy::type_complexity)]
27 fn builder() -> SuspenseBoundaryPropsBuilder<((), ())> {
28 SuspenseBoundaryPropsBuilder {
29 owner: Owner::default(),
30 fields: ((), ()),
31 _phantom: ::core::default::Default::default(),
32 }
33 }
34}
35#[must_use]
36#[doc(hidden)]
37#[allow(dead_code, non_camel_case_types, non_snake_case)]
38pub struct SuspenseBoundaryPropsBuilder<TypedBuilderFields> {
39 owner: Owner,
40 fields: TypedBuilderFields,
41 _phantom: (),
42}
43impl Properties for SuspenseBoundaryProps
44where
45 Self: Clone,
46{
47 type Builder = SuspenseBoundaryPropsBuilder<((), ())>;
48 fn builder() -> Self::Builder {
49 SuspenseBoundaryProps::builder()
50 }
51 fn memoize(&mut self, new: &Self) -> bool {
52 let equal = self == new;
53 self.fallback.__point_to(&new.fallback);
54 if !equal {
55 let new_clone = new.clone();
56 self.children = new_clone.children;
57 }
58 equal
59 }
60}
61#[doc(hidden)]
62#[allow(dead_code, non_camel_case_types, non_snake_case)]
63pub trait SuspenseBoundaryPropsBuilder_Optional<T> {
64 fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
65}
66impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for () {
67 fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
68 default()
69 }
70}
71impl<T> SuspenseBoundaryPropsBuilder_Optional<T> for (T,) {
72 fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
73 self.0
74 }
75}
76#[allow(dead_code, non_camel_case_types, missing_docs)]
77impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
78 #[allow(clippy::type_complexity)]
79 pub fn fallback<__Marker>(
80 self,
81 fallback: impl SuperInto<Callback<SuspenseContext, Element>, __Marker>,
82 ) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
83 let fallback = (with_owner(self.owner.clone(), move || {
84 SuperInto::super_into(fallback)
85 }),);
86 let (_, children) = self.fields;
87 SuspenseBoundaryPropsBuilder {
88 owner: self.owner,
89 fields: (fallback, children),
90 _phantom: self._phantom,
91 }
92 }
93}
94#[doc(hidden)]
95#[allow(dead_code, non_camel_case_types, non_snake_case)]
96pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback {}
97#[doc(hidden)]
98#[allow(dead_code, non_camel_case_types, missing_docs)]
99impl<__children> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
100 #[deprecated(note = "Repeated field fallback")]
101 #[allow(clippy::type_complexity)]
102 pub fn fallback(
103 self,
104 _: SuspenseBoundaryPropsBuilder_Error_Repeated_field_fallback,
105 ) -> SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)> {
106 self
107 }
108}
109#[allow(dead_code, non_camel_case_types, missing_docs)]
110impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, ())> {
111 #[allow(clippy::type_complexity)]
112 pub fn children(
113 self,
114 children: Element,
115 ) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
116 let children = (children,);
117 let (fallback, _) = self.fields;
118 SuspenseBoundaryPropsBuilder {
119 owner: self.owner,
120 fields: (fallback, children),
121 _phantom: self._phantom,
122 }
123 }
124}
125#[doc(hidden)]
126#[allow(dead_code, non_camel_case_types, non_snake_case)]
127pub enum SuspenseBoundaryPropsBuilder_Error_Repeated_field_children {}
128#[doc(hidden)]
129#[allow(dead_code, non_camel_case_types, missing_docs)]
130impl<__fallback> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
131 #[deprecated(note = "Repeated field children")]
132 #[allow(clippy::type_complexity)]
133 pub fn children(
134 self,
135 _: SuspenseBoundaryPropsBuilder_Error_Repeated_field_children,
136 ) -> SuspenseBoundaryPropsBuilder<(__fallback, (Element,))> {
137 self
138 }
139}
140#[doc(hidden)]
141#[allow(dead_code, non_camel_case_types, non_snake_case)]
142pub enum SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback {}
143#[doc(hidden)]
144#[allow(dead_code, non_camel_case_types, missing_docs, clippy::panic)]
145impl<__children> SuspenseBoundaryPropsBuilder<((), __children)> {
146 #[deprecated(note = "Missing required field fallback")]
147 pub fn build(
148 self,
149 _: SuspenseBoundaryPropsBuilder_Error_Missing_required_field_fallback,
150 ) -> SuspenseBoundaryProps {
151 panic!()
152 }
153}
154#[doc(hidden)]
155#[allow(dead_code, non_camel_case_types, missing_docs)]
156pub struct SuspenseBoundaryPropsWithOwner {
157 inner: SuspenseBoundaryProps,
158 owner: Owner,
159}
160#[automatically_derived]
161#[allow(dead_code, non_camel_case_types, missing_docs)]
162impl ::core::clone::Clone for SuspenseBoundaryPropsWithOwner {
163 #[inline]
164 fn clone(&self) -> SuspenseBoundaryPropsWithOwner {
165 SuspenseBoundaryPropsWithOwner {
166 inner: ::core::clone::Clone::clone(&self.inner),
167 owner: ::core::clone::Clone::clone(&self.owner),
168 }
169 }
170}
171impl PartialEq for SuspenseBoundaryPropsWithOwner {
172 fn eq(&self, other: &Self) -> bool {
173 self.inner.eq(&other.inner)
174 }
175}
176impl SuspenseBoundaryPropsWithOwner {
177 pub fn into_vcomponent<M: 'static>(
179 self,
180 render_fn: impl ComponentFunction<SuspenseBoundaryProps, M>,
181 ) -> VComponent {
182 let component_name = std::any::type_name_of_val(&render_fn);
183 VComponent::new(
184 move |wrapper: Self| render_fn.rebuild(wrapper.inner),
185 self,
186 component_name,
187 )
188 }
189}
190impl Properties for SuspenseBoundaryPropsWithOwner {
191 type Builder = ();
192 fn builder() -> Self::Builder {
193 unreachable!()
194 }
195 fn memoize(&mut self, new: &Self) -> bool {
196 self.inner.memoize(&new.inner)
197 }
198}
199#[allow(dead_code, non_camel_case_types, missing_docs)]
200impl<__children: SuspenseBoundaryPropsBuilder_Optional<Element>>
201 SuspenseBoundaryPropsBuilder<((Callback<SuspenseContext, Element>,), __children)>
202{
203 pub fn build(self) -> SuspenseBoundaryPropsWithOwner {
204 let (fallback, children) = self.fields;
205 let fallback = fallback.0;
206 let children = SuspenseBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
207 SuspenseBoundaryPropsWithOwner {
208 inner: SuspenseBoundaryProps { fallback, children },
209 owner: self.owner,
210 }
211 }
212}
213#[automatically_derived]
214#[allow(non_camel_case_types)]
215impl ::core::cmp::PartialEq for SuspenseBoundaryProps {
216 #[inline]
217 fn eq(&self, other: &SuspenseBoundaryProps) -> bool {
218 self.fallback == other.fallback && self.children == other.children
219 }
220}
221
222#[allow(non_snake_case)]
245pub fn SuspenseBoundary(mut __props: SuspenseBoundaryProps) -> Element {
246 unreachable!("SuspenseBoundary should not be called directly")
247}
248#[allow(non_snake_case)]
249#[doc(hidden)]
250mod SuspenseBoundary_completions {
251 #[doc(hidden)]
252 #[allow(non_camel_case_types)]
253 pub enum Component {
255 SuspenseBoundary {},
256 }
257}
258use generational_box::Owner;
259#[allow(unused)]
260pub use SuspenseBoundary_completions::Component::SuspenseBoundary;
261
262impl SuspenseBoundaryProps {
264 pub(crate) fn downcast_from_props(props: &mut dyn AnyProps) -> Option<&mut Self> {
266 let inner: Option<&mut SuspenseBoundaryPropsWithOwner> = props.props_mut().downcast_mut();
267 inner.map(|inner| &mut inner.inner)
268 }
269
270 pub(crate) fn create<M: WriteMutations>(
271 mount: MountId,
272 idx: usize,
273 component: &VComponent,
274 parent: Option<ElementRef>,
275 dom: &mut VirtualDom,
276 to: Option<&mut M>,
277 ) -> usize {
278 let mut scope_id = ScopeId(dom.get_mounted_dyn_node(mount, idx));
279 if scope_id.is_placeholder() {
281 {
282 let suspense_context = SuspenseContext::new();
283
284 let suspense_boundary_location =
285 crate::scope_context::SuspenseLocation::SuspenseBoundary(
286 suspense_context.clone(),
287 );
288 dom.runtime
289 .clone()
290 .with_suspense_location(suspense_boundary_location, || {
291 let scope_state = dom
292 .new_scope(component.props.duplicate(), component.name)
293 .state();
294 suspense_context.mount(scope_state.id);
295 scope_id = scope_state.id;
296 });
297 }
298
299 dom.set_mounted_dyn_node(mount, idx, scope_id.0);
301 }
302 dom.runtime.clone().with_scope_on_stack(scope_id, || {
303 let scope_state = &mut dom.scopes[scope_id.0];
304 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
305 let suspense_context =
306 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
307 .unwrap();
308
309 let children = props.children.clone();
310
311 suspense_context.under_suspense_boundary(&dom.runtime(), || {
313 children.as_vnode().create(dom, parent, None::<&mut M>);
314 });
315
316 let scope_state = &mut dom.scopes[scope_id.0];
318 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
319 props.children.clone_from(&children);
320
321 let scope_state = &mut dom.scopes[scope_id.0];
322 let suspense_context = scope_state
323 .state()
324 .suspense_location()
325 .suspense_context()
326 .unwrap()
327 .clone();
328 let nodes_created = if !suspense_context.suspended_futures().is_empty() {
330 let (node, nodes_created) =
331 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
332 let scope_state = &mut dom.scopes[scope_id.0];
333 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
334 let suspense_context =
335 SuspenseContext::downcast_suspense_boundary_from_scope(
336 &dom.runtime,
337 scope_id,
338 )
339 .unwrap();
340 suspense_context.set_suspended_nodes(children.into());
341 let suspense_placeholder = props.fallback.call(suspense_context);
342 let nodes_created = suspense_placeholder.as_vnode().create(dom, parent, to);
343 (suspense_placeholder, nodes_created)
344 });
345
346 let scope_state = &mut dom.scopes[scope_id.0];
347 scope_state.last_rendered_node = Some(node);
348
349 nodes_created
350 } else {
351 debug_assert!(children.as_vnode().mount.get().mounted());
353 let nodes_created = suspense_context
354 .under_suspense_boundary(&dom.runtime(), || {
355 children.as_vnode().create(dom, parent, to)
356 });
357 let scope_state = &mut dom.scopes[scope_id.0];
358 scope_state.last_rendered_node = Some(children);
359 let suspense_context =
360 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
361 .unwrap();
362 suspense_context.take_suspended_nodes();
363 mark_suspense_resolved(dom, scope_id);
364
365 nodes_created
366 };
367 nodes_created
368 })
369 }
370
371 #[doc(hidden)]
372 pub fn resolve_suspense<M: WriteMutations>(
376 scope_id: ScopeId,
377 dom: &mut VirtualDom,
378 to: &mut M,
379 only_write_templates: impl FnOnce(&mut M),
380 replace_with: usize,
381 ) {
382 dom.runtime.clone().with_scope_on_stack(scope_id, || {
383 let _runtime = RuntimeGuard::new(dom.runtime());
384 let Some(scope_state) = dom.scopes.get_mut(scope_id.0) else {
385 return;
386 };
387
388 let suspense_context = scope_state
390 .state()
391 .suspense_location()
392 .suspense_context()
393 .unwrap()
394 .clone();
395 suspense_context.inner.suspended_tasks.borrow_mut().clear();
396
397 let currently_rendered = scope_state.last_rendered_node.as_ref().unwrap().clone();
399 let mount = currently_rendered.as_vnode().mount.get();
400 let parent = {
401 let mounts = dom.runtime.mounts.borrow();
402 mounts
403 .get(mount.0)
404 .expect("suspense placeholder is not mounted")
405 .parent
406 };
407
408 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
409
410 let children = props.children.clone();
412 let suspense_context =
413 SuspenseContext::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
414 .unwrap();
415 let suspended = suspense_context.take_suspended_nodes();
417 if let Some(node) = suspended {
418 node.remove_node(&mut *dom, None::<&mut M>, None);
419 }
420 currently_rendered
422 .as_vnode()
423 .remove_node(&mut *dom, Some(to), Some(replace_with));
424
425 only_write_templates(to);
427
428 children.as_vnode().mount.take();
429
430 suspense_context.under_suspense_boundary(&dom.runtime(), || {
432 children.as_vnode().create(dom, parent, Some(to));
433 });
434
435 let scope_state = &mut dom.scopes[scope_id.0];
437 let props = Self::downcast_from_props(&mut *scope_state.props).unwrap();
438 props.children.clone_from(&children);
439 scope_state.last_rendered_node = Some(children);
440 })
441 }
442
443 pub(crate) fn diff<M: WriteMutations>(
444 scope_id: ScopeId,
445 dom: &mut VirtualDom,
446 to: Option<&mut M>,
447 ) {
448 dom.runtime.clone().with_scope_on_stack(scope_id, || {
449 let scope = &mut dom.scopes[scope_id.0];
450 let myself = Self::downcast_from_props(&mut *scope.props)
451 .unwrap()
452 .clone();
453
454 let last_rendered_node = scope.last_rendered_node.as_ref().unwrap().clone();
455
456 let Self {
457 fallback, children, ..
458 } = myself;
459
460 let suspense_context = scope.state().suspense_boundary().unwrap().clone();
461 let suspended_nodes = suspense_context.suspended_nodes();
462 let suspended = !suspense_context.suspended_futures().is_empty();
463 match (suspended_nodes, suspended) {
464 (Some(suspended_nodes), true) => {
467 let new_suspended_nodes: VNode = children.into();
468
469 let new_placeholder =
471 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
472 let old_placeholder = last_rendered_node;
473 let new_placeholder = fallback.call(suspense_context.clone());
474
475 old_placeholder.as_vnode().diff_node(
476 new_placeholder.as_vnode(),
477 dom,
478 to,
479 );
480 new_placeholder
481 });
482
483 dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
485
486 suspense_context.under_suspense_boundary(&dom.runtime(), || {
488 suspended_nodes.diff_node(&new_suspended_nodes, dom, None::<&mut M>);
489 });
490
491 let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
492 &dom.runtime,
493 scope_id,
494 )
495 .unwrap();
496 suspense_context.set_suspended_nodes(new_suspended_nodes);
497 }
498 (None, false) => {
500 let old_children = last_rendered_node;
501 let new_children = children;
502
503 suspense_context.under_suspense_boundary(&dom.runtime(), || {
504 old_children
505 .as_vnode()
506 .diff_node(new_children.as_vnode(), dom, to);
507 });
508
509 dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
511 }
512 (None, true) => {
514 let old_children = last_rendered_node.as_vnode();
515 let new_children: VNode = children.into();
516
517 let new_placeholder = fallback.call(suspense_context.clone());
518
519 let mount = old_children.mount.get();
521 let parent = dom.get_mounted_parent(mount);
522
523 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
524 old_children.move_node_to_background(
525 std::slice::from_ref(new_placeholder.as_vnode()),
526 parent,
527 dom,
528 to,
529 );
530 });
531
532 suspense_context.under_suspense_boundary(&dom.runtime(), || {
534 old_children.diff_node(&new_children, dom, None::<&mut M>);
535 });
536
537 dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
539
540 let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
541 &dom.runtime,
542 scope_id,
543 )
544 .unwrap();
545 suspense_context.set_suspended_nodes(new_children);
546
547 un_resolve_suspense(dom, scope_id);
548 }
549 (Some(_), false) => {
551 let old_suspended_nodes = suspense_context.take_suspended_nodes().unwrap();
553 let old_placeholder = last_rendered_node;
554 let new_children = children;
555
556 suspense_context.under_suspense_boundary(&dom.runtime(), || {
558 old_suspended_nodes.diff_node(new_children.as_vnode(), dom, None::<&mut M>);
559
560 let mount = old_placeholder.as_vnode().mount.get();
562 let parent = dom.get_mounted_parent(mount);
563 old_placeholder.as_vnode().replace(
564 std::slice::from_ref(new_children.as_vnode()),
565 parent,
566 dom,
567 to,
568 );
569 });
570
571 dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
573
574 mark_suspense_resolved(dom, scope_id);
575 }
576 }
577 })
578 }
579}
580
581fn mark_suspense_resolved(dom: &mut VirtualDom, scope_id: ScopeId) {
583 dom.resolved_scopes.push(scope_id);
584}
585
586fn un_resolve_suspense(dom: &mut VirtualDom, scope_id: ScopeId) {
588 dom.resolved_scopes.retain(|&id| id != scope_id);
589}
590
591impl SuspenseContext {
592 pub fn under_suspense_boundary<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
594 runtime.with_suspense_location(SuspenseLocation::UnderSuspense(self.clone()), f)
595 }
596
597 pub fn in_suspense_placeholder<O>(&self, runtime: &Runtime, f: impl FnOnce() -> O) -> O {
599 runtime.with_suspense_location(SuspenseLocation::InSuspensePlaceholder(self.clone()), f)
600 }
601
602 pub fn downcast_suspense_boundary_from_scope(
604 runtime: &Runtime,
605 scope_id: ScopeId,
606 ) -> Option<Self> {
607 runtime
608 .get_state(scope_id)
609 .and_then(|scope| scope.suspense_boundary())
610 }
611
612 pub(crate) fn remove_suspended_nodes<M: WriteMutations>(
613 dom: &mut VirtualDom,
614 scope_id: ScopeId,
615 destroy_component_state: bool,
616 ) {
617 let Some(scope) = Self::downcast_suspense_boundary_from_scope(&dom.runtime, scope_id)
618 else {
619 return;
620 };
621 if let Some(node) = scope.take_suspended_nodes() {
623 node.remove_node_inner(dom, None::<&mut M>, destroy_component_state, None)
624 }
625 }
626}