hyper-react 0.99.6 → 1.0.0.lap21
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 +30 -37
- data/.rubocop.yml +1159 -0
- data/.travis.yml +32 -0
- data/Appraisals +31 -0
- data/CHANGELOG.md +143 -0
- data/DOCS.md +1515 -0
- data/Gemfile +2 -5
- data/LICENSE +19 -0
- data/README.md +5 -33
- data/Rakefile +25 -6
- data/UPGRADING.md +24 -0
- data/component-name-lookup.md +145 -0
- data/dciy.toml +3 -0
- data/dciy_prepare.sh +8 -0
- data/dciy_run.sh +10 -0
- data/hyper-react.gemspec +24 -18
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
- data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
- data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
- data/lib/hyper-react.rb +66 -4
- data/lib/rails-helpers/top_level_rails_component.rb +75 -0
- data/lib/react/api.rb +203 -0
- data/lib/react/callbacks.rb +41 -0
- data/lib/react/children.rb +30 -0
- data/lib/react/component.rb +177 -0
- data/lib/react/component/api.rb +69 -0
- data/lib/react/component/base.rb +13 -0
- data/lib/react/component/class_methods.rb +181 -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 +78 -0
- data/lib/react/component/should_component_update.rb +99 -0
- data/lib/react/component/tags.rb +108 -0
- data/lib/react/config.rb +5 -0
- data/lib/react/config/client.rb.erb +19 -0
- data/lib/react/config/server.rb +23 -0
- data/lib/react/element.rb +150 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/opal-jquery/element.rb +26 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/hash.rb +13 -0
- data/lib/react/native_library.rb +87 -0
- data/lib/react/object.rb +15 -0
- data/lib/react/react-source-browser.rb +3 -0
- data/lib/react/react-source-server.rb +3 -0
- data/lib/react/react-source.rb +16 -0
- data/lib/react/ref_callback.rb +31 -0
- data/lib/react/rendering_context.rb +146 -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/top_level.rb +110 -0
- data/lib/react/top_level_render.rb +28 -0
- data/lib/react/validator.rb +136 -0
- data/lib/reactive-ruby/component_loader.rb +43 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +235 -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 +15 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/lib/reactrb/auto-import.rb +27 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- data/spec/controller_helper_spec.rb +35 -0
- data/spec/index.html.erb +11 -0
- data/spec/react/callbacks_spec.rb +142 -0
- data/spec/react/children_spec.rb +132 -0
- data/spec/react/component/base_spec.rb +36 -0
- data/spec/react/component_spec.rb +1073 -0
- data/spec/react/dsl_spec.rb +323 -0
- data/spec/react/element_spec.rb +132 -0
- data/spec/react/event_spec.rb +39 -0
- data/spec/react/native_library_spec.rb +387 -0
- data/spec/react/observable_spec.rb +31 -0
- data/spec/react/opal_jquery_extensions_spec.rb +68 -0
- data/spec/react/param_declaration_spec.rb +253 -0
- data/spec/react/react_spec.rb +278 -0
- data/spec/react/refs_callback_spec.rb +65 -0
- data/spec/react/server_spec.rb +25 -0
- data/spec/react/state_spec.rb +52 -0
- data/spec/react/test/dsl_spec.rb +43 -0
- data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
- data/spec/react/test/rspec_spec.rb +62 -0
- data/spec/react/test/session_spec.rb +88 -0
- data/spec/react/test/utils_spec.rb +28 -0
- data/spec/react/top_level_component_spec.rb +103 -0
- data/spec/react/tutorial/tutorial_spec.rb +42 -0
- data/spec/react/validator_spec.rb +134 -0
- data/spec/reactive-ruby/component_loader_spec.rb +74 -0
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
- data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
- data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/test_app/README.md +24 -0
- data/spec/test_app/Rakefile +6 -0
- data/spec/test_app/app/assets/config/manifest.js +3 -0
- data/spec/test_app/app/assets/images/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/application.rb +7 -0
- data/spec/test_app/app/assets/javascripts/cable.js +13 -0
- data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
- data/spec/test_app/app/assets/stylesheets/application.css +15 -0
- data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
- data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
- data/spec/test_app/app/controllers/application_controller.rb +3 -0
- data/spec/test_app/app/controllers/concerns/.keep +0 -0
- data/spec/test_app/app/helpers/application_helper.rb +2 -0
- data/spec/test_app/app/jobs/application_job.rb +2 -0
- data/spec/test_app/app/mailers/application_mailer.rb +4 -0
- data/spec/test_app/app/models/application_record.rb +3 -0
- data/spec/test_app/app/models/concerns/.keep +0 -0
- data/spec/test_app/app/views/components.rb +11 -0
- data/spec/test_app/app/views/components/hello_world.rb +11 -0
- data/spec/test_app/app/views/components/todo.rb +14 -0
- data/spec/test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
- data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
- data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
- data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
- data/spec/test_app/bin/bundle +3 -0
- data/spec/test_app/bin/rails +4 -0
- data/spec/test_app/bin/rake +4 -0
- data/spec/test_app/bin/setup +38 -0
- data/spec/test_app/bin/update +29 -0
- data/spec/test_app/bin/yarn +11 -0
- data/spec/test_app/config.ru +5 -0
- data/spec/test_app/config/application.rb +45 -0
- data/spec/test_app/config/boot.rb +6 -0
- data/spec/test_app/config/cable.yml +10 -0
- data/spec/test_app/config/database.yml +25 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +54 -0
- data/spec/test_app/config/environments/production.rb +91 -0
- data/spec/test_app/config/environments/test.rb +42 -0
- data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/test_app/config/initializers/assets.rb +14 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
- data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/test_app/config/initializers/inflections.rb +16 -0
- data/spec/test_app/config/initializers/mime_types.rb +4 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/locales/en.yml +33 -0
- data/spec/test_app/config/puma.rb +56 -0
- data/spec/test_app/config/routes.rb +3 -0
- data/spec/test_app/config/secrets.yml +32 -0
- data/spec/test_app/config/spring.rb +6 -0
- data/spec/test_app/db/development.sqlite3 +0 -0
- data/spec/test_app/db/schema.rb +15 -0
- data/spec/test_app/db/seeds.rb +7 -0
- data/spec/test_app/db/test.sqlite3 +0 -0
- data/spec/test_app/lib/assets/.keep +0 -0
- data/spec/test_app/log/.keep +0 -0
- data/spec/test_app/package.json +5 -0
- data/spec/test_app/public/404.html +67 -0
- data/spec/test_app/public/422.html +67 -0
- data/spec/test_app/public/500.html +66 -0
- data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/test_app/public/apple-touch-icon.png +0 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/vendor/es5-shim.min.js +7 -0
- data/spec/vendor/jquery-2.2.4.min.js +4 -0
- metadata +401 -61
- data/CODE_OF_CONDUCT.md +0 -49
- data/lib/react/version.rb +0 -3
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'hyperloop-config'
|
2
|
+
Hyperloop::Context
|
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)
|
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
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def callbacks_for(callback_name)
|
32
|
+
wrapper_name = "_#{callback_name}_callbacks"
|
33
|
+
if superclass.respond_to? :callbacks_for
|
34
|
+
superclass.callbacks_for(callback_name)
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end + send(wrapper_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module React
|
2
|
+
class Children
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(children)
|
6
|
+
@children = children
|
7
|
+
end
|
8
|
+
|
9
|
+
def each(&block)
|
10
|
+
return to_enum(__callee__) { length } unless block_given?
|
11
|
+
return [] unless length > 0
|
12
|
+
collection = []
|
13
|
+
%x{
|
14
|
+
React.Children.forEach(#{@children}, function(context){
|
15
|
+
#{
|
16
|
+
element = React::Element.new(`context`)
|
17
|
+
block.call(element)
|
18
|
+
collection << element
|
19
|
+
}
|
20
|
+
})
|
21
|
+
}
|
22
|
+
collection
|
23
|
+
end
|
24
|
+
|
25
|
+
def length
|
26
|
+
@length ||= `React.Children.count(#{@children})`
|
27
|
+
end
|
28
|
+
alias_method :size, :length
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,177 @@
|
|
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
|
+
require 'native'
|
12
|
+
|
13
|
+
module Hyperloop
|
14
|
+
class Component
|
15
|
+
module Mixin
|
16
|
+
def self.included(base)
|
17
|
+
base.include(Hyperloop::Store::Mixin)
|
18
|
+
base.include(React::Component::API)
|
19
|
+
base.include(React::Callbacks)
|
20
|
+
base.include(React::Component::Tags)
|
21
|
+
base.include(React::Component::DslInstanceMethods)
|
22
|
+
base.include(React::Component::ShouldComponentUpdate)
|
23
|
+
base.class_eval do
|
24
|
+
class_attribute :initial_state
|
25
|
+
define_callback :before_mount
|
26
|
+
define_callback :after_mount
|
27
|
+
define_callback :before_receive_props
|
28
|
+
define_callback :before_update
|
29
|
+
define_callback :after_update
|
30
|
+
define_callback :before_unmount
|
31
|
+
define_callback :after_error
|
32
|
+
end
|
33
|
+
base.extend(React::Component::ClassMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.deprecation_warning(message)
|
37
|
+
React::Component.deprecation_warning(name, message)
|
38
|
+
end
|
39
|
+
|
40
|
+
def deprecation_warning(message)
|
41
|
+
React::Component.deprecation_warning(self.class.name, message)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(native_element)
|
45
|
+
@native = native_element
|
46
|
+
init_store
|
47
|
+
end
|
48
|
+
|
49
|
+
def emit(event_name, *args)
|
50
|
+
if React::Event::BUILT_IN_EVENTS.include?(built_in_event_name = "on#{event_name.to_s.event_camelize}")
|
51
|
+
params[built_in_event_name].call(*args)
|
52
|
+
else
|
53
|
+
params["on_#{event_name}"].call(*args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def component_will_mount
|
58
|
+
React::IsomorphicHelpers.load_context(true) if React::IsomorphicHelpers.on_opal_client?
|
59
|
+
React::State.set_state_context_to(self) { run_callback(:before_mount) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def component_did_mount
|
63
|
+
React::State.set_state_context_to(self) do
|
64
|
+
run_callback(:after_mount)
|
65
|
+
React::State.update_states_to_observe
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def component_will_receive_props(next_props)
|
70
|
+
# need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
|
71
|
+
# for now we are just using it to clear processed_params
|
72
|
+
React::State.set_state_context_to(self) { self.run_callback(:before_receive_props, next_props) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def component_will_update(next_props, next_state)
|
76
|
+
React::State.set_state_context_to(self) { self.run_callback(:before_update, next_props, next_state) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def component_did_update(prev_props, prev_state)
|
80
|
+
React::State.set_state_context_to(self) do
|
81
|
+
self.run_callback(:after_update, prev_props, prev_state)
|
82
|
+
React::State.update_states_to_observe
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def component_will_unmount
|
87
|
+
React::State.set_state_context_to(self) do
|
88
|
+
self.run_callback(:before_unmount)
|
89
|
+
React::State.remove
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def component_did_catch(error, info)
|
94
|
+
React::State.set_state_context_to(self) do
|
95
|
+
if self.class.callbacks_for(:after_error) == []
|
96
|
+
if `typeof error.$backtrace === "function"`
|
97
|
+
`console.error(error.$backtrace().$join("\n"))`
|
98
|
+
else
|
99
|
+
`console.error(error, info)`
|
100
|
+
end
|
101
|
+
else
|
102
|
+
self.run_callback(:after_error, error, info)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_reader :waiting_on_resources
|
108
|
+
|
109
|
+
def update_react_js_state(object, name, value)
|
110
|
+
if object
|
111
|
+
name = "#{object.class}.#{name}" unless object == self
|
112
|
+
# Date.now() has only millisecond precision, if several notifications of
|
113
|
+
# observer happen within a millisecond, updates may get lost.
|
114
|
+
# to mitigate this the Math.random() appends some random number
|
115
|
+
# this way notifactions will happen as expected by the rest of hyperloop
|
116
|
+
set_state(
|
117
|
+
'***_state_updated_at-***' => `Date.now() + Math.random()`,
|
118
|
+
name => value
|
119
|
+
)
|
120
|
+
else
|
121
|
+
set_state name => value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def set_state_synchronously?
|
126
|
+
@native.JS[:__opalInstanceSyncSetState]
|
127
|
+
end
|
128
|
+
|
129
|
+
def render
|
130
|
+
raise 'no render defined'
|
131
|
+
end unless method_defined?(:render)
|
132
|
+
|
133
|
+
def _render_wrapper
|
134
|
+
React::State.set_state_context_to(self, true) do
|
135
|
+
element = React::RenderingContext.render(nil) { render || '' }
|
136
|
+
@waiting_on_resources =
|
137
|
+
element.waiting_on_resources if element.respond_to? :waiting_on_resources
|
138
|
+
element
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def watch(value, &on_change)
|
143
|
+
Observable.new(value, on_change)
|
144
|
+
end
|
145
|
+
|
146
|
+
def define_state(*args, &block)
|
147
|
+
React::State.initialize_states(self, self.class.define_state(*args, &block))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
module React
|
154
|
+
module Component
|
155
|
+
def self.included(base)
|
156
|
+
# note this is turned off during old style testing: See the spec_helper
|
157
|
+
deprecation_warning base, "The module name React::Component has been deprecated. Use Hyperloop::Component::Mixin instead."
|
158
|
+
base.include Hyperloop::Component::Mixin
|
159
|
+
end
|
160
|
+
def self.deprecation_warning(name, message)
|
161
|
+
@deprecation_messages ||= []
|
162
|
+
message = "Warning: Deprecated feature used in #{name}. #{message}"
|
163
|
+
unless @deprecation_messages.include? message
|
164
|
+
@deprecation_messages << message
|
165
|
+
React::IsomorphicHelpers.log message, :warning
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
module ComponentNoNotice
|
170
|
+
def self.included(base)
|
171
|
+
base.include Hyperloop::Component::Mixin
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
module React
|
177
|
+
end
|
@@ -0,0 +1,69 @@
|
|
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
|
+
end
|
15
|
+
|
16
|
+
def set_props(prop, &block)
|
17
|
+
raise "set_props: setProps() is no longer supported by react"
|
18
|
+
end
|
19
|
+
alias :set_props! :set_props
|
20
|
+
|
21
|
+
def set_state(state, &block)
|
22
|
+
set_or_replace_state_or_prop(state, 'setState', &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_state!(state, &block)
|
26
|
+
set_or_replace_state_or_prop(state, 'setState', &block)
|
27
|
+
`#{self}.native.forceUpdate()`
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def set_or_replace_state_or_prop(state_or_prop, method, &block)
|
33
|
+
raise "No native ReactComponent associated" unless @native
|
34
|
+
`var state_prop_n = #{state_or_prop.shallow_to_n}`
|
35
|
+
# the state object is initalized when the ruby component is instanciated
|
36
|
+
# this is detected by self.native.__opalInstanceInitializedState
|
37
|
+
# which is set in the netive component constructor in react/api.rb
|
38
|
+
# the setState update callback is not called when initalizing initial state
|
39
|
+
if block
|
40
|
+
%x{
|
41
|
+
if (#{@native}.__opalInstanceInitializedState === true) {
|
42
|
+
#{@native}[method](state_prop_n, function(){
|
43
|
+
#{block.call}
|
44
|
+
});
|
45
|
+
} else {
|
46
|
+
for (var sp in state_prop_n) {
|
47
|
+
if (state_prop_n.hasOwnProperty(sp)) {
|
48
|
+
#{@native}.state[sp] = state_prop_n[sp];
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
else
|
54
|
+
%x{
|
55
|
+
if (#{@native}.__opalInstanceInitializedState === true) {
|
56
|
+
#{@native}[method](state_prop_n);
|
57
|
+
} else {
|
58
|
+
for (var sp in state_prop_n) {
|
59
|
+
if (state_prop_n.hasOwnProperty(sp)) {
|
60
|
+
#{@native}.state[sp] = state_prop_n[sp];
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
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,181 @@
|
|
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.allow_undefined_props = true
|
96
|
+
validator_in_lexical_scope = validator
|
97
|
+
props_wrapper.define_method(name) do
|
98
|
+
@_all_others ||= validator_in_lexical_scope.undefined_props(props)
|
99
|
+
end
|
100
|
+
|
101
|
+
validator_in_lexial_scope = validator
|
102
|
+
props_wrapper.define_method(name) do
|
103
|
+
@_all_others ||= validator_in_lexial_scope.undefined_props(props)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def define_state(*states, &block)
|
108
|
+
deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
|
109
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
110
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
111
|
+
states.each { |name| state(name => default_initial_value) } # was states_hash[name] = default_initial_value
|
112
|
+
states_hash.each { |name, value| state(name => value) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def export_state(*states, &block)
|
116
|
+
deprecation_warning "'export_state' is deprecated. Use the 'state' macro to declare states."
|
117
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
118
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
119
|
+
states.each { |name| states_hash[name] = default_initial_value }
|
120
|
+
states_hash.each do |name, value|
|
121
|
+
state(name => value, scope: :class, reader: true)
|
122
|
+
singleton_class.define_method("#{name}!") do |*args|
|
123
|
+
mutate.__send__(name, *args)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def native_mixin(item)
|
129
|
+
native_mixins << item
|
130
|
+
end
|
131
|
+
|
132
|
+
def native_mixins
|
133
|
+
@native_mixins ||= []
|
134
|
+
end
|
135
|
+
|
136
|
+
def static_call_back(name, &block)
|
137
|
+
static_call_backs[name] = block
|
138
|
+
end
|
139
|
+
|
140
|
+
def static_call_backs
|
141
|
+
@static_call_backs ||= {}
|
142
|
+
end
|
143
|
+
|
144
|
+
def export_component(opts = {})
|
145
|
+
export_name = (opts[:as] || name).split('::')
|
146
|
+
first_name = export_name.first
|
147
|
+
Native(`Opal.global`)[first_name] = add_item_to_tree(
|
148
|
+
Native(`Opal.global`)[first_name],
|
149
|
+
[React::API.create_native_react_class(self)] + export_name[1..-1].reverse
|
150
|
+
).to_n
|
151
|
+
end
|
152
|
+
|
153
|
+
def imports(component_name)
|
154
|
+
React::API.import_native_component(
|
155
|
+
self, React::API.eval_native_react_component(component_name)
|
156
|
+
)
|
157
|
+
define_method(:render) {} # define a dummy render method - will never be called...
|
158
|
+
rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
|
159
|
+
raise "#{self} cannot import '#{component_name}': #{e.message}."
|
160
|
+
# rubocop:enable Lint/RescueException
|
161
|
+
ensure
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_item_to_tree(current_tree, new_item)
|
166
|
+
if Native(current_tree).class != Native::Object || new_item.length == 1
|
167
|
+
new_item.inject { |a, e| { e => a } }
|
168
|
+
else
|
169
|
+
Native(current_tree)[new_item.last] = add_item_to_tree(
|
170
|
+
Native(current_tree)[new_item.last], new_item[0..-2]
|
171
|
+
)
|
172
|
+
current_tree
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_n
|
177
|
+
React::API.class_eval('@@component_classes')[self]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|