1use std::cell::RefCell;
7use std::collections::{HashMap, HashSet};
8use std::hash::Hash;
9use std::mem;
10use std::rc::Rc;
11
12use wasm_bindgen::*;
13use web_sys::{Element, HtmlElement};
14
15use crate::internal::append;
16use crate::prelude::*;
17use crate::reactive::Owner;
18
19pub struct KeyedProps<T: 'static, F, K, Key>
21where
22 F: Fn(T) -> TemplateResult,
23 K: Fn(&T) -> Key,
24 Key: Hash + Eq,
25{
26 pub iterable: StateHandle<Vec<T>>,
27 pub template: F,
28 pub key: K,
29}
30
31pub fn Keyed<T, F: 'static, K: 'static, Key: 'static>(
53 props: KeyedProps<T, F, K, Key>,
54) -> TemplateResult
55where
56 F: Fn(T) -> TemplateResult,
57 K: Fn(&T) -> Key,
58 Key: Clone + Hash + Eq,
59 T: Clone + PartialEq,
60{
61 let KeyedProps {
62 iterable,
63 template,
64 key: key_fn,
65 } = props;
66 let iterable = Rc::new(iterable);
67 let key_fn = Rc::new(key_fn);
68
69 type TemplateValue<T> = (Owner, T, TemplateResult, usize );
70
71 let templates: Rc<RefCell<HashMap<Key, TemplateValue<T>>>> =
73 Rc::new(RefCell::new(HashMap::new()));
74
75 let fragment = web_sys::window()
76 .unwrap()
77 .document()
78 .unwrap()
79 .create_document_fragment();
80
81 let marker = web_sys::window()
82 .unwrap()
83 .document()
84 .unwrap()
85 .create_comment("");
86
87 append(&fragment, &marker);
88
89 create_effect({
90 let iterable = Rc::clone(&iterable);
91 let key_fn = Rc::clone(&key_fn);
92 let templates = Rc::clone(&templates);
93 let marker = marker.clone();
94 move || {
95 if iterable.get().is_empty() {
97 for (_, (owner, _value, template, _i)) in templates.borrow_mut().drain() {
98 drop(owner); template.node.unchecked_into::<HtmlElement>().remove();
100 }
101 return;
102 }
103
104 {
106 let mut templates = templates.borrow_mut();
107 let new_keys: HashSet<Key> =
108 iterable.get().iter().map(|item| key_fn(item)).collect();
109
110 let excess_nodes = templates
111 .iter()
112 .filter(|item| new_keys.get(item.0).is_none())
113 .map(|x| (x.0.clone(), (x.1 .2.clone(), x.1 .3)))
114 .collect::<Vec<_>>();
115
116 for node in &excess_nodes {
117 let removed_index = node.1 .1;
118 templates.remove(&node.0);
119
120 for (_, _, _, i) in templates.values_mut() {
122 if *i > removed_index {
123 *i -= 1;
124 }
125 }
126 }
127
128 for node in excess_nodes {
129 node.1 .0.node.unchecked_into::<Element>().remove();
130 }
131 }
132
133 struct PreviousData<T> {
134 value: T,
135 index: usize,
136 }
137
138 let previous_values: HashMap<_, PreviousData<T>> = {
139 let templates = templates.borrow();
140 templates
141 .iter()
142 .map(|x| {
143 (
144 (*x.0).clone(),
145 PreviousData {
146 value: x.1 .1.clone(),
147 index: x.1 .3,
148 },
149 )
150 })
151 .collect()
152 };
153
154 for (i, item) in iterable.get().iter().enumerate() {
156 let key = key_fn(item);
157
158 let previous_value = previous_values.get(&key);
159
160 if previous_value.is_none() {
161 let mut new_template = None;
164 let owner = create_root(|| new_template = Some(template(item.clone())));
165
166 templates.borrow_mut().insert(
167 key.clone(),
168 (owner, item.clone(), new_template.clone().unwrap(), i),
169 );
170
171 if let Some(next_item) = iterable.get().get(i + 1) {
172 let templates = templates.borrow();
173 if let Some(next_node) = templates.get(&key_fn(next_item)) {
174 next_node
175 .2
176 .node
177 .unchecked_ref::<HtmlElement>()
178 .before_with_node_1(&new_template.unwrap().node)
179 .unwrap();
180 } else {
181 marker
182 .before_with_node_1(&new_template.unwrap().node)
183 .unwrap();
184 }
185 } else {
186 marker
187 .before_with_node_1(&new_template.unwrap().node)
188 .unwrap();
189 }
190 } else if match previous_value {
191 Some(prev) => prev.index,
192 _ => unreachable!(),
193 } != i
194 {
195 let node = templates.borrow().get(&key).unwrap().2.node.clone();
199
200 if let Some(next_item) = iterable.get().get(i + 1) {
201 let templates = templates.borrow();
202 let next_node = templates.get(&key_fn(next_item)).unwrap();
203 next_node
204 .2
205 .node
206 .unchecked_ref::<HtmlElement>()
207 .before_with_node_1(&node)
208 .unwrap(); } else {
210 marker.before_with_node_1(&node).unwrap(); }
212
213 templates.borrow_mut().get_mut(&key).unwrap().3 = i;
214 } else if match previous_value {
215 Some(prev) => &prev.value,
216 _ => unreachable!(),
217 } != item
218 {
219 let mut templates = templates.borrow_mut();
223 let (old_owner, _, _, _) = templates
224 .get_mut(&key)
225 .expect("previous value is different but must be valid");
226 let old_owner = mem::replace(old_owner, Owner::new() );
227 drop(old_owner);
228
229 let mut new_template = None;
230 let owner = create_root(|| new_template = Some(template(item.clone())));
231
232 let (_, _, old_node, _) = mem::replace(
233 templates.get_mut(&key).unwrap(),
234 (owner, item.clone(), new_template.clone().unwrap(), i),
235 );
236
237 let parent = old_node.node.parent_node().unwrap();
238 parent
239 .replace_child(&new_template.unwrap().node, &old_node.node)
240 .unwrap();
241 }
242 }
243 }
244 });
245
246 for item in iterable.get().iter() {
247 let key = key_fn(item);
248 let template = templates.borrow().get(&key).unwrap().2.clone();
249
250 marker.before_with_node_1(&template.node).unwrap();
251 }
252
253 TemplateResult::new(fragment.into())
254}
255
256pub struct IndexedProps<T: 'static, F>
258where
259 F: Fn(T) -> TemplateResult,
260{
261 pub iterable: StateHandle<Vec<T>>,
262 pub template: F,
263}
264
265pub fn Indexed<T, F: 'static>(props: IndexedProps<T, F>) -> TemplateResult
286where
287 T: Clone + PartialEq,
288 F: Fn(T) -> TemplateResult,
289{
290 let templates: Rc<RefCell<Vec<(Owner, TemplateResult)>>> = Rc::new(RefCell::new(Vec::new()));
291
292 let previous_values = RefCell::new(Vec::new());
294
295 let fragment = web_sys::window()
296 .unwrap()
297 .document()
298 .unwrap()
299 .create_document_fragment();
300
301 let marker = web_sys::window()
302 .unwrap()
303 .document()
304 .unwrap()
305 .create_comment("");
306
307 append(&fragment, &marker);
308
309 create_effect({
310 let templates = Rc::clone(&templates);
311 let marker = marker.clone();
312 move || {
313 if props.iterable.get().is_empty() {
315 for (owner, template) in templates.borrow_mut().drain(..) {
316 drop(owner); template.node.unchecked_into::<HtmlElement>().remove();
318 }
319 return;
320 }
321
322 for (i, item) in props.iterable.get().iter().enumerate() {
324 let previous_values = previous_values.borrow();
325 let previous_value = previous_values.get(i);
326
327 if previous_value.is_none() || previous_value.unwrap() != item {
328 templates.borrow_mut().get_mut(i).and_then(|(owner, _)| {
331 let old_owner = mem::replace(owner, Owner::new() );
333 drop(old_owner);
334 None::<()>
335 });
336
337 let mut new_template = None;
338 let owner = create_root(|| new_template = Some((props.template)(item.clone())));
339
340 if templates.borrow().get(i).is_some() {
341 let old_node = mem::replace(
342 &mut templates.borrow_mut()[i],
343 (owner, new_template.as_ref().unwrap().clone()),
344 );
345
346 let parent = old_node.1.node.parent_node().unwrap();
347 parent
348 .replace_child(&new_template.unwrap().node, &old_node.1.node)
349 .unwrap();
350 } else {
351 debug_assert!(templates.borrow().len() == i, "pushing new value scenario");
352
353 templates
354 .borrow_mut()
355 .push((owner, new_template.as_ref().unwrap().clone()));
356
357 marker
358 .before_with_node_1(&new_template.unwrap().node)
359 .unwrap();
360 }
361 }
362 }
363
364 if templates.borrow().len() > props.iterable.get().len() {
365 let mut templates = templates.borrow_mut();
366 let excess_nodes = templates.drain(props.iterable.get().len()..);
367
368 for node in excess_nodes {
369 node.1.node.unchecked_into::<Element>().remove();
370 }
371 }
372
373 *previous_values.borrow_mut() = (*props.iterable.get()).clone();
374 }
375 });
376
377 for template in templates.borrow().iter() {
378 marker.before_with_node_1(&template.1.node).unwrap();
379 }
380
381 TemplateResult::new(fragment.into())
382}