1use crate::*;
2
3impl Renderer {
5 pub fn render(&mut self, vnode: VirtualNode) {
7 let new_unwrapped: VirtualNode = self.unwrap_component(&vnode);
8 if let Some(old_vnode) = self.try_get_current_tree() {
9 let old_unwrapped: VirtualNode = self.unwrap_component(old_vnode);
10 self.patch_root(&old_unwrapped, &new_unwrapped);
11 } else {
12 let dom_node: Node = self.create_dom_node(&new_unwrapped);
13 while let Some(child) = self.get_root().first_child() {
14 self.get_root().remove_child(&child).unwrap();
15 }
16 self.get_root().append_child(&dom_node).unwrap();
17 }
18 self.set_current_tree(Some(vnode));
19 }
20
21 fn patch_root(&mut self, old_node: &VirtualNode, new_node: &VirtualNode) {
23 let dom_child: Option<Node> = self.get_root().first_child();
24 let is_element: bool = if let Some(ref dom_child) = dom_child {
25 dom_child.dyn_ref::<Element>().is_some()
26 } else {
27 false
28 };
29 if is_element {
30 let element: Element = dom_child.unwrap().dyn_into::<Element>().unwrap();
31 self.patch_node(old_node, new_node, &element);
32 } else if let Some(dom_child) = dom_child {
33 let new_dom: Node = self.create_dom_node(new_node);
34 self.get_root().replace_child(&new_dom, &dom_child).unwrap();
35 } else {
36 let new_dom: Node = self.create_dom_node(new_node);
37 self.get_root().append_child(&new_dom).unwrap();
38 }
39 }
40
41 fn patch_node(
43 &mut self,
44 old_node: &VirtualNode,
45 new_node: &VirtualNode,
46 dom_element: &Element,
47 ) {
48 match (old_node, new_node) {
49 (VirtualNode::Text(old_text), VirtualNode::Text(new_text)) => {
50 if old_text.get_content() != new_text.get_content() {
51 dom_element.set_text_content(Some(new_text.get_content()));
52 }
53 }
54 (
55 VirtualNode::Element {
56 tag: old_tag,
57 attributes: old_attrs,
58 children: old_children,
59 key: _old_key,
60 },
61 VirtualNode::Element {
62 tag: new_tag,
63 attributes: new_attrs,
64 children: new_children,
65 key: _new_key,
66 },
67 ) => {
68 if !Self::tags_equal(old_tag, new_tag) {
69 let new_dom: Node = self.create_dom_node(new_node);
70 if let Some(parent) = dom_element.parent_node() {
71 parent.replace_child(&new_dom, dom_element).unwrap();
72 }
73 return;
74 }
75 self.patch_attributes(dom_element, old_attrs, new_attrs);
76 self.patch_children(dom_element, old_children, new_children);
77 }
78 (VirtualNode::Fragment(old_children), VirtualNode::Fragment(new_children)) => {
79 self.patch_children(dom_element, old_children, new_children);
80 }
81 _ => {
82 let new_dom: Node = self.create_dom_node(new_node);
83 if let Some(parent) = dom_element.parent_node() {
84 parent.replace_child(&new_dom, dom_element).unwrap();
85 }
86 }
87 }
88 }
89
90 fn patch_attributes(
92 &mut self,
93 element: &Element,
94 old_attrs: &[AttributeEntry],
95 new_attrs: &[AttributeEntry],
96 ) {
97 let mut old_map: HashMap<&str, &AttributeValue> = HashMap::new();
98 for attr in old_attrs {
99 old_map.insert(attr.get_name(), attr.get_value());
100 }
101 let mut new_map: HashMap<&str, &AttributeValue> = HashMap::new();
102 for attr in new_attrs {
103 new_map.insert(attr.get_name(), attr.get_value());
104 }
105 for name in old_map.keys() {
106 if !new_map.contains_key(*name) {
107 Self::remove_dom_attribute_or_property(element, name);
108 }
109 }
110 for attr in new_attrs {
111 let should_set: bool = match old_map.get(attr.get_name().as_str()) {
112 Some(old_value) => !Self::attribute_values_equal(old_value, attr.get_value()),
113 None => true,
114 };
115 if should_set {
116 match attr.get_value() {
117 AttributeValue::Text(value) => {
118 if value.is_empty() {
119 Self::remove_dom_attribute_or_property(element, attr.get_name());
120 } else {
121 Self::set_dom_attribute_or_property(element, attr.get_name(), value);
122 }
123 }
124 AttributeValue::Signal(signal) => {
125 let value: String = signal.get();
126 if value.is_empty() && !Self::is_boolean_property(attr.get_name()) {
127 Self::remove_dom_attribute_or_property(element, attr.get_name());
128 } else {
129 Self::set_dom_attribute_or_property(element, attr.get_name(), &value);
130 }
131 }
132 AttributeValue::Event(handler) => {
133 self.attach_event_listener(element, handler);
134 }
135 AttributeValue::Dynamic(_) => {}
136 AttributeValue::Css(css_class) => {
137 css_class.inject_style();
138 Self::set_dom_attribute_or_property(
139 element,
140 attr.get_name(),
141 css_class.get_name(),
142 );
143 }
144 }
145 }
146 }
147 }
148
149 fn is_boolean_property(name: &str) -> bool {
152 matches!(name, "checked" | "disabled" | "selected" | "readonly")
153 }
154
155 fn remove_dom_attribute_or_property(element: &Element, name: &str) {
165 if name == "value" {
166 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
167 input.set_value("");
168 return;
169 }
170 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
171 textarea.set_value("");
172 return;
173 }
174 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
175 select.set_value("");
176 return;
177 }
178 }
179 if name == "checked"
180 && let Some(input) = element.dyn_ref::<HtmlInputElement>()
181 {
182 input.set_checked(false);
183 return;
184 }
185 if name == "disabled" {
186 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
187 input.set_disabled(false);
188 return;
189 }
190 if let Some(button) = element.dyn_ref::<HtmlButtonElement>() {
191 button.set_disabled(false);
192 return;
193 }
194 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
195 select.set_disabled(false);
196 return;
197 }
198 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
199 textarea.set_disabled(false);
200 return;
201 }
202 }
203 if name == "selected"
204 && let Some(option) = element.dyn_ref::<HtmlOptionElement>()
205 {
206 option.set_selected(false);
207 return;
208 }
209 if name == "readonly" {
210 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
211 input.set_read_only(false);
212 return;
213 }
214 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
215 textarea.set_read_only(false);
216 return;
217 }
218 }
219 let _ = element.remove_attribute(name);
220 }
221
222 fn set_dom_attribute_or_property(element: &Element, name: &str, value: &str) {
230 if name == "value" {
231 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
232 input.set_value(value);
233 return;
234 }
235 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
236 textarea.set_value(value);
237 return;
238 }
239 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
240 select.set_value(value);
241 return;
242 }
243 }
244 if name == "checked"
245 && let Some(input) = element.dyn_ref::<HtmlInputElement>()
246 {
247 input.set_checked(value == "true");
248 return;
249 }
250 if name == "disabled" {
251 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
252 input.set_disabled(value == "true");
253 return;
254 }
255 if let Some(button) = element.dyn_ref::<HtmlButtonElement>() {
256 button.set_disabled(value == "true");
257 return;
258 }
259 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
260 select.set_disabled(value == "true");
261 return;
262 }
263 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
264 textarea.set_disabled(value == "true");
265 return;
266 }
267 }
268 if name == "selected"
269 && let Some(option) = element.dyn_ref::<HtmlOptionElement>()
270 {
271 option.set_selected(value == "true");
272 return;
273 }
274 if name == "readonly" {
275 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
276 input.set_read_only(value == "true");
277 return;
278 }
279 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
280 textarea.set_read_only(value == "true");
281 return;
282 }
283 }
284 let _ = element.set_attribute(name, value);
285 }
286
287 fn tags_equal(a: &Tag, b: &Tag) -> bool {
289 match (a, b) {
290 (Tag::Element(a_name), Tag::Element(b_name)) => a_name == b_name,
291 (Tag::Component(a_name), Tag::Component(b_name)) => a_name == b_name,
292 _ => false,
293 }
294 }
295
296 fn attribute_values_equal(a: &AttributeValue, b: &AttributeValue) -> bool {
304 match (a, b) {
305 (AttributeValue::Text(a_val), AttributeValue::Text(b_val)) => a_val == b_val,
306 (AttributeValue::Signal(_a_sig), AttributeValue::Signal(_b_sig)) => false,
307 (AttributeValue::Event(_a_ev), AttributeValue::Event(_b_ev)) => false,
308 (AttributeValue::Dynamic(a_dyn), AttributeValue::Dynamic(b_dyn)) => a_dyn == b_dyn,
309 (AttributeValue::Css(a_css), AttributeValue::Css(b_css)) => {
310 a_css.get_name() == b_css.get_name()
311 }
312 _ => false,
313 }
314 }
315
316 fn get_child_node(parent: &Element, index: u32) -> Option<Node> {
318 let mut current: Option<Node> = parent.first_child();
319 let mut current_index: u32 = 0;
320 while let Some(node) = current {
321 if current_index == index {
322 return Some(node);
323 }
324 current = node.next_sibling();
325 current_index += 1;
326 }
327 None
328 }
329
330 fn patch_children(
338 &mut self,
339 parent: &Element,
340 old_children: &[VirtualNode],
341 new_children: &[VirtualNode],
342 ) {
343 let old_len: usize = old_children.len();
344 let new_len: usize = new_children.len();
345 let common_len: usize = old_len.min(new_len);
346 for index in 0..common_len {
347 let old_child: &VirtualNode = &old_children[index];
348 let new_child: &VirtualNode = &new_children[index];
349 if let Some(dom_child) = Self::get_child_node(parent, index as u32) {
350 if let Some(element) = dom_child.dyn_ref::<Element>() {
351 self.patch_node(old_child, new_child, element);
352 } else if let (VirtualNode::Text(old_text), VirtualNode::Text(new_text)) =
353 (old_child, new_child)
354 {
355 if old_text.get_content() != new_text.get_content() {
356 dom_child.set_text_content(Some(new_text.get_content()));
357 }
358 } else {
359 let new_dom: Node = self.create_dom_node(new_child);
360 if let Some(parent_node) = dom_child.parent_node() {
361 let _ = parent_node.replace_child(&new_dom, &dom_child);
362 }
363 }
364 }
365 }
366 if new_len > old_len {
367 for new_child in new_children.iter().skip(common_len) {
368 let new_dom: Node = self.create_dom_node(new_child);
369 parent.append_child(&new_dom).unwrap();
370 }
371 } else if old_len > new_len {
372 for _ in common_len..old_len {
373 if let Some(last_child) = parent.last_child() {
374 parent.remove_child(&last_child).unwrap();
375 }
376 }
377 }
378 }
379
380 fn create_dom_node(&mut self, node: &VirtualNode) -> Node {
382 match node {
383 VirtualNode::Element {
384 tag,
385 attributes,
386 children,
387 ..
388 } => {
389 let document: Document = window().unwrap().document().unwrap();
390 let element: Element = match tag {
391 Tag::Element(name) => document.create_element(name).unwrap(),
392 Tag::Component(_) => {
393 let unwrapped: VirtualNode = self.unwrap_component(node);
394 return self.create_dom_node(&unwrapped);
395 }
396 };
397 for attr in attributes {
398 match attr.get_value() {
399 AttributeValue::Text(value) => {
400 if !value.is_empty() || Self::is_boolean_property(attr.get_name()) {
401 Self::set_dom_attribute_or_property(
402 &element,
403 attr.get_name(),
404 value,
405 );
406 }
407 }
408 AttributeValue::Signal(signal) => {
409 let initial_value: String = signal.get();
410 if !initial_value.is_empty()
411 || Self::is_boolean_property(attr.get_name())
412 {
413 Self::set_dom_attribute_or_property(
414 &element,
415 attr.get_name(),
416 &initial_value,
417 );
418 }
419 let attr_name: String = attr.get_name().clone();
420 let element_clone: Element = element.clone();
421 let signal_for_sub: Signal<String> = *signal;
422 let signal_inner: Signal<String> = signal_for_sub;
423 signal_for_sub.subscribe(move || {
424 let new_value: String = signal_inner.get();
425 if new_value.is_empty() && !Self::is_boolean_property(&attr_name) {
426 Self::remove_dom_attribute_or_property(
427 &element_clone,
428 &attr_name,
429 );
430 } else {
431 Self::set_dom_attribute_or_property(
432 &element_clone,
433 &attr_name,
434 &new_value,
435 );
436 }
437 });
438 }
439 AttributeValue::Event(handler) => {
440 self.attach_event_listener(&element, handler);
441 }
442 AttributeValue::Dynamic(_) => {}
443 AttributeValue::Css(css_class) => {
444 css_class.inject_style();
445 Self::set_dom_attribute_or_property(
446 &element,
447 attr.get_name(),
448 css_class.get_name(),
449 );
450 }
451 }
452 }
453 for child in children {
454 let child_node: Node = self.create_dom_node(child);
455 element.append_child(&child_node).unwrap();
456 }
457 element.into()
458 }
459 VirtualNode::Text(text_node) => {
460 let document: Document = window().unwrap().document().unwrap();
461 let text: Text = document.create_text_node(text_node.get_content());
462 if let Some(signal) = text_node.try_get_signal() {
463 let text_clone: Text = text.clone();
464 let signal_clone: Signal<String> = *signal;
465 signal_clone.subscribe({
466 let signal_inner: Signal<String> = signal_clone;
467 move || {
468 let new_value: String = signal_inner.get();
469 text_clone.set_text_content(Some(&new_value));
470 }
471 });
472 }
473 text.into()
474 }
475 VirtualNode::Fragment(children) => {
476 let document: Document = window().unwrap().document().unwrap();
477 let fragment: Element = document.create_element("div").unwrap();
478 for child in children {
479 let child_node: Node = self.create_dom_node(child);
480 fragment.append_child(&child_node).unwrap();
481 }
482 fragment.into()
483 }
484 VirtualNode::Dynamic(dynamic_node) => {
485 let document: Document = window().unwrap().document().unwrap();
486 let placeholder: Element = document.create_element("div").unwrap();
487 let style: &str = "display: contents;";
488 let _ = placeholder.set_attribute("style", style);
489 let mut hook_context: HookContext = dynamic_node.hook_context;
490 hook_context.reset_hook_index();
491 let initial_vnode: VirtualNode = with_hook_context(hook_context, || {
492 let mut borrowed = dynamic_node.render_fn.borrow_mut();
493 borrowed()
494 });
495 let initial_unwrapped: VirtualNode = self.unwrap_component(&initial_vnode);
496 let initial_dom: Node = self.create_dom_node(&initial_unwrapped);
497 placeholder.append_child(&initial_dom).unwrap();
498 let render_fn_clone: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
499 Rc::clone(&dynamic_node.render_fn);
500 let placeholder_clone: Element = placeholder.clone();
501 let mut renderer_for_sub: Renderer = Renderer::new(placeholder_clone.clone());
502 renderer_for_sub.set_current_tree(Some(initial_unwrapped));
503 let renderer_ref: Rc<RefCell<Renderer>> = Rc::new(RefCell::new(renderer_for_sub));
504 let renderer_ref_for_sub: Rc<RefCell<Renderer>> = Rc::clone(&renderer_ref);
505 let render_fn_for_sub: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
506 Rc::clone(&render_fn_clone);
507 let window: Window = window().unwrap();
508 let closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
509 if placeholder_clone.parent_node().is_none() {
510 return;
511 }
512 hook_context.reset_hook_index();
513 let new_vnode: VirtualNode = with_hook_context(hook_context, || {
514 let mut borrowed = render_fn_for_sub.borrow_mut();
515 borrowed()
516 });
517 let mut renderer = renderer_ref_for_sub.borrow_mut();
518 renderer.render(new_vnode);
519 }));
520 window
521 .add_event_listener_with_callback(
522 &NativeEventName::EuvSignalUpdate.to_string(),
523 closure.as_ref().unchecked_ref(),
524 )
525 .unwrap();
526 closure.forget();
527 placeholder.into()
528 }
529 VirtualNode::Empty => {
530 let document: Document = window().unwrap().document().unwrap();
531 document.create_text_node("").into()
532 }
533 }
534 }
535
536 fn unwrap_component(&self, node: &VirtualNode) -> VirtualNode {
538 match node {
539 VirtualNode::Element {
540 tag: Tag::Component(_),
541 children,
542 ..
543 } => {
544 if children.len() == 1 {
545 self.unwrap_component(&children[0])
546 } else {
547 VirtualNode::Fragment(children.clone())
548 }
549 }
550 VirtualNode::Element {
551 tag,
552 attributes,
553 children,
554 key,
555 } => {
556 let unwrapped_children: Vec<VirtualNode> = children
557 .iter()
558 .map(|child| self.unwrap_component(child))
559 .collect();
560 VirtualNode::Element {
561 tag: tag.clone(),
562 attributes: attributes.clone(),
563 children: unwrapped_children,
564 key: key.clone(),
565 }
566 }
567 VirtualNode::Fragment(children) => {
568 let unwrapped_children: Vec<VirtualNode> = children
569 .iter()
570 .map(|child| self.unwrap_component(child))
571 .collect();
572 VirtualNode::Fragment(unwrapped_children)
573 }
574 other => other.clone(),
575 }
576 }
577
578 fn attach_event_listener(&self, element: &Element, handler: &NativeEventHandler) {
591 let euv_id: usize = match element.get_attribute("data-euv-id") {
592 Some(id_str) => id_str.parse::<usize>().unwrap_or_else(|_| {
593 let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
594 let _ = element.set_attribute("data-euv-id", &new_id.to_string());
595 new_id
596 }),
597 None => {
598 let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
599 let _ = element.set_attribute("data-euv-id", &new_id.to_string());
600 new_id
601 }
602 };
603 let event_name: String = handler.get_event_name().clone();
604 let key: (usize, String) = (euv_id, event_name.clone());
605 let registry: &mut HashMap<(usize, String), Rc<RefCell<Option<NativeEventHandler>>>> =
607 get_handler_registry();
608 if let Some(existing_wrapper) = registry.get(&key) {
609 let mut wrapper: RefMut<Option<NativeEventHandler>> = existing_wrapper.borrow_mut();
610 *wrapper = Some(handler.clone());
611 } else {
612 let handler_wrapper: Rc<RefCell<Option<NativeEventHandler>>> =
613 Rc::new(RefCell::new(Some(handler.clone())));
614 let wrapper_for_closure: Rc<RefCell<Option<NativeEventHandler>>> =
615 Rc::clone(&handler_wrapper);
616 let event_name_for_closure: String = event_name.clone();
617 let closure: Closure<dyn FnMut(Event)> =
618 Closure::wrap(Box::new(move |event: Event| {
619 if let Some(active_handler) = wrapper_for_closure.borrow_mut().as_ref() {
620 let euv_event: NativeEvent =
621 super::r#fn::convert_web_event(&event, &event_name_for_closure);
622 active_handler.handle(euv_event);
623 }
624 }));
625 element
626 .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
627 .unwrap();
628 closure.forget();
629 registry.insert(key, handler_wrapper);
630 }
631 }
632}