1 | module Hyalite |
||
2 | module MultiChildren |
||
3 | def mount_children(nested_children, mount_ready, context) |
||
4 | children = instantiate_children(nested_children, context) |
||
5 | @rendered_children = children |
||
6 | index = 0 |
||
7 | children.keys.map do |name| |
||
8 | child = children[name] |
||
9 | root_id = root_node_id + name |
||
10 | mount_image = Reconciler.mount_component(children[name], root_id, mount_ready, context) |
||
11 | child.mount_index = index |
||
12 | index += 1 |
||
13 | mount_image |
||
14 | end |
||
15 | end |
||
16 | |||
17 | def unmount_children |
||
18 | if @rendered_children |
||
19 | Reconciler.unmount_children(@rendered_children) |
||
20 | @rendered_children = nil |
||
21 | end |
||
22 | end |
||
23 | |||
24 | def update_children(next_nested_children, mount_ready, context) |
||
25 | MultiChildren.wrap_update do |
||
26 | prev_children = @rendered_children |
||
27 | next_children = Reconciler.update_children(prev_children, next_nested_children, mount_ready, context) |
||
28 | @rendered_children = next_children |
||
29 | return if next_children.nil? && prev_children.nil? |
||
30 | |||
31 | |||
32 | last_index = 0 |
||
33 | next_index = 0 |
||
34 | next_children.each do |name, next_child| |
||
35 | prev_child = prev_children && prev_children[name] |
||
36 | if prev_child == next_child |
||
37 | move_child(prev_child, next_index, last_index) |
||
38 | last_index = [prev_child.mount_index, last_index].max |
||
39 | prev_child.mount_index = next_index |
||
40 | else |
||
41 | if prev_child |
||
42 | last_index = [prev_child.mount_index, last_index].max |
||
43 | unmount_child(prev_child) |
||
44 | end |
||
45 | |||
46 | mount_child_by_name_at_index(next_child, name, next_index, mount_ready, context) |
||
47 | end |
||
48 | next_index += 1 |
||
49 | end |
||
50 | |||
51 | prev_children.each do |name, prev_child| |
||
52 | unless next_children && next_children.has_key?(name) |
||
53 | unmount_child(prev_child) |
||
54 | end |
||
55 | end |
||
56 | end |
||
57 | end |
||
58 | |||
59 | def update_text_content(next_content) |
||
60 | MultiChildren.wrap_update do |
||
61 | prev_children = @rendered_children |
||
62 | if prev_children |
||
63 | Reconciler.unmount_children(prev_children) |
||
64 | prev_children.each do |prev_child| |
||
65 | unmount_child(prev_child) |
||
66 | end |
||
67 | end |
||
68 | set_text_content(next_content) |
||
69 | end |
||
70 | end |
||
71 | |||
72 | def move_child(child, to_index, last_index) |
||
73 | if child.mount_index < last_index |
||
74 | enqueue_move(root_node_id, child.mount_index, to_index) |
||
75 | end |
||
76 | end |
||
77 | |||
78 | def remove_child(child) |
||
79 | enqueue_remove(root_node_id, child.mount_index) |
||
80 | end |
||
81 | |||
82 | def unmount_child(child) |
||
83 | remove_child(child) |
||
84 | child.mount_index = nil |
||
85 | end |
||
86 | |||
87 | def instantiate_children(nested_child_nodes, context) |
||
88 | Reconciler.flatten_children(nested_child_nodes).map {|name, child| |
||
89 | child_instance = Hyalite.instantiate_component(child) |
||
90 | [name, child_instance] |
||
91 | }.to_h |
||
92 | end |
||
93 | |||
94 | private |
||
95 | |||
96 | class << self |
||
97 | def wrap_update(&block) |
||
98 | self.update_depth += 1 |
||
99 | error_thrown = true |
||
100 | yield |
||
101 | error_thrown = false |
||
102 | ensure |
||
103 | self.update_depth -= 1 |
||
104 | if self.update_depth == 0 |
||
105 | unless error_thrown |
||
106 | self.process_queue |
||
107 | else |
||
108 | self.clear_queue |
||
109 | end |
||
110 | end |
||
111 | end |
||
112 | |||
113 | def update_depth |
||
114 | @update_depth ||= 0 |
||
115 | end |
||
116 | |||
117 | def update_depth=(depth) |
||
118 | @update_depth = depth |
||
119 | end |
||
120 | |||
121 | def update_queue |
||
122 | @update_queue ||= [] |
||
123 | end |
||
124 | |||
125 | def markup_queue |
||
126 | @markup_queue ||= [] |
||
127 | end |
||
128 | |||
129 | def clear_queue |
||
130 | self.update_queue.clear |
||
131 | self.markup_queue.clear |
||
132 | end |
||
133 | |||
134 | def process_queue |
||
135 | if MultiChildren.update_queue.any? |
||
136 | process_children_updates(MultiChildren.update_queue, MultiChildren.markup_queue) |
||
137 | clear_queue |
||
138 | end |
||
139 | end |
||
140 | |||
141 | def process_children_updates(updates, markup) |
||
142 | updates.each do |update| |
||
143 | update[:parentNode] = Mount.node(update[:parentID]) |
||
144 | end |
||
145 | process_updates(updates, markup) |
||
146 | end |
||
147 | |||
148 | def process_updates(updates, markup_list) |
||
149 | initial_children = {} |
||
150 | updated_children = [] |
||
151 | |||
152 | updates.each_with_index do |update, updated_index| |
||
153 | if update[:type] == :move_existing || update[:type] == :remove_node |
||
154 | updated_index = update[:fromIndex] |
||
155 | updated_child = update[:parentNode].elements[updated_index] |
||
156 | parent_id = update[:parentID] |
||
157 | |||
158 | initial_children[parent_id] ||= [] |
||
159 | initial_children[parent_id] << updated_child |
||
160 | |||
161 | updated_children << updated_child |
||
162 | end |
||
163 | end |
||
164 | |||
165 | if markup_list.any? && markup_list[0].is_a?(String) |
||
166 | #rendered_markup = Danger.dangerouslyRenderMarkup(markupList); |
||
167 | raise "not implemented" |
||
168 | else |
||
169 | rendered_markup = markup_list |
||
170 | end |
||
171 | |||
172 | updated_children.each do |child| |
||
173 | child.remove |
||
174 | end |
||
175 | |||
176 | updates.each do |update| |
||
177 | case update[:type] |
||
178 | when :insert_markup |
||
179 | insert_child_at( |
||
180 | update[:parentNode], |
||
181 | rendered_markup[update[:markupIndex]], |
||
182 | update[:toIndex]) |
||
183 | when :move_existing |
||
184 | insert_child_at( |
||
185 | update[:parentNode], |
||
186 | initial_children[update[:parentID]][update[:fromIndex]], |
||
187 | update[:toIndex]) |
||
188 | when :set_markup |
||
189 | update[:parentNode].inner_html = update[:textContent] |
||
190 | when :text_content |
||
191 | update[:parentNode].text = update[:textContent] |
||
192 | when :remove_node |
||
193 | # Already removed above. |
||
194 | end |
||
195 | end |
||
196 | end |
||
197 | |||
198 | |||
199 | def insert_child_at(parent_node, child_node, index) |
||
200 | if index >= parent_node.children.to_ary.length |
||
201 | parent_node.add_child(child_node) |
||
202 | else |
||
203 | parent_node.children.to_ary[index].add_previous_sibling(child_node) |
||
204 | end |
||
205 | end |
||
206 | end |
||
207 | |||
208 | def mount_child_by_name_at_index(child, name, index, mount_ready, context) |
||
209 | root_id = root_node_id + name |
||
210 | mount_image = Reconciler.mount_component(child, root_id, mount_ready, context) |
||
211 | child.mount_index = index |
||
212 | create_child(child, mount_image) |
||
213 | end |
||
214 | |||
215 | def create_child(child, mount_image) |
||
216 | enqueue_markup(root_node_id, mount_image, child.mount_index) |
||
217 | end |
||
218 | |||
219 | def set_text_content(text_content) |
||
220 | enqueue_text_content(root_node_id, text_content) |
||
221 | end |
||
222 | |||
223 | View Code Duplication | def enqueue_remove(parent_id, from_index) |
|
0 ignored issues
–
show
Duplication
introduced
by
Loading history...
|
|||
224 | MultiChildren.update_queue << { |
||
225 | parentID: parent_id, |
||
226 | parentNode: nil, |
||
227 | type: :remove_node, |
||
228 | markupIndex: nil, |
||
229 | content: nil, |
||
230 | fromIndex: from_index, |
||
231 | toIndex: nil |
||
232 | } |
||
233 | end |
||
234 | |||
235 | View Code Duplication | def enqueue_move(parent_id, from_index, to_index) |
|
0 ignored issues
–
show
|
|||
236 | MultiChildren.update_queue << { |
||
237 | parentID: parent_id, |
||
238 | parentNode: nil, |
||
239 | type: :move_existing, |
||
240 | markupIndex: nil, |
||
241 | content: nil, |
||
242 | fromIndex: from_index, |
||
243 | toIndex: to_index |
||
244 | } |
||
245 | end |
||
246 | |||
247 | View Code Duplication | def enqueue_text_content(parent_id, text_content) |
|
0 ignored issues
–
show
|
|||
248 | MultiChildren.update_queue << { |
||
249 | parentID: parent_id, |
||
250 | parentNode: nil, |
||
251 | type: :text_content, |
||
252 | markupIndex: nil, |
||
253 | textContent: text_content, |
||
254 | fromIndex: nil, |
||
255 | toIndex: nil |
||
256 | } |
||
257 | end |
||
258 | |||
259 | def enqueue_markup(parent_id, markup, to_index) |
||
260 | MultiChildren.markup_queue << markup |
||
261 | MultiChildren.update_queue << { |
||
262 | parentID: parent_id, |
||
263 | parentNode: nil, |
||
264 | type: :insert_markup, |
||
265 | markupIndex: MultiChildren.markup_queue.length - 1, |
||
266 | textContent: nil, |
||
267 | fromIndex: nil, |
||
268 | toIndex: to_index |
||
269 | } |
||
270 | end |
||
271 | |||
272 | end |
||
273 | end |
||
274 |