1use std::{cell::RefCell, rc::Rc};
2
3use indexmap::IndexMap;
4use wasm_bindgen::closure::Closure;
5use wasm_bindgen::JsValue;
6
7use crate::{
8 dom::{
9 self, dom_node, dom_node::DomInner, Application, DomAttr, DomAttrValue, DomNode, Program,
10 },
11 vdom::{
12 Attribute, AttributeValue, ComponentEventCallback, EventCallback, Patch, PatchType,
13 TreePath,
14 },
15};
16
17#[derive(Debug)]
22pub struct DomPatch {
23 pub patch_path: TreePath,
25 pub target_element: DomNode,
27 pub target_parent: DomNode,
29 pub patch_variant: PatchVariant,
31}
32
33#[derive(Debug)]
35pub enum PatchVariant {
36 InsertBeforeNode {
38 nodes: Vec<DomNode>,
40 },
41 InsertAfterNode {
43 nodes: Vec<DomNode>,
45 },
46 AppendChildren {
48 children: Vec<DomNode>,
50 },
51 AddAttributes {
53 attrs: Vec<DomAttr>,
55 },
56 RemoveAttributes {
58 attrs: Vec<DomAttr>,
60 },
61 ReplaceNode {
63 replacement: Vec<DomNode>,
65 },
66 RemoveNode,
68 ClearChildren,
70 MoveBeforeNode {
72 for_moving: Vec<DomNode>,
74 },
75 MoveAfterNode {
77 for_moving: Vec<DomNode>,
79 },
80}
81
82impl DomNode {
83 pub(crate) fn find_node(&self, path: &mut TreePath) -> Option<DomNode> {
84 match &self.inner {
85 DomInner::StatefulComponent { .. } => {
86 log::info!(
87 "This is a stateful component, should return the element
88 inside relative to the child container at this path: {:?}",
89 path
90 );
91 Some(self.clone())
93 }
94 _ => {
95 if path.is_empty() {
96 Some(self.clone())
97 } else {
98 let idx = path.remove_first();
99 if let Some(children) = self.children() {
100 if let Some(child) = children.get(idx) {
101 child.find_node(path)
102 } else {
103 log::warn!("There is no child at index: {idx}");
104 None
105 }
106 } else {
107 log::warn!("Traversing to a childless node..");
108 None
109 }
110 }
111 }
112 }
113 }
114
115 pub(crate) fn find_all_nodes(
116 &self,
117 nodes_to_find: &[(&TreePath, Option<&&'static str>)],
118 ) -> IndexMap<TreePath, (DomNode, DomNode)> {
119 let mut nodes_to_patch = IndexMap::with_capacity(nodes_to_find.len());
120 for (path, tag) in nodes_to_find {
121 let mut traverse_path: TreePath = (*path).clone();
122 if let Some(found) = self.find_node(&mut traverse_path) {
123 let mut parent_path = path.backtrack();
124 let target_parent = self
125 .find_node(&mut parent_path)
126 .expect("must find the parent");
127 nodes_to_patch.insert((*path).clone(), (found, target_parent));
128 } else {
129 log::warn!(
130 "can not find: {:?} {:?} target_node: {:?}",
131 path,
132 tag,
133 &self
134 );
135 log::info!(
136 "real entire dom: {:#?}",
137 dom_node::render_real_dom_to_string(&self.as_node())
138 );
139 log::warn!("entire dom: {}", self.render_to_string());
140 }
141 }
142 nodes_to_patch
143 }
144}
145
146impl<APP> Program<APP>
147where
148 APP: Application + 'static,
149{
150 pub(crate) fn convert_patches(
152 &self,
153 target_node: &DomNode,
154 patches: &[Patch<APP::MSG>],
155 ) -> Result<Vec<DomPatch>, JsValue> {
156 convert_patches(target_node, patches, self.create_ev_callback())
157 }
158
159 pub fn convert_patch(
161 &self,
162 nodes_lookup: &IndexMap<TreePath, (DomNode, DomNode)>,
163 target_element: &DomNode,
164 target_parent: &DomNode,
165 patch: &Patch<APP::MSG>,
166 ) -> DomPatch {
167 convert_patch(
168 nodes_lookup,
169 target_element,
170 target_parent,
171 patch,
172 self.create_ev_callback(),
173 )
174 }
175}
176
177pub fn convert_patches<Msg, F>(
179 target_node: &DomNode,
180 patches: &[Patch<Msg>],
181 ev_callback: F,
182) -> Result<Vec<DomPatch>, JsValue>
183where
184 Msg: 'static,
185 F: Fn(Msg) + 'static + Clone,
186{
187 let nodes_to_find: Vec<(&TreePath, Option<&&'static str>)> = patches
188 .iter()
189 .map(|patch| (patch.path(), patch.tag()))
190 .chain(
191 patches
192 .iter()
193 .flat_map(|patch| patch.node_paths())
194 .map(|path| (path, None)),
195 )
196 .collect();
197
198 let nodes_lookup = target_node.find_all_nodes(&nodes_to_find);
199
200 let dom_patches:Vec<DomPatch> = patches.iter().map(|patch|{
201 let patch_path = patch.path();
202 let patch_tag = patch.tag();
203 if let Some((target_node, target_parent)) = nodes_lookup.get(patch_path) {
204 let target_tag = target_node.tag();
205 if let (Some(patch_tag), Some(target_tag)) = (patch_tag, target_tag) {
206 if **patch_tag != target_tag{
207 panic!(
208 "expecting a tag: {patch_tag:?}, but found: {target_tag:?}"
209 );
210 }
211 }
212 convert_patch(&nodes_lookup, target_node, target_parent, patch, ev_callback.clone())
213 } else {
214 unreachable!("Getting here means we didn't find the element of next node that we are supposed to patch, patch_path: {:?}, with tag: {:?}", patch_path, patch_tag);
215 }
216 }).collect();
217
218 Ok(dom_patches)
219}
220
221pub fn convert_patch<Msg, F>(
223 nodes_lookup: &IndexMap<TreePath, (DomNode, DomNode)>,
224 target_element: &DomNode,
225 target_parent: &DomNode,
226 patch: &Patch<Msg>,
227 ev_callback: F,
228) -> DomPatch
229where
230 Msg: 'static,
231 F: Fn(Msg) + 'static + Clone,
232{
233 let target_element = target_element.clone();
234 let target_parent = target_parent.clone();
235 let Patch {
236 patch_path,
237 patch_type,
238 ..
239 } = patch;
240
241 let patch_path = patch_path.clone();
242
243 match patch_type {
244 PatchType::InsertBeforeNode { nodes } => {
245 let nodes = nodes
246 .iter()
247 .map(|for_insert| dom::create_dom_node(for_insert, ev_callback.clone()))
248 .collect();
249 DomPatch {
250 patch_path,
251 target_element,
252 target_parent,
253 patch_variant: PatchVariant::InsertBeforeNode { nodes },
254 }
255 }
256 PatchType::InsertAfterNode { nodes } => {
257 let nodes = nodes
258 .iter()
259 .map(|for_insert| dom::create_dom_node(for_insert, ev_callback.clone()))
260 .collect();
261 DomPatch {
262 patch_path,
263 target_element,
264 target_parent,
265 patch_variant: PatchVariant::InsertAfterNode { nodes },
266 }
267 }
268
269 PatchType::AddAttributes { attrs } => {
270 let attrs = Attribute::merge_attributes_of_same_name(attrs.iter().copied());
272 DomPatch {
273 patch_path,
274 target_element,
275 target_parent,
276 patch_variant: PatchVariant::AddAttributes {
277 attrs: attrs
278 .iter()
279 .map(|a| convert_attr(a, ev_callback.clone()))
280 .collect(),
281 },
282 }
283 }
284 PatchType::RemoveAttributes { attrs } => DomPatch {
285 patch_path,
286 target_element,
287 target_parent,
288 patch_variant: PatchVariant::RemoveAttributes {
289 attrs: attrs
290 .iter()
291 .map(|a| convert_attr(a, ev_callback.clone()))
292 .collect(),
293 },
294 },
295
296 PatchType::ReplaceNode { replacement } => {
297 let replacement = replacement
298 .iter()
299 .map(|node| dom::create_dom_node(node, ev_callback.clone()))
300 .collect();
301 DomPatch {
302 patch_path,
303 target_element,
304 target_parent,
305 patch_variant: PatchVariant::ReplaceNode { replacement },
306 }
307 }
308 PatchType::RemoveNode => DomPatch {
309 patch_path,
310 target_element,
311 target_parent,
312 patch_variant: PatchVariant::RemoveNode,
313 },
314 PatchType::ClearChildren => DomPatch {
315 patch_path,
316 target_element,
317 target_parent,
318 patch_variant: PatchVariant::ClearChildren,
319 },
320 PatchType::MoveBeforeNode { nodes_path } => {
321 let for_moving = nodes_path
322 .iter()
323 .map(|path| {
324 let (node, _) = nodes_lookup.get(path).expect("must have found the node");
325 node.clone()
326 })
327 .collect();
328 DomPatch {
329 patch_path,
330 target_element,
331 target_parent,
332 patch_variant: PatchVariant::MoveBeforeNode { for_moving },
333 }
334 }
335 PatchType::MoveAfterNode { nodes_path } => {
336 let for_moving = nodes_path
337 .iter()
338 .map(|path| {
339 let (node, _) = nodes_lookup.get(path).expect("must have found the node");
340 node.clone()
341 })
342 .collect();
343 DomPatch {
344 patch_path,
345 target_element,
346 target_parent,
347 patch_variant: PatchVariant::MoveAfterNode { for_moving },
348 }
349 }
350 PatchType::AppendChildren { children } => {
351 let children = children
352 .iter()
353 .map(|for_insert| dom::create_dom_node(for_insert, ev_callback.clone()))
354 .collect();
355
356 DomPatch {
357 patch_path,
358 target_element,
359 target_parent,
360 patch_variant: PatchVariant::AppendChildren { children },
361 }
362 }
363 }
364}
365
366pub(crate) fn convert_attr<Msg, F>(attr: &Attribute<Msg>, ev_callback: F) -> DomAttr
367where
368 Msg: 'static,
369 F: Fn(Msg) + 'static + Clone,
370{
371 DomAttr {
372 namespace: attr.namespace,
373 name: attr.name,
374 value: attr
375 .value
376 .iter()
377 .filter_map(|v| convert_attr_value(v, ev_callback.clone()))
378 .collect(),
379 }
380}
381
382fn convert_attr_value<Msg, F>(
383 attr_value: &AttributeValue<Msg>,
384 ev_callback: F,
385) -> Option<DomAttrValue>
386where
387 Msg: 'static,
388 F: Fn(Msg) + 'static,
389{
390 match attr_value {
391 AttributeValue::Simple(v) => Some(DomAttrValue::Simple(v.clone())),
392 AttributeValue::Style(v) => Some(DomAttrValue::Style(v.clone())),
393 AttributeValue::EventListener(v) => Some(DomAttrValue::EventListener(
394 convert_event_listener(v, ev_callback),
395 )),
396 AttributeValue::ComponentEventListener(v) => Some(DomAttrValue::EventListener(
397 convert_component_event_listener(v),
398 )),
399 AttributeValue::Empty => None,
400 }
401}
402
403fn convert_event_listener<F, Msg>(
404 event_listener: &EventCallback<Msg>,
405 callback: F,
406) -> Closure<dyn FnMut(web_sys::Event)>
407where
408 Msg: 'static,
409 F: Fn(Msg) + 'static,
410{
411 let event_listener = event_listener.clone();
412 let closure: Closure<dyn FnMut(web_sys::Event)> = Closure::new(move |event: web_sys::Event| {
413 let msg = event_listener.emit(dom::Event::from(event));
414 callback(msg);
415 });
416 closure
417}
418
419pub fn apply_dom_patches(
422 root_node: Rc<RefCell<Option<DomNode>>>,
423 mount_node: Rc<RefCell<Option<DomNode>>>,
424 dom_patches: impl IntoIterator<Item = DomPatch>,
425) -> Result<(), JsValue> {
426 for dom_patch in dom_patches {
427 apply_dom_patch(Rc::clone(&root_node), Rc::clone(&mount_node), dom_patch)?;
428 }
429 Ok(())
430}
431
432pub(crate) fn apply_dom_patch(
436 root_node: Rc<RefCell<Option<DomNode>>>,
437 mount_node: Rc<RefCell<Option<DomNode>>>,
438 dom_patch: DomPatch,
439) -> Result<(), JsValue> {
440 let DomPatch {
441 patch_path,
442 target_element,
443 target_parent,
444 patch_variant,
445 } = dom_patch;
446
447 match patch_variant {
448 PatchVariant::InsertBeforeNode { nodes } => {
449 target_parent.insert_before(&target_element, nodes);
450 }
451
452 PatchVariant::InsertAfterNode { nodes } => {
453 target_parent.insert_after(&target_element, nodes);
454 }
455 PatchVariant::AppendChildren { children } => {
456 target_element.append_children(children);
457 }
458
459 PatchVariant::AddAttributes { attrs } => {
460 target_element.set_dom_attrs(attrs).unwrap();
461 }
462 PatchVariant::RemoveAttributes { attrs } => {
463 for attr in attrs.iter() {
464 for att_value in attr.value.iter() {
465 match att_value {
466 DomAttrValue::Simple(_) => {
467 target_element.remove_dom_attr(attr)?;
468 }
469 DomAttrValue::EventListener(_) => {
471 let DomInner::Element { listeners, .. } = &target_element.inner else {
472 unreachable!("must be an element");
473 };
474 if let Some(listener) = listeners.borrow_mut().as_mut() {
475 listener.retain(|event, _| *event != attr.name)
476 }
477 }
478 DomAttrValue::Style(_) => {
479 target_element.remove_dom_attr(attr)?;
480 }
481 DomAttrValue::Empty => (),
482 }
483 }
484 }
485 }
486
487 PatchVariant::ReplaceNode { mut replacement } => {
492 let first_node = replacement.remove(0);
493
494 if target_element.is_fragment() {
495 assert!(
496 patch_path.is_empty(),
497 "this should only happen to root node"
498 );
499 let mut mount_node = mount_node.borrow_mut();
500 let mount_node = mount_node.as_mut().expect("must have a mount node");
501 mount_node.append_children(vec![first_node.clone()]);
502 mount_node.append_children(replacement);
503 } else {
504 if patch_path.path.is_empty() {
505 let mut mount_node = mount_node.borrow_mut();
506 let mount_node = mount_node.as_mut().expect("must have a mount node");
507 mount_node.replace_child(&target_element, first_node.clone());
508 } else {
509 target_parent.replace_child(&target_element, first_node.clone());
510 }
511 target_parent.insert_after(&first_node, replacement);
513 }
514 if patch_path.path.is_empty() {
515 *root_node.borrow_mut() = Some(first_node);
516 }
517 }
518 PatchVariant::RemoveNode => {
519 target_parent.remove_children(&[&target_element]);
520 }
521 PatchVariant::ClearChildren => {
522 target_element.clear_children();
523 }
524 PatchVariant::MoveBeforeNode { for_moving } => {
525 target_parent.remove_children(&for_moving.iter().collect::<Vec<_>>());
526 target_parent.insert_before(&target_element, for_moving);
527 }
528
529 PatchVariant::MoveAfterNode { for_moving } => {
530 target_parent.remove_children(&for_moving.iter().collect::<Vec<_>>());
531 target_parent.insert_after(&target_element, for_moving);
532 }
533 }
534 Ok(())
535}
536
537fn convert_component_event_listener(
538 component_callback: &ComponentEventCallback,
539) -> Closure<dyn FnMut(web_sys::Event)> {
540 let component_callback = component_callback.clone();
541 let closure: Closure<dyn FnMut(web_sys::Event)> = Closure::new(move |event: web_sys::Event| {
542 component_callback.emit(dom::Event::from(event));
543 });
544 closure
545}