hyalite 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/Rakefile +7 -0
- data/client/hyalite.rb +98 -0
- data/client/hyalite/adler32.rb +33 -0
- data/client/hyalite/browser_event.rb +201 -0
- data/client/hyalite/callback_queue.rb +22 -0
- data/client/hyalite/component.rb +77 -0
- data/client/hyalite/composite_component.rb +237 -0
- data/client/hyalite/dom_component.rb +329 -0
- data/client/hyalite/dom_operations.rb +17 -0
- data/client/hyalite/dom_property.rb +218 -0
- data/client/hyalite/dom_property_operations.rb +117 -0
- data/client/hyalite/element.rb +17 -0
- data/client/hyalite/event_dispatcher.rb +99 -0
- data/client/hyalite/event_plugin/change_event_plugin.rb +83 -0
- data/client/hyalite/event_plugin/event_plugin_registry.rb +49 -0
- data/client/hyalite/event_plugin/simple_event_plugin.rb +276 -0
- data/client/hyalite/input_wrapper.rb +94 -0
- data/client/hyalite/instance_handles.rb +78 -0
- data/client/hyalite/internal_component.rb +11 -0
- data/client/hyalite/linked_value_utils.rb +27 -0
- data/client/hyalite/mount.rb +285 -0
- data/client/hyalite/multi_children.rb +272 -0
- data/client/hyalite/reconcile_transaction.rb +41 -0
- data/client/hyalite/reconciler.rb +122 -0
- data/client/hyalite/short_hand.rb +23 -0
- data/client/hyalite/synthetic_event.rb +18 -0
- data/client/hyalite/text_component.rb +42 -0
- data/client/hyalite/transaction.rb +47 -0
- data/client/hyalite/try.rb +7 -0
- data/client/hyalite/update_queue.rb +52 -0
- data/client/hyalite/updates.rb +129 -0
- data/client/hyalite/utils.rb +19 -0
- data/example/Gemfile +5 -0
- data/example/Gemfile.lock +46 -0
- data/example/app/application.rb +27 -0
- data/example/config.ru +12 -0
- data/example/index.html.haml +8 -0
- data/hyalite.gemspec +27 -0
- data/lib/hyalite.rb +6 -0
- data/lib/hyalite/main.rb +5 -0
- data/lib/hyalite/version.rb +3 -0
- metadata +145 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'hyalite/short_hand'
|
2
|
+
|
3
|
+
module Hyalite
|
4
|
+
module Component
|
5
|
+
|
6
|
+
attr_accessor :props, :state, :context, :refs
|
7
|
+
|
8
|
+
def init_component(props, context, updator)
|
9
|
+
@props = props
|
10
|
+
@context = context
|
11
|
+
@updator = updator
|
12
|
+
@state = initial_state
|
13
|
+
@refs = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def initial_state
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
def context_types
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
def child_context
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
def component_did_mount
|
29
|
+
end
|
30
|
+
|
31
|
+
def component_will_mount
|
32
|
+
end
|
33
|
+
|
34
|
+
def component_will_unmount
|
35
|
+
end
|
36
|
+
|
37
|
+
def component_will_update(props, state, context)
|
38
|
+
end
|
39
|
+
|
40
|
+
def component_did_update(props, state, context)
|
41
|
+
end
|
42
|
+
|
43
|
+
def should_component_update(props, state, context)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_state(states, &block)
|
48
|
+
@updator.enqueue_set_state(self, states)
|
49
|
+
if block_given?
|
50
|
+
@updator.enqueue_callback(self, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def render
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class EmptyComponent
|
59
|
+
include Component
|
60
|
+
|
61
|
+
def self.empty_element
|
62
|
+
@instance ||= ElementObject.new(EmptyComponent, nil, nil, nil, nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
def render
|
66
|
+
Hyalite.create_element("noscript", nil, nil)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class TopLevelWrapper
|
71
|
+
include Component
|
72
|
+
|
73
|
+
def render
|
74
|
+
@props
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'hyalite/update_queue'
|
2
|
+
require 'hyalite/reconciler'
|
3
|
+
require 'hyalite/internal_component'
|
4
|
+
|
5
|
+
module Hyalite
|
6
|
+
class CompositeComponent
|
7
|
+
include InternalComponent
|
8
|
+
|
9
|
+
attr_reader :current_element, :rendered_component
|
10
|
+
attr_accessor :top_level_wrapper, :pending_state_queue, :mount_order, :pending_callbacks, :pending_element
|
11
|
+
|
12
|
+
@next_mount_id = 1
|
13
|
+
|
14
|
+
def self.next_mount_id
|
15
|
+
@next_mount_id += 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(element)
|
19
|
+
@element = element
|
20
|
+
@current_element = @element
|
21
|
+
end
|
22
|
+
|
23
|
+
def mount_component(root_id, mount_ready, context)
|
24
|
+
@context = context
|
25
|
+
@mount_order = CompositeComponent.next_mount_id
|
26
|
+
@root_node_id = root_id
|
27
|
+
|
28
|
+
public_context = mask_context(@context)
|
29
|
+
@instance = @current_element.type.new
|
30
|
+
@instance.init_component(@current_element.props, public_context, UpdateQueue)
|
31
|
+
|
32
|
+
Hyalite.instance_map[@instance] = self
|
33
|
+
|
34
|
+
@pending_state_queue = nil
|
35
|
+
@pending_replace_state = false
|
36
|
+
@pending_force_update = false
|
37
|
+
|
38
|
+
@instance.component_will_mount
|
39
|
+
if @pending_state_queue
|
40
|
+
@instance.state = process_pending_state(@instance.props, @instance.context)
|
41
|
+
end
|
42
|
+
|
43
|
+
@rendered_component = Hyalite.instantiate_component(render_component(@instance))
|
44
|
+
|
45
|
+
markup = Reconciler.mount_component(
|
46
|
+
@rendered_component,
|
47
|
+
root_id,
|
48
|
+
mount_ready,
|
49
|
+
context.merge(@instance.child_context)
|
50
|
+
)
|
51
|
+
|
52
|
+
mount_ready.enqueue { @instance.component_did_mount }
|
53
|
+
|
54
|
+
markup
|
55
|
+
end
|
56
|
+
|
57
|
+
def unmount_component
|
58
|
+
|
59
|
+
@instance.component_will_unmount
|
60
|
+
|
61
|
+
Reconciler.unmount_component(@rendered_component)
|
62
|
+
@rendered_component = nil
|
63
|
+
@instance = nil
|
64
|
+
|
65
|
+
@pending_state_queue = nil
|
66
|
+
@pending_replace_state = false
|
67
|
+
@pending_force_update = false
|
68
|
+
@pending_callbacks = nil
|
69
|
+
@pending_element = nil
|
70
|
+
|
71
|
+
@context = nil
|
72
|
+
@root_node_id = nil
|
73
|
+
@top_level_wrapper = nil
|
74
|
+
|
75
|
+
Hyalite.instance_map.delete(@instance)
|
76
|
+
end
|
77
|
+
|
78
|
+
def perform_update_if_necessary(mount_ready)
|
79
|
+
if @pending_element
|
80
|
+
receive_component(@pending_element, mount_ready, @context)
|
81
|
+
end
|
82
|
+
|
83
|
+
if (@pending_state_queue && @pending_state_queue.any?) || @pending_force_update
|
84
|
+
update_component(
|
85
|
+
mount_ready,
|
86
|
+
@current_element,
|
87
|
+
@current_element,
|
88
|
+
@context,
|
89
|
+
@context)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def public_instance
|
94
|
+
@instance
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
"CompositeComponent: instance: #{@instance.inspect}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def attach_ref(ref, component)
|
102
|
+
@instance.refs ||= {}
|
103
|
+
@instance.refs[ref] = component.public_instance
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def render_component(instance)
|
109
|
+
Hyalite.current_owner(self) do
|
110
|
+
instance.render
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def mask_context(context)
|
115
|
+
context.select {|k, v| @current_element.type.context_types.has_key? k }
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_component(mount_ready, prev_parent_element, next_parent_element, prev_unmasked_context, next_unmasked_context)
|
119
|
+
next_context = (@context == next_unmasked_context ? @instance.context : mask_context(next_unmasked_context))
|
120
|
+
|
121
|
+
next_props = next_parent_element.props
|
122
|
+
next_state = process_pending_state(next_props, next_context)
|
123
|
+
|
124
|
+
should_update =
|
125
|
+
@pending_force_update ||
|
126
|
+
@instance.should_component_update(next_props, next_state, next_context)
|
127
|
+
|
128
|
+
if should_update
|
129
|
+
@pending_force_update = false
|
130
|
+
perform_component_update(
|
131
|
+
next_parent_element,
|
132
|
+
next_props,
|
133
|
+
next_state,
|
134
|
+
next_context,
|
135
|
+
mount_ready,
|
136
|
+
next_unmasked_context
|
137
|
+
)
|
138
|
+
else
|
139
|
+
@current_element = next_parent_element
|
140
|
+
@context = next_unmasked_context
|
141
|
+
@instance.props = next_props
|
142
|
+
@instance.state = next_state
|
143
|
+
@instance.context = next_context
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def receive_component(next_element, mount_ready, next_context)
|
148
|
+
prev_element = @current_element
|
149
|
+
prev_context = @context
|
150
|
+
|
151
|
+
@pending_element = nil
|
152
|
+
|
153
|
+
update_component(
|
154
|
+
mount_ready,
|
155
|
+
prev_element,
|
156
|
+
next_element,
|
157
|
+
prev_context,
|
158
|
+
next_context
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def perform_component_update(next_element, next_props, next_state, next_context, mount_ready, unmasked_context)
|
163
|
+
prev_props = @instance.props
|
164
|
+
prev_state = @instance.state
|
165
|
+
prev_context = @instance.context
|
166
|
+
|
167
|
+
@instance.component_will_update(next_props, next_state, next_context)
|
168
|
+
|
169
|
+
@current_element = next_element
|
170
|
+
@context = unmasked_context
|
171
|
+
@instance.props = next_props
|
172
|
+
@instance.state = next_state
|
173
|
+
@instance.context = next_context
|
174
|
+
|
175
|
+
update_rendered_component(mount_ready, unmasked_context)
|
176
|
+
|
177
|
+
mount_ready.enqueue do
|
178
|
+
@instance.component_did_update(prev_props, prev_state, prev_context)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def update_rendered_component(mount_ready, context)
|
183
|
+
prev_component_instance = @rendered_component
|
184
|
+
prev_rendered_element = prev_component_instance.current_element
|
185
|
+
next_rendered_element = render_validated_component
|
186
|
+
if Reconciler.should_update_component(prev_rendered_element, next_rendered_element)
|
187
|
+
Reconciler.receive_component(
|
188
|
+
prev_component_instance,
|
189
|
+
next_rendered_element,
|
190
|
+
mount_ready,
|
191
|
+
context
|
192
|
+
)
|
193
|
+
else
|
194
|
+
this_id = @root_node_id
|
195
|
+
prev_component_id = prev_component_instance.root_node_id
|
196
|
+
Reconciler.unmount_component(prev_component_instance)
|
197
|
+
|
198
|
+
@rendered_component = Hyalite.instantiate_component(next_rendered_element)
|
199
|
+
next_markup = Reconciler.mount_component(
|
200
|
+
@rendered_component,
|
201
|
+
this_id,
|
202
|
+
mount_ready,
|
203
|
+
context
|
204
|
+
)
|
205
|
+
replace_node_with_markup_by_id(prev_component_id, next_markup)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
def replace_node_with_markup_by_id(id, markup)
|
211
|
+
node = Mount.node(id)
|
212
|
+
node.replace(markup)
|
213
|
+
end
|
214
|
+
|
215
|
+
def render_validated_component
|
216
|
+
Hyalite.current_owner(self) do
|
217
|
+
@instance.render
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def process_pending_state(props, context)
|
222
|
+
replace = @pending_replace_state
|
223
|
+
@pending_replace_state = false
|
224
|
+
|
225
|
+
return @instance.state unless @pending_state_queue
|
226
|
+
|
227
|
+
queue = @pending_state_queue
|
228
|
+
@pending_state_queue = nil
|
229
|
+
|
230
|
+
|
231
|
+
queue.each_with_object(replace ? {} : @instance.state) do |partial, next_state|
|
232
|
+
next_state.merge! (partial.is_a?(Proc) ? partial.call(@instance, next_state, props, context) : partial)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'hyalite/multi_children'
|
2
|
+
require 'hyalite/dom_property_operations'
|
3
|
+
require 'hyalite/internal_component'
|
4
|
+
require 'hyalite/browser_event'
|
5
|
+
require 'hyalite/input_wrapper'
|
6
|
+
|
7
|
+
module Hyalite
|
8
|
+
class DOMComponent
|
9
|
+
include MultiChildren
|
10
|
+
include InternalComponent
|
11
|
+
|
12
|
+
attr_reader :root_node_id
|
13
|
+
|
14
|
+
def initialize(element)
|
15
|
+
@element = element
|
16
|
+
@tag = @element.type.downcase
|
17
|
+
@input_wrapper = InputWrapper.new(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_element
|
21
|
+
@element
|
22
|
+
end
|
23
|
+
|
24
|
+
def mount_component(root_id, mount_ready, context)
|
25
|
+
return if @tag == "noscript"
|
26
|
+
@root_node_id = root_id
|
27
|
+
|
28
|
+
props = current_element.props
|
29
|
+
|
30
|
+
case @tag
|
31
|
+
# when 'iframe', 'img', 'form', 'video', 'audio'
|
32
|
+
# this._wrapperState = {
|
33
|
+
# listeners: null,
|
34
|
+
# }
|
35
|
+
# transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
|
36
|
+
# when 'button'
|
37
|
+
# props = ReactDOMButton.getNativeProps(this, props, nativeParent);
|
38
|
+
when 'input'
|
39
|
+
@input_wrapper.mount_wrapper
|
40
|
+
props = @input_wrapper.native_props
|
41
|
+
# when 'option'
|
42
|
+
# ReactDOMOption.mountWrapper(this, props, nativeParent);
|
43
|
+
# props = ReactDOMOption.getNativeProps(this, props);
|
44
|
+
# when 'select'
|
45
|
+
# ReactDOMSelect.mountWrapper(this, props, nativeParent);
|
46
|
+
# props = ReactDOMSelect.getNativeProps(this, props);
|
47
|
+
# transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
|
48
|
+
# when 'textarea'
|
49
|
+
# ReactDOMTextarea.mountWrapper(this, props, nativeParent);
|
50
|
+
# props = ReactDOMTextarea.getNativeProps(this, props);
|
51
|
+
# transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
|
52
|
+
end
|
53
|
+
|
54
|
+
element = create_open_tag_markup_and_put_listeners(mount_ready, @element.props)
|
55
|
+
create_content_markup(mount_ready, element, context)
|
56
|
+
end
|
57
|
+
|
58
|
+
def unmount_component
|
59
|
+
case @tag
|
60
|
+
# when 'iframe', 'img', 'form'
|
61
|
+
# listeners = this._wrapperState.listeners;
|
62
|
+
# if (listeners) {
|
63
|
+
# for (var i = 0; i < listeners.length; i++) {
|
64
|
+
# listeners[i].remove();
|
65
|
+
# }
|
66
|
+
# }
|
67
|
+
when 'input'
|
68
|
+
@input_wrapper.unmount_wrapper
|
69
|
+
end
|
70
|
+
|
71
|
+
unmount_children
|
72
|
+
BrowserEvent.delete_all_listeners(@root_node_id)
|
73
|
+
Mount.purge_id(@root_node_id)
|
74
|
+
@root_node_id = nil
|
75
|
+
# @wrapper_state = null;
|
76
|
+
if @node_with_legacy_properties
|
77
|
+
node = @node_with_legacy_properties
|
78
|
+
node.internal_component = nil
|
79
|
+
@node_with_legacy_properties = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def receive_component(next_element, mount_ready, context)
|
84
|
+
prev_element = @element
|
85
|
+
@element = next_element
|
86
|
+
update_component(mount_ready, prev_element, next_element, context);
|
87
|
+
end
|
88
|
+
|
89
|
+
def update_component(mount_ready, prev_element, next_element, context)
|
90
|
+
last_props = prev_element.props
|
91
|
+
next_props = @element.props
|
92
|
+
|
93
|
+
case @tag
|
94
|
+
# when 'button':
|
95
|
+
# lastProps = ReactDOMButton.getNativeProps(this, lastProps);
|
96
|
+
# nextProps = ReactDOMButton.getNativeProps(this, nextProps);
|
97
|
+
when 'input'
|
98
|
+
@input_wrapper.update_wrapper
|
99
|
+
last_props = @input_wrapper.native_props(last_props)
|
100
|
+
# nextProps = ReactDOMInput.getNativeProps(this, nextProps);
|
101
|
+
# when 'option':
|
102
|
+
# lastProps = ReactDOMOption.getNativeProps(this, lastProps);
|
103
|
+
# nextProps = ReactDOMOption.getNativeProps(this, nextProps);
|
104
|
+
# when 'select':
|
105
|
+
# lastProps = ReactDOMSelect.getNativeProps(this, lastProps);
|
106
|
+
# nextProps = ReactDOMSelect.getNativeProps(this, nextProps);
|
107
|
+
# when 'textarea':
|
108
|
+
# ReactDOMTextarea.updateWrapper(this);
|
109
|
+
# lastProps = ReactDOMTextarea.getNativeProps(this, lastProps);
|
110
|
+
# nextProps = ReactDOMTextarea.getNativeProps(this, nextProps);
|
111
|
+
end
|
112
|
+
|
113
|
+
# assertValidProps(this, nextProps);
|
114
|
+
update_dom_properties(last_props, next_props, mount_ready)
|
115
|
+
update_dom_children(last_props, next_props, mount_ready, context)
|
116
|
+
|
117
|
+
# if (!canDefineProperty && this._nodeWithLegacyProperties) {
|
118
|
+
# this._nodeWithLegacyProperties.props = nextProps;
|
119
|
+
# }
|
120
|
+
|
121
|
+
if @tag == 'select'
|
122
|
+
mount_ready.enqueue { post_update_select_wrapper }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def public_instance
|
127
|
+
native_node
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_s
|
131
|
+
{
|
132
|
+
tag: @tag,
|
133
|
+
root_node_id: root_node_id,
|
134
|
+
rendered_children: @rendered_children,
|
135
|
+
}.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def native_node
|
141
|
+
@native_node ||= Mount.node(@root_node_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
def update_dom_properties(last_props, next_props, mount_ready)
|
145
|
+
style_updates = {}
|
146
|
+
|
147
|
+
last_props.each do |prop_key, prop_value|
|
148
|
+
next if next_props.has_key?(prop_key)
|
149
|
+
|
150
|
+
if prop_key == :style
|
151
|
+
@previous_style_copy.each do |style_name, style|
|
152
|
+
style_updates[style_name] = ''
|
153
|
+
end
|
154
|
+
|
155
|
+
@previous_style_copy = nil
|
156
|
+
elsif BrowserEvent.include?(prop_key)
|
157
|
+
if last_props.has_key? prop_key
|
158
|
+
BrowserEvent.delete_listener(root_node_id, prop_key)
|
159
|
+
end
|
160
|
+
elsif DOMProperty.include?(prop_key) || DOMProperty.is_custom_attribute(prop_key)
|
161
|
+
node = Mount.node(root_node_id)
|
162
|
+
DOMPropertyOperations.delete_value_for_property(node, prop_key, prop_value)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
next_props.each do |prop_key, next_prop|
|
167
|
+
last_prop = prop_key == :style ? @previous_style_copy : last_props[prop_key]
|
168
|
+
next if next_prop == last_prop
|
169
|
+
|
170
|
+
if prop_key == :style
|
171
|
+
if next_prop
|
172
|
+
next_prop = @previous_style_copy = next_prop.clone
|
173
|
+
else
|
174
|
+
@previous_style_copy = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
if last_prop
|
178
|
+
last_prop.each do |style_name, style|
|
179
|
+
unless next_prop.has_key?(style_name)
|
180
|
+
style_updates[style_name] = ''
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
next_prop.each do |style_name, style|
|
185
|
+
if next_prop.has_key?(style_name) && last_prop[style_name] != next_prop[style_name]
|
186
|
+
style_updates[style_name] = next_prop[style_name]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
else
|
190
|
+
style_updates = next_prop
|
191
|
+
end
|
192
|
+
elsif BrowserEvent.include?(prop_key)
|
193
|
+
if next_prop
|
194
|
+
enqueue_put_listener(root_node_id, prop_key, next_prop, mount_ready)
|
195
|
+
elsif last_prop
|
196
|
+
BrowserEvent.delete_listener(root_node_id, prop_key)
|
197
|
+
end
|
198
|
+
elsif is_custom_component(@tag, next_props)
|
199
|
+
node = Mount.node(root_node_id)
|
200
|
+
DOMPropertyOperations.set_value_for_attribute(node, prop_key, next_prop);
|
201
|
+
elsif DOMProperty.include?(prop_key) || DOMProperty.is_custom_attribute(prop_key)
|
202
|
+
node = Mount.node(root_node_id)
|
203
|
+
if next_prop
|
204
|
+
DOMPropertyOperations.set_value_for_property(node, prop_key, next_prop);
|
205
|
+
else
|
206
|
+
DOMPropertyOperations.delete_value_for_property(node, prop_key)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if style_updates.any?
|
212
|
+
node = Mount.node(root_node_id)
|
213
|
+
node.style(style_updates)
|
214
|
+
end
|
215
|
+
rescue => e
|
216
|
+
p e
|
217
|
+
raise
|
218
|
+
end
|
219
|
+
|
220
|
+
def update_dom_children(last_props, next_props, mount_ready, context)
|
221
|
+
last_content = last_props[:children] if is_text_content(last_props[:children])
|
222
|
+
next_content = next_props[:children] if is_text_content(next_props[:children])
|
223
|
+
|
224
|
+
last_html = last_props[:dangerouslySetInnerHTML].try {|_| _['__html'] }
|
225
|
+
next_html = next_props[:dangerouslySetInnerHTML].try {|_| _['__html'] }
|
226
|
+
|
227
|
+
last_children = last_props[:children] unless last_content
|
228
|
+
next_children = next_props[:children] unless next_content
|
229
|
+
|
230
|
+
last_has_content_or_html = !last_content.nil? || !last_html.nil?
|
231
|
+
next_has_content_or_html = !next_content.nil? || !next_html.nil?
|
232
|
+
if last_children && next_children.nil?
|
233
|
+
update_children(nil, mount_ready, context)
|
234
|
+
elsif last_has_content_or_html && !next_has_content_or_html
|
235
|
+
update_text_content('')
|
236
|
+
end
|
237
|
+
|
238
|
+
if next_content
|
239
|
+
unless last_content == next_content
|
240
|
+
update_text_content(next_content.to_s)
|
241
|
+
end
|
242
|
+
elsif next_html
|
243
|
+
unless last_html == next_html
|
244
|
+
update_markup(next_html.to_s)
|
245
|
+
end
|
246
|
+
elsif next_children
|
247
|
+
update_children(next_children, mount_ready, context)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def create_open_tag_markup_and_put_listeners(mount_ready, props)
|
252
|
+
element = $document.create_element(@tag)
|
253
|
+
|
254
|
+
props.each do |prop_key, prop_value|
|
255
|
+
next unless prop_value
|
256
|
+
|
257
|
+
if BrowserEvent.include?(prop_key)
|
258
|
+
enqueue_put_listener(@root_node_id, prop_key, prop_value, mount_ready)
|
259
|
+
else
|
260
|
+
if prop_key == :style
|
261
|
+
if prop_value
|
262
|
+
prop_value = @previous_style_copy = props[:style].clone
|
263
|
+
end
|
264
|
+
DOMPropertyOperations.create_markup_for_styles(element, prop_value)
|
265
|
+
else
|
266
|
+
if is_custom_component(@tag, props)
|
267
|
+
DOMPropertyOperations.create_markup_for_custom_attribute(element, prop_key, prop_value)
|
268
|
+
else
|
269
|
+
DOMPropertyOperations.create_markup_for_property(element, prop_key, prop_value)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
#return element if mount_ready.render_to_static_markup
|
276
|
+
|
277
|
+
element[Mount::ID_ATTR_NAME] = @root_node_id
|
278
|
+
element
|
279
|
+
end
|
280
|
+
|
281
|
+
def create_content_markup(mount_ready, element, context)
|
282
|
+
children = @element.props[:children]
|
283
|
+
|
284
|
+
inner_html = @element.props[:dangerouslySetInnerHTML]
|
285
|
+
if inner_html
|
286
|
+
if inner_html[:__html]
|
287
|
+
html = inner_html[:__html]
|
288
|
+
`element.native.innerHTML = html`
|
289
|
+
end
|
290
|
+
elsif is_text_content(children)
|
291
|
+
element.inner_dom = Browser::DOM::Text.create(@element.props[:children])
|
292
|
+
else
|
293
|
+
mount_images = mount_children(@element.props[:children], mount_ready, context)
|
294
|
+
mount_images.each do |image|
|
295
|
+
if image.is_a?(String)
|
296
|
+
element.text = image
|
297
|
+
else
|
298
|
+
image.append_to(element) if image
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
element
|
303
|
+
end
|
304
|
+
|
305
|
+
def enqueue_put_listener(id, event_name, listener, mount_ready)
|
306
|
+
container = Mount.container_for_id(id)
|
307
|
+
if container
|
308
|
+
doc = container.node_type == Browser::DOM::Node::ELEMENT_NODE ? container.document : container
|
309
|
+
BrowserEvent.listen_to(event_name, doc)
|
310
|
+
end
|
311
|
+
mount_ready.enqueue do
|
312
|
+
BrowserEvent.put_listener(id, event_name, listener)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def is_custom_component(tag, props)
|
317
|
+
tag.include?('-') || props.has_key?(:is)
|
318
|
+
end
|
319
|
+
|
320
|
+
def is_text_content(children)
|
321
|
+
case children
|
322
|
+
when String, Numeric
|
323
|
+
true
|
324
|
+
else
|
325
|
+
false
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|