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].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
This code seems to be duplicated in your project.
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
This code seems to be duplicated in your project.
Loading history...
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
This code seems to be duplicated in your project.
Loading history...
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