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}