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