1
|
|
|
module Hyalite |
2
|
|
View Code Duplication |
module MultiChildren |
|
|
|
|
3
|
|
|
def mount_children(nested_children, mount_ready, context) |
4
|
|
|
children = instantiate_children(nested_children, mount_ready, 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
|
|
View Code Duplication |
|
|
|
|
|
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, nil) |
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].content = 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
|
|
View Code Duplication |
|
|
|
|
|
223
|
|
|
def enqueue_remove(parent_id, from_index) |
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
|
|
View Code Duplication |
|
|
|
|
|
235
|
|
|
def enqueue_move(parent_id, from_index, to_index) |
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
|
|
|
def enqueue_text_content(parent_id, text_content) |
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
|
|
|
|