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.
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