1use std::{
2 borrow::Cow,
3 cell::{Ref, RefCell},
4 fmt,
5 rc::Rc,
6};
7
8use indexmap::IndexMap;
9use wasm_bindgen::{closure::Closure, JsCast, JsValue};
10use web_sys::{self, Node};
11
12use crate::{
13 dom::{
14 component::StatelessModel, document, dom_patch, events::MountEvent, Application, DomAttr,
15 GroupedDomAttrValues, Program, StatefulComponent, StatefulModel,
16 },
17 html::lookup,
18 vdom::{self, Attribute, Leaf, TreePath},
19};
20
21pub(crate) type EventClosure = Closure<dyn FnMut(web_sys::Event)>;
22pub type NamedEventClosures = IndexMap<&'static str, EventClosure>;
23
24#[derive(Clone, Debug)]
30pub struct DomNode {
31 pub(crate) inner: DomInner,
32}
33
34#[derive(Clone)]
35pub enum DomInner {
36 Element {
38 element: web_sys::Element,
40 listeners: Rc<RefCell<Option<NamedEventClosures>>>,
43 children: Rc<RefCell<Vec<DomNode>>>,
46 has_mount_callback: bool,
48 },
49 Text(web_sys::Text),
51 Symbol(Cow<'static, str>),
52 Comment(web_sys::Comment),
54 Fragment {
56 fragment: web_sys::DocumentFragment,
58 children: Rc<RefCell<Vec<DomNode>>>,
60 },
61 StatefulComponent {
63 comp: Rc<RefCell<dyn StatefulComponent>>,
64 dom_node: Rc<DomNode>,
65 },
66}
67
68impl fmt::Debug for DomInner {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 match self {
71 Self::Element {
72 element, children, ..
73 } => {
74 f.debug_struct("Element")
75 .field("tag", &element.tag_name().to_lowercase())
76 .field(
77 "children",
78 &children
79 .borrow()
80 .iter()
81 .map(|c| c.tag())
82 .collect::<Vec<_>>(),
83 )
84 .finish()?;
85 Ok(())
86 }
87 Self::Text(text_node) => f
88 .debug_tuple("Text")
89 .field(&text_node.whole_text().expect("whole text"))
90 .finish(),
91 Self::Symbol(symbol) => f.debug_tuple("Symbol").field(&symbol).finish(),
92 Self::Comment(_) => write!(f, "Comment"),
93 Self::Fragment { .. } => write!(f, "Fragment"),
94 Self::StatefulComponent { .. } => write!(f, "StatefulComponent"),
95 }
96 }
97}
98
99impl From<web_sys::Node> for DomNode {
100 fn from(node: web_sys::Node) -> Self {
101 match node.node_type() {
102 Node::ELEMENT_NODE => {
103 let element: web_sys::Element = node.unchecked_into();
104 let child_nodes = element.child_nodes();
105 let children_count = child_nodes.length();
106 let children = (0..children_count)
107 .map(|i| {
108 let child = child_nodes.get(i).expect("child");
109 DomNode::from(child)
110 })
111 .collect();
112 DomNode {
113 inner: DomInner::Element {
114 element,
115 listeners: Rc::new(RefCell::new(None)),
116 children: Rc::new(RefCell::new(children)),
117 has_mount_callback: false,
118 },
119 }
120 }
121 Node::TEXT_NODE => {
122 let text_node: web_sys::Text = node.unchecked_into();
123 DomNode {
124 inner: DomInner::Text(text_node),
125 }
126 }
127 Node::COMMENT_NODE => {
128 let comment: web_sys::Comment = node.unchecked_into();
129 DomNode {
130 inner: DomInner::Comment(comment),
131 }
132 }
133 Node::DOCUMENT_FRAGMENT_NODE => {
134 let fragment: web_sys::DocumentFragment = node.unchecked_into();
135 DomNode {
136 inner: DomInner::Fragment {
137 fragment,
138 children: Rc::new(RefCell::new(vec![])),
139 },
140 }
141 }
142 _node_type => todo!("for: {_node_type:?}"),
143 }
144 }
145}
146
147impl PartialEq for DomNode {
148 fn eq(&self, other: &Self) -> bool {
149 match (&self.inner, &other.inner) {
150 (DomInner::Element { element: v, .. }, DomInner::Element { element: o, .. }) => v == o,
151 (DomInner::Fragment { fragment: v, .. }, DomInner::Fragment { fragment: o, .. }) => {
152 v == o
153 }
154 (DomInner::Text(v), DomInner::Text(o)) => v == o,
155 (DomInner::Symbol(v), DomInner::Symbol(o)) => v == o,
156 (DomInner::Comment(v), DomInner::Comment(o)) => v == o,
157 (DomInner::StatefulComponent { .. }, DomInner::StatefulComponent { .. }) => todo!(),
158 _ => false,
159 }
160 }
161}
162
163impl DomNode {
164 pub(crate) fn children(&self) -> Option<Ref<'_, Vec<DomNode>>> {
165 match &self.inner {
166 DomInner::Element { children, .. } => Some(children.borrow()),
167 DomInner::Fragment { children, .. } => Some(children.borrow()),
168 _ => None,
169 }
170 }
171
172 pub fn is_element(&self) -> bool {
174 matches!(&self.inner, DomInner::Element { .. })
175 }
176
177 pub fn is_fragment(&self) -> bool {
179 matches!(&self.inner, DomInner::Fragment { .. })
180 }
181
182 pub fn is_text_node(&self) -> bool {
184 matches!(&self.inner, DomInner::Text(_))
185 }
186
187 pub fn is_comment(&self) -> bool {
189 matches!(&self.inner, DomInner::Comment(_))
190 }
191
192 pub fn is_symbol(&self) -> bool {
194 matches!(&self.inner, DomInner::Symbol(_))
195 }
196
197 pub fn is_stateful_component(&self) -> bool {
199 matches!(&self.inner, DomInner::StatefulComponent { .. })
200 }
201
202 pub(crate) fn tag(&self) -> Option<String> {
203 match &self.inner {
204 DomInner::Element { element, .. } => Some(element.tag_name().to_lowercase()),
205 _ => None,
206 }
207 }
208
209 pub fn as_node(&self) -> web_sys::Node {
211 match &self.inner {
212 DomInner::Element { element, .. } => element.clone().unchecked_into(),
213 DomInner::Fragment { fragment, .. } => fragment.clone().unchecked_into(),
214 DomInner::Text(text_node) => text_node.clone().unchecked_into(),
215 DomInner::Symbol(_) => unreachable!("symbol should be handled separately"),
216 DomInner::Comment(comment_node) => comment_node.clone().unchecked_into(),
217 DomInner::StatefulComponent { dom_node, .. } => dom_node.as_node(),
218 }
219 }
220
221 #[track_caller]
223 pub fn as_element(&self) -> web_sys::Element {
224 match &self.inner {
225 DomInner::Element { element, .. } => element.clone(),
226 DomInner::Fragment { fragment, .. } => {
227 let fragment: web_sys::Element = fragment.clone().unchecked_into();
228 fragment
229 }
230 DomInner::Text(text_node) => text_node.clone().unchecked_into(),
231 DomInner::Symbol(_) => unreachable!("symbol should be handled separately"),
232 DomInner::Comment(comment_node) => comment_node.clone().unchecked_into(),
233 DomInner::StatefulComponent { dom_node, .. } => dom_node.as_element(),
234 }
235 }
236
237 pub fn as_symbol(&self) -> Option<&str> {
239 match &self.inner {
240 DomInner::Symbol(symbol) => Some(symbol),
241 _ => None,
242 }
243 }
244
245 pub fn outer_html(&self) -> String {
247 let DomInner::Element { element, .. } = &self.inner else {
248 unreachable!("should only be called to element");
249 };
250 element.outer_html()
251 }
252
253 pub fn append_children(&self, for_append: Vec<DomNode>) {
255 match &self.inner {
256 DomInner::Element {
257 element, children, ..
258 } => {
259 for child in for_append.into_iter() {
260 if let Some(symbol) = child.as_symbol() {
261 element
262 .insert_adjacent_html(intern("beforeend"), symbol)
263 .expect("must not error");
264 } else {
265 element
266 .append_child(&child.as_node())
267 .expect("append child");
268 child.dispatch_mount_event();
269 }
270 children.borrow_mut().push(child);
271 }
272 }
273 DomInner::Fragment {
274 fragment, children, ..
275 } => {
276 for child in for_append.into_iter() {
277 fragment
278 .append_child(&child.as_node())
279 .expect("append child");
280 child.dispatch_mount_event();
281 children.borrow_mut().push(child);
282 }
283 }
284 _ => unreachable!(
285 "appending should only be called to Element and Fragment, found: {:#?}",
286 self
287 ),
288 }
289 }
290
291 pub(crate) fn insert_before(&self, target_element: &DomNode, for_insert: Vec<DomNode>) {
293 let DomInner::Element { children, .. } = &self.inner else {
294 unreachable!("parent must be an element");
295 };
296
297 let mut target_index = None;
298 for (i, child) in children.borrow().iter().enumerate() {
299 if target_element == child {
300 target_index = Some(i);
301 break;
302 }
303 }
304 for insert_node in for_insert.iter() {
307 target_element
308 .as_element()
309 .insert_adjacent_element(intern("beforebegin"), &insert_node.as_element())
310 .expect("must insert before this element");
311 insert_node.dispatch_mount_event();
312 }
313
314 for insert_node in for_insert.into_iter().rev() {
317 if let Some(target_index) = target_index {
318 children.borrow_mut().insert(target_index, insert_node);
319 } else {
320 unreachable!("should have a self index");
321 }
322 }
323 }
324
325 pub(crate) fn insert_after(&self, target_element: &DomNode, for_insert: Vec<DomNode>) {
327 let DomInner::Element { children, .. } = &self.inner else {
328 unreachable!("parent must be an element");
329 };
330 let mut target_index = None;
331 for (i, child) in children.borrow().iter().enumerate() {
332 if target_element == child {
333 target_index = Some(i);
334 break;
335 }
336 }
337 for insert_node in for_insert.into_iter().rev() {
338 target_element
339 .as_element()
340 .insert_adjacent_element(intern("afterend"), &insert_node.as_element())
341 .expect("must insert after this element");
342 insert_node.dispatch_mount_event();
343
344 if let Some(target_index) = target_index {
345 children.borrow_mut().insert(target_index + 1, insert_node);
346 } else {
347 unreachable!("should have a self index");
348 }
349 }
350 }
351
352 pub(crate) fn replace_child(&self, target_child: &DomNode, replacement: DomNode) {
354 match &self.inner {
355 DomInner::Element { children, .. } => {
356 let mut child_index = None;
357 for (i, ch) in children.borrow().iter().enumerate() {
358 if ch == target_child {
359 child_index = Some(i);
360 break;
361 }
362 }
363 if let Some(child_index) = child_index {
364 children.borrow_mut().remove(child_index);
365 target_child
366 .as_element()
367 .replace_with_with_node_1(&replacement.as_node())
368 .expect("must replace child");
369 replacement.dispatch_mount_event();
370 children.borrow_mut().insert(child_index, replacement);
371 } else {
372 log::warn!(
373 "unable to find target_child: {target_child:#?} with a replacement: {:?}",
374 replacement
375 );
376 unreachable!("must find the child...");
378 }
379 }
380 _ => todo!(),
381 }
382 }
383
384 pub(crate) fn remove_children(&self, for_remove: &[&DomNode]) {
386 match &self.inner {
387 DomInner::Element {
388 element, children, ..
389 } => {
390 let mut child_indexes = vec![];
391 for (i, ch) in children.borrow().iter().enumerate() {
392 for remove_node in for_remove.iter() {
393 if ch == *remove_node {
394 child_indexes.push(i);
395 break;
396 }
397 }
398 }
399 assert_eq!(child_indexes.len(), for_remove.len(), "must find all");
400
401 for child_index in child_indexes.into_iter().rev() {
405 let child = children.borrow_mut().remove(child_index);
406 element
407 .remove_child(&child.as_node())
408 .expect("remove child");
409 }
410 }
411 _ => todo!(),
412 }
413 }
414
415 pub(crate) fn clear_children(&self) {
417 match &self.inner {
418 DomInner::Element {
419 element, children, ..
420 } => {
421 children.borrow_mut().clear();
422 while let Some(last_child) = element.last_child() {
426 element
427 .remove_child(&last_child)
428 .expect("must remove child");
429 }
430 }
431 _ => todo!(),
432 }
433 }
434
435 pub(crate) fn replace_node(&self, replacement: DomNode) {
436 self.as_element()
438 .replace_with_with_node_1(&replacement.as_node())
439 .expect("must replace child");
440 }
441
442 pub fn set_dom_attrs(&self, attrs: impl IntoIterator<Item = DomAttr>) -> Result<(), JsValue> {
444 for attr in attrs.into_iter() {
445 self.set_dom_attr(attr)?;
446 }
447 Ok(())
448 }
449
450 pub fn set_dom_attr(&self, attr: DomAttr) -> Result<(), JsValue> {
452 match &self.inner {
453 DomInner::Element {
454 element, listeners, ..
455 } => {
456 let attr_name = intern(attr.name);
457 let attr_namespace = attr.namespace;
458
459 let GroupedDomAttrValues {
460 listeners: event_callbacks,
461 plain_values,
462 styles,
463 } = attr.group_values();
464
465 Self::add_event_dom_listeners(element, attr_name, &event_callbacks)
466 .expect("event listeners");
467 let is_none = listeners.borrow().is_none();
468 if is_none {
469 let listener_closures: IndexMap<
470 &'static str,
471 Closure<dyn FnMut(web_sys::Event)>,
472 > = IndexMap::from_iter(event_callbacks.into_iter().map(|c| (attr_name, c)));
473 *listeners.borrow_mut() = Some(listener_closures);
474 } else if let Some(listeners) = listeners.borrow_mut().as_mut() {
475 for event_cb in event_callbacks.into_iter() {
476 listeners.insert(attr_name, event_cb);
477 }
478 }
479
480 DomAttr::set_element_style(element, attr_name, styles);
481 DomAttr::set_element_simple_values(
482 element,
483 attr_name,
484 attr_namespace,
485 plain_values,
486 );
487 }
488 DomInner::StatefulComponent { comp, .. } => {
489 log::info!("applying attribute change for stateful component...{attr:?}");
490 comp.borrow_mut().attribute_changed(attr);
491 }
492 _ => {
493 log::info!("set the dom attr for {self:?}, with dom_attr: {attr:?}");
494 unreachable!("should only be called for element");
495 }
496 }
497 Ok(())
498 }
499
500 pub(crate) fn remove_dom_attr(&self, attr: &DomAttr) -> Result<(), JsValue> {
501 let DomInner::Element { element, .. } = &self.inner else {
502 unreachable!("expecting an element");
503 };
504 DomAttr::remove_element_dom_attr(element, attr)
505 }
506
507 pub(crate) fn add_event_dom_listeners(
509 target: &web_sys::EventTarget,
510 attr_name: &'static str,
511 event_listeners: &[EventClosure],
512 ) -> Result<(), JsValue> {
513 for event_cb in event_listeners.iter() {
514 Self::add_event_listener(target, attr_name, event_cb)?;
515 }
516 Ok(())
517 }
518
519 pub(crate) fn add_event_listener(
521 event_target: &web_sys::EventTarget,
522 event_name: &str,
523 listener: &EventClosure,
524 ) -> Result<(), JsValue> {
525 event_target.add_event_listener_with_callback(
526 intern(event_name),
527 listener.as_ref().unchecked_ref(),
528 )?;
529 Ok(())
530 }
531
532 fn should_dispatch_mount_event(&self) -> bool {
535 match self.inner {
536 DomInner::Element {
537 has_mount_callback, ..
538 } => has_mount_callback,
539 DomInner::StatefulComponent { .. } => true,
540 _ => false,
541 }
542 }
543
544 fn dispatch_mount_event(&self) {
545 if self.should_dispatch_mount_event() {
546 let event_target: web_sys::EventTarget = self.as_element().unchecked_into();
547 event_target
548 .dispatch_event(&MountEvent::create_web_event())
549 .expect("must be ok");
550 }
551 }
552
553 #[allow(unused)]
554 pub(crate) fn find_child(&self, target_child: &DomNode, path: TreePath) -> Option<TreePath> {
555 if self == target_child {
556 Some(path)
557 } else {
558 let children = self.children()?;
559 for (i, child) in children.iter().enumerate() {
560 let child_path = path.traverse(i);
561 let found = child.find_child(target_child, child_path);
562 if found.is_some() {
563 return found;
564 }
565 }
566 None
567 }
568 }
569
570 pub fn render_to_string(&self) -> String {
572 let mut buffer = String::new();
573 self.render(&mut buffer).expect("must render");
574 buffer
575 }
576
577 fn render(&self, buffer: &mut dyn fmt::Write) -> fmt::Result {
578 match &self.inner {
579 DomInner::Text(text_node) => {
580 let text = text_node.whole_text().expect("whole text");
581 write!(buffer, "{text}")?;
582 Ok(())
583 }
584 DomInner::Comment(comment) => {
585 write!(buffer, "<!--{}-->", comment.data())
586 }
587 DomInner::Symbol(symbol) => {
588 write!(buffer, "{symbol}")
589 }
590 DomInner::Element {
591 element, children, ..
592 } => {
593 let tag = element.tag_name().to_lowercase();
594 let is_self_closing = lookup::is_self_closing(&tag);
595
596 write!(buffer, "<{tag}")?;
597 let attrs = element.attributes();
598 let attrs_len = attrs.length();
599 for i in 0..attrs_len {
600 let attr = attrs.item(i).expect("attr");
601 write!(buffer, " {}=\"{}\"", attr.local_name(), attr.value())?;
602 }
603 if is_self_closing {
604 write!(buffer, "/>")?;
605 } else {
606 write!(buffer, ">")?;
607 }
608
609 for child in children.borrow().iter() {
610 child.render(buffer)?;
611 }
612 if !is_self_closing {
613 write!(buffer, "</{tag}>")?;
614 }
615 Ok(())
616 }
617 DomInner::Fragment { children, .. } => {
618 for child in children.borrow().iter() {
619 child.render(buffer)?;
620 }
621 Ok(())
622 }
623 DomInner::StatefulComponent { comp: _, dom_node } => {
624 dom_node.render(buffer)?;
625 Ok(())
626 }
627 }
628 }
629}
630
631#[cfg(feature = "with-interning")]
632#[inline(always)]
633pub fn intern(s: &str) -> &str {
634 wasm_bindgen::intern(s)
635}
636
637#[cfg(not(feature = "with-interning"))]
638#[inline(always)]
639pub fn intern(s: &str) -> &str {
640 s
641}
642
643impl<APP> Program<APP>
644where
645 APP: Application + 'static,
646{
647 pub fn create_dom_node(&self, node: &vdom::Node<APP::MSG>) -> DomNode {
649 let ev_callback = self.create_ev_callback();
650 create_dom_node(node, ev_callback)
651 }
652
653 pub(crate) fn create_ev_callback(&self) -> impl Fn(APP::MSG) + Clone {
654 let program = self.downgrade();
655 let ev_callback = move |msg| {
656 let mut program = program.upgrade().expect("must upgrade");
657 program.dispatch(msg);
658 };
659 ev_callback
660 }
661}
662
663pub fn create_dom_node<Msg, F>(node: &vdom::Node<Msg>, ev_callback: F) -> DomNode
665where
666 Msg: 'static,
667 F: Fn(Msg) + 'static + Clone,
668{
669 match node {
670 vdom::Node::Element(elm) => create_element_node(elm, ev_callback),
671 vdom::Node::Leaf(leaf) => create_leaf_node(leaf, ev_callback),
672 }
673}
674
675fn create_element_node<Msg, F>(elm: &vdom::Element<Msg>, ev_callback: F) -> DomNode
676where
677 Msg: 'static,
678 F: Fn(Msg) + 'static + Clone,
679{
680 let document = document();
681 let element = if let Some(namespace) = elm.namespace() {
682 document
683 .create_element_ns(Some(intern(namespace)), intern(elm.tag()))
684 .expect("Unable to create element")
685 } else {
686 document
687 .create_element(intern(elm.tag()))
688 .expect("create element")
689 };
690 let attrs = Attribute::merge_attributes_of_same_name(elm.attributes().iter());
693
694 let dom_node = DomNode {
695 inner: DomInner::Element {
696 element,
697 listeners: Rc::new(RefCell::new(None)),
698 children: Rc::new(RefCell::new(vec![])),
699 has_mount_callback: elm.has_mount_callback(),
700 },
701 };
702 let dom_attrs = attrs
703 .iter()
704 .map(|a| dom_patch::convert_attr(a, ev_callback.clone()));
705 dom_node.set_dom_attrs(dom_attrs).expect("set dom attrs");
706 let children: Vec<DomNode> = elm
707 .children()
708 .iter()
709 .map(|child| create_dom_node(child, ev_callback.clone()))
710 .collect();
711 dom_node.append_children(children);
712 dom_node
713}
714
715fn create_leaf_node<Msg, F>(leaf: &vdom::Leaf<Msg>, ev_callback: F) -> DomNode
716where
717 Msg: 'static,
718 F: Fn(Msg) + 'static + Clone,
719{
720 match leaf {
721 Leaf::Text(txt) => DomNode {
722 inner: DomInner::Text(document().create_text_node(txt)),
723 },
724 Leaf::Symbol(symbol) => DomNode {
725 inner: DomInner::Symbol(symbol.clone()),
726 },
727 Leaf::Comment(comment) => DomNode {
728 inner: DomInner::Comment(document().create_comment(comment)),
729 },
730 Leaf::Fragment(nodes) => create_fragment_node(nodes, ev_callback),
731
732 Leaf::NodeList(nodes) => create_fragment_node(nodes, ev_callback),
736
737 Leaf::StatefulComponent(comp) => {
738 DomNode {
740 inner: DomInner::StatefulComponent {
741 comp: Rc::clone(&comp.comp),
742 dom_node: Rc::new(create_stateful_component(comp, ev_callback)),
743 },
744 }
745 }
746 Leaf::StatelessComponent(comp) => create_stateless_component(comp, ev_callback),
747
748 Leaf::TemplatedView(view) => {
749 unreachable!("template view should not be created: {:#?}", view)
750 }
751 Leaf::DocType(_) => unreachable!("doc type is never converted"),
752 }
753}
754
755fn create_fragment_node<'a, Msg, F>(
756 nodes: impl IntoIterator<Item = &'a vdom::Node<Msg>>,
757 ev_callback: F,
758) -> DomNode
759where
760 Msg: 'static,
761 F: Fn(Msg) + 'static + Clone,
762{
763 let fragment = document().create_document_fragment();
764 let dom_node = DomNode {
765 inner: DomInner::Fragment {
766 fragment,
767 children: Rc::new(RefCell::new(vec![])),
768 },
769 };
770 let children = nodes
771 .into_iter()
772 .map(|node| create_dom_node(node, ev_callback.clone()))
773 .collect();
774 dom_node.append_children(children);
775 dom_node
776}
777
778pub fn render_real_dom_to_string(node: &web_sys::Node) -> String {
780 let mut f = String::new();
781 render_real_dom(node, &mut f).expect("must not error");
782 f
783}
784
785pub fn render_real_dom(node: &web_sys::Node, buffer: &mut dyn fmt::Write) -> fmt::Result {
787 match node.node_type() {
788 Node::TEXT_NODE => {
789 let text_node: &web_sys::Text = node.unchecked_ref();
790 let text = text_node.whole_text().expect("whole text");
791 write!(buffer, "{text}")?;
792 Ok(())
793 }
794 Node::COMMENT_NODE => {
795 let comment: &web_sys::Comment = node.unchecked_ref();
796 write!(buffer, "<!--{}-->", comment.data())
797 }
798 Node::ELEMENT_NODE => {
799 let element: &web_sys::Element = node.unchecked_ref();
800 let tag = element.tag_name().to_lowercase();
801 let is_self_closing = lookup::is_self_closing(&tag);
802
803 write!(buffer, "<{tag}")?;
804 let attrs = element.attributes();
805 let attrs_len = attrs.length();
806 for i in 0..attrs_len {
807 let attr = attrs.item(i).expect("attr");
808 write!(buffer, " {}=\"{}\"", attr.local_name(), attr.value())?;
809 }
810 if is_self_closing {
811 write!(buffer, "/>")?;
812 } else {
813 write!(buffer, ">")?;
814 }
815
816 let child_nodes = element.child_nodes();
817 let child_count = child_nodes.length();
818 for i in 0..child_count {
819 let child = child_nodes.get(i).unwrap();
820 render_real_dom(&child, buffer)?;
821 }
822 if !is_self_closing {
823 write!(buffer, "</{tag}>")?;
824 }
825 Ok(())
826 }
827 _ => todo!("for other else"),
828 }
829}
830
831#[allow(unused)]
832pub(crate) fn create_stateless_component<Msg, F>(
833 comp: &StatelessModel<Msg>,
834 ev_callback: F,
835) -> DomNode
836where
837 Msg: 'static,
838 F: Fn(Msg) + 'static + Clone,
839{
840 let comp_view = &comp.view;
841 let real_comp_view = comp_view.unwrap_template_ref();
842 create_dom_node(real_comp_view, ev_callback)
843}
844
845fn create_stateful_component<Msg, F>(comp: &StatefulModel<Msg>, ev_callback: F) -> DomNode
858where
859 Msg: 'static,
860 F: Fn(Msg) + 'static + Clone,
861{
862 let comp_node = create_dom_node(
863 &crate::html::div(
864 [crate::html::attributes::class("component")]
865 .into_iter()
866 .chain(comp.attrs.clone()),
867 [],
868 ),
869 ev_callback.clone(),
870 );
871
872 let dom_attrs: Vec<DomAttr> = comp
873 .attrs
874 .iter()
875 .map(|a| dom_patch::convert_attr(a, ev_callback.clone()))
876 .collect();
877
878 for dom_attr in dom_attrs.into_iter() {
879 log::info!("calling attribute changed..");
880 comp.comp.borrow_mut().attribute_changed(dom_attr);
881 }
882
883 let created_children = comp
887 .children
888 .iter()
889 .map(|child| create_dom_node(child, ev_callback.clone()))
890 .collect();
891 comp.comp.borrow_mut().append_children(created_children);
892 comp_node
893}