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 dynamic_id: usize = Self::ensure_dynamic_id(dom_element);
90 let mut hook_context: HookContext = new_dynamic.get_hook_context();
91 hook_context.reset_hook_index();
92 let initial_vnode: VirtualNode = with_hook_context(hook_context, || {
93 let mut borrowed: RefMut<dyn FnMut() -> VirtualNode> =
94 new_dynamic.get_render_fn().borrow_mut();
95 borrowed()
96 });
97 let initial_unwrapped: VirtualNode = self.unwrap_component(&initial_vnode);
98 let initial_dom: Node = self.create_dom_node(&initial_unwrapped);
99 dom_element.append_child(&initial_dom).unwrap();
100 let render_fn_clone: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
101 new_dynamic.get_render_fn().clone();
102 let placeholder_clone: Element = dom_element.clone();
103 let mut renderer_for_sub: Renderer = Renderer::new(placeholder_clone.clone());
104 renderer_for_sub.set_current_tree(Some(initial_unwrapped));
105 let renderer_ref: Rc<RefCell<Renderer>> = Rc::new(RefCell::new(renderer_for_sub));
106 let renderer_ref_for_sub: Rc<RefCell<Renderer>> = Rc::clone(&renderer_ref);
107 let render_fn_for_sub: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
108 Rc::clone(&render_fn_clone);
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 register_dynamic_listener(dynamic_id, re_render_closure);
119 }
120 _ => {
121 let new_dom: Node = self.create_dom_node(new_node);
122 if let Some(parent) = dom_element.parent_node() {
123 parent.replace_child(&new_dom, dom_element).unwrap();
124 }
125 }
126 }
127 }
128
129 fn patch_attributes(
131 &mut self,
132 element: &Element,
133 old_attrs: &[AttributeEntry],
134 new_attrs: &[AttributeEntry],
135 ) {
136 let mut old_map: HashMap<&str, &AttributeValue> = HashMap::new();
137 for attr in old_attrs {
138 old_map.insert(attr.get_name(), attr.get_value());
139 }
140 let mut new_map: HashMap<&str, &AttributeValue> = HashMap::new();
141 for attr in new_attrs {
142 new_map.insert(attr.get_name(), attr.get_value());
143 }
144 for name in old_map.keys() {
145 if !new_map.contains_key(*name) {
146 Self::remove_dom_attribute_or_property(element, name);
147 }
148 }
149 for attr in new_attrs {
150 let should_set: bool = match old_map.get(attr.get_name().as_str()) {
151 Some(old_value) => !Self::attribute_values_equal(old_value, attr.get_value()),
152 None => true,
153 };
154 if should_set {
155 match attr.get_value() {
156 AttributeValue::Text(value) => {
157 if value.is_empty() {
158 Self::remove_dom_attribute_or_property(element, attr.get_name());
159 } else {
160 Self::set_dom_attribute_or_property(element, attr.get_name(), value);
161 }
162 }
163 AttributeValue::Signal(signal) => {
164 let value: String = signal.get();
165 if value.is_empty() && !Self::is_boolean_property(attr.get_name()) {
166 Self::remove_dom_attribute_or_property(element, attr.get_name());
167 } else {
168 Self::set_dom_attribute_or_property(element, attr.get_name(), &value);
169 }
170 }
171 AttributeValue::Event(handler) => {
172 self.attach_event_listener(element, handler);
173 }
174 AttributeValue::Dynamic(_) => {}
175 AttributeValue::Css(css_class) => {
176 css_class.inject_style();
177 Self::set_dom_attribute_or_property(
178 element,
179 attr.get_name(),
180 css_class.get_name(),
181 );
182 }
183 }
184 }
185 }
186 }
187
188 fn is_boolean_property(name: &str) -> bool {
191 matches!(name, "checked" | "disabled" | "selected" | "readonly")
192 }
193
194 fn remove_dom_attribute_or_property(element: &Element, name: &str) {
204 if name == "value" {
205 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
206 input.set_value("");
207 return;
208 }
209 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
210 textarea.set_value("");
211 return;
212 }
213 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
214 select.set_value("");
215 return;
216 }
217 }
218 if name == "checked"
219 && let Some(input) = element.dyn_ref::<HtmlInputElement>()
220 {
221 input.set_checked(false);
222 return;
223 }
224 if name == "disabled" {
225 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
226 input.set_disabled(false);
227 return;
228 }
229 if let Some(button) = element.dyn_ref::<HtmlButtonElement>() {
230 button.set_disabled(false);
231 return;
232 }
233 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
234 select.set_disabled(false);
235 return;
236 }
237 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
238 textarea.set_disabled(false);
239 return;
240 }
241 }
242 if name == "selected"
243 && let Some(option) = element.dyn_ref::<HtmlOptionElement>()
244 {
245 option.set_selected(false);
246 return;
247 }
248 if name == "readonly" {
249 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
250 input.set_read_only(false);
251 return;
252 }
253 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
254 textarea.set_read_only(false);
255 return;
256 }
257 }
258 let _ = element.remove_attribute(name);
259 }
260
261 fn set_dom_attribute_or_property(element: &Element, name: &str, value: &str) {
269 if name == "value" {
270 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
271 input.set_value(value);
272 return;
273 }
274 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
275 textarea.set_value(value);
276 return;
277 }
278 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
279 select.set_value(value);
280 return;
281 }
282 }
283 if name == "checked"
284 && let Some(input) = element.dyn_ref::<HtmlInputElement>()
285 {
286 input.set_checked(value == "true");
287 return;
288 }
289 if name == "disabled" {
290 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
291 input.set_disabled(value == "true");
292 return;
293 }
294 if let Some(button) = element.dyn_ref::<HtmlButtonElement>() {
295 button.set_disabled(value == "true");
296 return;
297 }
298 if let Some(select) = element.dyn_ref::<HtmlSelectElement>() {
299 select.set_disabled(value == "true");
300 return;
301 }
302 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
303 textarea.set_disabled(value == "true");
304 return;
305 }
306 }
307 if name == "selected"
308 && let Some(option) = element.dyn_ref::<HtmlOptionElement>()
309 {
310 option.set_selected(value == "true");
311 return;
312 }
313 if name == "readonly" {
314 if let Some(input) = element.dyn_ref::<HtmlInputElement>() {
315 input.set_read_only(value == "true");
316 return;
317 }
318 if let Some(textarea) = element.dyn_ref::<HtmlTextAreaElement>() {
319 textarea.set_read_only(value == "true");
320 return;
321 }
322 }
323 let _ = element.set_attribute(name, value);
324 }
325
326 fn tags_equal(a: &Tag, b: &Tag) -> bool {
328 match (a, b) {
329 (Tag::Element(a_name), Tag::Element(b_name)) => a_name == b_name,
330 (Tag::Component(a_name), Tag::Component(b_name)) => a_name == b_name,
331 _ => false,
332 }
333 }
334
335 fn attribute_values_equal(a: &AttributeValue, b: &AttributeValue) -> bool {
343 match (a, b) {
344 (AttributeValue::Text(a_val), AttributeValue::Text(b_val)) => a_val == b_val,
345 (AttributeValue::Signal(_a_sig), AttributeValue::Signal(_b_sig)) => false,
346 (AttributeValue::Event(_a_ev), AttributeValue::Event(_b_ev)) => false,
347 (AttributeValue::Dynamic(a_dyn), AttributeValue::Dynamic(b_dyn)) => a_dyn == b_dyn,
348 (AttributeValue::Css(a_css), AttributeValue::Css(b_css)) => {
349 a_css.get_name() == b_css.get_name()
350 }
351 _ => false,
352 }
353 }
354
355 fn get_child_node(parent: &Element, index: u32) -> Option<Node> {
357 let mut current: Option<Node> = parent.first_child();
358 let mut current_index: u32 = 0;
359 while let Some(node) = current {
360 if current_index == index {
361 return Some(node);
362 }
363 current = node.next_sibling();
364 current_index += 1;
365 }
366 None
367 }
368
369 fn patch_children(
377 &mut self,
378 parent: &Element,
379 old_children: &[VirtualNode],
380 new_children: &[VirtualNode],
381 ) {
382 let old_len: usize = old_children.len();
383 let new_len: usize = new_children.len();
384 let common_len: usize = old_len.min(new_len);
385 for index in 0..common_len {
386 let old_child: &VirtualNode = &old_children[index];
387 let new_child: &VirtualNode = &new_children[index];
388 if let Some(dom_child) = Self::get_child_node(parent, index as u32) {
389 if let Some(element) = dom_child.dyn_ref::<Element>() {
390 self.patch_node(old_child, new_child, element);
391 } else if let (VirtualNode::Text(old_text), VirtualNode::Text(new_text)) =
392 (old_child, new_child)
393 {
394 if old_text.get_content() != new_text.get_content() {
395 dom_child.set_text_content(Some(new_text.get_content()));
396 }
397 } else {
398 let new_dom: Node = self.create_dom_node(new_child);
399 if let Some(parent_node) = dom_child.parent_node() {
400 let _ = parent_node.replace_child(&new_dom, &dom_child);
401 }
402 }
403 }
404 }
405 if new_len > old_len {
406 for new_child in new_children.iter().skip(common_len) {
407 let new_dom: Node = self.create_dom_node(new_child);
408 parent.append_child(&new_dom).unwrap();
409 }
410 } else if old_len > new_len {
411 for _ in common_len..old_len {
412 if let Some(last_child) = parent.last_child() {
413 parent.remove_child(&last_child).unwrap();
414 }
415 }
416 }
417 }
418
419 fn create_dom_node(&mut self, node: &VirtualNode) -> Node {
421 match node {
422 VirtualNode::Element {
423 tag,
424 attributes,
425 children,
426 ..
427 } => {
428 let document: Document = window().unwrap().document().unwrap();
429 let element: Element = match tag {
430 Tag::Element(name) => document.create_element(name).unwrap(),
431 Tag::Component(_) => {
432 let unwrapped: VirtualNode = self.unwrap_component(node);
433 return self.create_dom_node(&unwrapped);
434 }
435 };
436 for attr in attributes {
437 match attr.get_value() {
438 AttributeValue::Text(value) => {
439 if !value.is_empty() || Self::is_boolean_property(attr.get_name()) {
440 Self::set_dom_attribute_or_property(
441 &element,
442 attr.get_name(),
443 value,
444 );
445 }
446 }
447 AttributeValue::Signal(signal) => {
448 let initial_value: String = signal.get();
449 if !initial_value.is_empty()
450 || Self::is_boolean_property(attr.get_name())
451 {
452 Self::set_dom_attribute_or_property(
453 &element,
454 attr.get_name(),
455 &initial_value,
456 );
457 }
458 let attr_name: String = attr.get_name().clone();
459 let element_clone: Element = element.clone();
460 let signal_for_sub: Signal<String> = *signal;
461 let signal_inner: Signal<String> = signal_for_sub;
462 signal_for_sub.subscribe(move || {
463 let new_value: String = signal_inner.get();
464 if new_value.is_empty() && !Self::is_boolean_property(&attr_name) {
465 Self::remove_dom_attribute_or_property(
466 &element_clone,
467 &attr_name,
468 );
469 } else {
470 Self::set_dom_attribute_or_property(
471 &element_clone,
472 &attr_name,
473 &new_value,
474 );
475 }
476 });
477 }
478 AttributeValue::Event(handler) => {
479 self.attach_event_listener(&element, handler);
480 }
481 AttributeValue::Dynamic(_) => {}
482 AttributeValue::Css(css_class) => {
483 css_class.inject_style();
484 Self::set_dom_attribute_or_property(
485 &element,
486 attr.get_name(),
487 css_class.get_name(),
488 );
489 }
490 }
491 }
492 for child in children {
493 let child_node: Node = self.create_dom_node(child);
494 element.append_child(&child_node).unwrap();
495 }
496 element.into()
497 }
498 VirtualNode::Text(text_node) => {
499 let document: Document = window().unwrap().document().unwrap();
500 let text: Text = document.create_text_node(text_node.get_content());
501 if let Some(signal) = text_node.try_get_signal() {
502 let text_clone: Text = text.clone();
503 let signal_clone: Signal<String> = *signal;
504 signal_clone.subscribe({
505 let signal_inner: Signal<String> = signal_clone;
506 move || {
507 let new_value: String = signal_inner.get();
508 text_clone.set_text_content(Some(&new_value));
509 }
510 });
511 }
512 text.into()
513 }
514 VirtualNode::Fragment(children) => {
515 let document: Document = window().unwrap().document().unwrap();
516 let fragment: Element = document.create_element("div").unwrap();
517 for child in children {
518 let child_node: Node = self.create_dom_node(child);
519 fragment.append_child(&child_node).unwrap();
520 }
521 fragment.into()
522 }
523 VirtualNode::Dynamic(dynamic_node) => {
524 let document: Document = window().unwrap().document().unwrap();
525 let placeholder: Element = document.create_element("div").unwrap();
526 let style: &str = "display: contents;";
527 let _ = placeholder.set_attribute("style", style);
528 let dynamic_id: usize = Self::assign_dynamic_id(&placeholder);
529 let mut hook_context: HookContext = dynamic_node.get_hook_context();
530 hook_context.reset_hook_index();
531 let initial_vnode: VirtualNode = with_hook_context(hook_context, || {
532 let mut borrowed: RefMut<dyn FnMut() -> VirtualNode> =
533 dynamic_node.get_render_fn().borrow_mut();
534 borrowed()
535 });
536 let initial_unwrapped: VirtualNode = self.unwrap_component(&initial_vnode);
537 let initial_dom: Node = self.create_dom_node(&initial_unwrapped);
538 placeholder.append_child(&initial_dom).unwrap();
539 let render_fn_clone: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
540 dynamic_node.get_render_fn().clone();
541 let placeholder_clone: Element = placeholder.clone();
542 let mut renderer_for_sub: Renderer = Renderer::new(placeholder_clone.clone());
543 renderer_for_sub.set_current_tree(Some(initial_unwrapped));
544 let renderer_ref: Rc<RefCell<Renderer>> = Rc::new(RefCell::new(renderer_for_sub));
545 let renderer_ref_for_sub: Rc<RefCell<Renderer>> = Rc::clone(&renderer_ref);
546 let render_fn_for_sub: Rc<RefCell<dyn FnMut() -> VirtualNode>> =
547 Rc::clone(&render_fn_clone);
548 let closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
549 if placeholder_clone.parent_node().is_none() {
550 return;
551 }
552 hook_context.reset_hook_index();
553 let new_vnode: VirtualNode =
554 with_hook_context(hook_context, || render_fn_for_sub.borrow_mut()());
555 {
556 let renderer: Ref<'_, Renderer> = renderer_ref_for_sub.borrow();
557 if let Some(old_vnode) = renderer.try_get_current_tree() {
558 let new_unwrapped: VirtualNode = renderer.unwrap_component(&new_vnode);
559 if old_vnode == &new_unwrapped {
560 return;
561 }
562 }
563 }
564 renderer_ref_for_sub.borrow_mut().render(new_vnode);
565 }));
566 register_dynamic_listener(dynamic_id, closure);
567 placeholder.into()
568 }
569 VirtualNode::Empty => {
570 let document: Document = window().unwrap().document().unwrap();
571 document.create_text_node("").into()
572 }
573 }
574 }
575
576 fn unwrap_component(&self, node: &VirtualNode) -> VirtualNode {
578 match node {
579 VirtualNode::Element {
580 tag: Tag::Component(_),
581 children,
582 ..
583 } => {
584 if children.len() == 1 {
585 self.unwrap_component(&children[0])
586 } else {
587 VirtualNode::Fragment(children.clone())
588 }
589 }
590 VirtualNode::Element {
591 tag,
592 attributes,
593 children,
594 key,
595 } => {
596 let unwrapped_children: Vec<VirtualNode> = children
597 .iter()
598 .map(|child| self.unwrap_component(child))
599 .collect();
600 VirtualNode::Element {
601 tag: tag.clone(),
602 attributes: attributes.clone(),
603 children: unwrapped_children,
604 key: key.clone(),
605 }
606 }
607 VirtualNode::Fragment(children) => {
608 let unwrapped_children: Vec<VirtualNode> = children
609 .iter()
610 .map(|child| self.unwrap_component(child))
611 .collect();
612 VirtualNode::Fragment(unwrapped_children)
613 }
614 other => other.clone(),
615 }
616 }
617
618 fn assign_dynamic_id(placeholder: &Element) -> usize {
640 let dynamic_id: usize = NEXT_EUV_DYNAMIC_ID.fetch_add(1, Ordering::Relaxed);
641 let _ = placeholder.set_attribute("data-euv-dynamic-id", &dynamic_id.to_string());
642 dynamic_id
643 }
644
645 fn ensure_dynamic_id(dom_element: &Element) -> usize {
660 match dom_element.get_attribute("data-euv-dynamic-id") {
661 Some(id_str) => id_str.parse::<usize>().unwrap_or_else(|_| {
662 let new_id: usize = NEXT_EUV_DYNAMIC_ID.fetch_add(1, Ordering::Relaxed);
663 let _ = dom_element.set_attribute("data-euv-dynamic-id", &new_id.to_string());
664 new_id
665 }),
666 None => {
667 let new_id: usize = NEXT_EUV_DYNAMIC_ID.fetch_add(1, Ordering::Relaxed);
668 let _ = dom_element.set_attribute("data-euv-dynamic-id", &new_id.to_string());
669 new_id
670 }
671 }
672 }
673
674 fn attach_event_listener(&self, element: &Element, handler: &NativeEventHandler) {
675 let euv_id: usize = match element.get_attribute("data-euv-id") {
676 Some(id_str) => id_str.parse::<usize>().unwrap_or_else(|_| {
677 let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
678 let _ = element.set_attribute("data-euv-id", &new_id.to_string());
679 new_id
680 }),
681 None => {
682 let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
683 let _ = element.set_attribute("data-euv-id", &new_id.to_string());
684 new_id
685 }
686 };
687 let event_name: String = handler.get_event_name().clone();
688 let key: (usize, String) = (euv_id, event_name.clone());
689 let registry: &mut HashMap<(usize, String), Rc<RefCell<Option<NativeEventHandler>>>> =
690 get_handler_registry();
691 if let Some(existing_wrapper) = registry.get(&key) {
692 let mut wrapper: RefMut<Option<NativeEventHandler>> = existing_wrapper.borrow_mut();
693 *wrapper = Some(handler.clone());
694 } else {
695 let handler_wrapper: Rc<RefCell<Option<NativeEventHandler>>> =
696 Rc::new(RefCell::new(Some(handler.clone())));
697 let wrapper_for_closure: Rc<RefCell<Option<NativeEventHandler>>> =
698 Rc::clone(&handler_wrapper);
699 let event_name_for_closure: String = event_name.clone();
700 let closure: Closure<dyn FnMut(Event)> =
701 Closure::wrap(Box::new(move |event: Event| {
702 if let Some(active_handler) = wrapper_for_closure.borrow_mut().as_ref() {
703 let euv_event: NativeEvent =
704 convert_web_event(&event, &event_name_for_closure);
705 active_handler.handle(euv_event);
706 }
707 event.stop_propagation();
708 }));
709 element
710 .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
711 .unwrap();
712 closure.forget();
713 registry.insert(key, handler_wrapper);
714 }
715 }
716}