hyper-component 0.12.3 → 0.99.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +27 -0
- data/.gitignore +42 -41
- data/.travis.yml +29 -0
- data/CHANGELOG.md +143 -0
- data/DOCS.md +1515 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +244 -193
- data/LICENSE +5 -7
- data/README.md +49 -0
- data/Rakefile +40 -0
- data/hyper-component.gemspec +41 -31
- data/lib/hyper-component.rb +44 -9
- data/lib/rails-helpers/top_level_rails_component.rb +79 -0
- data/lib/react/api.rb +270 -0
- data/lib/react/callbacks.rb +42 -0
- data/lib/react/children.rb +38 -0
- data/lib/react/component.rb +189 -0
- data/lib/react/component/api.rb +70 -0
- data/lib/react/component/base.rb +13 -0
- data/lib/react/component/class_methods.rb +175 -0
- data/lib/react/component/dsl_instance_methods.rb +23 -0
- data/lib/react/component/params.rb +6 -0
- data/lib/react/component/props_wrapper.rb +90 -0
- data/lib/react/component/should_component_update.rb +99 -0
- data/lib/react/component/tags.rb +116 -0
- data/lib/react/config.rb +5 -0
- data/lib/react/element.rb +159 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/opal-jquery/element.rb +37 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/native_library.rb +87 -0
- data/lib/react/object.rb +15 -0
- data/lib/react/react-source-server.rb +3 -0
- data/lib/react/react-source.rb +17 -0
- data/lib/react/ref_callback.rb +31 -0
- data/lib/react/rendering_context.rb +149 -0
- data/lib/react/server.rb +19 -0
- data/lib/react/state_wrapper.rb +23 -0
- data/lib/react/test.rb +16 -0
- data/lib/react/test/dsl.rb +17 -0
- data/lib/react/test/matchers/render_html_matcher.rb +56 -0
- data/lib/react/test/rspec.rb +15 -0
- data/lib/react/test/session.rb +37 -0
- data/lib/react/test/utils.rb +71 -0
- data/lib/react/to_key.rb +26 -0
- data/lib/react/top_level.rb +110 -0
- data/lib/react/top_level_render.rb +28 -0
- data/lib/react/validator.rb +132 -0
- data/lib/reactive-ruby/component_loader.rb +43 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +233 -0
- data/lib/reactive-ruby/rails.rb +8 -0
- data/lib/reactive-ruby/rails/component_mount.rb +48 -0
- data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
- data/lib/reactive-ruby/rails/railtie.rb +20 -0
- data/lib/reactive-ruby/serializers.rb +23 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +46 -0
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
- data/lib/{hyperloop/component → reactive-ruby}/version.rb +1 -1
- data/lib/reactrb/auto-import.rb +27 -0
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
- data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
- data/misc/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
- data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +121 -0
- data/misc/how-component-name-lookup-works.md +145 -0
- data/misc/hyperloop-logo-small-pink.png +0 -0
- data/misc/logo1.png +0 -0
- data/misc/logo2.png +0 -0
- data/misc/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- metadata +260 -37
- data/CODE_OF_CONDUCT.md +0 -49
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'hyperloop-config'
|
2
|
+
|
3
|
+
module React
|
4
|
+
module Callbacks
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_callback(name, *args)
|
10
|
+
self.class.callbacks_for(name).each do |callback|
|
11
|
+
if callback.is_a?(Proc)
|
12
|
+
instance_exec(*args, &callback)
|
13
|
+
else
|
14
|
+
send(callback, *args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def define_callback(callback_name, &after_define_hook)
|
21
|
+
wrapper_name = "_#{callback_name}_callbacks"
|
22
|
+
define_singleton_method(wrapper_name) do
|
23
|
+
Hyperloop::Context.set_var(self, "@#{wrapper_name}", force: true) { [] }
|
24
|
+
end
|
25
|
+
define_singleton_method(callback_name) do |*args, &block|
|
26
|
+
send(wrapper_name).concat(args)
|
27
|
+
send(wrapper_name).push(block) if block_given?
|
28
|
+
after_define_hook.call(*args, &block) if after_define_hook
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def callbacks_for(callback_name)
|
33
|
+
wrapper_name = "_#{callback_name}_callbacks"
|
34
|
+
if superclass.respond_to? :callbacks_for
|
35
|
+
superclass.callbacks_for(callback_name)
|
36
|
+
else
|
37
|
+
[]
|
38
|
+
end + send(wrapper_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module React
|
2
|
+
class Children
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(children)
|
6
|
+
@children = children
|
7
|
+
end
|
8
|
+
|
9
|
+
def render
|
10
|
+
each(&:render)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_proc
|
14
|
+
-> () { render }
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
return to_enum(__callee__) { length } unless block_given?
|
19
|
+
return [] unless length > 0
|
20
|
+
collection = []
|
21
|
+
%x{
|
22
|
+
React.Children.forEach(#{@children}, function(context){
|
23
|
+
#{
|
24
|
+
element = React::Element.new(`context`)
|
25
|
+
block.call(element)
|
26
|
+
collection << element
|
27
|
+
}
|
28
|
+
})
|
29
|
+
}
|
30
|
+
collection
|
31
|
+
end
|
32
|
+
|
33
|
+
def length
|
34
|
+
@length ||= `React.Children.count(#{@children})`
|
35
|
+
end
|
36
|
+
alias_method :size, :length
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'react/ext/string'
|
2
|
+
require 'react/ext/hash'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'react/callbacks'
|
5
|
+
require 'react/rendering_context'
|
6
|
+
require 'hyper-store'
|
7
|
+
require 'react/state_wrapper'
|
8
|
+
require 'react/component/api'
|
9
|
+
require 'react/component/class_methods'
|
10
|
+
require 'react/component/props_wrapper'
|
11
|
+
|
12
|
+
module Hyperloop
|
13
|
+
class Component
|
14
|
+
class << self
|
15
|
+
def mounted_components
|
16
|
+
@mounted_components ||= Set.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def force_update!
|
20
|
+
components = mounted_components.to_a
|
21
|
+
components.each do |comp|
|
22
|
+
next unless mounted_components.include? comp
|
23
|
+
comp.force_update!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Mixin
|
29
|
+
def self.included(base)
|
30
|
+
base.include(Hyperloop::Store::Mixin)
|
31
|
+
base.include(React::Component::API)
|
32
|
+
base.include(React::Callbacks)
|
33
|
+
base.include(React::Component::Tags)
|
34
|
+
base.include(React::Component::DslInstanceMethods)
|
35
|
+
base.include(React::Component::ShouldComponentUpdate)
|
36
|
+
base.class_eval do
|
37
|
+
class_attribute :initial_state
|
38
|
+
define_callback :before_mount
|
39
|
+
define_callback :after_mount
|
40
|
+
define_callback :before_receive_props
|
41
|
+
define_callback :before_update
|
42
|
+
define_callback :after_update
|
43
|
+
define_callback :before_unmount
|
44
|
+
define_callback(:after_error) { React::API.add_after_error_hook(base) }
|
45
|
+
end
|
46
|
+
base.extend(React::Component::ClassMethods)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.deprecation_warning(message)
|
50
|
+
React::Component.deprecation_warning(name, message)
|
51
|
+
end
|
52
|
+
|
53
|
+
def deprecation_warning(message)
|
54
|
+
React::Component.deprecation_warning(self.class.name, message)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(native_element)
|
58
|
+
@native = native_element
|
59
|
+
init_store
|
60
|
+
end
|
61
|
+
|
62
|
+
def emit(event_name, *args)
|
63
|
+
if React::Event::BUILT_IN_EVENTS.include?(built_in_event_name = "on#{event_name.to_s.event_camelize}")
|
64
|
+
params[built_in_event_name].call(*args)
|
65
|
+
else
|
66
|
+
params["on_#{event_name}"].call(*args)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def component_will_mount
|
71
|
+
React::IsomorphicHelpers.load_context(true) if React::IsomorphicHelpers.on_opal_client?
|
72
|
+
React::State.set_state_context_to(self) do
|
73
|
+
Hyperloop::Component.mounted_components << self
|
74
|
+
run_callback(:before_mount)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def component_did_mount
|
79
|
+
React::State.set_state_context_to(self) do
|
80
|
+
run_callback(:after_mount)
|
81
|
+
React::State.update_states_to_observe
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def component_will_receive_props(next_props)
|
86
|
+
# need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
|
87
|
+
# for now we are just using it to clear processed_params
|
88
|
+
React::State.set_state_context_to(self) { run_callback(:before_receive_props, next_props) }
|
89
|
+
@_receiving_props = true
|
90
|
+
end
|
91
|
+
|
92
|
+
def component_will_update(next_props, next_state)
|
93
|
+
React::State.set_state_context_to(self) { run_callback(:before_update, next_props, next_state) }
|
94
|
+
params._reset_all_others_cache if @_receiving_props
|
95
|
+
@_receiving_props = false
|
96
|
+
end
|
97
|
+
|
98
|
+
def component_did_update(prev_props, prev_state)
|
99
|
+
React::State.set_state_context_to(self) do
|
100
|
+
run_callback(:after_update, prev_props, prev_state)
|
101
|
+
React::State.update_states_to_observe
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def component_will_unmount
|
106
|
+
React::State.set_state_context_to(self) do
|
107
|
+
run_callback(:before_unmount)
|
108
|
+
React::State.remove
|
109
|
+
Hyperloop::Component.mounted_components.delete self
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def component_did_catch(error, info)
|
114
|
+
React::State.set_state_context_to(self) do
|
115
|
+
run_callback(:after_error, error, info)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_reader :waiting_on_resources
|
120
|
+
|
121
|
+
def update_react_js_state(object, name, value)
|
122
|
+
if object
|
123
|
+
name = "#{object.class}.#{name}" unless object == self
|
124
|
+
# Date.now() has only millisecond precision, if several notifications of
|
125
|
+
# observer happen within a millisecond, updates may get lost.
|
126
|
+
# to mitigate this the Math.random() appends some random number
|
127
|
+
# this way notifactions will happen as expected by the rest of hyperloop
|
128
|
+
set_state(
|
129
|
+
'***_state_updated_at-***' => `Date.now() + Math.random()`,
|
130
|
+
name => value
|
131
|
+
)
|
132
|
+
else
|
133
|
+
set_state name => value
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def set_state_synchronously?
|
138
|
+
@native.JS[:__opalInstanceSyncSetState]
|
139
|
+
end
|
140
|
+
|
141
|
+
def render
|
142
|
+
raise 'no render defined'
|
143
|
+
end unless method_defined?(:render)
|
144
|
+
|
145
|
+
def _render_wrapper
|
146
|
+
React::State.set_state_context_to(self, true) do
|
147
|
+
element = React::RenderingContext.render(nil) { render || '' }
|
148
|
+
@waiting_on_resources =
|
149
|
+
element.waiting_on_resources if element.respond_to? :waiting_on_resources
|
150
|
+
element
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def watch(value, &on_change)
|
155
|
+
Observable.new(value, on_change)
|
156
|
+
end
|
157
|
+
|
158
|
+
def define_state(*args, &block)
|
159
|
+
React::State.initialize_states(self, self.class.define_state(*args, &block))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
module React
|
166
|
+
module Component
|
167
|
+
def self.included(base)
|
168
|
+
# note this is turned off during old style testing: See the spec_helper
|
169
|
+
deprecation_warning base, "The module name React::Component has been deprecated. Use Hyperloop::Component::Mixin instead."
|
170
|
+
base.include Hyperloop::Component::Mixin
|
171
|
+
end
|
172
|
+
def self.deprecation_warning(name, message)
|
173
|
+
@deprecation_messages ||= []
|
174
|
+
message = "Warning: Deprecated feature used in #{name}. #{message}"
|
175
|
+
unless @deprecation_messages.include? message
|
176
|
+
@deprecation_messages << message
|
177
|
+
React::IsomorphicHelpers.log message, :warning
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
module ComponentNoNotice
|
182
|
+
def self.included(base)
|
183
|
+
base.include Hyperloop::Component::Mixin
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
module React
|
189
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
module API
|
4
|
+
def dom_node
|
5
|
+
`ReactDOM.findDOMNode(#{self}.native)` # react >= v0.15.0
|
6
|
+
end
|
7
|
+
|
8
|
+
def mounted?
|
9
|
+
`(#{self}.is_mounted === undefined) ? false : #{self}.is_mounted`
|
10
|
+
end
|
11
|
+
|
12
|
+
def force_update!
|
13
|
+
`#{self}.native.forceUpdate()`
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_props(prop, &block)
|
18
|
+
raise "set_props: setProps() is no longer supported by react"
|
19
|
+
end
|
20
|
+
alias :set_props! :set_props
|
21
|
+
|
22
|
+
def set_state(state, &block)
|
23
|
+
set_or_replace_state_or_prop(state, 'setState', &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_state!(state, &block)
|
27
|
+
set_or_replace_state_or_prop(state, 'setState', &block)
|
28
|
+
`#{self}.native.forceUpdate()`
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def set_or_replace_state_or_prop(state_or_prop, method, &block)
|
34
|
+
raise "No native ReactComponent associated" unless @native
|
35
|
+
`var state_prop_n = #{state_or_prop.shallow_to_n}`
|
36
|
+
# the state object is initalized when the ruby component is instantiated
|
37
|
+
# this is detected by self.native.__opalInstanceInitializedState
|
38
|
+
# which is set in the native component constructor in react/api.rb
|
39
|
+
# the setState update callback is not called when initalizing initial state
|
40
|
+
if block
|
41
|
+
%x{
|
42
|
+
if (#{@native}.__opalInstanceInitializedState === true) {
|
43
|
+
#{@native}[method](state_prop_n, function(){
|
44
|
+
block.$call();
|
45
|
+
});
|
46
|
+
} else {
|
47
|
+
for (var sp in state_prop_n) {
|
48
|
+
if (state_prop_n.hasOwnProperty(sp)) {
|
49
|
+
#{@native}.state[sp] = state_prop_n[sp];
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
else
|
55
|
+
%x{
|
56
|
+
if (#{@native}.__opalInstanceInitializedState === true) {
|
57
|
+
#{@native}[method](state_prop_n);
|
58
|
+
} else {
|
59
|
+
for (var sp in state_prop_n) {
|
60
|
+
if (state_prop_n.hasOwnProperty(sp)) {
|
61
|
+
#{@native}.state[sp] = state_prop_n[sp];
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
class Base
|
4
|
+
def self.inherited(child)
|
5
|
+
# note this is turned off during old style testing: See the spec_helper
|
6
|
+
unless child.to_s == "React::Component::HyperTestDummy"
|
7
|
+
React::Component.deprecation_warning child, "The class name React::Component::Base has been deprecated. Use Hyperloop::Component instead."
|
8
|
+
end
|
9
|
+
child.include(ComponentNoNotice)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
# class level methods (macros) for components
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def deprecation_warning(message)
|
7
|
+
React::Component.deprecation_warning(self, message)
|
8
|
+
end
|
9
|
+
|
10
|
+
def reactrb_component?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def backtrace(*args)
|
15
|
+
@dont_catch_exceptions = (args[0] == :none)
|
16
|
+
@backtrace_off = @dont_catch_exceptions || (args[0] == :off)
|
17
|
+
end
|
18
|
+
|
19
|
+
def append_backtrace(message_array, backtrace)
|
20
|
+
message_array << " #{backtrace[0]}"
|
21
|
+
backtrace[1..-1].each { |line| message_array << line }
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(container = nil, params = {}, &block)
|
25
|
+
if container
|
26
|
+
container = container.type if container.is_a? React::Element
|
27
|
+
define_method :render do
|
28
|
+
React::RenderingContext.render(container, params) { instance_eval(&block) if block }
|
29
|
+
end
|
30
|
+
else
|
31
|
+
define_method(:render) { instance_eval(&block) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# method missing will assume the method is a class name, and will treat this a render of
|
36
|
+
# of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
|
37
|
+
|
38
|
+
def method_missing(name, *args, &children)
|
39
|
+
Object.method_missing(name, *args, &children) unless args.empty?
|
40
|
+
React::RenderingContext.render(
|
41
|
+
self, class: React::Element.haml_class_name(name), &children
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def validator
|
46
|
+
@validator ||= Validator.new(props_wrapper)
|
47
|
+
end
|
48
|
+
|
49
|
+
def prop_types
|
50
|
+
if self.validator
|
51
|
+
{
|
52
|
+
_componentValidator: %x{
|
53
|
+
function(props, propName, componentName) {
|
54
|
+
var errors = #{validator.validate(Hash.new(`props`))};
|
55
|
+
return #{`errors`.count > 0 ? `new Error(#{"In component `#{name}`\n" + `errors`.join("\n")})` : `undefined`};
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
else
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_props
|
65
|
+
validator.default_props
|
66
|
+
end
|
67
|
+
|
68
|
+
def params(&block)
|
69
|
+
validator.build(&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def props_wrapper
|
73
|
+
@props_wrapper ||= Class.new(PropsWrapper)
|
74
|
+
end
|
75
|
+
|
76
|
+
def param(*args)
|
77
|
+
if args[0].is_a? Hash
|
78
|
+
options = args[0]
|
79
|
+
name = options.first[0]
|
80
|
+
default = options.first[1]
|
81
|
+
options.delete(name)
|
82
|
+
options.merge!({default: default})
|
83
|
+
else
|
84
|
+
name = args[0]
|
85
|
+
options = args[1] || {}
|
86
|
+
end
|
87
|
+
if options[:default]
|
88
|
+
validator.optional(name, options)
|
89
|
+
else
|
90
|
+
validator.requires(name, options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def collect_other_params_as(name)
|
95
|
+
validator.all_other_params(name) { props }
|
96
|
+
end
|
97
|
+
|
98
|
+
alias other_params collect_other_params_as
|
99
|
+
alias others collect_other_params_as
|
100
|
+
|
101
|
+
def define_state(*states, &block)
|
102
|
+
deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
|
103
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
104
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
105
|
+
states.each { |name| state(name => default_initial_value) } # was states_hash[name] = default_initial_value
|
106
|
+
states_hash.each { |name, value| state(name => value) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def export_state(*states, &block)
|
110
|
+
deprecation_warning "'export_state' is deprecated. Use the 'state' macro to declare states."
|
111
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
112
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
113
|
+
states.each { |name| states_hash[name] = default_initial_value }
|
114
|
+
states_hash.each do |name, value|
|
115
|
+
state(name => value, scope: :class, reader: true)
|
116
|
+
singleton_class.define_method("#{name}!") do |*args|
|
117
|
+
mutate.__send__(name, *args)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def native_mixin(item)
|
123
|
+
native_mixins << item
|
124
|
+
end
|
125
|
+
|
126
|
+
def native_mixins
|
127
|
+
@native_mixins ||= []
|
128
|
+
end
|
129
|
+
|
130
|
+
def static_call_back(name, &block)
|
131
|
+
static_call_backs[name] = block
|
132
|
+
end
|
133
|
+
|
134
|
+
def static_call_backs
|
135
|
+
@static_call_backs ||= {}
|
136
|
+
end
|
137
|
+
|
138
|
+
def export_component(opts = {})
|
139
|
+
export_name = (opts[:as] || name).split('::')
|
140
|
+
first_name = export_name.first
|
141
|
+
Native(`Opal.global`)[first_name] = add_item_to_tree(
|
142
|
+
Native(`Opal.global`)[first_name],
|
143
|
+
[React::API.create_native_react_class(self)] + export_name[1..-1].reverse
|
144
|
+
).to_n
|
145
|
+
end
|
146
|
+
|
147
|
+
def imports(component_name)
|
148
|
+
React::API.import_native_component(
|
149
|
+
self, React::API.eval_native_react_component(component_name)
|
150
|
+
)
|
151
|
+
define_method(:render) {} # define a dummy render method - will never be called...
|
152
|
+
rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
|
153
|
+
raise "#{self} cannot import '#{component_name}': #{e.message}."
|
154
|
+
# rubocop:enable Lint/RescueException
|
155
|
+
ensure
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def add_item_to_tree(current_tree, new_item)
|
160
|
+
if Native(current_tree).class != Native::Object || new_item.length == 1
|
161
|
+
new_item.inject { |a, e| { e => a } }
|
162
|
+
else
|
163
|
+
Native(current_tree)[new_item.last] = add_item_to_tree(
|
164
|
+
Native(current_tree)[new_item.last], new_item[0..-2]
|
165
|
+
)
|
166
|
+
current_tree
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_n
|
171
|
+
React::API.class_eval('@@component_classes')[self]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|