hyper-component 0.12.3 → 0.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|