diff --git a/crates/core/src/dom/dom_node.rs b/crates/core/src/dom/dom_node.rs index ceb74717..203fa625 100644 --- a/crates/core/src/dom/dom_node.rs +++ b/crates/core/src/dom/dom_node.rs @@ -19,7 +19,7 @@ use std::cell::RefCell; use std::fmt; use std::rc::Rc; use wasm_bindgen::{closure::Closure, JsCast, JsValue}; -use web_sys::{self, Element, Node}; +use web_sys::{self, Node}; pub(crate) type EventClosure = Closure; pub type NamedEventClosures = IndexMap<&'static str, EventClosure>; @@ -524,7 +524,14 @@ impl DomNode { plain_values, ); } - _ => unreachable!("should only be called for element"), + DomInner::StatefulComponent{comp,.. } => { + log::info!("applying attribute change for stateful component...{attr:?}"); + comp.borrow_mut().attribute_changed(attr); + } + _ => { + log::info!("set the dom attr for {self:?}, with dom_attr: {attr:?}"); + unreachable!("should only be called for element"); + } } Ok(()) } @@ -707,22 +714,17 @@ where // the root node let attrs = Attribute::merge_attributes_of_same_name(elm.attributes().iter()); - let listeners = self.set_element_dom_attrs( - &element, - attrs - .iter() - .map(|a| self.convert_attr(a)) - .collect::>(), - ); let dom_node = DomNode { inner: DomInner::Element { element, - listeners: Rc::new(RefCell::new(listeners)), + listeners: Rc::new(RefCell::new(None)), children: Rc::new(RefCell::new(vec![])), has_mount_callback: elm.has_mount_callback(), }, parent: parent_node, }; + let dom_attrs = attrs.iter().map(|a|self.convert_attr(a)); + dom_node.set_dom_attrs(dom_attrs).expect("set dom attrs"); let dom_node_rc = Rc::new(Some(dom_node.clone())); let children: Vec = elm .children() @@ -870,72 +872,6 @@ where self.create_dom_node(parent_node, real_comp_view) } - /// set element with the dom attrs - pub(crate) fn set_element_dom_attrs( - &self, - element: &Element, - attrs: Vec, - ) -> Option { - attrs - .into_iter() - .filter_map(|att| self.set_element_dom_attr(element, att)) - .reduce(|mut acc, e| { - e.into_iter().for_each(|(k, v)| { - acc.insert(k, v); - }); - acc - }) - } - - fn set_element_dom_attr(&self, element: &Element, attr: DomAttr) -> Option { - let attr_name = intern(attr.name); - let attr_namespace = attr.namespace; - - let GroupedDomAttrValues { - listeners, - plain_values, - styles, - } = attr.group_values(); - - DomAttr::set_element_style(element, attr_name, styles); - DomAttr::set_element_simple_values(element, attr_name, attr_namespace, plain_values); - self.add_event_listeners(element, attr_name, &listeners) - .unwrap(); - if !listeners.is_empty() { - let event_closures = - IndexMap::from_iter(listeners.into_iter().map(|cb| (attr_name, cb))); - Some(event_closures) - } else { - None - } - } - - pub(crate) fn add_event_listeners( - &self, - event_target: &web_sys::EventTarget, - event_name: &str, - listeners: &[EventClosure], - ) -> Result<(), JsValue> { - for listener in listeners.iter() { - self.add_event_listener(event_target, event_name, listener) - .unwrap(); - } - Ok(()) - } - - /// add a event listener to a target element - pub(crate) fn add_event_listener( - &self, - event_target: &web_sys::EventTarget, - event_name: &str, - listener: &Closure, - ) -> Result<(), JsValue> { - event_target.add_event_listener_with_callback( - intern(event_name), - listener.as_ref().unchecked_ref(), - )?; - Ok(()) - } } /// render the underlying real dom node into string diff --git a/crates/core/src/dom/dom_patch.rs b/crates/core/src/dom/dom_patch.rs index 1f9d0de4..223c4337 100644 --- a/crates/core/src/dom/dom_patch.rs +++ b/crates/core/src/dom/dom_patch.rs @@ -80,17 +80,14 @@ pub enum PatchVariant { impl DomNode { pub(crate) fn find_node(&self, path: &mut TreePath) -> Option { match &self.inner { - DomInner::StatefulComponent { comp, .. } => { + DomInner::StatefulComponent {.. } => { log::info!( "This is a stateful component, should return the element inside relative to the child container at this path: {:?}", path ); - let child_container = comp - .borrow() - .child_container() - .expect("stateful component should provide the child container"); - child_container.find_node(path) + // just return self and handle its own patches + Some(self.clone()) } _ => { if path.is_empty() { diff --git a/crates/core/src/vdom/attribute/attribute_value.rs b/crates/core/src/vdom/attribute/attribute_value.rs index c29621a3..c21ca7eb 100644 --- a/crates/core/src/vdom/attribute/attribute_value.rs +++ b/crates/core/src/vdom/attribute/attribute_value.rs @@ -28,6 +28,9 @@ impl PartialEq for AttributeValue { (AttributeValue::EventListener(this), AttributeValue::EventListener(other)) => { this == other } + (AttributeValue::ComponentEventListener(this), AttributeValue::ComponentEventListener(other)) => { + this == other + } (AttributeValue::Empty, AttributeValue::Empty) => true, (_, _) => false, } diff --git a/crates/core/src/vdom/attribute/callback.rs b/crates/core/src/vdom/attribute/callback.rs index b0743331..4c101d7d 100644 --- a/crates/core/src/vdom/attribute/callback.rs +++ b/crates/core/src/vdom/attribute/callback.rs @@ -113,19 +113,10 @@ impl Clone for Callback { } } -/// This is the best approximation of comparison whether 2 callbacks are equal. -/// -/// There is no 100% guarante that this is true since we can not compare 2 closures event if they -/// have the same logic. -/// -/// This is done by comparing the type_id of the input and type_id of the output. -/// +/// Compare if the callbacks are equal +/// Note, we are only comparing the type_id of the function, the input and the output impl PartialEq for Callback { fn eq(&self, other: &Self) -> bool { - // NOTE: We are not comparing the func field since it will always - // be false when closures are generated from the view. - // We want the callback comparison to return true as much as possible - // so as to not keep adding the callback to the event listeners self.event_type_id == other.event_type_id && self.msg_type_id == other.msg_type_id && self.func_type_id == other.func_type_id diff --git a/crates/core/src/vdom/diff.rs b/crates/core/src/vdom/diff.rs index 24608cd4..c703477b 100644 --- a/crates/core/src/vdom/diff.rs +++ b/crates/core/src/vdom/diff.rs @@ -195,7 +195,15 @@ pub fn diff_recursive<'a, MSG>( patches.extend(patch); } (Leaf::StatefulComponent(old_comp), Leaf::StatefulComponent(new_comp)) => { + let attr_patches = create_attribute_patches(&"component", &old_comp.attrs, &new_comp.attrs, path); + if !attr_patches.is_empty(){ + log::info!("stateful component attr_patches: {attr_patches:#?}"); + } + patches.extend(attr_patches); let patch = diff_nodes(None, &old_comp.children, &new_comp.children, path); + if !patch.is_empty(){ + log::info!("stateful component patch: {patch:#?}"); + } patches.extend(patch); } (Leaf::TemplatedView(_old_view), _) => { @@ -219,7 +227,7 @@ pub fn diff_recursive<'a, MSG>( }; if !skip_attributes { - let attr_patches = create_attribute_patches(old_element, new_element, path); + let attr_patches = create_attribute_patches(old_element.tag(), old_element.attributes(), new_element.attributes(), path); patches.extend(attr_patches); } @@ -326,8 +334,9 @@ fn diff_non_keyed_nodes<'a, MSG>( /// - merging attributes of the same name #[allow(clippy::type_complexity)] fn create_attribute_patches<'a, MSG>( - old_element: &'a Element, - new_element: &'a Element, + old_tag: &'a Tag, + old_attributes: &'a [Attribute], + new_attributes: &'a [Attribute], path: &SkipPath, ) -> Vec> { let skip_indices = if let Some(skip_diff) = &path.skip_diff { @@ -342,8 +351,6 @@ fn create_attribute_patches<'a, MSG>( let has_skip_indices = !skip_indices.is_empty(); - let new_attributes = new_element.attributes(); - let old_attributes = old_element.attributes(); let mut patches = vec![]; @@ -355,8 +362,8 @@ fn create_attribute_patches<'a, MSG>( let mut add_attributes: Vec<&Attribute> = vec![]; let mut remove_attributes: Vec<&Attribute> = vec![]; - let new_attributes_grouped = new_element.group_indexed_attributes_per_name(); - let old_attributes_grouped = old_element.group_indexed_attributes_per_name(); + let new_attributes_grouped = Element::group_indexed_attributes_per_name(new_attributes); + let old_attributes_grouped = Element::group_indexed_attributes_per_name(old_attributes); // for all new elements that doesn't exist in the old elements // or the values differ @@ -412,14 +419,14 @@ fn create_attribute_patches<'a, MSG>( if !add_attributes.is_empty() { patches.push(Patch::add_attributes( - &old_element.tag, + old_tag, path.path.clone(), add_attributes, )); } if !remove_attributes.is_empty() { patches.push(Patch::remove_attributes( - &old_element.tag, + old_tag, path.path.clone(), remove_attributes, )); diff --git a/crates/core/src/vdom/element.rs b/crates/core/src/vdom/element.rs index 3b745573..acb7ffd9 100644 --- a/crates/core/src/vdom/element.rs +++ b/crates/core/src/vdom/element.rs @@ -191,11 +191,11 @@ impl Element { /// grouped the attributes, but retain the index of the attribute /// relative to its location in the element pub fn group_indexed_attributes_per_name<'a>( - &'a self, + attrs: &'a [Attribute], ) -> IndexMap<&'a AttributeName, Vec<(usize, &'a Attribute)>> { let mut grouped: IndexMap<&'a AttributeName, Vec<(usize, &'a Attribute)>> = IndexMap::new(); - for (i, attr) in self.attributes().iter().enumerate() { + for (i, attr) in attrs.iter().enumerate() { if let Some(existing) = grouped.get_mut(&attr.name) { existing.push((i, attr)); } else {