use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::mem;
use std::rc::Rc;
use wasm_bindgen::*;
use web_sys::{Element, HtmlElement};
use crate::internal::append;
use crate::prelude::*;
use crate::reactive::Owner;
pub struct KeyedProps<T: 'static, F, K, Key>
where
F: Fn(T) -> TemplateResult,
K: Fn(&T) -> Key,
Key: Hash + Eq,
{
pub iterable: StateHandle<Vec<T>>,
pub template: F,
pub key: K,
}
pub fn Keyed<T, F: 'static, K: 'static, Key: 'static>(
props: KeyedProps<T, F, K, Key>,
) -> TemplateResult
where
F: Fn(T) -> TemplateResult,
K: Fn(&T) -> Key,
Key: Clone + Hash + Eq,
T: Clone + PartialEq,
{
let KeyedProps {
iterable,
template,
key: key_fn,
} = props;
let iterable = Rc::new(iterable);
let key_fn = Rc::new(key_fn);
type TemplateValue<T> = (Owner, T, TemplateResult, usize );
let templates: Rc<RefCell<HashMap<Key, TemplateValue<T>>>> =
Rc::new(RefCell::new(HashMap::new()));
let fragment = web_sys::window()
.unwrap()
.document()
.unwrap()
.create_document_fragment();
let marker = web_sys::window()
.unwrap()
.document()
.unwrap()
.create_comment("");
append(&fragment, &marker);
create_effect({
let iterable = Rc::clone(&iterable);
let key_fn = Rc::clone(&key_fn);
let templates = Rc::clone(&templates);
let marker = marker.clone();
move || {
if iterable.get().is_empty() {
for (_, (owner, _value, template, _i)) in templates.borrow_mut().drain() {
drop(owner); template.node.unchecked_into::<HtmlElement>().remove();
}
return;
}
{
let mut templates = templates.borrow_mut();
let new_keys: HashSet<Key> =
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::<Vec<_>>();
for node in &excess_nodes {
let removed_index = node.1 .1;
templates.remove(&node.0);
for (_, _, _, i) in templates.values_mut() {
if *i > removed_index {
*i -= 1;
}
}
}
for node in excess_nodes {
node.1 .0.node.unchecked_into::<Element>().remove();
}
}
struct PreviousData<T> {
value: T,
index: usize,
}
let previous_values: HashMap<_, PreviousData<T>> = {
let templates = templates.borrow();
templates
.iter()
.map(|x| {
(
(*x.0).clone(),
PreviousData {
value: x.1 .1.clone(),
index: x.1 .3,
},
)
})
.collect()
};
for (i, item) in iterable.get().iter().enumerate() {
let key = key_fn(item);
let previous_value = previous_values.get(&key);
if previous_value.is_none() {
let mut new_template = None;
let owner = create_root(|| new_template = Some(template(item.clone())));
templates.borrow_mut().insert(
key.clone(),
(owner, item.clone(), new_template.clone().unwrap(), i),
);
if let Some(next_item) = iterable.get().get(i + 1) {
let templates = templates.borrow();
if let Some(next_node) = templates.get(&key_fn(next_item)) {
next_node
.2
.node
.unchecked_ref::<HtmlElement>()
.before_with_node_1(&new_template.unwrap().node)
.unwrap();
} else {
marker
.before_with_node_1(&new_template.unwrap().node)
.unwrap();
}
} else {
marker
.before_with_node_1(&new_template.unwrap().node)
.unwrap();
}
} else if match previous_value {
Some(prev) => prev.index,
_ => unreachable!(),
} != i
{
let node = templates.borrow().get(&key).unwrap().2.node.clone();
if let Some(next_item) = iterable.get().get(i + 1) {
let templates = templates.borrow();
let next_node = templates.get(&key_fn(next_item)).unwrap();
next_node
.2
.node
.unchecked_ref::<HtmlElement>()
.before_with_node_1(&node)
.unwrap(); } else {
marker.before_with_node_1(&node).unwrap(); }
templates.borrow_mut().get_mut(&key).unwrap().3 = i;
} else if match previous_value {
Some(prev) => &prev.value,
_ => unreachable!(),
} != item
{
let mut templates = templates.borrow_mut();
let (old_owner, _, _, _) = templates
.get_mut(&key)
.expect("previous value is different but must be valid");
let old_owner = mem::replace(old_owner, Owner::new() );
drop(old_owner);
let mut new_template = None;
let owner = create_root(|| new_template = Some(template(item.clone())));
let (_, _, old_node, _) = mem::replace(
templates.get_mut(&key).unwrap(),
(owner, item.clone(), new_template.clone().unwrap(), i),
);
let parent = old_node.node.parent_node().unwrap();
parent
.replace_child(&new_template.unwrap().node, &old_node.node)
.unwrap();
}
}
}
});
for item in iterable.get().iter() {
let key = key_fn(item);
let template = templates.borrow().get(&key).unwrap().2.clone();
marker.before_with_node_1(&template.node).unwrap();
}
TemplateResult::new(fragment.into())
}
pub struct IndexedProps<T: 'static, F>
where
F: Fn(T) -> TemplateResult,
{
pub iterable: StateHandle<Vec<T>>,
pub template: F,
}
pub fn Indexed<T, F: 'static>(props: IndexedProps<T, F>) -> TemplateResult
where
T: Clone + PartialEq,
F: Fn(T) -> TemplateResult,
{
let templates: Rc<RefCell<Vec<(Owner, TemplateResult)>>> = Rc::new(RefCell::new(Vec::new()));
let previous_values = RefCell::new(Vec::new());
let fragment = web_sys::window()
.unwrap()
.document()
.unwrap()
.create_document_fragment();
let marker = web_sys::window()
.unwrap()
.document()
.unwrap()
.create_comment("");
append(&fragment, &marker);
create_effect({
let templates = Rc::clone(&templates);
let marker = marker.clone();
move || {
if props.iterable.get().is_empty() {
for (owner, template) in templates.borrow_mut().drain(..) {
drop(owner); template.node.unchecked_into::<HtmlElement>().remove();
}
return;
}
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 {
templates.borrow_mut().get_mut(i).and_then(|(owner, _)| {
let old_owner = mem::replace(owner, Owner::new() );
drop(old_owner);
None::<()>
});
let mut new_template = None;
let owner = create_root(|| new_template = Some((props.template)(item.clone())));
if templates.borrow().get(i).is_some() {
let old_node = mem::replace(
&mut templates.borrow_mut()[i],
(owner, new_template.as_ref().unwrap().clone()),
);
let parent = old_node.1.node.parent_node().unwrap();
parent
.replace_child(&new_template.unwrap().node, &old_node.1.node)
.unwrap();
} else {
debug_assert!(templates.borrow().len() == i, "pushing new value scenario");
templates
.borrow_mut()
.push((owner, new_template.as_ref().unwrap().clone()));
marker
.before_with_node_1(&new_template.unwrap().node)
.unwrap();
}
}
}
if templates.borrow().len() > props.iterable.get().len() {
let mut templates = templates.borrow_mut();
let excess_nodes = templates.drain(props.iterable.get().len()..);
for node in excess_nodes {
node.1.node.unchecked_into::<Element>().remove();
}
}
*previous_values.borrow_mut() = (*props.iterable.get()).clone();
}
});
for template in templates.borrow().iter() {
marker.before_with_node_1(&template.1.node).unwrap();
}
TemplateResult::new(fragment.into())
}