hirola_core/templating/
flow.rs1use crate::generic_node::GenericNode;
6use crate::render::{Error, Render};
7use futures_signals::signal_vec::{SignalVec, SignalVecExt, VecDiff};
8use std::cell::RefCell;
9use std::future::ready;
10use std::rc::Rc;
11
12#[derive(Debug)]
15pub struct IndexedProps<T, I: SignalVec<Item = T> + Unpin, F, G: GenericNode>
16where
17    F: Fn(T) -> G,
18{
19    pub iterable: I,
20    pub template: F,
21}
22
23pub struct Indexed<T, I: SignalVec<Item = T> + Unpin, F, G: GenericNode>
46where
47    F: Fn(T) -> G,
48{
49    pub props: IndexedProps<T, I, F, G>,
50}
51
52impl<T, F, I, N: GenericNode> Render<N> for Indexed<T, I, F, N>
53where
54    T: 'static + Clone,
55    I: 'static + SignalVecExt<Item = T> + Unpin,
56    F: Fn(T) -> N + 'static,
57{
58    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
59        let props = self.props;
60        let template = props.template;
61
62        let iterable = SignalVecExt::map(props.iterable, template);
63
64        let marker = N::marker();
65
66        struct State<N: GenericNode> {
67            element: N,
68            marker: N,
69            children: Vec<N>,
70        }
71
72        impl<N: GenericNode> State<N> {
73            fn new(element: N, marker: N) -> Rc<RefCell<Self>> {
74                Rc::new(RefCell::new(State {
75                    element,
76                    marker,
77                    children: vec![],
78                }))
79            }
80
81            fn clear(&mut self) {
82                for dom in self.children.drain(..) {
83                    self.element.remove_child(&dom);
84                    drop(dom)
85                }
86            }
87
88            fn insert_at(&self, new_index: usize, child: &N) {
89                if let Some(dom) = self.children.get(new_index) {
90                    self.element.insert_child_before(child, Some(dom));
91                } else {
92                    self.element.insert_child_before(child, Some(&self.marker));
93                }
94            }
95
96            fn process_change(&mut self, change: VecDiff<N>) {
98                match change {
99                    VecDiff::Replace { values } => {
100                        self.clear();
101                        self.children = values;
102                        for dom in self.children.iter_mut() {
103                            self.element.insert_child_before(dom, Some(&self.marker));
104                        }
105                    }
106
107                    VecDiff::InsertAt { index, value } => {
108                        self.insert_at(index, &value);
109                        self.children.insert(index, value);
110                    }
111
112                    VecDiff::Push { value } => {
113                        let marker = self.marker.clone();
114                        self.element.insert_child_before(&value, Some(&marker));
115                        self.children.push(value);
116                    }
117
118                    VecDiff::UpdateAt { index, mut value } => {
119                        let dom = &mut self.children[index];
120                        self.element.replace_child(&value, &self.marker);
121                        ::std::mem::swap(dom, &mut value);
122                    }
123
124                    VecDiff::Move {
125                        old_index,
126                        new_index,
127                    } => {
128                        let value = self.children.remove(old_index);
129
130                        self.insert_at(new_index, &value);
131
132                        self.children.insert(new_index, value);
133                    }
134
135                    VecDiff::RemoveAt { index } => {
136                        let dom = self.children.remove(index);
137                        let children = dom.children().take();
138                        for child in children {
139                            child.remove_self()
140                        }
141
142                        drop(dom)
143                    }
144
145                    VecDiff::Pop {} => {
146                        let dom = self.children.pop().unwrap();
148                        let children = dom.children().take();
149                        for child in children {
150                            child.remove_self()
151                        }
152                        drop(dom)
153                    }
154
155                    VecDiff::Clear {} => {
156                        self.clear();
157                    }
158                }
159            }
160        }
161
162        parent.append_child(&marker.clone());
163
164        let state = State::new(parent.clone(), marker);
165
166        let fut = iterable.for_each(move |change| {
167            let mut state = state.borrow_mut();
168            state.process_change(change);
169            ready(())
170        });
171        parent.effect(fut);
172        Ok(())
173    }
174}