diff --git a/maple-core/src/flow.rs b/maple-core/src/flow.rs index eae64b5b4..993f55bf0 100644 --- a/maple-core/src/flow.rs +++ b/maple-core/src/flow.rs @@ -92,6 +92,44 @@ where let templates = Rc::clone(&templates); let marker = marker.clone(); move || { + // Fast path for empty array. Remove all nodes from DOM in templates. + if iterable.get().is_empty() { + for (_, (owner, _value, template, _i)) in templates.borrow_mut().drain() { + drop(owner); // destroy owner + template.node.unchecked_into::().remove(); + } + return; + } + + // Remove old nodes not in iterable. + { + let mut templates = templates.borrow_mut(); + let new_keys: HashSet = + iterable.get().iter().map(|item| key_fn(item)).collect(); + + let excess_nodes = templates + .iter() + .filter(|item| new_keys.get(item.0).is_none()) + .map(|x| (x.0.clone(), (x.1 .2.clone(), x.1 .3))) + .collect::>(); + + for node in &excess_nodes { + let removed_index = node.1 .1; + templates.remove(&node.0); + + // Offset indexes of other templates by 1. + for (_, _, _, i) in templates.values_mut() { + if *i > removed_index { + *i -= 1; + } + } + } + + for node in excess_nodes { + node.1 .0.node.unchecked_into::().remove(); + } + } + struct PreviousData { value: T, index: usize, @@ -202,28 +240,6 @@ where .unwrap(); } } - - if templates.borrow().len() > iterable.get().len() { - // remove extra templates - - let mut templates = templates.borrow_mut(); - let new_keys: HashSet = - iterable.get().iter().map(|item| key_fn(item)).collect(); - - let excess_nodes = templates - .iter() - .filter(|item| new_keys.get(item.0).is_none()) - .map(|x| (x.0.clone(), x.1 .2.clone())) - .collect::>(); - - for node in &excess_nodes { - templates.remove(&node.0); - } - - for node in excess_nodes { - node.1.node.unchecked_into::().remove(); - } - } } }); @@ -294,13 +310,22 @@ where let templates = Rc::clone(&templates); let marker = marker.clone(); move || { + // Fast path for empty array. Remove all nodes from DOM in templates. + if props.iterable.get().is_empty() { + for (owner, template) in templates.borrow_mut().drain(..) { + drop(owner); // destroy owner + template.node.unchecked_into::().remove(); + } + return; + } + // Find values that changed by comparing to previous_values. for (i, item) in props.iterable.get().iter().enumerate() { let previous_values = previous_values.borrow(); let previous_value = previous_values.get(i); if previous_value.is_none() || previous_value.unwrap() != item { - // value changed, re-render item + // Value changed, re-render item. templates.borrow_mut().get_mut(i).and_then(|(owner, _)| { // destroy old owner diff --git a/maple-core/tests/integration/keyed.rs b/maple-core/tests/integration/keyed.rs index f45fbc6db..d8329ce7e 100644 --- a/maple-core/tests/integration/keyed.rs +++ b/maple-core/tests/integration/keyed.rs @@ -69,6 +69,60 @@ fn swap_rows() { assert_eq!(p.text_content().unwrap(), "123"); } +#[wasm_bindgen_test] +fn delete_row() { + let count = Signal::new(vec![1, 2, 3]); + + let node = cloned!((count) => template! { + ul { + Keyed(KeyedProps { + iterable: count.handle(), + template: |item| template! { + li { (item) } + }, + key: |item| *item, + }) + } + }); + + render_to(|| node, &test_div()); + + let p = document().query_selector("ul").unwrap().unwrap(); + assert_eq!(p.text_content().unwrap(), "123"); + + count.set({ + let mut tmp = (*count.get()).clone(); + tmp.remove(1); + tmp + }); + assert_eq!(p.text_content().unwrap(), "13"); +} + +#[wasm_bindgen_test] +fn clear() { + let count = Signal::new(vec![1, 2, 3]); + + let node = cloned!((count) => template! { + ul { + Keyed(KeyedProps { + iterable: count.handle(), + template: |item| template! { + li { (item) } + }, + key: |item| *item, + }) + } + }); + + render_to(|| node, &test_div()); + + let p = document().query_selector("ul").unwrap().unwrap(); + assert_eq!(p.text_content().unwrap(), "123"); + + count.set(Vec::new()); + assert_eq!(p.text_content().unwrap(), ""); +} + #[wasm_bindgen_test] fn insert_front() { let count = Signal::new(vec![1, 2, 3]); diff --git a/maple-core/tests/integration/non_keyed.rs b/maple-core/tests/integration/non_keyed.rs index 7d7ab8263..d96481bdc 100644 --- a/maple-core/tests/integration/non_keyed.rs +++ b/maple-core/tests/integration/non_keyed.rs @@ -67,6 +67,58 @@ fn swap_rows() { assert_eq!(p.text_content().unwrap(), "123"); } +#[wasm_bindgen_test] +fn delete_row() { + let count = Signal::new(vec![1, 2, 3]); + + let node = cloned!((count) => template! { + ul { + Indexed(IndexedProps { + iterable: count.handle(), + template: |item| template! { + li { (item) } + }, + }) + } + }); + + render_to(|| node, &test_div()); + + let p = document().query_selector("ul").unwrap().unwrap(); + assert_eq!(p.text_content().unwrap(), "123"); + + count.set({ + let mut tmp = (*count.get()).clone(); + tmp.remove(1); + tmp + }); + assert_eq!(p.text_content().unwrap(), "13"); +} + +#[wasm_bindgen_test] +fn clear() { + let count = Signal::new(vec![1, 2, 3]); + + let node = cloned!((count) => template! { + ul { + Indexed(IndexedProps { + iterable: count.handle(), + template: |item| template! { + li { (item) } + }, + }) + } + }); + + render_to(|| node, &test_div()); + + let p = document().query_selector("ul").unwrap().unwrap(); + assert_eq!(p.text_content().unwrap(), "123"); + + count.set(Vec::new()); + assert_eq!(p.text_content().unwrap(), ""); +} + #[wasm_bindgen_test] fn insert_front() { let count = Signal::new(vec![1, 2, 3]);