hyper-component 0.99.6 → 1.0.alpha1
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 +4 -4
- data/CHANGELOG.md +3 -3
- data/Gemfile +4 -3
- data/Gemfile.lock +51 -36
- data/{misc/how-component-name-lookup-works.md → how-component-name-lookup-works.md} +1 -1
- data/hyper-component.gemspec +9 -8
- data/lib/hyper-component.rb +31 -43
- data/lib/hyperstack/component.rb +145 -0
- data/lib/hyperstack/component/auto-import.rb +44 -0
- data/lib/hyperstack/component/children.rb +40 -0
- data/lib/hyperstack/component/element.rb +129 -0
- data/lib/hyperstack/component/event.rb +78 -0
- data/lib/hyperstack/component/haml.rb +18 -0
- data/lib/hyperstack/component/isomorphic_helpers.rb +235 -0
- data/lib/hyperstack/component/jquery.rb +2 -0
- data/lib/hyperstack/component/native_library.rb +92 -0
- data/lib/hyperstack/component/react_api.rb +142 -0
- data/lib/hyperstack/component/server.rb +21 -0
- data/lib/hyperstack/component/version.rb +5 -0
- data/lib/hyperstack/ext/component/boolean.rb +14 -0
- data/lib/{react/ext/opal-jquery → hyperstack/ext/component}/element.rb +17 -12
- data/lib/{react/ext → hyperstack/ext/component}/hash.rb +0 -0
- data/lib/{react/to_key.rb → hyperstack/ext/component/number.rb} +0 -12
- data/lib/hyperstack/ext/component/object.rb +32 -0
- data/lib/{reactive-ruby → hyperstack/ext/component}/serializers.rb +0 -0
- data/lib/{react/ext → hyperstack/ext/component}/string.rb +0 -0
- data/lib/hyperstack/internal/component.rb +16 -0
- data/lib/hyperstack/internal/component/class_methods.rb +212 -0
- data/lib/hyperstack/internal/component/haml.rb +56 -0
- data/lib/hyperstack/internal/component/instance_methods.rb +92 -0
- data/lib/hyperstack/internal/component/props_wrapper.rb +125 -0
- data/lib/hyperstack/internal/component/rails.rb +11 -0
- data/lib/hyperstack/internal/component/rails/component_loader.rb +49 -0
- data/lib/hyperstack/internal/component/rails/component_mount.rb +52 -0
- data/lib/{reactive-ruby → hyperstack/internal/component}/rails/controller_helper.rb +0 -0
- data/lib/hyperstack/internal/component/rails/railtie.rb +24 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +52 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +52 -0
- data/lib/hyperstack/internal/component/react_wrapper.rb +308 -0
- data/lib/hyperstack/internal/component/rendering_context.rb +165 -0
- data/lib/hyperstack/internal/component/should_component_update.rb +101 -0
- data/lib/hyperstack/internal/component/tags.rb +109 -0
- data/lib/hyperstack/internal/component/top_level_rails_component.rb +83 -0
- data/lib/hyperstack/internal/component/validator.rb +149 -0
- data/lib/react/react-source.rb +2 -2
- data/unmounting-objects.md +78 -0
- metadata +73 -85
- data/DOCS.md +0 -1515
- data/LICENSE +0 -19
- data/README.md +0 -49
- data/lib/hyper-component/jquery.rb +0 -2
- data/lib/rails-helpers/top_level_rails_component.rb +0 -79
- data/lib/react/api.rb +0 -272
- data/lib/react/callbacks.rb +0 -42
- data/lib/react/children.rb +0 -38
- data/lib/react/component.rb +0 -189
- data/lib/react/component/api.rb +0 -70
- data/lib/react/component/base.rb +0 -13
- data/lib/react/component/class_methods.rb +0 -175
- data/lib/react/component/dsl_instance_methods.rb +0 -23
- data/lib/react/component/params.rb +0 -6
- data/lib/react/component/props_wrapper.rb +0 -90
- data/lib/react/component/should_component_update.rb +0 -99
- data/lib/react/component/tags.rb +0 -116
- data/lib/react/config.rb +0 -5
- data/lib/react/element.rb +0 -167
- data/lib/react/event.rb +0 -76
- data/lib/react/native_library.rb +0 -87
- data/lib/react/object.rb +0 -15
- data/lib/react/ref_callback.rb +0 -31
- data/lib/react/rendering_context.rb +0 -149
- data/lib/react/server.rb +0 -19
- data/lib/react/state_wrapper.rb +0 -23
- data/lib/react/test.rb +0 -16
- data/lib/react/test/dsl.rb +0 -17
- data/lib/react/test/matchers/render_html_matcher.rb +0 -56
- data/lib/react/test/rspec.rb +0 -15
- data/lib/react/test/session.rb +0 -37
- data/lib/react/test/utils.rb +0 -71
- data/lib/react/top_level.rb +0 -110
- data/lib/react/top_level_render.rb +0 -30
- data/lib/react/validator.rb +0 -132
- data/lib/reactive-ruby/component_loader.rb +0 -43
- data/lib/reactive-ruby/isomorphic_helpers.rb +0 -233
- data/lib/reactive-ruby/rails.rb +0 -8
- data/lib/reactive-ruby/rails/component_mount.rb +0 -48
- data/lib/reactive-ruby/rails/railtie.rb +0 -20
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +0 -46
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +0 -46
- data/lib/reactive-ruby/version.rb +0 -5
- data/lib/reactrb/auto-import.rb +0 -27
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +0 -3
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +0 -2
- data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +0 -6
- data/misc/generators/reactive_ruby/test_app/templates/script/rails +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +0 -13
- data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +0 -11
- data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +0 -14
- 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 +0 -121
- 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 +0 -9
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module Hyperstack
|
|
2
|
+
module Component
|
|
3
|
+
# NativeLibrary handles importing JS libraries. Importing native components is handled
|
|
4
|
+
# by the React::Base. It also provides several methods used by auto-import.rb
|
|
5
|
+
|
|
6
|
+
# A NativeLibrary is simply a wrapper that holds the name of the native js library.
|
|
7
|
+
# It responds to const_missing and method_missing by looking up objects within the js library.
|
|
8
|
+
# If the object is a react component it is wrapped by a reactrb component class, otherwise
|
|
9
|
+
# a nested NativeLibrary is returned.
|
|
10
|
+
|
|
11
|
+
# Two macros are provided: imports (for naming the native library) and renames which allows
|
|
12
|
+
# the members of a library to be given different names within the ruby name space.
|
|
13
|
+
|
|
14
|
+
# Public methods used by auto-import.rb are import_const_from_native and find_and_render_component
|
|
15
|
+
class NativeLibrary
|
|
16
|
+
class << self
|
|
17
|
+
def imports(native_name)
|
|
18
|
+
@__hyperstack_component_native_prefix = "#{native_name}."
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def rename(rename_list)
|
|
23
|
+
# rename_list is a hash in the form: native_name => ruby_name, native_name => ruby_name
|
|
24
|
+
rename_list.each do |js_name, ruby_name|
|
|
25
|
+
native_name = lookup_native_name(js_name)
|
|
26
|
+
if lookup_native_name(js_name)
|
|
27
|
+
create_component_wrapper(self, native_name, ruby_name) ||
|
|
28
|
+
create_library_wrapper(self, native_name, ruby_name)
|
|
29
|
+
else
|
|
30
|
+
raise "class #{name} < Hyperstack::Component::NativeLibrary could not import #{js_name}. "\
|
|
31
|
+
"Native value #{scope_native_name(js_name)} is undefined."
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def import_const_from_native(klass, const_name, create_library)
|
|
37
|
+
native_name = lookup_native_name(const_name) ||
|
|
38
|
+
lookup_native_name(const_name[0].downcase + const_name[1..-1])
|
|
39
|
+
native_name && (
|
|
40
|
+
create_component_wrapper(klass, native_name, const_name) || (
|
|
41
|
+
create_library &&
|
|
42
|
+
create_library_wrapper(klass, native_name, const_name)))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def const_missing(const_name)
|
|
46
|
+
import_const_from_native(self, const_name, true) || super
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def method_missing(method, *args, &block)
|
|
50
|
+
component_class = const_get(method) if const_defined?(method, false)
|
|
51
|
+
component_class ||= import_const_from_native(self, method, false)
|
|
52
|
+
raise 'could not import a react component named: '\
|
|
53
|
+
"#{scope_native_name method}" unless component_class
|
|
54
|
+
Hyperstack::Internal::Component::RenderingContext.render(component_class, *args, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def lookup_native_name(js_name)
|
|
60
|
+
native_name = scope_native_name(js_name)
|
|
61
|
+
`eval(#{native_name}) !== undefined && native_name`
|
|
62
|
+
# rubocop:disable Lint/RescueException # that is what eval raises in Opal >= 0.10.
|
|
63
|
+
rescue Exception
|
|
64
|
+
nil
|
|
65
|
+
# rubocop:enable Lint/RescueException
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def scope_native_name(js_name)
|
|
69
|
+
"#{@__hyperstack_component_native_prefix}#{js_name}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def create_component_wrapper(klass, native_name, ruby_name)
|
|
73
|
+
if Hyperstack::Internal::Component::ReactWrapper.native_react_component?(native_name)
|
|
74
|
+
new_klass = klass.const_set ruby_name, Class.new(NativeLibrary).imports(native_name)
|
|
75
|
+
new_klass.class_eval do
|
|
76
|
+
include Component
|
|
77
|
+
imports native_name
|
|
78
|
+
end
|
|
79
|
+
new_klass
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def create_library_wrapper(klass, native_name, ruby_name)
|
|
84
|
+
klass.const_set ruby_name, Class.new(NativeLibrary).imports(native_name)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
# handles the case of an application NativeLibrary called...
|
|
88
|
+
# NativeLibrary !
|
|
89
|
+
imports 'NativeLibrary'
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require "native"
|
|
2
|
+
require 'active_support/core_ext/object/try'
|
|
3
|
+
require 'hyperstack/internal/component/tags'
|
|
4
|
+
|
|
5
|
+
module Hyperstack
|
|
6
|
+
module Component
|
|
7
|
+
module ReactAPI
|
|
8
|
+
|
|
9
|
+
ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
|
|
10
|
+
async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
|
|
11
|
+
className cols colSpan content contentEditable contextMenu controls coords
|
|
12
|
+
crossOrigin data dateTime defer dir disabled download draggable encType form
|
|
13
|
+
formAction formEncType formMethod formNoValidate formTarget frameBorder height
|
|
14
|
+
hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
|
|
15
|
+
marginHeight marginWidth max maxLength media mediaGroup method min multiple
|
|
16
|
+
muted name noValidate open pattern placeholder poster preload radioGroup
|
|
17
|
+
readOnly rel required role rows rowSpan sandbox scope scrolling seamless
|
|
18
|
+
selected shape size sizes span spellCheck src srcDoc srcSet start step style
|
|
19
|
+
tabIndex target title type useMap value width wmode dangerouslySetInnerHTML) +
|
|
20
|
+
#SVG ATTRIBUTES
|
|
21
|
+
%w(clipPath cx cy d dx dy fill fillOpacity fontFamily
|
|
22
|
+
fontSize fx fy gradientTransform gradientUnits markerEnd
|
|
23
|
+
markerMid markerStart offset opacity patternContentUnits
|
|
24
|
+
patternUnits points preserveAspectRatio r rx ry spreadMethod
|
|
25
|
+
stopColor stopOpacity stroke strokeDasharray strokeLinecap
|
|
26
|
+
strokeOpacity strokeWidth textAnchor transform version
|
|
27
|
+
viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
|
|
28
|
+
xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y)
|
|
29
|
+
HASH_ATTRIBUTES = %w(data aria)
|
|
30
|
+
HTML_TAGS = Hyperstack::Internal::Component::Tags::HTML_TAGS
|
|
31
|
+
|
|
32
|
+
def self.html_tag?(name)
|
|
33
|
+
tags = HTML_TAGS
|
|
34
|
+
%x{
|
|
35
|
+
for(var i = 0; i < tags.length; i++) {
|
|
36
|
+
if(tags[i] === name)
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.html_attr?(name)
|
|
44
|
+
attrs = ATTRIBUTES
|
|
45
|
+
%x{
|
|
46
|
+
for(var i = 0; i < attrs.length; i++) {
|
|
47
|
+
if(attrs[i] === name)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.create_element(type, *properties, &block)
|
|
55
|
+
Hyperstack::Internal::Component::ReactWrapper.create_element(type, *properties, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# def self.render(element, container)
|
|
59
|
+
# %x{
|
|
60
|
+
# console.error(
|
|
61
|
+
# "Warning: Using deprecated behavior of `Hyperstack::Component::ReactAPI.render`,",
|
|
62
|
+
# "require \"react/top_level_render\" to get the correct behavior."
|
|
63
|
+
# );
|
|
64
|
+
# }
|
|
65
|
+
# container = `container.$$class ? container[0] : container`
|
|
66
|
+
# if !(`typeof ReactDOM === 'undefined'`)
|
|
67
|
+
# component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
|
|
68
|
+
# else
|
|
69
|
+
# raise "render is not defined. In React >= v15 you must import it with ReactDOM"
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# component.class.include(React::Component::API)
|
|
73
|
+
# component
|
|
74
|
+
# end
|
|
75
|
+
|
|
76
|
+
def self.render(element, container)
|
|
77
|
+
raise "ReactDOM.render is not defined. In React >= v15 you must import it with ReactDOM" if (`typeof ReactDOM === 'undefined'`)
|
|
78
|
+
|
|
79
|
+
container = `container.$$class ? container[0] : container`
|
|
80
|
+
|
|
81
|
+
if block_given?
|
|
82
|
+
cb = %x{
|
|
83
|
+
function(){
|
|
84
|
+
setTimeout(function(){
|
|
85
|
+
#{yield}
|
|
86
|
+
}, 0)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
native = `ReactDOM.render(#{element.to_n}, container, cb)`
|
|
90
|
+
else
|
|
91
|
+
native = `ReactDOM.render(#{element.to_n}, container)`
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
return unless `#{native} !== null`
|
|
95
|
+
|
|
96
|
+
if `#{native}.__opalInstance !== undefined && #{native}.__opalInstance !== null`
|
|
97
|
+
`#{native}.__opalInstance`
|
|
98
|
+
elsif `ReactDOM.findDOMNode !== undefined && #{native}.nodeType === undefined`
|
|
99
|
+
`ReactDOM.findDOMNode(#{native})`
|
|
100
|
+
else
|
|
101
|
+
native
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.is_valid_element(element)
|
|
106
|
+
%x{ console.error("Warning: `is_valid_element` is deprecated in favor of `is_valid_element?`."); }
|
|
107
|
+
element.kind_of?(Hyperstack::Component::Element) && `React.isValidElement(#{element.to_n})`
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.is_valid_element?(element)
|
|
111
|
+
element.kind_of?(Hyperstack::Component::Element) && `React.isValidElement(#{element.to_n})`
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.render_to_string(element)
|
|
115
|
+
%x{ console.error("Warning: `Hyperstack::Component::ReactAPI.render_to_string` is deprecated in favor of `React::Server.render_to_string`."); }
|
|
116
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
117
|
+
Hyperstack::Internal::Component::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
|
|
118
|
+
else
|
|
119
|
+
raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.render_to_static_markup(element)
|
|
124
|
+
%x{ console.error("Warning: `Hyperstack::Component::ReactAPI.render_to_static_markup` is deprecated in favor of `React::Server.render_to_static_markup`."); }
|
|
125
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
126
|
+
Hyperstack::Internal::Component::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
|
|
127
|
+
else
|
|
128
|
+
raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.unmount_component_at_node(node)
|
|
133
|
+
if !(`typeof ReactDOM === 'undefined'`)
|
|
134
|
+
`ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
|
|
135
|
+
else
|
|
136
|
+
raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Hyperstack
|
|
2
|
+
module Component
|
|
3
|
+
module Server
|
|
4
|
+
def self.render_to_string(element)
|
|
5
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
6
|
+
Hyperstack::Internal::Component::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
|
|
7
|
+
else
|
|
8
|
+
raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.render_to_static_markup(element)
|
|
13
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
14
|
+
Hyperstack::Internal::Component::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
|
|
15
|
+
else
|
|
16
|
+
raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# to_key method returns a suitable unique id that can be used as
|
|
2
|
+
# a react `key`. Other classes may override to_key as needed
|
|
3
|
+
# for example hyper_mesh returns the object id of the internal
|
|
4
|
+
# backing record.
|
|
5
|
+
#
|
|
6
|
+
# to_key is automatically called on objects passed as keys for
|
|
7
|
+
# example Foo(key: my_object) results in Foo(key: my_object.to_key)
|
|
8
|
+
|
|
9
|
+
# for Boolean to_key can be true or false
|
|
10
|
+
class Boolean
|
|
11
|
+
def to_key
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -14,20 +14,23 @@ Element.instance_eval do
|
|
|
14
14
|
|
|
15
15
|
define_method :render do |container = nil, params = {}, &block|
|
|
16
16
|
# create an invisible component class and hang it off the DOM element
|
|
17
|
-
if `#{
|
|
18
|
-
klass = Class.new
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
others :all_the_params
|
|
23
|
-
end
|
|
24
|
-
`#{self.to_n}._reactrb_component_class = #{klass}`
|
|
17
|
+
if `#{to_n}._reactrb_component_class === undefined`
|
|
18
|
+
klass = Class.new
|
|
19
|
+
klass.include Hyperstack::Component
|
|
20
|
+
klass.others :all_the_params
|
|
21
|
+
`#{to_n}._reactrb_component_class = klass`
|
|
25
22
|
else
|
|
26
|
-
klass = `#{
|
|
23
|
+
klass = `#{to_n}._reactrb_component_class`
|
|
27
24
|
end
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
klass.class_eval do
|
|
26
|
+
render(container, params, &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Hyperstack::Component::ReactAPI.render(
|
|
30
|
+
Hyperstack::Component::ReactAPI.create_element(
|
|
31
|
+
klass, container: container, params: params, block: block
|
|
32
|
+
), self
|
|
33
|
+
)
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
# mount_components is useful for dynamically generated page segments for example
|
|
@@ -41,3 +44,5 @@ Element.instance_eval do
|
|
|
41
44
|
}
|
|
42
45
|
Element.expose :mount_components
|
|
43
46
|
end
|
|
47
|
+
|
|
48
|
+
DOM = Element
|
|
File without changes
|
|
@@ -5,11 +5,6 @@
|
|
|
5
5
|
#
|
|
6
6
|
# to_key is automatically called on objects passed as keys for
|
|
7
7
|
# example Foo(key: my_object) results in Foo(key: my_object.to_key)
|
|
8
|
-
class Object
|
|
9
|
-
def to_key
|
|
10
|
-
object_id
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
8
|
|
|
14
9
|
# for Number to_key can just be the number itself
|
|
15
10
|
class Number
|
|
@@ -17,10 +12,3 @@ class Number
|
|
|
17
12
|
self
|
|
18
13
|
end
|
|
19
14
|
end
|
|
20
|
-
|
|
21
|
-
# for Boolean to_key can be true or false
|
|
22
|
-
class Boolean
|
|
23
|
-
def to_key
|
|
24
|
-
self
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# Lazy load HTML tag constants in the form DIV or A
|
|
3
|
+
# This is needed to allow for a tags to be used in expressions like
|
|
4
|
+
# render(DIV) do ...
|
|
5
|
+
# By lazy loading we don't unecessarily create a pile of constant element tags
|
|
6
|
+
# that will probably never get used.
|
|
7
|
+
|
|
8
|
+
class Object
|
|
9
|
+
class << self
|
|
10
|
+
alias _reactrb_tag_original_const_missing const_missing
|
|
11
|
+
|
|
12
|
+
def const_missing(const_name)
|
|
13
|
+
# Opal uses const_missing to initially define things,
|
|
14
|
+
# so we always call the original, and respond to the exception
|
|
15
|
+
_reactrb_tag_original_const_missing(const_name)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
Hyperstack::Internal::Component::Tags.html_tag_class_for(const_name) || raise(e)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# to_key method returns a suitable unique id that can be used as
|
|
23
|
+
# a react `key`. Other classes may override to_key as needed
|
|
24
|
+
# for example hyper_mesh returns the object id of the internal
|
|
25
|
+
# backing record.
|
|
26
|
+
#
|
|
27
|
+
# to_key is automatically called on objects passed as keys for
|
|
28
|
+
# example Foo(key: my_object) results in Foo(key: my_object.to_key)
|
|
29
|
+
def to_key
|
|
30
|
+
object_id
|
|
31
|
+
end
|
|
32
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'hyperstack-config'
|
|
2
|
+
|
|
3
|
+
module Hyperstack
|
|
4
|
+
|
|
5
|
+
define_setting :prerendering, :off if RUBY_ENGINE != 'opal'
|
|
6
|
+
|
|
7
|
+
module Internal
|
|
8
|
+
module Component
|
|
9
|
+
class << self
|
|
10
|
+
def mounted_components
|
|
11
|
+
@__hyperstack_component_mounted_components ||= Set.new
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module Hyperstack
|
|
2
|
+
module Internal
|
|
3
|
+
module Component
|
|
4
|
+
# class level methods (macros) for components
|
|
5
|
+
module ClassMethods
|
|
6
|
+
|
|
7
|
+
def deprecation_warning(message)
|
|
8
|
+
Hyperstack.deprecation_warning(self, message)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def hyper_component?
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def param_accessor_style(*args)
|
|
16
|
+
props_wrapper.param_accessor_style(*args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def backtrace(*args)
|
|
20
|
+
@__hyperstack_component_dont_catch_exceptions = (args[0] == :none)
|
|
21
|
+
@__hyperstack_component_backtrace_off = @__hyperstack_component_dont_catch_exceptions || (args[0] == :off)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def append_backtrace(message_array, backtrace)
|
|
25
|
+
message_array << " #{backtrace[0]}"
|
|
26
|
+
backtrace[1..-1].each { |line| message_array << line }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render(container = nil, params = {}, &block)
|
|
30
|
+
if container
|
|
31
|
+
container = container.type if container.is_a? Hyperstack::Component::Element
|
|
32
|
+
define_method :render do
|
|
33
|
+
RenderingContext.render(container, params) { instance_eval(&block) if block }
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
define_method(:render) { instance_eval(&block) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# method missing will assume the method is a class name, and will treat this a render of
|
|
41
|
+
# of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
|
|
42
|
+
|
|
43
|
+
def method_missing(name, *args, &children)
|
|
44
|
+
if args.any? || !Hyperstack::Component::Element.respond_to?(:haml_class_name)
|
|
45
|
+
super
|
|
46
|
+
# this was:
|
|
47
|
+
# Object.method_missing(name, *args, &children) unless args.empty?
|
|
48
|
+
# Which does not show the actual component that broke.
|
|
49
|
+
# Not sure why this was like this, in tags.rb there is a similar method
|
|
50
|
+
# missing that calls Object._reactrb_import_component_class(name) which
|
|
51
|
+
# makes sure to autoimport the component. This is not needed here, as
|
|
52
|
+
# we already have the class.
|
|
53
|
+
else
|
|
54
|
+
RenderingContext.render(
|
|
55
|
+
self, class: Hyperstack::Component::Element.haml_class_name(name), &children
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def validator
|
|
61
|
+
return @__hyperstack_component_validator if @__hyperstack_component_validator
|
|
62
|
+
if superclass.respond_to?(:validator)
|
|
63
|
+
@__hyperstack_component_validator = superclass.validator.copy(props_wrapper)
|
|
64
|
+
else
|
|
65
|
+
@__hyperstack_component_validator = Validator.new(props_wrapper)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def prop_types
|
|
70
|
+
if self.validator
|
|
71
|
+
{
|
|
72
|
+
_componentValidator: %x{
|
|
73
|
+
function(props, propName, componentName) {
|
|
74
|
+
var errors = #{validator.validate(Hash.new(`props`))};
|
|
75
|
+
return #{`errors`.count > 0 ? `new Error(#{"In component `#{name}`\n" + `errors`.join("\n")})` : `undefined`};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
{}
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def default_props
|
|
85
|
+
validator.default_props
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def params(&block)
|
|
89
|
+
validator.build(&block)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def props_wrapper
|
|
93
|
+
return @__hyperstack_component_props_wrapper if @__hyperstack_component_props_wrapper
|
|
94
|
+
if superclass.respond_to? :props_wrapper
|
|
95
|
+
@__hyperstack_component_props_wrapper = Class.new(superclass.props_wrapper)
|
|
96
|
+
else
|
|
97
|
+
@__hyperstack_component_props_wrapper ||= Class.new(PropsWrapper)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def param(*args)
|
|
102
|
+
if args[0].is_a? Hash
|
|
103
|
+
options = args[0]
|
|
104
|
+
name = options.first[0]
|
|
105
|
+
default = options.first[1]
|
|
106
|
+
options.delete(name)
|
|
107
|
+
options.merge!({default: default})
|
|
108
|
+
else
|
|
109
|
+
name = args[0]
|
|
110
|
+
options = args[1] || {}
|
|
111
|
+
end
|
|
112
|
+
if options[:type] == Proc
|
|
113
|
+
options[:default] ||= nil
|
|
114
|
+
options[:allow_nil] = true unless options.key?(:allow_nil)
|
|
115
|
+
end
|
|
116
|
+
if options[:default]
|
|
117
|
+
validator.optional(name, options)
|
|
118
|
+
else
|
|
119
|
+
validator.requires(name, options)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def collect_other_params_as(name)
|
|
124
|
+
validator.all_other_params(name) { props }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
alias other_params collect_other_params_as
|
|
128
|
+
alias others collect_other_params_as
|
|
129
|
+
|
|
130
|
+
def triggers(name, opts = {})
|
|
131
|
+
aka = opts[:alias] || "#{name}!"
|
|
132
|
+
name = name =~ /^<(.+)>$/ ? name.gsub(/^<(.+)>$/, '\1') : "on_#{name}"
|
|
133
|
+
validator.event(name)
|
|
134
|
+
define_method(aka) { |*args| props[name]&.call(*args) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def define_state(*states, &block)
|
|
138
|
+
deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
|
|
139
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
|
140
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
|
141
|
+
states.each { |name| state(name => default_initial_value) } # was states_hash[name] = default_initial_value
|
|
142
|
+
states_hash.each { |name, value| state(name => value) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def export_state(*states, &block)
|
|
146
|
+
deprecation_warning "'export_state' is deprecated. Use the 'state' macro to declare states."
|
|
147
|
+
default_initial_value = (block && block.arity == 0) ? yield : nil
|
|
148
|
+
states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
|
|
149
|
+
states.each { |name| states_hash[name] = default_initial_value }
|
|
150
|
+
states_hash.each do |name, value|
|
|
151
|
+
state(name => value, scope: :class, reader: true)
|
|
152
|
+
singleton_class.define_method("#{name}!") do |*args|
|
|
153
|
+
mutate.__send__(name, *args)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def native_mixin(item)
|
|
159
|
+
native_mixins << item
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def native_mixins
|
|
163
|
+
@__hyperstack_component_native_mixins ||= []
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def static_call_back(name, &block)
|
|
167
|
+
static_call_backs[name] = block
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def static_call_backs
|
|
171
|
+
@__hyperstack_component_static_call_backs ||= {}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def export_component(opts = {})
|
|
175
|
+
export_name = (opts[:as] || name).split('::')
|
|
176
|
+
first_name = export_name.first
|
|
177
|
+
Native(`Opal.global`)[first_name] = add_item_to_tree(
|
|
178
|
+
Native(`Opal.global`)[first_name],
|
|
179
|
+
[ReactWrapper.create_native_react_class(self)] + export_name[1..-1].reverse
|
|
180
|
+
).to_n
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def imports(component_name)
|
|
184
|
+
ReactWrapper.import_native_component(
|
|
185
|
+
self, ReactWrapper.eval_native_react_component(component_name)
|
|
186
|
+
)
|
|
187
|
+
define_method(:render) {} # define a dummy render method - will never be called...
|
|
188
|
+
rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
|
|
189
|
+
raise "#{self} cannot import '#{component_name}': #{e.message}."
|
|
190
|
+
# rubocop:enable Lint/RescueException
|
|
191
|
+
ensure
|
|
192
|
+
self
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def add_item_to_tree(current_tree, new_item)
|
|
196
|
+
if Native(current_tree).class != Native::Object || new_item.length == 1
|
|
197
|
+
new_item.inject { |a, e| { e => a } }
|
|
198
|
+
else
|
|
199
|
+
Native(current_tree)[new_item.last] = add_item_to_tree(
|
|
200
|
+
Native(current_tree)[new_item.last], new_item[0..-2]
|
|
201
|
+
)
|
|
202
|
+
current_tree
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def to_n
|
|
207
|
+
ReactWrapper.class_eval('@@component_classes')[self]
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|