Completed
Push — master ( 2c8d61...d12e3c )
by Yoh
48s
created

client/hyalite/multi_children.rb (3 issues)

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].children[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
      def insert_child_at(parent_node, child_node, index)
199
        if index >= parent_node.children.length
200
          parent_node.add_child(child_node)
201
        else
202
          parent_node.children[index].add_previous_sibling(child_node)
203
        end
204
      end
205
    end
206
207
    def mount_child_by_name_at_index(child, name, index, mount_ready, context)
208
      root_id = root_node_id + name
209
      mount_image = Reconciler.mount_component(child, root_id, mount_ready, context)
210
      child.mount_index = index
211
      create_child(child, mount_image)
212
    end
213
214
    def create_child(child, mount_image)
215
      enqueue_markup(root_node_id, mount_image, child.mount_index)
216
    end
217
218
    def set_text_content(text_content)
219
      enqueue_text_content(root_node_id, text_content)
220
    end
221
222
    def enqueue_remove(parent_id, from_index)
223 View Code Duplication
      MultiChildren.update_queue << {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
224
        parentID: parent_id,
225
        parentNode: nil,
226
        type: :remove_node,
227
        markupIndex: nil,
228
        content: nil,
229
        fromIndex: from_index,
230
        toIndex: nil
231
      }
232
    end
233
234
    def enqueue_move(parent_id, from_index, to_index)
235 View Code Duplication
      MultiChildren.update_queue << {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
236
        parentID: parent_id,
237
        parentNode: nil,
238
        type: :move_existing,
239
        markupIndex: nil,
240
        content: nil,
241
        fromIndex: from_index,
242
        toIndex: to_index
243
      }
244
    end
245
246
    def enqueue_text_content(parent_id, text_content)
247 View Code Duplication
      MultiChildren.update_queue << {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
248
        parentID: parent_id,
249
        parentNode: nil,
250
        type: :text_content,
251
        markupIndex: nil,
252
        textContent: text_content,
253
        fromIndex: nil,
254
        toIndex: nil
255
      }
256
    end
257
258
    def enqueue_markup(parent_id, markup, to_index)
259
      MultiChildren.markup_queue << markup
260
      MultiChildren.update_queue << {
261
        parentID: parent_id,
262
        parentNode: nil,
263
        type: :insert_markup,
264
        markupIndex: MultiChildren.markup_queue.length - 1,
265
        textContent: nil,
266
        fromIndex: nil,
267
        toIndex: to_index
268
      }
269
    end
270
271
  end
272
end
273