1use wasm_bindgen::prelude::*;
2use wasm_bindgen::JsCast;
3use web_sys::*;
4
5use super::VNode;
6
7const SVG_NS: &str = "http://www.w3.org/2000/svg";
9
10pub fn mount_to_dom(
20 vnode: &VNode,
21 parent: &web_sys::Node,
22 anchor: Option<&web_sys::Node>,
23) -> Option<web_sys::Node> {
24 mount_to_dom_inner(vnode, parent, anchor, None)
25}
26
27fn mount_to_dom_inner(
29 vnode: &VNode,
30 parent: &web_sys::Node,
31 anchor: Option<&web_sys::Node>,
32 namespace: Option<&str>,
33) -> Option<web_sys::Node> {
34 match vnode {
35 VNode::Element(el) => {
36 let document = web_sys::window()?.document()?;
37
38 let ns = if el.tag == "svg" {
42 Some(SVG_NS)
43 } else {
44 namespace
45 };
46
47 let elem: web_sys::Element = match ns {
49 Some(ns_str) => document.create_element_ns(Some(ns_str), &el.tag).ok()?,
50 None => document.create_element(&el.tag).ok()?,
51 };
52
53 for (name, value) in &el.attrs {
55 let _ = elem.set_attribute(name, value);
56 }
57
58 for (event_name, handler) in &el.events {
60 let handler_cls = {
61 let handler = handler.clone();
62 Closure::wrap(Box::new(move |event: web_sys::Event| {
63 handler.call(event);
64 }) as Box<dyn FnMut(web_sys::Event)>)
65 };
66
67 let js_func: &js_sys::Function = handler_cls.as_ref().unchecked_ref();
68 let _ = elem.add_event_listener_with_callback(event_name, js_func);
69
70 el.listener_closures
72 .borrow_mut()
73 .push((event_name, handler_cls));
74 }
75
76 for child in &el.children {
78 if let Some(child_node) = mount_to_dom_inner(child, &elem, None, ns) {
79 let _ = elem.append_child(&child_node);
80 }
81 }
82
83 let node: web_sys::Node = elem.into();
84 *el.dom_ref.borrow_mut() = Some(node.clone());
85
86 if let Some(ref anchor_node) = anchor {
88 let _ = parent.insert_before(&node, Some(anchor_node));
89 } else {
90 let _ = parent.append_child(&node);
91 }
92
93 Some(node)
94 }
95
96 VNode::Text(text) => {
97 let document = web_sys::window()?.document()?;
98 let text_node = document.create_text_node(text);
99 let node: web_sys::Node = text_node.into();
100
101 if let Some(ref anchor_node) = anchor {
102 let _ = parent.insert_before(&node, Some(anchor_node));
103 } else {
104 let _ = parent.append_child(&node);
105 }
106
107 Some(node)
108 }
109
110 VNode::Fragment(children) => {
111 let mut first_child = None;
112 for child in children {
113 if let Some(child_node) = mount_to_dom_inner(child, parent, anchor, namespace) {
114 if first_child.is_none() {
115 first_child = Some(child_node);
116 }
117 }
118 }
119 first_child
120 }
121
122 VNode::Dynamic(render_fn, dom_ref) => {
123 let f = render_fn.borrow();
124 let new_vnode = f();
125 drop(f);
126
127 if let Some(node) = mount_to_dom_inner(&new_vnode, parent, anchor, namespace) {
128 *dom_ref.borrow_mut() = Some(node.clone());
129 Some(node)
130 } else {
131 None
132 }
133 }
134
135 VNode::Empty => None,
136 }
137}