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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -3
  3. data/Gemfile +4 -3
  4. data/Gemfile.lock +51 -36
  5. data/{misc/how-component-name-lookup-works.md → how-component-name-lookup-works.md} +1 -1
  6. data/hyper-component.gemspec +9 -8
  7. data/lib/hyper-component.rb +31 -43
  8. data/lib/hyperstack/component.rb +145 -0
  9. data/lib/hyperstack/component/auto-import.rb +44 -0
  10. data/lib/hyperstack/component/children.rb +40 -0
  11. data/lib/hyperstack/component/element.rb +129 -0
  12. data/lib/hyperstack/component/event.rb +78 -0
  13. data/lib/hyperstack/component/haml.rb +18 -0
  14. data/lib/hyperstack/component/isomorphic_helpers.rb +235 -0
  15. data/lib/hyperstack/component/jquery.rb +2 -0
  16. data/lib/hyperstack/component/native_library.rb +92 -0
  17. data/lib/hyperstack/component/react_api.rb +142 -0
  18. data/lib/hyperstack/component/server.rb +21 -0
  19. data/lib/hyperstack/component/version.rb +5 -0
  20. data/lib/hyperstack/ext/component/boolean.rb +14 -0
  21. data/lib/{react/ext/opal-jquery → hyperstack/ext/component}/element.rb +17 -12
  22. data/lib/{react/ext → hyperstack/ext/component}/hash.rb +0 -0
  23. data/lib/{react/to_key.rb → hyperstack/ext/component/number.rb} +0 -12
  24. data/lib/hyperstack/ext/component/object.rb +32 -0
  25. data/lib/{reactive-ruby → hyperstack/ext/component}/serializers.rb +0 -0
  26. data/lib/{react/ext → hyperstack/ext/component}/string.rb +0 -0
  27. data/lib/hyperstack/internal/component.rb +16 -0
  28. data/lib/hyperstack/internal/component/class_methods.rb +212 -0
  29. data/lib/hyperstack/internal/component/haml.rb +56 -0
  30. data/lib/hyperstack/internal/component/instance_methods.rb +92 -0
  31. data/lib/hyperstack/internal/component/props_wrapper.rb +125 -0
  32. data/lib/hyperstack/internal/component/rails.rb +11 -0
  33. data/lib/hyperstack/internal/component/rails/component_loader.rb +49 -0
  34. data/lib/hyperstack/internal/component/rails/component_mount.rb +52 -0
  35. data/lib/{reactive-ruby → hyperstack/internal/component}/rails/controller_helper.rb +0 -0
  36. data/lib/hyperstack/internal/component/rails/railtie.rb +24 -0
  37. data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +52 -0
  38. data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +52 -0
  39. data/lib/hyperstack/internal/component/react_wrapper.rb +308 -0
  40. data/lib/hyperstack/internal/component/rendering_context.rb +165 -0
  41. data/lib/hyperstack/internal/component/should_component_update.rb +101 -0
  42. data/lib/hyperstack/internal/component/tags.rb +109 -0
  43. data/lib/hyperstack/internal/component/top_level_rails_component.rb +83 -0
  44. data/lib/hyperstack/internal/component/validator.rb +149 -0
  45. data/lib/react/react-source.rb +2 -2
  46. data/unmounting-objects.md +78 -0
  47. metadata +73 -85
  48. data/DOCS.md +0 -1515
  49. data/LICENSE +0 -19
  50. data/README.md +0 -49
  51. data/lib/hyper-component/jquery.rb +0 -2
  52. data/lib/rails-helpers/top_level_rails_component.rb +0 -79
  53. data/lib/react/api.rb +0 -272
  54. data/lib/react/callbacks.rb +0 -42
  55. data/lib/react/children.rb +0 -38
  56. data/lib/react/component.rb +0 -189
  57. data/lib/react/component/api.rb +0 -70
  58. data/lib/react/component/base.rb +0 -13
  59. data/lib/react/component/class_methods.rb +0 -175
  60. data/lib/react/component/dsl_instance_methods.rb +0 -23
  61. data/lib/react/component/params.rb +0 -6
  62. data/lib/react/component/props_wrapper.rb +0 -90
  63. data/lib/react/component/should_component_update.rb +0 -99
  64. data/lib/react/component/tags.rb +0 -116
  65. data/lib/react/config.rb +0 -5
  66. data/lib/react/element.rb +0 -167
  67. data/lib/react/event.rb +0 -76
  68. data/lib/react/native_library.rb +0 -87
  69. data/lib/react/object.rb +0 -15
  70. data/lib/react/ref_callback.rb +0 -31
  71. data/lib/react/rendering_context.rb +0 -149
  72. data/lib/react/server.rb +0 -19
  73. data/lib/react/state_wrapper.rb +0 -23
  74. data/lib/react/test.rb +0 -16
  75. data/lib/react/test/dsl.rb +0 -17
  76. data/lib/react/test/matchers/render_html_matcher.rb +0 -56
  77. data/lib/react/test/rspec.rb +0 -15
  78. data/lib/react/test/session.rb +0 -37
  79. data/lib/react/test/utils.rb +0 -71
  80. data/lib/react/top_level.rb +0 -110
  81. data/lib/react/top_level_render.rb +0 -30
  82. data/lib/react/validator.rb +0 -132
  83. data/lib/reactive-ruby/component_loader.rb +0 -43
  84. data/lib/reactive-ruby/isomorphic_helpers.rb +0 -233
  85. data/lib/reactive-ruby/rails.rb +0 -8
  86. data/lib/reactive-ruby/rails/component_mount.rb +0 -48
  87. data/lib/reactive-ruby/rails/railtie.rb +0 -20
  88. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +0 -46
  89. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +0 -46
  90. data/lib/reactive-ruby/version.rb +0 -5
  91. data/lib/reactrb/auto-import.rb +0 -27
  92. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +0 -3
  93. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +0 -5
  94. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +0 -2
  95. data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +0 -6
  96. data/misc/generators/reactive_ruby/test_app/templates/script/rails +0 -5
  97. data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +0 -13
  98. data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +0 -11
  99. data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +0 -14
  100. data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  101. data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +0 -121
  102. data/misc/hyperloop-logo-small-pink.png +0 -0
  103. data/misc/logo1.png +0 -0
  104. data/misc/logo2.png +0 -0
  105. data/misc/logo3.png +0 -0
  106. data/path_release_steps.md +0 -9
@@ -0,0 +1,2 @@
1
+ require 'opal-jquery'
2
+ require 'hyperstack/ext/component/element'
@@ -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,5 @@
1
+ module Hyperstack
2
+ module Component
3
+ VERSION = '1.0.alpha1' # '1.0.alpha1'
4
+ end
5
+ 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 `#{self.to_n}._reactrb_component_class === undefined`
18
- klass = Class.new(Hyperloop::Component) do
19
- # react won't rerender the components unless it sees some params
20
- # changing, so we just copy them all in, but we still just reuse
21
- # the render macro to define the action
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 = `#{self.to_n}._reactrb_component_class`
23
+ klass = `#{to_n}._reactrb_component_class`
27
24
  end
28
- # define / redefine the render method
29
- klass.render(container, params, &block)
30
- React.render(React.create_element(klass, {container: container, params: params, block: block}), self)
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
@@ -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
@@ -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