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,23 @@
|
|
1
|
+
require "react/children"
|
2
|
+
|
3
|
+
module React
|
4
|
+
module Component
|
5
|
+
module DslInstanceMethods
|
6
|
+
def children
|
7
|
+
Children.new(`#{@native}.props.children`)
|
8
|
+
end
|
9
|
+
|
10
|
+
def params
|
11
|
+
@params ||= self.class.props_wrapper.new(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def props
|
15
|
+
Hash.new(`#{@native}.props`)
|
16
|
+
end
|
17
|
+
|
18
|
+
def refs
|
19
|
+
Hash.new(`#{@native}.refs`)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
|
4
|
+
class PropsWrapper
|
5
|
+
attr_reader :component
|
6
|
+
|
7
|
+
def self.define_param(name, param_type)
|
8
|
+
if param_type == Observable
|
9
|
+
define_method("#{name}") do
|
10
|
+
value_for(name)
|
11
|
+
end
|
12
|
+
define_method("#{name}!") do |*args|
|
13
|
+
current_value = value_for(name)
|
14
|
+
if args.count > 0
|
15
|
+
props[name].call args[0]
|
16
|
+
current_value
|
17
|
+
else
|
18
|
+
# rescue in case we in middle of render... What happens during a
|
19
|
+
# render that causes exception?
|
20
|
+
# Where does `dont_update_state` come from?
|
21
|
+
props[name].call current_value unless @dont_update_state rescue nil
|
22
|
+
props[name]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
elsif param_type == Proc
|
26
|
+
define_method("#{name}") do |*args, &block|
|
27
|
+
props[name].call(*args, &block) if props[name]
|
28
|
+
end
|
29
|
+
else
|
30
|
+
define_method("#{name}") do
|
31
|
+
fetch_from_cache(name) do
|
32
|
+
if param_type.respond_to? :_react_param_conversion
|
33
|
+
param_type._react_param_conversion props[name], nil
|
34
|
+
elsif param_type.is_a?(Array) &&
|
35
|
+
param_type[0].respond_to?(:_react_param_conversion)
|
36
|
+
props[name].collect do |param|
|
37
|
+
param_type[0]._react_param_conversion param, nil
|
38
|
+
end
|
39
|
+
else
|
40
|
+
props[name]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.define_all_others(name)
|
48
|
+
define_method("#{name}") do
|
49
|
+
@_all_others_cache ||= yield(props)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def initialize(component)
|
55
|
+
@component = component
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](prop)
|
59
|
+
props[prop]
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def _reset_all_others_cache
|
64
|
+
@_all_others_cache = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def fetch_from_cache(name)
|
70
|
+
last, value = cache[name]
|
71
|
+
return value if last.equal?(props[name])
|
72
|
+
yield.tap do |value|
|
73
|
+
cache[name] = [props[name], value]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def cache
|
78
|
+
@cache ||= Hash.new { |h, k| h[k] = [] }
|
79
|
+
end
|
80
|
+
|
81
|
+
def props
|
82
|
+
component.props
|
83
|
+
end
|
84
|
+
|
85
|
+
def value_for(name)
|
86
|
+
self[name].instance_variable_get("@value") if self[name]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
#
|
4
|
+
# React assumes all components should update, unless a component explicitly overrides
|
5
|
+
# the shouldComponentUpdate method. Reactrb does an explicit check doing a shallow
|
6
|
+
# compare of params, and using a timestamp to determine if state has changed.
|
7
|
+
|
8
|
+
# If needed components can provide their own #needs_update? method which will be
|
9
|
+
# passed the next params and state opal hashes.
|
10
|
+
|
11
|
+
# Attached to these hashes is a #changed? method that returns whether the hash contains
|
12
|
+
# changes as calculated by the base mechanism. This way implementations of #needs_update?
|
13
|
+
# can use the base comparison mechanism as needed.
|
14
|
+
|
15
|
+
# For example
|
16
|
+
# def needs_update?(next_params, next_state)
|
17
|
+
# # use a special comparison method
|
18
|
+
# return false if next_state.changed? || next_params.changed?
|
19
|
+
# # do some other special checks
|
20
|
+
# end
|
21
|
+
|
22
|
+
# Note that beginning in 0.9 we will use standard ruby compare on all params further reducing
|
23
|
+
# the need for needs_update?
|
24
|
+
#
|
25
|
+
module ShouldComponentUpdate
|
26
|
+
def should_component_update?(next_props, next_state)
|
27
|
+
State.set_state_context_to(self, false) do
|
28
|
+
# rubocop:disable Style/DoubleNegation # we must return true/false to js land
|
29
|
+
if respond_to?(:needs_update?)
|
30
|
+
!!call_needs_update(next_props, next_state)
|
31
|
+
else
|
32
|
+
(props_changed?(next_props) || native_state_changed?(next_state))
|
33
|
+
end
|
34
|
+
# rubocop:enable Style/DoubleNegation
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# create opal hashes for next params and state, and attach
|
39
|
+
# the changed? method to each hash
|
40
|
+
|
41
|
+
def call_needs_update(next_params, next_state)
|
42
|
+
component = self
|
43
|
+
next_params.define_singleton_method(:changed?) do
|
44
|
+
component.props_changed?(self)
|
45
|
+
end
|
46
|
+
next_state.define_singleton_method(:changed?) do
|
47
|
+
component.native_state_changed?(next_state)
|
48
|
+
end
|
49
|
+
needs_update?(next_params, next_state)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Whenever state changes, reactrb updates a timestamp on the state object.
|
53
|
+
# We can rapidly check for state changes comparing the incoming state time_stamp
|
54
|
+
# with the current time stamp.
|
55
|
+
|
56
|
+
# we receive a Opal Ruby Hash here, always, so the Hash is either empty or filled
|
57
|
+
# Hash is converted to native object
|
58
|
+
# if the Hash was empty, the Object has no keys
|
59
|
+
|
60
|
+
# Different versions of react treat empty state differently, so we first
|
61
|
+
# convert anything that looks like an empty state to "false" for consistency.
|
62
|
+
|
63
|
+
# Then we test if one state is empty and the other is not, then we return false.
|
64
|
+
# Then we test if both states are empty we return true.
|
65
|
+
# If either state does not have a time stamp then we have to assume a change.
|
66
|
+
# Otherwise we check time stamps
|
67
|
+
|
68
|
+
# rubocop:disable Metrics/MethodLength # for effeciency we want this to be one method
|
69
|
+
def native_state_changed?(next_state_hash)
|
70
|
+
# next_state = next_state_hash.to_n
|
71
|
+
# %x{
|
72
|
+
# var current_state = #{@native}.state
|
73
|
+
# var normalized_next_state =
|
74
|
+
# !next_state || Object.keys(next_state).length === 0 ? false : next_state
|
75
|
+
# var normalized_current_state =
|
76
|
+
# !current_state || Object.keys(current_state).length === 0 ? false : current_state
|
77
|
+
# if (!normalized_current_state != !normalized_next_state) return(true)
|
78
|
+
# if (!normalized_current_state && !normalized_next_state) return(false)
|
79
|
+
# if (!normalized_current_state['***_state_updated_at-***'] &&
|
80
|
+
# !normalized_next_state['***_state_updated_at-***']) return(false)
|
81
|
+
# if (!normalized_current_state['***_state_updated_at-***'] ||
|
82
|
+
# !normalized_next_state['***_state_updated_at-***']) return(true)
|
83
|
+
# return (normalized_current_state['***_state_updated_at-***'] !=
|
84
|
+
# normalized_next_state['***_state_updated_at-***'])
|
85
|
+
# }
|
86
|
+
state_hash = Hash.new(`#{@native}.state`)
|
87
|
+
next_state_hash != state_hash
|
88
|
+
end
|
89
|
+
# rubocop:enable Metrics/MethodLength
|
90
|
+
|
91
|
+
# Do a shallow compare on the two hashes. Starting in 0.9 we will do a deep compare. ???
|
92
|
+
|
93
|
+
def props_changed?(next_props)
|
94
|
+
props = Hash.new(`#{@native}.props`)
|
95
|
+
next_props != props
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
# contains the name of all HTML tags, and the mechanism to register a component
|
4
|
+
# class as a new tag
|
5
|
+
module Tags
|
6
|
+
HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
|
7
|
+
button canvas caption cite code col colgroup data datalist dd del details dfn
|
8
|
+
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
9
|
+
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
10
|
+
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
11
|
+
output p param picture pre progress q rp rt ruby s samp script section select
|
12
|
+
small source span strong style sub summary sup table tbody td textarea tfoot th
|
13
|
+
thead time title tr track u ul var video wbr) +
|
14
|
+
# The SVG Tags
|
15
|
+
%w(circle clipPath defs ellipse g line linearGradient mask path pattern polygon polyline
|
16
|
+
radialGradient rect stop svg text tspan)
|
17
|
+
|
18
|
+
# the present method is retained as a legacy behavior
|
19
|
+
def present(component, *params, &children)
|
20
|
+
React::RenderingContext.render(component, *params, &children)
|
21
|
+
end
|
22
|
+
|
23
|
+
# define each predefined tag (upcase) as an instance method and a constant
|
24
|
+
# deprecated: define each predefined tag (downcase) as the alias of the instance method
|
25
|
+
|
26
|
+
HTML_TAGS.each do |tag|
|
27
|
+
|
28
|
+
define_method(tag.upcase) do |*params, &children|
|
29
|
+
React::RenderingContext.render(tag, *params, &children)
|
30
|
+
end
|
31
|
+
|
32
|
+
const_set tag.upcase, tag
|
33
|
+
|
34
|
+
# deprecated: remove
|
35
|
+
if tag == 'p'
|
36
|
+
define_method(tag) do |*params, &children|
|
37
|
+
if children || params.count == 0 || (params.count == 1 && params.first.is_a?(Hash))
|
38
|
+
React::RenderingContext.render(tag, *params, &children)
|
39
|
+
else
|
40
|
+
Kernel.p(*params)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
else
|
44
|
+
alias_method tag, tag.upcase
|
45
|
+
end
|
46
|
+
# end of deprecated code
|
47
|
+
end
|
48
|
+
|
49
|
+
# this is used for haml style (i.e. DIV.foo.bar) class tags which is deprecated
|
50
|
+
def self.html_tag_class_for(tag)
|
51
|
+
downcased_tag = tag.downcase
|
52
|
+
if tag =~ /[A-Z]+/ && HTML_TAGS.include?(downcased_tag)
|
53
|
+
Object.const_set tag, React.create_element(downcased_tag)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# use method_missing to look up component names in the form of "Foo(..)"
|
58
|
+
# where there is no preceeding scope.
|
59
|
+
|
60
|
+
def method_missing(name, *params, &children)
|
61
|
+
component = find_component(name)
|
62
|
+
return React::RenderingContext.render(component, *params, &children) if component
|
63
|
+
Object.method_missing(name, *params, &children)
|
64
|
+
end
|
65
|
+
|
66
|
+
# install methods with the same name as the component in the parent class/module
|
67
|
+
# thus component names in the form Foo::Bar(...) will work
|
68
|
+
|
69
|
+
class << self
|
70
|
+
def included(component)
|
71
|
+
name, parent = find_name_and_parent(component)
|
72
|
+
tag_names_module = Module.new do
|
73
|
+
define_method name do |*params, &children|
|
74
|
+
React::RenderingContext.render(component, *params, &children)
|
75
|
+
end
|
76
|
+
# handle deprecated _as_node style
|
77
|
+
define_method "#{name}_as_node" do |*params, &children|
|
78
|
+
React::RenderingContext.build_only(component, *params, &children)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
parent.extend(tag_names_module)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def find_name_and_parent(component)
|
87
|
+
split_name = component.name && component.name.split('::')
|
88
|
+
if split_name && split_name.length > 1
|
89
|
+
[split_name.last, split_name.inject([Module]) { |a, e| a + [a.last.const_get(e)] }[-2]]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def find_component(name)
|
97
|
+
component = lookup_const(name)
|
98
|
+
if component && !component.method_defined?(:render)
|
99
|
+
raise "#{name} does not appear to be a react component."
|
100
|
+
end
|
101
|
+
component
|
102
|
+
end
|
103
|
+
|
104
|
+
def lookup_const(name)
|
105
|
+
return nil unless name =~ /^[A-Z]/
|
106
|
+
#html_tag = React::Component::Tags.html_tag_class(name)
|
107
|
+
#return html_tag if html_tag
|
108
|
+
scopes = self.class.name.to_s.split('::').inject([Module]) do |nesting, next_const|
|
109
|
+
nesting + [nesting.last.const_get(next_const)]
|
110
|
+
end.reverse
|
111
|
+
scope = scopes.detect { |s| s.const_defined?(name) }
|
112
|
+
scope.const_get(name) if scope
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/react/config.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'react/ext/string'
|
2
|
+
|
3
|
+
module React
|
4
|
+
#
|
5
|
+
# Wraps the React Native element class
|
6
|
+
#
|
7
|
+
# adds the #on method to add event handlers to the element
|
8
|
+
#
|
9
|
+
# adds the #render method to place elements in the DOM and
|
10
|
+
# #delete (alias/deprecated #as_node) method to remove elements from the DOM
|
11
|
+
#
|
12
|
+
# handles the haml style class notation so that
|
13
|
+
# div.bar.blat becomes div(class: "bar blat")
|
14
|
+
# by using method missing
|
15
|
+
#
|
16
|
+
class Element
|
17
|
+
include Native
|
18
|
+
|
19
|
+
alias_native :element_type, :type
|
20
|
+
alias_native :props, :props
|
21
|
+
|
22
|
+
attr_reader :type
|
23
|
+
attr_reader :properties
|
24
|
+
attr_reader :block
|
25
|
+
|
26
|
+
attr_accessor :waiting_on_resources
|
27
|
+
|
28
|
+
def initialize(native_element, type = nil, properties = {}, block = nil)
|
29
|
+
@type = type
|
30
|
+
@properties = (`typeof #{properties} === 'undefined'` ? nil : properties) || {}
|
31
|
+
@block = block
|
32
|
+
@native = native_element
|
33
|
+
end
|
34
|
+
|
35
|
+
# Attach event handlers.
|
36
|
+
|
37
|
+
def on(*event_names, &block)
|
38
|
+
event_names.each { |event_name| merge_event_prop!(event_name, &block) }
|
39
|
+
@native = `React.cloneElement(#{@native}, #{@properties.shallow_to_n})`
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Render element into DOM in the current rendering context.
|
44
|
+
# Used for elements that are not yet in DOM, i.e. they are provided as children
|
45
|
+
# or they have been explicitly removed from the rendering context using the delete method.
|
46
|
+
|
47
|
+
def render(props = {}, &new_block)
|
48
|
+
if props.empty?
|
49
|
+
React::RenderingContext.render(self)
|
50
|
+
else
|
51
|
+
props = API.convert_props(props)
|
52
|
+
React::RenderingContext.render(
|
53
|
+
Element.new(`React.cloneElement(#{@native}, #{props.shallow_to_n})`,
|
54
|
+
type, @properties.merge(props), block),
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Delete (remove) element from rendering context, the element may later be added back in
|
60
|
+
# using the render method.
|
61
|
+
|
62
|
+
def delete
|
63
|
+
React::RenderingContext.delete(self)
|
64
|
+
end
|
65
|
+
# Deprecated version of delete method
|
66
|
+
alias as_node delete
|
67
|
+
|
68
|
+
# Any other method applied to an element will be treated as class name (haml style) thus
|
69
|
+
# div.foo.bar(id: :fred) is the same as saying div(class: "foo bar", id: :fred)
|
70
|
+
#
|
71
|
+
# single underscores become dashes, and double underscores become a single underscore
|
72
|
+
#
|
73
|
+
# params may be provide to each class (but typically only to the last for easy reading.)
|
74
|
+
|
75
|
+
def method_missing(class_name, args = {}, &new_block)
|
76
|
+
return dup.render.method_missing(class_name, args, &new_block) unless rendered?
|
77
|
+
React::RenderingContext.replace(
|
78
|
+
self,
|
79
|
+
RenderingContext.build do
|
80
|
+
RenderingContext.render(type, build_new_properties(class_name, args), &new_block)
|
81
|
+
end
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def rendered?
|
86
|
+
React::RenderingContext.rendered? self
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.haml_class_name(class_name)
|
90
|
+
class_name.gsub(/__|_/, '__' => '_', '_' => '-')
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def build_new_properties(class_name, args)
|
96
|
+
class_name = self.class.haml_class_name(class_name)
|
97
|
+
new_props = @properties.dup
|
98
|
+
new_props[:className] = "\
|
99
|
+
#{class_name} #{new_props[:className]} #{args.delete(:class)} #{args.delete(:className)}\
|
100
|
+
".split(' ').uniq.join(' ')
|
101
|
+
new_props.merge! args
|
102
|
+
end
|
103
|
+
|
104
|
+
# built in events, events going to native components, and events going to reactrb
|
105
|
+
|
106
|
+
# built in events will have their event param translated to the Event wrapper
|
107
|
+
# and the name will camelcased and have on prefixed, so :click becomes onClick.
|
108
|
+
#
|
109
|
+
# events emitting from native components are assumed to have the same camel case and
|
110
|
+
# on prefixed.
|
111
|
+
#
|
112
|
+
# events emitting from reactrb components will just have on_ prefixed. So
|
113
|
+
# :play_button_pushed attaches to the :on_play_button_pushed param
|
114
|
+
#
|
115
|
+
# in all cases the default name convention can be overriden by wrapping in <...> brackets.
|
116
|
+
# So on("<MyEvent>") will attach to the "MyEvent" param.
|
117
|
+
|
118
|
+
def merge_event_prop!(event_name, &block)
|
119
|
+
if event_name =~ /^<(.+)>$/
|
120
|
+
merge_component_event_prop! event_name.gsub(/^<(.+)>$/, '\1'), &block
|
121
|
+
elsif React::Event::BUILT_IN_EVENTS.include?(name = "on#{event_name.event_camelize}")
|
122
|
+
merge_built_in_event_prop! name, &block
|
123
|
+
elsif @type.instance_variable_get('@native_import')
|
124
|
+
merge_component_event_prop! name, &block
|
125
|
+
else
|
126
|
+
merge_component_event_prop! "on_#{event_name}", &block
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def merge_built_in_event_prop!(prop_name)
|
131
|
+
@properties.merge!(
|
132
|
+
prop_name => %x{
|
133
|
+
function(){
|
134
|
+
var react_event = arguments[0];
|
135
|
+
var all_args;
|
136
|
+
var other_args;
|
137
|
+
if (arguments.length > 1) {
|
138
|
+
all_args = Array.prototype.slice.call(arguments);
|
139
|
+
other_args = all_args.slice(1, arguments.length);
|
140
|
+
return #{yield(React::Event.new(`react_event`), *(`other_args`))};
|
141
|
+
} else {
|
142
|
+
return #{yield(React::Event.new(`react_event`))};
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
def merge_component_event_prop!(prop_name)
|
150
|
+
@properties.merge!(
|
151
|
+
prop_name => %x{
|
152
|
+
function(){
|
153
|
+
return #{yield(*Array(`arguments`))}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|