wal_core/virtual_dom/
vnode.rs

1use web_sys::Node;
2
3use super::{VComponent, VElement, VList, VText};
4
5/// VNode is enum representing node in virtual DOM tree.
6/// Provides a wrapper over different types of nodes along with concise and convinient API for VDOM manipulation.
7#[derive(PartialEq, Debug)]
8pub enum VNode {
9    /// Represents [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) in DOM and contains [VElement],
10    Element(VElement),
11    /// Represents [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) in DOM and contains [VText],
12    Text(VText),
13    /// Represents a series of adjacent [virtual nodes](VNode) located at the same depth, contains [VList],
14    List(VList),
15    /// Represents user-defined custom component, contains [VComponent].
16    Component(VComponent),
17}
18
19impl VNode {
20    pub(crate) fn patch(&mut self, last: Option<VNode>, ancestor: &Node) {
21        match self {
22            VNode::Element(velement) => velement.patch(last, ancestor),
23            VNode::Text(vtext) => vtext.patch(last, ancestor),
24            VNode::Component(vcomp) => vcomp.patch(last, ancestor),
25            VNode::List(vlist) => vlist.patch(last, ancestor),
26        };
27    }
28
29    pub(crate) fn erase(&self) {
30        match self {
31            VNode::Element(v) => v.erase(),
32            VNode::Text(v) => v.erase(),
33            VNode::List(v) => v.erase(),
34            VNode::Component(v) => v.erase(),
35        }
36    }
37
38    pub(crate) fn set_depth(&mut self, depth: u32) {
39        match self {
40            VNode::Component(vcomp) => vcomp.set_depth(depth),
41            VNode::List(vlist) => vlist.set_depth(depth),
42            VNode::Element(velem) => velem.set_depth(depth),
43            VNode::Text(_) => {}
44        }
45    }
46}
47
48impl From<VElement> for VNode {
49    fn from(velement: VElement) -> Self {
50        Self::Element(velement)
51    }
52}
53
54impl From<VComponent> for VNode {
55    fn from(vcomp: VComponent) -> Self {
56        Self::Component(vcomp)
57    }
58}
59
60impl From<VText> for VNode {
61    fn from(vtext: VText) -> Self {
62        Self::Text(vtext)
63    }
64}
65
66impl From<VList> for VNode {
67    fn from(vlist: VList) -> Self {
68        Self::List(vlist)
69    }
70}
71
72impl<T: ToString> From<T> for VNode {
73    fn from(t: T) -> Self {
74        Self::Text(VText::new(t))
75    }
76}
77
78impl<T: Into<VNode>> FromIterator<T> for VNode {
79    fn from_iter<U: IntoIterator<Item = T>>(iter: U) -> Self {
80        Self::List(VList::new(iter.into_iter().map(Into::into).collect(), None))
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use crate::{
87        component::{behavior::Behavior, Component},
88        virtual_dom::{VComponent, VElement, VList, VText},
89    };
90    use wasm_bindgen_test::wasm_bindgen_test;
91
92    use super::VNode;
93    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
94
95    #[wasm_bindgen_test]
96    fn from_to_string() {
97        let target = String::from("tmp");
98        assert_eq!(
99            VNode::Text(VText {
100                text: "tmp".to_string(),
101                dom: None
102            }),
103            target.into()
104        );
105    }
106
107    #[wasm_bindgen_test]
108    fn from_vec_string() {
109        let target = vec![String::from("tmp")];
110        assert_eq!(
111            VNode::List(VList::new(vec![VText::new("tmp").into()], None)),
112            VNode::from_iter(target)
113        );
114    }
115
116    #[wasm_bindgen_test]
117    fn from_vec_elements() {
118        let target = vec![VElement::new(
119            "div".to_string(),
120            [].into(),
121            [].into(),
122            None,
123            [].into(),
124        )];
125        assert_eq!(
126            VNode::List(VList::new(
127                vec![VNode::Element(VElement::new(
128                    "div".to_string(),
129                    [].into(),
130                    [].into(),
131                    None,
132                    [].into()
133                ))],
134                None
135            )),
136            VNode::from_iter(target)
137        );
138    }
139
140    #[wasm_bindgen_test]
141    fn from_vec_lists() {
142        let target = vec![VList::new(vec![], None)];
143        assert_eq!(
144            VNode::List(VList::new(
145                vec![VNode::List(VList::new(vec![], None,))],
146                None
147            )),
148            VNode::from_iter(target)
149        );
150    }
151
152    struct Comp;
153    impl Component for Comp {
154        type Message = ();
155        type Properties = ();
156
157        fn new(_props: Self::Properties) -> Self {
158            Comp
159        }
160        fn view(&self, _behavior: &mut impl Behavior<Self>) -> VNode {
161            VText::new("Test").into()
162        }
163        fn update(&mut self, _message: Self::Message) -> bool {
164            false
165        }
166    }
167
168    #[wasm_bindgen_test]
169    fn from_vec_comp() {
170        let target = vec![VComponent::new::<Comp>((), None)];
171        assert_eq!(
172            VNode::List(VList::new(
173                vec![VNode::Component(VComponent::new::<Comp>((), None))],
174                None
175            )),
176            VNode::from_iter(target)
177        );
178    }
179}