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