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