react-opal 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/react-opal.rb +15 -0
- data/lib/react/opal/callbacks.rb +35 -0
- data/lib/react/opal/component.rb +221 -0
- data/lib/react/opal/component/api.rb +29 -0
- data/lib/react/opal/component_factory.rb +78 -0
- data/lib/react/opal/event.rb +76 -0
- data/lib/react/opal/ext/hash.rb +9 -0
- data/lib/react/opal/native_element.rb +52 -0
- data/lib/react/opal/props_children.rb +50 -0
- data/lib/react/opal/testing.rb +14 -0
- data/lib/react/opal/top_level.rb +101 -0
- data/lib/react/opal/validator.rb +69 -0
- data/lib/react/opal/version.rb +3 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bbee40ce70c05f1c8be8904a0516736a898e0965
|
4
|
+
data.tar.gz: 3cb8fa21df7d35d38041b72ba48354e4f0bdf183
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a8506067fcb72df4a5c1b1160229a209bec151a37c3b36e932a1ea27ad27d02d08aafc88d7b8f5ece44e2a8e34cecdc4629500930ed20e2179c58e392206719
|
7
|
+
data.tar.gz: c88db5dc1b1a919992ed9d5aafae113b5ca62c75b00e36687aff4ae72172e2137e4765129263f2a8daf46894cee33292700e5a51afbe9bac1ba1c7a6fea41c3b
|
data/lib/react-opal.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
if RUBY_ENGINE == 'opal'
|
2
|
+
require 'react/opal/top_level'
|
3
|
+
require 'react/opal/props_children'
|
4
|
+
require 'react/opal/component'
|
5
|
+
require 'react/opal/native_element'
|
6
|
+
require 'react/opal/event'
|
7
|
+
require 'react/opal/component_factory'
|
8
|
+
require 'react/opal/validator'
|
9
|
+
else
|
10
|
+
require 'opal'
|
11
|
+
require 'react/opal/version'
|
12
|
+
require 'opal-activesupport'
|
13
|
+
|
14
|
+
Opal.append_path File.expand_path('../', __FILE__).untaint
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
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
|
+
attribute_name = "_#{name}_callbacks"
|
11
|
+
callbacks = self.class.send(attribute_name)
|
12
|
+
callbacks.each do |callback|
|
13
|
+
if callback.is_a?(Proc)
|
14
|
+
instance_exec(*args, &callback)
|
15
|
+
else
|
16
|
+
send(callback, *args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def define_callback(callback_name)
|
23
|
+
attribute_name = "_#{callback_name}_callbacks"
|
24
|
+
class_attribute(attribute_name)
|
25
|
+
self.send("#{attribute_name}=", [])
|
26
|
+
define_singleton_method(callback_name) do |*args, &block|
|
27
|
+
callbacks = self.send(attribute_name)
|
28
|
+
callbacks.concat(args)
|
29
|
+
callbacks.push(block) if block_given?
|
30
|
+
self.send("#{attribute_name}=", callbacks)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
require 'react/opal/callbacks'
|
3
|
+
require 'react/opal/ext/hash'
|
4
|
+
require 'react/opal/component/api'
|
5
|
+
|
6
|
+
module React
|
7
|
+
module Component
|
8
|
+
def self.included(base)
|
9
|
+
base.include(API)
|
10
|
+
base.include(React::Callbacks)
|
11
|
+
base.class_eval do
|
12
|
+
class_attribute :init_state, :validator, :context_types, :child_context_types, :child_context_get
|
13
|
+
define_callback :before_mount
|
14
|
+
define_callback :after_mount
|
15
|
+
define_callback :before_receive_props
|
16
|
+
define_callback :before_update
|
17
|
+
define_callback :after_update
|
18
|
+
define_callback :before_unmount
|
19
|
+
end
|
20
|
+
base.extend(ClassMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
def params
|
24
|
+
Hash.new(`#{self}.props`).inject({}) do |memo, (k, v)|
|
25
|
+
memo[k.underscore] = v
|
26
|
+
memo
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def refs
|
31
|
+
Hash.new(`#{self}.refs`)
|
32
|
+
end
|
33
|
+
|
34
|
+
def context
|
35
|
+
Hash.new(`#{self}.context`)
|
36
|
+
end
|
37
|
+
|
38
|
+
def emit(event_name, *args)
|
39
|
+
self.params["on_#{event_name.to_s}"].call(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def component_will_mount
|
43
|
+
self.run_callback(:before_mount)
|
44
|
+
end
|
45
|
+
|
46
|
+
def component_did_mount
|
47
|
+
self.run_callback(:after_mount)
|
48
|
+
end
|
49
|
+
|
50
|
+
def component_will_receive_props(next_props)
|
51
|
+
self.run_callback(:before_receive_props, Hash.new(next_props))
|
52
|
+
end
|
53
|
+
|
54
|
+
def should_component_update?(next_props, next_state)
|
55
|
+
self.respond_to?(:needs_update?) ? self.needs_update?(Hash.new(next_props), Hash.new(next_state)) : true
|
56
|
+
end
|
57
|
+
|
58
|
+
def component_will_update(next_props, next_state)
|
59
|
+
self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state))
|
60
|
+
end
|
61
|
+
|
62
|
+
def component_did_update(prev_props, prev_state)
|
63
|
+
self.run_callback(:after_update, Hash.new(prev_props), Hash.new(prev_state))
|
64
|
+
end
|
65
|
+
|
66
|
+
def component_will_unmount
|
67
|
+
self.run_callback(:before_unmount)
|
68
|
+
end
|
69
|
+
|
70
|
+
def p(*args, &block)
|
71
|
+
if block || args.count == 0 || (args.count == 1 && args.first.is_a?(Hash))
|
72
|
+
_p_tag(*args, &block)
|
73
|
+
else
|
74
|
+
Kernel.p(*args)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def method_missing(name, *args, &block)
|
79
|
+
unless (React::HTML_TAGS.include?(name) || name == 'present' || name == '_p_tag')
|
80
|
+
return super
|
81
|
+
end
|
82
|
+
|
83
|
+
if name == "present"
|
84
|
+
name = args.shift
|
85
|
+
end
|
86
|
+
|
87
|
+
if name == "_p_tag"
|
88
|
+
name = "p"
|
89
|
+
end
|
90
|
+
|
91
|
+
@buffer = [] unless @buffer
|
92
|
+
if block
|
93
|
+
current = @buffer
|
94
|
+
@buffer = []
|
95
|
+
result = block.call
|
96
|
+
element = React.create_element(name, *args) { @buffer.count == 0 ? result : @buffer }
|
97
|
+
@buffer = current
|
98
|
+
else
|
99
|
+
element = React.create_element(name, *args)
|
100
|
+
end
|
101
|
+
|
102
|
+
@buffer << element
|
103
|
+
element
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_n
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
module ClassMethods
|
111
|
+
def prop_types
|
112
|
+
if self.validator
|
113
|
+
{
|
114
|
+
_componentValidator: %x{
|
115
|
+
function(props, propName, componentName) {
|
116
|
+
var errors = #{validator.validate(Hash.new(`props`))};
|
117
|
+
var error = new Error(#{"In component `" + self.name + "`\n" + `errors`.join("\n")});
|
118
|
+
return #{`errors`.count > 0 ? `error` : `undefined`};
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
else
|
123
|
+
{}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def initial_state
|
128
|
+
self.init_state || {}
|
129
|
+
end
|
130
|
+
|
131
|
+
def default_props
|
132
|
+
self.validator ? self.validator.default_props : {}
|
133
|
+
end
|
134
|
+
|
135
|
+
def params(&block)
|
136
|
+
if self.validator
|
137
|
+
self.validator.evaluate_more_rules(&block)
|
138
|
+
else
|
139
|
+
self.validator = React::Validator.build(&block)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def define_state_prop(prop, &block)
|
144
|
+
define_state prop
|
145
|
+
update_value = lambda do |new_value|
|
146
|
+
new_value = instance_exec(new_value, &block) if block
|
147
|
+
self.send("#{prop}=", new_value)
|
148
|
+
end
|
149
|
+
before_mount do
|
150
|
+
# need to execute in context of each object
|
151
|
+
instance_exec params[prop], &update_value
|
152
|
+
end
|
153
|
+
before_receive_props do |new_props|
|
154
|
+
# need to execute in context of each object
|
155
|
+
instance_exec new_props[prop], &update_value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_prop_type(klass)
|
160
|
+
if klass == Proc
|
161
|
+
`React.PropTypes.func`
|
162
|
+
elsif klass.is_a?(Proc)
|
163
|
+
`React.PropTypes.object`
|
164
|
+
elsif klass == Boolean
|
165
|
+
`React.PropTypes.bool`
|
166
|
+
elsif klass.ancestors.include?(Numeric)
|
167
|
+
`React.PropTypes.number`
|
168
|
+
elsif klass == String
|
169
|
+
`React.PropTypes.string`
|
170
|
+
elsif klass == Array
|
171
|
+
`React.PropTypes.array`
|
172
|
+
else
|
173
|
+
`React.PropTypes.object`
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def consume_context(item, klass)
|
178
|
+
self.context_types ||= {}
|
179
|
+
self.context_types[item] = get_prop_type(klass)
|
180
|
+
end
|
181
|
+
|
182
|
+
def provide_context(item, klass, &block)
|
183
|
+
self.child_context_types ||= {}
|
184
|
+
self.child_context_types[item] = get_prop_type(klass)
|
185
|
+
self.child_context_get ||= {}
|
186
|
+
self.child_context_get[item] = block
|
187
|
+
unless method_defined?(:get_child_context)
|
188
|
+
define_method(:get_child_context) do
|
189
|
+
Hash[self.child_context_get.map do |item, blk|
|
190
|
+
[item, instance_eval(&blk)]
|
191
|
+
end].to_n
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def define_state(*states)
|
197
|
+
raise "Block could be only given when define exactly one state" if block_given? && states.count > 1
|
198
|
+
|
199
|
+
self.init_state = {} unless self.init_state
|
200
|
+
|
201
|
+
if block_given?
|
202
|
+
self.init_state[states[0]] = yield
|
203
|
+
end
|
204
|
+
states.each do |name|
|
205
|
+
# getter
|
206
|
+
define_method("#{name}") do
|
207
|
+
self.state[name]
|
208
|
+
end
|
209
|
+
# setter
|
210
|
+
define_method("#{name}=") do |new_state|
|
211
|
+
hash = {}
|
212
|
+
hash[name] = new_state
|
213
|
+
self.set_state(hash)
|
214
|
+
|
215
|
+
new_state
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
module API
|
4
|
+
def self.included(base)
|
5
|
+
base.include(::React::PropsChildren)
|
6
|
+
end
|
7
|
+
|
8
|
+
def state
|
9
|
+
Hash.new(`#{self}.state`)
|
10
|
+
end
|
11
|
+
|
12
|
+
def force_update!
|
13
|
+
`#{self}.forceUpdate()`
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_state(state, &block)
|
17
|
+
%x{
|
18
|
+
#{self}.setState(#{state.shallow_to_n}, function(){
|
19
|
+
#{block.call if block}
|
20
|
+
});
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def dom_node
|
25
|
+
raise "`dom_node` is deprecated in favor of `React.find_dom_node`"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module React
|
2
|
+
class ComponentFactory
|
3
|
+
@@component_classes = {}
|
4
|
+
|
5
|
+
def self.clear_component_class_cache
|
6
|
+
@@component_classes = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.native_component_class(klass)
|
10
|
+
@@component_classes[klass.to_s] ||= begin
|
11
|
+
klass.class_eval do
|
12
|
+
include(React::Component::API)
|
13
|
+
# In Opal 0.8, native_alias fails if the method isn't there but we don't want to force all of these to be implemented
|
14
|
+
optional_native_alias = lambda do |js, ruby|
|
15
|
+
not_there = `!(#{self}.$$proto['$' + #{ruby}])`
|
16
|
+
native_alias js, ruby unless not_there
|
17
|
+
end
|
18
|
+
optional_native_alias[:componentWillMount, :component_will_mount]
|
19
|
+
optional_native_alias[:componentDidMount, :component_did_mount]
|
20
|
+
optional_native_alias[:componentWillReceiveProps, :component_will_receive_props]
|
21
|
+
optional_native_alias[:shouldComponentUpdate, :should_component_update?]
|
22
|
+
optional_native_alias[:componentWillUpdate, :component_will_update]
|
23
|
+
optional_native_alias[:componentDidUpdate, :component_did_update]
|
24
|
+
optional_native_alias[:componentWillUnmount, :component_will_unmount]
|
25
|
+
optional_native_alias[:getChildContext, :get_child_context]
|
26
|
+
native_alias :render, :render
|
27
|
+
end
|
28
|
+
%x{
|
29
|
+
if (!Object.assign) {
|
30
|
+
Object.defineProperty(Object, 'assign', {
|
31
|
+
enumerable: false,
|
32
|
+
configurable: true,
|
33
|
+
writable: true,
|
34
|
+
value: function(target, firstSource) {
|
35
|
+
'use strict';
|
36
|
+
if (target === undefined || target === null) {
|
37
|
+
throw new TypeError('Cannot convert first argument to object');
|
38
|
+
}
|
39
|
+
|
40
|
+
var to = Object(target);
|
41
|
+
for (var i = 1; i < arguments.length; i++) {
|
42
|
+
var nextSource = arguments[i];
|
43
|
+
if (nextSource === undefined || nextSource === null) {
|
44
|
+
continue;
|
45
|
+
}
|
46
|
+
|
47
|
+
var keysArray = Object.keys(Object(nextSource));
|
48
|
+
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
|
49
|
+
var nextKey = keysArray[nextIndex];
|
50
|
+
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
|
51
|
+
if (desc !== undefined && desc.enumerable) {
|
52
|
+
to[nextKey] = nextSource[nextKey];
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return to;
|
57
|
+
}
|
58
|
+
});
|
59
|
+
}
|
60
|
+
function ctor(props){
|
61
|
+
this.constructor = ctor;
|
62
|
+
this.state = #{klass.respond_to?(:initial_state) ? klass.initial_state.to_n : `{}`};
|
63
|
+
React.Component.apply(this, arguments);
|
64
|
+
#{klass}.$$alloc.prototype.$initialize.call(this, Opal.Hash.$new(props));
|
65
|
+
};
|
66
|
+
ctor.prototype = klass.$$proto;
|
67
|
+
Object.assign(ctor.prototype, React.Component.prototype);
|
68
|
+
ctor.propTypes = #{klass.respond_to?(:prop_types) ? klass.prop_types.to_n : `{}`};
|
69
|
+
ctor.contextTypes = #{klass.respond_to?(:context_types) ? klass.context_types.to_n : `{}`};
|
70
|
+
ctor.childContextTypes = #{klass.respond_to?(:child_context_types) ? klass.child_context_types.to_n : `{}`};
|
71
|
+
ctor.defaultProps = #{klass.respond_to?(:default_props) ? klass.default_props.to_n : `{}`};
|
72
|
+
ctor.displayName = #{klass.to_s};
|
73
|
+
}
|
74
|
+
`ctor`
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module React
|
2
|
+
class Event
|
3
|
+
include Native
|
4
|
+
alias_native :bubbles, :bubbles
|
5
|
+
alias_native :cancelable, :cancelable
|
6
|
+
alias_native :current_target, :currentTarget
|
7
|
+
alias_native :default_prevented, :defaultPrevented
|
8
|
+
alias_native :event_phase, :eventPhase
|
9
|
+
alias_native :is_trusted?, :isTrusted
|
10
|
+
alias_native :native_event, :nativeEvent
|
11
|
+
alias_native :target, :target
|
12
|
+
alias_native :timestamp, :timeStamp
|
13
|
+
alias_native :event_type, :type
|
14
|
+
alias_native :prevent_default, :preventDefault
|
15
|
+
alias_native :stop_propagation, :stopPropagation
|
16
|
+
# Clipboard
|
17
|
+
alias_native :clipboard_data, :clipboardData
|
18
|
+
# Keyboard
|
19
|
+
alias_native :alt_key, :altKey
|
20
|
+
alias_native :char_code, :charCode
|
21
|
+
alias_native :ctrl_key, :ctrlKey
|
22
|
+
alias_native :get_modifier_state, :getModifierState
|
23
|
+
alias_native :key, :key
|
24
|
+
alias_native :key_code, :keyCode
|
25
|
+
alias_native :locale, :locale
|
26
|
+
alias_native :location, :location
|
27
|
+
alias_native :meta_key, :metaKey
|
28
|
+
alias_native :repeat, :repeat
|
29
|
+
alias_native :shift_key, :shiftKey
|
30
|
+
alias_native :which, :which
|
31
|
+
# Focus
|
32
|
+
alias_native :related_target, :relatedTarget
|
33
|
+
# Mouse
|
34
|
+
alias_native :alt_key, :altKey
|
35
|
+
alias_native :button, :button
|
36
|
+
alias_native :buttons, :buttons
|
37
|
+
alias_native :client_x, :clientX
|
38
|
+
alias_native :client_y, :clientY
|
39
|
+
alias_native :ctrl_key, :ctrlKey
|
40
|
+
alias_native :get_modifier_state, :getModifierState
|
41
|
+
alias_native :meta_key, :metaKey
|
42
|
+
alias_native :page_x, :pageX
|
43
|
+
alias_native :page_y, :pageY
|
44
|
+
alias_native :related_target, :relatedTarget
|
45
|
+
alias_native :screen_x, :screen_x
|
46
|
+
alias_native :screen_y, :screen_y
|
47
|
+
alias_native :shift_key, :shift_key
|
48
|
+
# Touch
|
49
|
+
alias_native :alt_key, :altKey
|
50
|
+
alias_native :changed_touches, :changedTouches
|
51
|
+
alias_native :ctrl_key, :ctrlKey
|
52
|
+
alias_native :get_modifier_state, :getModifierState
|
53
|
+
alias_native :meta_key, :metaKey
|
54
|
+
alias_native :shift_key, :shiftKey
|
55
|
+
alias_native :target_touches, :targetTouches
|
56
|
+
alias_native :touches, :touches
|
57
|
+
# UI
|
58
|
+
alias_native :detail, :detail
|
59
|
+
alias_native :view, :view
|
60
|
+
# Wheel
|
61
|
+
alias_native :delta_mode, :deltaMode
|
62
|
+
alias_native :delta_x, :deltaX
|
63
|
+
alias_native :delta_y, :deltaY
|
64
|
+
alias_native :delta_z, :deltaZ
|
65
|
+
|
66
|
+
BUILT_IN_EVENTS = %w{onCopy onCut onPaste onKeyDown onKeyPress onKeyUp
|
67
|
+
onFocus onBlur onChange onInput onSubmit onClick onDoubleClick onDrag
|
68
|
+
onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop
|
69
|
+
onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver
|
70
|
+
onMouseUp onTouchCancel onTouchEnd onTouchMove onTouchStart onScroll}
|
71
|
+
|
72
|
+
def initialize(native_element)
|
73
|
+
@native = native_element
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module React
|
2
|
+
class NativeElement
|
3
|
+
include PropsChildren
|
4
|
+
|
5
|
+
# As of React 0.14, elements are now just plain object literals, so we can't inherit anymore
|
6
|
+
# We can just set each of the properties on our object though
|
7
|
+
# See var ReactElement = function (type, key, ref, self, source, owner, props) in the React source
|
8
|
+
def initialize(native)
|
9
|
+
%x{
|
10
|
+
self.$$typeof = #{native}.$$typeof;
|
11
|
+
self.type = #{native}.type;
|
12
|
+
self.key = #{native}.key;
|
13
|
+
self.ref = #{native}.ref;
|
14
|
+
self.props = #{native}.props;
|
15
|
+
self._owner = #{native}._owner;
|
16
|
+
self._store = #{native}._store;
|
17
|
+
self._self = #{native}._self;
|
18
|
+
self._source = #{native}._source;
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def element_type
|
23
|
+
self.JS[:type]
|
24
|
+
end
|
25
|
+
|
26
|
+
def on(event_name)
|
27
|
+
name = event_name.to_s.camelize
|
28
|
+
|
29
|
+
prop_key = "on#{name}"
|
30
|
+
|
31
|
+
if React::Event::BUILT_IN_EVENTS.include?(prop_key)
|
32
|
+
callback = %x{
|
33
|
+
function(event){
|
34
|
+
#{yield React::Event.new(`event`)}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
else
|
38
|
+
callback = %x{
|
39
|
+
function(){
|
40
|
+
#{yield *Array(`arguments`)}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
new_prop = `{}`
|
46
|
+
`new_prop[prop_key] = #{callback}`
|
47
|
+
|
48
|
+
cloned = `React.cloneElement(#{self}, #{new_prop})`
|
49
|
+
React::NativeElement.new cloned
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module React
|
2
|
+
module PropsChildren
|
3
|
+
def props
|
4
|
+
Hash.new(`#{self}.props`)
|
5
|
+
end
|
6
|
+
|
7
|
+
def children
|
8
|
+
nodes = `#{self}.props.children`
|
9
|
+
|
10
|
+
if `React.Children.count(nodes)` == 0
|
11
|
+
`[]`
|
12
|
+
elsif `React.Children.count(nodes)` == 1
|
13
|
+
if `(typeof nodes === 'string') || (typeof nodes === 'number')`
|
14
|
+
[nodes]
|
15
|
+
else
|
16
|
+
`[React.Children.only(nodes)]`
|
17
|
+
end
|
18
|
+
else
|
19
|
+
# Not sure the overhead of doing this..
|
20
|
+
class << nodes
|
21
|
+
include Enumerable
|
22
|
+
|
23
|
+
def to_n
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def each(&block)
|
28
|
+
if block_given?
|
29
|
+
%x{
|
30
|
+
React.Children.forEach(#{self.to_n}, function(context){
|
31
|
+
#{block.call(`context`)}
|
32
|
+
})
|
33
|
+
}
|
34
|
+
else
|
35
|
+
Enumerator.new(`React.Children.count(#{self.to_n})`) do |y|
|
36
|
+
%x{
|
37
|
+
React.Children.forEach(#{self.to_n}, function(context){
|
38
|
+
#{y << `context`}
|
39
|
+
})
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
nodes
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module React
|
2
|
+
module Testing
|
3
|
+
`var ReactTestUtils = React.addons.TestUtils`
|
4
|
+
|
5
|
+
def simulate_event(event_name, dom_element, event_data = {})
|
6
|
+
simulator = Native(`ReactTestUtils.Simulate`)
|
7
|
+
simulator[event_name].call(dom_element, event_data)
|
8
|
+
end
|
9
|
+
|
10
|
+
def render_to_document(element)
|
11
|
+
`ReactTestUtils.renderIntoDocument(#{element})`
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'native'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
module React
|
5
|
+
HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
|
6
|
+
button canvas caption cite code col colgroup data datalist dd del details dfn
|
7
|
+
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
8
|
+
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
9
|
+
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
10
|
+
output p param picture pre progress q rp rt ruby s samp script section select
|
11
|
+
small source span strong style sub summary sup table tbody td textarea tfoot th
|
12
|
+
thead time title tr track u ul var video wbr)
|
13
|
+
|
14
|
+
def self.create_element(type, properties = {})
|
15
|
+
params = []
|
16
|
+
|
17
|
+
# Component Spec or Normal DOM
|
18
|
+
native = `(typeof type === 'function')` || HTML_TAGS.include?(type)
|
19
|
+
params << if native
|
20
|
+
type
|
21
|
+
elsif type.kind_of?(Class)
|
22
|
+
raise "Provided class should define `render` method" if !(type.method_defined? :render)
|
23
|
+
React::ComponentFactory.native_component_class(type)
|
24
|
+
else
|
25
|
+
raise "#{type} not implemented"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Passed in properties
|
29
|
+
props = camel_case_hash_keys(properties) do |key, value|
|
30
|
+
if key == "class_name" && value.is_a?(Hash)
|
31
|
+
value.inject([]) { |ary, (k, v)| v ? ary.push(k) : ary }.join(" ")
|
32
|
+
elsif key == 'value_link'
|
33
|
+
process_value_link value
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
params << props.shallow_to_n
|
40
|
+
|
41
|
+
# Children Nodes
|
42
|
+
if block_given?
|
43
|
+
[yield].flatten.each do |ele|
|
44
|
+
params << ele
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
element = `React.createElement.apply(null, #{params})`
|
49
|
+
React::NativeElement.new(element)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.lower_camelize(str)
|
53
|
+
camelized = str.camelize
|
54
|
+
camelized[0].downcase + camelized[1..-1]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.camel_case_hash_keys(input)
|
58
|
+
as_array = input.map do |key, value|
|
59
|
+
new_value = block_given? ? yield(key, value) : value
|
60
|
+
[lower_camelize(key), new_value]
|
61
|
+
end
|
62
|
+
Hash[as_array]
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.process_value_link(arguments)
|
66
|
+
arguments = arguments.call if arguments.is_a? Proc
|
67
|
+
camel_case_hash_keys(arguments).to_n
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.render(element, container)
|
71
|
+
component = Native(`ReactDOM.render(#{element}, container, function(){#{yield if block_given?}})`)
|
72
|
+
component.class.include(React::Component::API)
|
73
|
+
component
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.is_valid_element(element)
|
77
|
+
`React.isValidElement(#{element})`
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.render_to_string(element)
|
81
|
+
`ReactDOMServer.renderToString(#{element})`
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.render_to_static_markup(element)
|
85
|
+
`ReactDOMServer.renderToStaticMarkup(#{element})`
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.unmount_component_at_node(node)
|
89
|
+
`ReactDOM.unmountComponentAtNode(node)`
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.expose_native_class(*args)
|
93
|
+
args.each do |klass|
|
94
|
+
`window[#{klass.to_s}] = #{React::ComponentFactory.native_component_class(klass)}`
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.find_dom_node(component)
|
99
|
+
`ReactDOM.findDOMNode(#{component})`
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module React
|
2
|
+
class Validator
|
3
|
+
def self.build(&block)
|
4
|
+
validator = self.new
|
5
|
+
validator.instance_eval(&block)
|
6
|
+
validator
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@rules = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate_more_rules(&block)
|
14
|
+
self.instance_eval(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def requires(prop_name, options = {})
|
18
|
+
rule = options
|
19
|
+
options[:required] = true
|
20
|
+
@rules[prop_name] = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def optional(prop_name, options = {})
|
24
|
+
rule = options
|
25
|
+
options[:required] = false
|
26
|
+
@rules[prop_name] = options
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate(props)
|
30
|
+
errors = []
|
31
|
+
props.keys.each do |prop_name|
|
32
|
+
errors << "Provided prop `#{prop_name}` not specified in spec" if @rules[prop_name] == nil
|
33
|
+
end
|
34
|
+
|
35
|
+
props = props.select {|key| @rules.keys.include?(key) }
|
36
|
+
|
37
|
+
# requires or not
|
38
|
+
(@rules.keys - props.keys).each do |prop_name|
|
39
|
+
errors << "Required prop `#{prop_name}` was not specified" if @rules[prop_name][:required]
|
40
|
+
end
|
41
|
+
|
42
|
+
# type
|
43
|
+
props.each do |prop_name, value|
|
44
|
+
if klass = @rules[prop_name][:type]
|
45
|
+
if klass.is_a?(Array)
|
46
|
+
errors << "Provided prop `#{prop_name}` was not an Array of the specified type `#{klass[0]}`" unless value.all?{ |ele| ele.is_a?(klass[0]) }
|
47
|
+
else
|
48
|
+
errors << "Provided prop `#{prop_name}` was not the specified type `#{klass}`" unless value.is_a?(klass)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# values
|
54
|
+
props.each do |prop_name, value|
|
55
|
+
if values = @rules[prop_name][:values]
|
56
|
+
errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value" unless values.include?(value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
errors
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_props
|
64
|
+
@rules
|
65
|
+
.select {|key, value| value.keys.include?("default") }
|
66
|
+
.inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: react-opal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.14.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brady Wied
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: opal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: opal-activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.0
|
41
|
+
description: Write reactive UI component with Ruby's elegancy and compiled to run
|
42
|
+
in Javascript.
|
43
|
+
email: brady@bswtechconsulting.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/react-opal.rb
|
49
|
+
- lib/react/opal/callbacks.rb
|
50
|
+
- lib/react/opal/component.rb
|
51
|
+
- lib/react/opal/component/api.rb
|
52
|
+
- lib/react/opal/component_factory.rb
|
53
|
+
- lib/react/opal/event.rb
|
54
|
+
- lib/react/opal/ext/hash.rb
|
55
|
+
- lib/react/opal/native_element.rb
|
56
|
+
- lib/react/opal/props_children.rb
|
57
|
+
- lib/react/opal/testing.rb
|
58
|
+
- lib/react/opal/top_level.rb
|
59
|
+
- lib/react/opal/validator.rb
|
60
|
+
- lib/react/opal/version.rb
|
61
|
+
homepage: https://github.com/wied03/react-opal
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.6.1
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Opal Ruby wrapper of React.js library.
|
85
|
+
test_files: []
|