dominator2/
operations.rs

1use std::cell::RefCell;
2use std::future::Future;
3use std::iter::IntoIterator;
4use std::rc::Rc;
5
6use discard::{Discard, DiscardOnDrop};
7use futures_signals::signal::{Signal, SignalExt};
8use futures_signals::signal_vec::{SignalVec, SignalVecExt, VecDiff};
9use futures_signals::{cancelable_future, CancelableFutureHandle};
10use futures_util::future::ready;
11use wasm_bindgen::UnwrapThrowExt;
12use wasm_bindgen_futures::spawn_local;
13use web_sys::Node;
14
15use crate::bindings;
16use crate::callbacks::Callbacks;
17use crate::dom::Dom;
18
19#[inline]
20pub(crate) fn spawn_future<F>(future: F) -> DiscardOnDrop<CancelableFutureHandle>
21where
22    F: Future<Output = ()> + 'static,
23{
24    // TODO make this more efficient ?
25    let (handle, future) = cancelable_future(future, || ());
26
27    spawn_local(future);
28
29    handle
30}
31
32#[inline]
33pub(crate) fn for_each<A, B>(signal: A, mut callback: B) -> CancelableFutureHandle
34where
35    A: Signal + 'static,
36    B: FnMut(A::Item) + 'static,
37{
38    DiscardOnDrop::leak(spawn_future(signal.for_each(move |value| {
39        callback(value);
40        ready(())
41    })))
42}
43
44#[inline]
45fn for_each_vec<A, B>(signal: A, mut callback: B) -> CancelableFutureHandle
46where
47    A: SignalVec + 'static,
48    B: FnMut(VecDiff<A::Item>) + 'static,
49{
50    DiscardOnDrop::leak(spawn_future(signal.for_each(move |value| {
51        callback(value);
52        ready(())
53    })))
54}
55
56pub(crate) fn insert_children_one(element: &Node, callbacks: &mut Callbacks, dom: &mut Dom) {
57    // TODO can this be made more efficient ?
58    callbacks
59        .after_insert
60        .append(&mut dom.callbacks.after_insert);
61    callbacks
62        .after_remove
63        .append(&mut dom.callbacks.after_remove);
64
65    bindings::append_child(element, &dom.element);
66}
67
68#[inline]
69pub(crate) fn insert_children_iter<A: std::borrow::BorrowMut<Dom>, B: IntoIterator<Item = A>>(
70    element: &Node,
71    callbacks: &mut Callbacks,
72    value: B,
73) {
74    for mut dom in value {
75        let dom = std::borrow::BorrowMut::borrow_mut(&mut dom);
76        insert_children_one(element, callbacks, dom);
77    }
78}
79
80fn after_insert(is_inserted: bool, callbacks: &mut Callbacks) {
81    callbacks.leak();
82
83    if is_inserted {
84        callbacks.trigger_after_insert();
85    }
86}
87
88#[inline]
89pub(crate) fn insert_child_signal<A>(element: Node, callbacks: &mut Callbacks, signal: A)
90where
91    A: Signal<Item = Option<Dom>> + 'static,
92{
93    struct State {
94        is_inserted: bool,
95        child: Option<Dom>,
96    }
97
98    impl State {
99        fn new() -> Rc<RefCell<Self>> {
100            Rc::new(RefCell::new(State {
101                is_inserted: false,
102                child: None,
103            }))
104        }
105
106        fn after_insert(state: Rc<RefCell<Self>>, callbacks: &mut Callbacks) {
107            callbacks.after_insert(move |_| {
108                let mut state = state.borrow_mut();
109
110                if !state.is_inserted {
111                    state.is_inserted = true;
112
113                    if let Some(ref mut child) = state.child {
114                        child.callbacks.trigger_after_insert();
115                    }
116                }
117            });
118        }
119
120        // TODO verify that this will drop `child`
121        fn after_remove(&mut self, element: &Node, marker: &Node, child: Option<Dom>) {
122            if let Some(old_child) = self.child.take() {
123                bindings::remove_child(&element, &old_child.element);
124
125                old_child.callbacks.discard();
126            }
127
128            self.child = child;
129
130            if let Some(new_child) = &mut self.child {
131                bindings::insert_child_before(element, &new_child.element, marker);
132
133                after_insert(self.is_inserted, &mut new_child.callbacks);
134            }
135        }
136
137        fn on_remove(&mut self) {
138            if let Some(old_child) = self.child.take() {
139                old_child.callbacks.discard();
140            }
141        }
142    }
143
144    struct OnRemove {
145        state: Rc<RefCell<State>>,
146        signal: CancelableFutureHandle,
147    }
148
149    impl Discard for OnRemove {
150        #[inline]
151        fn discard(self) {
152            self.signal.discard();
153            self.state.borrow_mut().on_remove();
154        }
155    }
156
157    // TODO replace with https://github.com/whatwg/dom/issues/736
158    let marker = bindings::create_empty_node();
159
160    bindings::append_child(&element, &marker);
161
162    let state = State::new();
163
164    State::after_insert(state.clone(), callbacks);
165
166    callbacks.after_remove(OnRemove {
167        state: state.clone(),
168        signal: for_each(signal, move |child| {
169            let mut state = state.borrow_mut();
170            state.after_remove(&element, &marker, child);
171        }),
172    });
173}
174
175#[inline]
176pub(crate) fn insert_children_signal_vec<A>(element: Node, callbacks: &mut Callbacks, signal: A)
177where
178    A: SignalVec<Item = Dom> + 'static,
179{
180    struct State {
181        element: Node,
182        marker: Node,
183        is_inserted: bool,
184        children: Vec<Dom>,
185    }
186
187    impl State {
188        fn new(element: Node, marker: Node) -> Rc<RefCell<Self>> {
189            Rc::new(RefCell::new(State {
190                element,
191                marker,
192                is_inserted: false,
193                children: vec![],
194            }))
195        }
196
197        fn after_insert(state: Rc<RefCell<Self>>, callbacks: &mut Callbacks) {
198            callbacks.after_insert(move |_| {
199                let mut state = state.borrow_mut();
200
201                if !state.is_inserted {
202                    state.is_inserted = true;
203
204                    for dom in state.children.iter_mut() {
205                        dom.callbacks.trigger_after_insert();
206                    }
207                }
208            });
209        }
210
211        fn clear(&mut self) {
212            for dom in self.children.drain(..) {
213                bindings::remove_child(&self.element, &dom.element);
214                dom.callbacks.discard();
215            }
216        }
217
218        fn on_remove(&mut self) {
219            for dom in self.children.drain(..) {
220                dom.callbacks.discard();
221            }
222        }
223
224        fn insert_at(&self, new_index: usize, child: &Node) {
225            if let Some(dom) = self.children.get(new_index) {
226                bindings::insert_child_before(&self.element, child, &dom.element);
227            } else {
228                bindings::insert_child_before(&self.element, child, &self.marker);
229            }
230        }
231
232        // TODO verify that this will drop `children`
233        fn process_change(&mut self, change: VecDiff<Dom>) {
234            match change {
235                VecDiff::Replace { values } => {
236                    self.clear();
237
238                    self.children = values;
239
240                    let is_inserted = self.is_inserted;
241
242                    // TODO use createDocumentFragment ?
243                    for dom in self.children.iter_mut() {
244                        bindings::insert_child_before(&self.element, &dom.element, &self.marker);
245
246                        after_insert(is_inserted, &mut dom.callbacks);
247                    }
248                }
249
250                VecDiff::InsertAt { index, mut value } => {
251                    self.insert_at(index, &value.element);
252
253                    after_insert(self.is_inserted, &mut value.callbacks);
254
255                    // TODO figure out a way to move this to the top
256                    self.children.insert(index, value);
257                }
258
259                VecDiff::Push { mut value } => {
260                    bindings::insert_child_before(&self.element, &value.element, &self.marker);
261
262                    after_insert(self.is_inserted, &mut value.callbacks);
263
264                    // TODO figure out a way to move this to the top
265                    self.children.push(value);
266                }
267
268                VecDiff::UpdateAt { index, mut value } => {
269                    let dom = &mut self.children[index];
270
271                    bindings::replace_child(&self.element, &value.element, &dom.element);
272
273                    after_insert(self.is_inserted, &mut value.callbacks);
274
275                    // TODO figure out a way to move this to the top
276                    // TODO test this
277                    ::std::mem::swap(dom, &mut value);
278
279                    value.callbacks.discard();
280                }
281
282                VecDiff::Move {
283                    old_index,
284                    new_index,
285                } => {
286                    let value = self.children.remove(old_index);
287
288                    self.insert_at(new_index, &value.element);
289
290                    self.children.insert(new_index, value);
291                }
292
293                VecDiff::RemoveAt { index } => {
294                    let dom = self.children.remove(index);
295
296                    bindings::remove_child(&self.element, &dom.element);
297
298                    dom.callbacks.discard();
299                }
300
301                VecDiff::Pop {} => {
302                    let dom = self.children.pop().unwrap_throw();
303
304                    bindings::remove_child(&self.element, &dom.element);
305
306                    dom.callbacks.discard();
307                }
308
309                VecDiff::Clear {} => {
310                    self.clear();
311                }
312            }
313        }
314    }
315
316    struct OnRemove {
317        state: Rc<RefCell<State>>,
318        signal: CancelableFutureHandle,
319    }
320
321    impl Discard for OnRemove {
322        #[inline]
323        fn discard(self) {
324            self.signal.discard();
325            self.state.borrow_mut().on_remove();
326        }
327    }
328
329    // TODO replace with https://github.com/whatwg/dom/issues/736
330    let marker = bindings::create_empty_node();
331
332    bindings::append_child(&element, &marker);
333
334    let state = State::new(element, marker);
335
336    State::after_insert(state.clone(), callbacks);
337
338    callbacks.after_remove(OnRemove {
339        state: state.clone(),
340        signal: for_each_vec(signal, move |change| {
341            let mut state = state.borrow_mut();
342            state.process_change(change);
343        }),
344    });
345}