hyper-component 0.99.6 → 1.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|