wal_core/virtual_dom/
vlist.rs1use itertools::{EitherOrBoth, Itertools};
2use web_sys::Node;
3
4use super::VNode;
5
6#[derive(PartialEq, Debug)]
8pub struct VList {
9 _key: Option<String>, pub(crate) nodes: Vec<VNode>,
11}
12
13impl VList {
14 pub fn new(nodes: Vec<VNode>, key: Option<String>) -> VList {
16 VList { nodes, _key: key }
17 }
18
19 pub fn new_empty(key: Option<String>) -> VList {
21 VList {
22 nodes: Vec::new(),
23 _key: key,
24 }
25 }
26
27 pub(crate) fn patch(&mut self, last: Option<VNode>, ancestor: &Node) {
28 let mut old_virt: Option<VList> = None;
29
30 match last {
31 None => {}
32 Some(VNode::List(vlist)) => {
33 old_virt = Some(vlist);
34 }
35 Some(VNode::Text(v)) => {
36 v.erase();
37 }
38 Some(VNode::Element(v)) => {
39 v.erase();
40 }
41 Some(VNode::Component(v)) => {
42 v.erase();
43 }
44 }
45
46 self.render(old_virt, ancestor);
47 }
48
49 pub(crate) fn erase(&self) {
50 for node in self.nodes.iter() {
51 node.erase();
52 }
53 }
54
55 pub(crate) fn set_depth(&mut self, depth: u32) {
56 for child in self.nodes.iter_mut() {
57 child.set_depth(depth);
58 }
59 }
60}
61
62impl VList {
63 fn render(&mut self, last: Option<VList>, ancestor: &Node) {
64 for e in self
65 .nodes
66 .iter_mut()
67 .zip_longest(last.map_or_else(Vec::new, |x| x.nodes.into_iter().collect()))
68 {
69 match e {
70 EitherOrBoth::Both(cur, old) => cur.patch(Some(old), ancestor),
71 EitherOrBoth::Left(cur) => cur.patch(None, ancestor),
72 EitherOrBoth::Right(old) => old.erase(),
73 }
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use wasm_bindgen_test::wasm_bindgen_test;
81
82 use crate::{
83 component::{behavior::Behavior, Component},
84 virtual_dom::{dom, VComponent, VElement, VNode, VText},
85 };
86
87 use super::VList;
88 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
89
90 macro_rules! function_name {
91 () => {{
92 fn f() {}
93 fn type_name_of<T>(_: T) -> &'static str {
94 std::any::type_name::<T>()
95 }
96 let name = type_name_of(f);
97 name.strip_suffix("::f").unwrap()
98 }};
99 }
100
101 const VALID_TEXT: &str = "";
102
103 #[wasm_bindgen_test]
104 fn patch_last_none() {
105 let ancestor = dom::create_element("div");
106 dom::set_attribute(&ancestor, "id", function_name!());
107 dom::append_child(&dom::get_root_element(), &ancestor);
108
109 let mut target = VList::new(vec![VText::new(VALID_TEXT).into()], None);
110 target.set_depth(0);
111 target.patch(None, &ancestor);
112 }
113
114 #[wasm_bindgen_test]
115 fn patch_last_text() {
116 let ancestor = dom::create_element("div");
117 dom::set_attribute(&ancestor, "id", function_name!());
118
119 let current = dom::create_text_node("I dont love Rust");
120 dom::append_child(&ancestor, ¤t);
121
122 dom::append_child(&dom::get_root_element(), &ancestor);
123
124 let text = VNode::Text(VText {
125 text: "I dont love Rust".into(),
126 dom: Some(current),
127 });
128
129 let mut target = VList::new(vec![VText::new(VALID_TEXT).into()], None);
130 target.set_depth(0);
131 target.patch(Some(text), &ancestor);
132 }
133
134 #[wasm_bindgen_test]
135 fn patch_last_elem() {
136 let ancestor = dom::create_element("div");
137 dom::set_attribute(&ancestor, "id", function_name!());
138
139 let current = dom::create_element("div");
140 dom::set_attribute(¤t, "id", "I dont love Rust");
141 dom::append_child(&ancestor, ¤t);
142
143 dom::append_child(&dom::get_root_element(), &ancestor);
144
145 let elem = VNode::Element(VElement {
146 tag_name: "div".into(),
147 attr: [("id".into(), "I dont love Rust".into())].into(),
148 event_handlers: vec![],
149 key: None,
150 children: vec![],
151 dom: Some(current),
152 });
153
154 let mut target = VList::new(vec![VText::new(VALID_TEXT).into()], None);
155 target.set_depth(0);
156 target.patch(Some(elem), &ancestor);
157 }
158
159 struct Comp;
160 impl Component for Comp {
161 type Message = ();
162 type Properties = ();
163
164 fn new(_props: Self::Properties) -> Self {
165 Comp
166 }
167 fn view(&self, _behavior: &mut impl Behavior<Self>) -> VNode {
168 VText::new("I dont love Rust").into()
169 }
170 fn update(&mut self, _message: Self::Message) -> bool {
171 false
172 }
173 }
174
175 #[wasm_bindgen_test]
176 fn patch_last_comp() {
177 let ancestor = dom::create_element("div");
178 dom::set_attribute(&ancestor, "id", function_name!());
179 dom::append_child(&dom::get_root_element(), &ancestor);
180
181 let mut comp = VNode::Component(VComponent::new::<Comp>((), None));
182 comp.set_depth(0);
183 comp.patch(None, &ancestor);
184
185 let mut target = VList::new(vec![VText::new(VALID_TEXT).into()], None);
186 target.set_depth(0);
187 target.patch(Some(comp), &ancestor);
188 }
189
190 #[wasm_bindgen_test]
191 fn patch_last_list() {
192 let ancestor = dom::create_element("div");
193 dom::set_attribute(&ancestor, "id", function_name!());
194 dom::append_child(&dom::get_root_element(), &ancestor);
195
196 let mut list = VNode::List(VList::new(
197 vec![VText::new("I dont love Rust").into()],
198 None,
199 ));
200 list.set_depth(0);
201 list.patch(None, &ancestor);
202
203 let mut target = VList::new(vec![VText::new(VALID_TEXT).into()], None);
204 target.set_depth(0);
205 target.patch(Some(list), &ancestor);
206 }
207}