diff --git a/maple-core/Cargo.toml b/maple-core/Cargo.toml index 3f3621548..d8cff1ab9 100644 --- a/maple-core/Cargo.toml +++ b/maple-core/Cargo.toml @@ -32,6 +32,7 @@ features = [ "Event", "HtmlElement", "HtmlInputElement", + "HtmlCollection", "Node", "Text", "Window", diff --git a/maple-core/src/generic_node/dom_node.rs b/maple-core/src/generic_node/dom_node.rs index d848e15f0..aa82f69a7 100644 --- a/maple-core/src/generic_node/dom_node.rs +++ b/maple-core/src/generic_node/dom_node.rs @@ -3,7 +3,8 @@ use std::cell::RefCell; use ref_cast::RefCast; -use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; use web_sys::{Element, Event, Node, Text}; use crate::generic_node::{EventListener, GenericNode}; @@ -174,10 +175,7 @@ pub fn render(template_result: impl FnOnce() -> TemplateResult) { /// For rendering under the `` tag, use [`render`] instead. /// /// _This API requires the following crate features to be activated: `dom`_ -pub fn render_to( - template_result: impl FnOnce() -> TemplateResult, - parent: &web_sys::Node, -) { +pub fn render_to(template_result: impl FnOnce() -> TemplateResult, parent: &Node) { let scope = create_root(|| { for node in template_result() { parent.append_child(&node.inner_element()).unwrap(); @@ -191,8 +189,8 @@ pub fn render_to( GLOBAL_SCOPES.with(|global_scopes| global_scopes.borrow_mut().push(scope)); } -/// Render a [`TemplateResult`] under a `parent` node by reusing existing nodes (client side hydration). -/// Alias for [`hydrate_to`] with `parent` being the `` tag. +/// Render a [`TemplateResult`] under a `parent` node by reusing existing nodes (client side +/// hydration). Alias for [`hydrate_to`] with `parent` being the `` tag. /// /// For rendering without hydration, use [`render`] instead. /// @@ -204,18 +202,31 @@ pub fn hydrate(template_result: impl FnOnce() -> TemplateResult) { hydrate_to(template_result, &document.body().unwrap()); } -/// Render a [`TemplateResult`] under a `parent` node by reusing existing nodes (client side hydration). -/// For rendering under the `` tag, use [`hydrate_to`] instead. +/// Gets the children of an [`Element`] by collecting them into a [`Vec`]. Note that the returned +/// value is **NOT** live. +fn get_children(parent: &Element) -> Vec { + let children = parent.children(); + let children_count = children.length(); + + let mut vec = Vec::new(); + vec.reserve(children_count as usize); + + for i in 0..children.length() { + vec.push(children.get_with_index(i).unwrap()) + } + + vec +} + +/// Render a [`TemplateResult`] under a `parent` node by reusing existing nodes (client side +/// hydration). For rendering under the `` tag, use [`hydrate_to`] instead. /// /// For rendering without hydration, use [`render`] instead. /// /// _This API requires the following crate features to be activated: `dom`_ -pub fn hydrate_to( - template_result: impl FnOnce() -> TemplateResult, - parent: &web_sys::Node, -) { - while let Some(child) = parent.first_child() { - child.unchecked_into::().remove(); +pub fn hydrate_to(template_result: impl FnOnce() -> TemplateResult, parent: &Node) { + for child in get_children(parent.unchecked_ref()) { + child.remove(); } let scope = create_root(|| { diff --git a/maple-core/src/reactive.rs b/maple-core/src/reactive.rs index 21a0e59d8..e184520a8 100644 --- a/maple-core/src/reactive.rs +++ b/maple-core/src/reactive.rs @@ -8,8 +8,8 @@ pub use effect::*; pub use motion::*; pub use signal::*; -/// Creates a new reactive root / scope. Generally, you won't need this method as it is called automatically -/// in [`render`](crate::generic_node::render). +/// Creates a new reactive root / scope. Generally, you won't need this method as it is called +/// automatically in [`render`](crate::generic_node::render). /// /// # Example /// ``` diff --git a/maple-core/src/reactive/effect.rs b/maple-core/src/reactive/effect.rs index 5bde2c538..9ecac6256 100644 --- a/maple-core/src/reactive/effect.rs +++ b/maple-core/src/reactive/effect.rs @@ -47,7 +47,8 @@ impl Drop for Running { } /// Owns the effects created in the current reactive scope. -/// The effects are dropped and the cleanup callbacks are called when the [`ReactiveScope`] is dropped. +/// The effects are dropped and the cleanup callbacks are called when the [`ReactiveScope`] is +/// dropped. #[derive(Default)] pub struct ReactiveScope { effects: Vec>>>, @@ -191,7 +192,8 @@ pub fn create_effect_initial( // Destroy old effects before new ones run. let old_scope = mem::replace( &mut running.borrow_mut().as_mut().unwrap().scope, - ReactiveScope::new(), /* placeholder until an actual ReactiveScope is created */ + ReactiveScope::new(), /* placeholder until an actual ReactiveScope + * is created */ ); drop(old_scope);