yew/dom_bundle/traits.rs
1use web_sys::Element;
2
3use super::{BNode, BSubtree, DomSlot};
4use crate::html::AnyScope;
5
6/// A Reconcile Target.
7///
8/// When a [Reconcilable] is attached, a reconcile target is created to store additional
9/// information.
10pub(super) trait ReconcileTarget {
11 /// Remove self from parent.
12 ///
13 /// Parent to detach is `true` if the parent element will also be detached.
14 fn detach(self, root: &BSubtree, parent: &Element, parent_to_detach: bool);
15
16 /// Move elements from one parent to another parent.
17 /// This is for example used by `VSuspense` to preserve component state without detaching
18 /// (which destroys component state).
19 fn shift(&self, next_parent: &Element, slot: DomSlot) -> DomSlot;
20}
21
22/// This trait provides features to update a tree by calculating a difference against another tree.
23pub(super) trait Reconcilable {
24 type Bundle: ReconcileTarget;
25
26 /// Attach a virtual node to the DOM tree.
27 ///
28 /// Parameters:
29 /// - `root`: bundle of the subtree root
30 /// - `parent_scope`: the parent `Scope` used for passing messages to the parent `Component`.
31 /// - `parent`: the parent node in the DOM.
32 /// - `slot`: to find where to put the node.
33 ///
34 /// Returns a reference to the newly inserted element.
35 /// The [`DomSlot`] points the first element (if there are multiple nodes created),
36 /// or is the passed in `slot` if there are no element is created.
37 fn attach(
38 self,
39
40 root: &BSubtree,
41 parent_scope: &AnyScope,
42 parent: &Element,
43 slot: DomSlot,
44 ) -> (DomSlot, Self::Bundle);
45
46 /// Scoped diff apply to other tree.
47 ///
48 /// Virtual rendering for the node. It uses parent node and existing
49 /// children (virtual and DOM) to check the difference and apply patches to
50 /// the actual DOM representation.
51 ///
52 /// Parameters:
53 /// - `parent_scope`: the parent `Scope` used for passing messages to the parent `Component`.
54 /// - `parent`: the parent node in the DOM.
55 /// - `slot`: the slot in `parent`'s children where to put the node.
56 /// - `bundle`: the node that this node will be replacing in the DOM. This method will remove
57 /// the `bundle` from the `parent` if it is of the wrong kind, and otherwise reuse it.
58 ///
59 /// Returns a reference to the newly inserted element.
60 fn reconcile_node(
61 self,
62
63 root: &BSubtree,
64 parent_scope: &AnyScope,
65 parent: &Element,
66 slot: DomSlot,
67 bundle: &mut BNode,
68 ) -> DomSlot;
69
70 fn reconcile(
71 self,
72 root: &BSubtree,
73 parent_scope: &AnyScope,
74 parent: &Element,
75 slot: DomSlot,
76 bundle: &mut Self::Bundle,
77 ) -> DomSlot;
78
79 /// Replace an existing bundle by attaching self and detaching the existing one
80 fn replace(
81 self,
82
83 root: &BSubtree,
84 parent_scope: &AnyScope,
85 parent: &Element,
86 slot: DomSlot,
87 bundle: &mut BNode,
88 ) -> DomSlot
89 where
90 Self: Sized,
91 Self::Bundle: Into<BNode>,
92 {
93 let (self_ref, self_) = self.attach(root, parent_scope, parent, slot);
94 let ancestor = std::mem::replace(bundle, self_.into());
95 ancestor.detach(root, parent, false);
96 self_ref
97 }
98}
99
100#[cfg(feature = "hydration")]
101mod feat_hydration {
102 use super::*;
103 use crate::dom_bundle::Fragment;
104
105 pub(in crate::dom_bundle) trait Hydratable: Reconcilable {
106 /// hydrates current tree.
107 ///
108 /// Returns a reference to the first node of the hydrated tree.
109 ///
110 /// # Important
111 ///
112 /// DOM tree is hydrated from top to bottom. This is different than [`Reconcilable`].
113 fn hydrate(
114 self,
115 root: &BSubtree,
116 parent_scope: &AnyScope,
117 parent: &Element,
118 fragment: &mut Fragment,
119 ) -> Self::Bundle;
120 }
121}
122
123#[cfg(feature = "hydration")]
124pub(in crate::dom_bundle) use feat_hydration::*;