hyper-component 1.0.alpha1.1 → 1.0.alpha1.6

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/Gemfile +5 -0
  4. data/hyper-component.gemspec +6 -9
  5. data/lib/hyper-component.rb +10 -1
  6. data/lib/hyperstack/component.rb +78 -10
  7. data/lib/hyperstack/component/children.rb +1 -1
  8. data/lib/hyperstack/component/element.rb +93 -21
  9. data/lib/hyperstack/component/event.rb +1 -1
  10. data/lib/hyperstack/component/free_render.rb +21 -0
  11. data/lib/hyperstack/component/isomorphic_helpers.rb +13 -8
  12. data/lib/hyperstack/component/version.rb +1 -1
  13. data/lib/hyperstack/component/while_loading.rb +27 -0
  14. data/lib/hyperstack/ext/component/array.rb +20 -0
  15. data/lib/hyperstack/ext/component/element.rb +22 -8
  16. data/lib/hyperstack/ext/component/enumerable.rb +18 -0
  17. data/lib/hyperstack/ext/component/kernel.rb +7 -0
  18. data/lib/hyperstack/ext/component/string.rb +1 -1
  19. data/lib/hyperstack/internal/component.rb +7 -1
  20. data/lib/hyperstack/internal/component/class_methods.rb +84 -8
  21. data/lib/hyperstack/internal/component/haml.rb +3 -12
  22. data/lib/hyperstack/internal/component/instance_methods.rb +38 -5
  23. data/lib/hyperstack/internal/component/props_wrapper.rb +29 -9
  24. data/lib/hyperstack/internal/component/rails/component_mount.rb +12 -0
  25. data/lib/hyperstack/internal/component/rails/controller_helper.rb +20 -1
  26. data/lib/hyperstack/internal/component/rails/railtie.rb +3 -2
  27. data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +5 -1
  28. data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +4 -2
  29. data/lib/hyperstack/internal/component/react_wrapper.rb +79 -62
  30. data/lib/hyperstack/internal/component/rendering_context.rb +95 -45
  31. data/lib/hyperstack/internal/component/rescue_wrapper.rb +40 -0
  32. data/lib/hyperstack/internal/component/should_component_update.rb +1 -1
  33. data/lib/hyperstack/internal/component/tags.rb +12 -3
  34. data/lib/hyperstack/internal/component/top_level_rails_component.rb +4 -0
  35. data/lib/hyperstack/internal/component/while_loading_wrapper.rb +29 -0
  36. data/lib/react/react-source.rb +2 -2
  37. metadata +50 -81
  38. data/Gemfile.lock +0 -363
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 913095570e036cd245e669fabf4ff0a7fcdf4e08acf5742d405ea701a3d9c1e8
4
- data.tar.gz: 5518a5a4f50529f4972cd26cd1fcc24cc90311800ca1d7703e39b9a353988edb
3
+ metadata.gz: 280f2800045d849e7cc7eb6e517673861a9a0dd15ea96a204bc246f7c35fdf96
4
+ data.tar.gz: 5f15dcac1988d4aa3443db720bb253e4b0f1cf36fa59884fafdf8d73ade1bbb5
5
5
  SHA512:
6
- metadata.gz: 526f42bedbb8c7c16c6dda09a61795316aca083f4fdb6c468f0907c304debd94ccc6059c1c271b7140f94d274cef65310829b8f2b9e95727082cf8196975f166
7
- data.tar.gz: 647b5d89b107e933e243eba3d584f1bb566be559b164d728312e10ba96080935286992ddf37c3d3056de763660911cf1bf754c150cbbaa4425f456125b39856b
6
+ metadata.gz: d56717e2ef07a3b4e94e3dbe933a1710167bf8a6a9318f2667e986d055d14af3f2a86daa426ef20bdd394352398dbdd6877615daf4b7a1454e7bea40d9014c04
7
+ data.tar.gz: 5d7920af6aa4843c8141c41b44ca7120d46be0be7a752336a7d48fb2cfc5aad76c985b2b5b3586391c4baed240b9c3d3655c59db3db07a6bdf0890c07e784614
data/.gitignore CHANGED
@@ -41,3 +41,7 @@ spec/test_app/db
41
41
  # ignore IDE files
42
42
  .idea
43
43
  .vscode
44
+
45
+ # ignore Gemfile.locks https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
46
+ /spec/test_app/Gemfile.lock
47
+ /Gemfile.lock
data/Gemfile CHANGED
@@ -4,5 +4,10 @@ gem 'hyper-spec', path: '../hyper-spec'
4
4
  gem 'hyperstack-config', path: '../hyperstack-config'
5
5
  gem 'hyper-store', path: '../hyper-store'
6
6
  gem 'hyper-state', path: '../hyper-state'
7
+ unless ENV['OPAL_VERSION']&.match("0.11")
8
+ gem 'opal-browser', git: 'https://github.com/opal/opal-browser'
9
+ end
10
+ gem 'hyper-trace', path: '../hyper-trace'
11
+
7
12
  #gem 'puma', '~> 3.11.0' # As of adding, version 3.12.0 isn't working so we are locking
8
13
  gemspec
@@ -23,30 +23,27 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_dependency 'hyper-state', Hyperstack::Component::VERSION
25
25
  spec.add_dependency 'hyperstack-config', Hyperstack::Component::VERSION
26
- spec.add_dependency 'libv8', '~> 6.3.0' # see https://github.com/discourse/mini_racer/issues/92
27
- spec.add_dependency 'mini_racer', '~> 0.1.15'
28
- spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
29
26
  spec.add_dependency 'opal-activesupport', '~> 0.3.1'
30
27
  spec.add_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
31
28
 
32
- spec.add_development_dependency 'bundler', '~> 1.16.0'
29
+ spec.add_development_dependency 'bundler'
33
30
  spec.add_development_dependency 'chromedriver-helper'
34
31
  spec.add_development_dependency 'hyper-spec', Hyperstack::Component::VERSION
35
32
  spec.add_development_dependency 'jquery-rails'
36
33
  spec.add_development_dependency 'listen'
37
34
  spec.add_development_dependency 'mime-types'
35
+ spec.add_development_dependency 'mini_racer'
38
36
  spec.add_development_dependency 'nokogiri'
39
37
  spec.add_development_dependency 'opal-jquery'
40
- spec.add_development_dependency 'opal-rails', '~> 0.9.4'
41
- spec.add_development_dependency 'opal-rspec'
42
- spec.add_development_dependency 'pry'
38
+ spec.add_development_dependency 'opal-rails', '>= 0.9.4', '< 2.0'
43
39
  spec.add_development_dependency 'pry-rescue'
40
+ spec.add_development_dependency 'pry-stack_explorer'
44
41
  spec.add_development_dependency 'puma'
45
- spec.add_development_dependency 'rails', '>= 4.0.0'
42
+ spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
46
43
  spec.add_development_dependency 'rails-controller-testing'
47
44
  spec.add_development_dependency 'rake'
48
45
  spec.add_development_dependency 'rspec-rails'
49
46
  spec.add_development_dependency 'rubocop', '~> 0.51.0'
50
- spec.add_development_dependency 'sqlite3'
47
+ spec.add_development_dependency 'sqlite3', '~> 1.4.2'
51
48
  spec.add_development_dependency 'timecop', '~> 0.8.1'
52
49
  end
@@ -1,9 +1,9 @@
1
1
  require 'hyperstack/internal/component'
2
2
 
3
- Hyperstack.import 'hyper-state'
4
3
  Hyperstack.js_import 'react/react-source-browser', client_only: true, defines: %w[ReactDOM React]
5
4
  Hyperstack.js_import 'react/react-source-server', server_only: true, defines: 'React'
6
5
  Hyperstack.import 'browser/delay', client_only: true
6
+ Hyperstack.import 'browser/interval', client_only: true
7
7
  Hyperstack.js_import 'react_ujs', defines: 'ReactRailsUJS'
8
8
  Hyperstack.import 'hyper-component' # TODO: confirm this does not break anything. Added while converting hyperloop->hyperstack
9
9
  Hyperstack.import 'hyperstack/component/auto-import' # TODO: confirm we can cancel the import
@@ -22,14 +22,23 @@ if RUBY_ENGINE == 'opal'
22
22
  require 'hyperstack/component/event'
23
23
  require 'hyperstack/internal/component/rendering_context'
24
24
  require 'hyperstack/ext/component/object'
25
+ require 'hyperstack/ext/component/kernel'
25
26
  require 'hyperstack/ext/component/number'
26
27
  require 'hyperstack/ext/component/boolean'
28
+ require 'hyperstack/ext/component/array'
29
+ require 'hyperstack/ext/component/enumerable'
27
30
  require 'hyperstack/component/isomorphic_helpers'
28
31
  require 'hyperstack/component/react_api'
29
32
  require 'hyperstack/internal/component/top_level_rails_component'
33
+ require 'hyperstack/component/while_loading'
34
+ require 'hyperstack/component/free_render'
35
+ require 'hyperstack/internal/component/rescue_wrapper'
36
+ require 'hyperstack/internal/component/while_loading_wrapper'
37
+
30
38
  require 'hyperstack/component/version'
31
39
  else
32
40
  require 'opal'
41
+ require 'hyper-state'
33
42
  require 'opal-activesupport'
34
43
  require 'hyperstack/component/version'
35
44
  require 'hyperstack/internal/component/rails'
@@ -1,7 +1,6 @@
1
1
  require 'hyperstack/ext/component/string'
2
2
  require 'hyperstack/ext/component/hash'
3
3
  require 'active_support/core_ext/class/attribute'
4
- require 'hyperstack/internal/auto_unmount'
5
4
  require 'hyperstack/internal/component/rendering_context'
6
5
  require 'hyperstack/internal/component'
7
6
  require 'hyperstack/internal/component/instance_methods'
@@ -19,15 +18,60 @@ module Hyperstack
19
18
  base.include(Hyperstack::Internal::Component::ShouldComponentUpdate)
20
19
  base.class_eval do
21
20
  class_attribute :initial_state
22
- define_callback :before_mount
21
+
22
+ method_args_deprecation_check = lambda do |name, sself, proc, *args|
23
+ if proc.arity.zero?
24
+ args = []
25
+ else
26
+ deprecation_warning "In the future #{name} callbacks will not receive any parameters."
27
+ end
28
+ sself.instance_exec(*args, &proc)
29
+ args
30
+ end
31
+
32
+ define_callback :before_mount, before_call_hook: method_args_deprecation_check
23
33
  define_callback :after_mount
24
- define_callback :before_receive_props
25
- define_callback :before_update
34
+ define_callback(
35
+ :before_new_params,
36
+ after_define_hook: lambda do |klass|
37
+ klass.deprecation_warning "`before_new_params` has been deprecated. The base "\
38
+ "method componentWillReceiveProps is deprecated in React without replacement"
39
+ end
40
+ )
41
+ define_callback(:before_update, before_call_hook: method_args_deprecation_check)
26
42
  define_callback :after_update
27
- #define_callback :before_unmount defined already by Async module
28
- define_callback(:after_error) { Hyperstack::Internal::Component::ReactWrapper.add_after_error_hook(base) }
43
+ define_callback(
44
+ :__hyperstack_component_after_render_hook,
45
+ before_call_hook: ->(_, sself, proc, *args) { [*sself.instance_exec(*args, &proc)] }
46
+ )
47
+ define_callback(
48
+ :__hyperstack_component_rescue_hook,
49
+ before_call_hook: ->(_, sself, proc, *args) { sself.instance_exec(*args, &proc) }
50
+ )
51
+ define_callback(
52
+ :after_error,
53
+ after_define_hook: ->(klass) { Hyperstack::Internal::Component::ReactWrapper.add_after_error_hook(klass) }
54
+ )
29
55
  end
30
56
  base.extend(Hyperstack::Internal::Component::ClassMethods)
57
+ unless `Opal.__hyperstack_component_original_defn`
58
+ %x{
59
+ Opal.__hyperstack_component_original_defn = Opal.defn
60
+ Opal.defn = function(klass, name, fn) {
61
+ #{
62
+ if `klass`.respond_to?(:hyper_component?) && !(`klass` < Hyperstack::Component::NativeLibrary)
63
+ if `name == '$render'` && !`klass`.allow_deprecated_render_definition?
64
+ Hyperstack.deprecation_warning(`klass`, 'Do not directly define the render method. Use the render macro instead.')
65
+ elsif `name == '$__hyperstack_component_render'`
66
+ `name = '$render'`
67
+ end
68
+ end
69
+ }
70
+ Opal.__hyperstack_component_original_defn(klass, name, fn)
71
+ }
72
+ }
73
+ nil
74
+ end
31
75
  end
32
76
 
33
77
  def self.mounted_components
@@ -75,13 +119,14 @@ module Hyperstack
75
119
  def component_did_mount
76
120
  observing(update_objects: true) do
77
121
  run_callback(:after_mount)
122
+ Hyperstack::Internal::Component::RenderingContext.quiet_test(self)
78
123
  end
79
124
  end
80
125
 
81
126
  def component_will_receive_props(next_props)
82
127
  # need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
83
128
  # for now we are just using it to clear processed_params
84
- observing(immediate_update: true) { run_callback(:before_receive_props, next_props) }
129
+ observing(immediate_update: true) { run_callback(:before_new_params, next_props) }
85
130
  @__hyperstack_component_receiving_props = true
86
131
  end
87
132
 
@@ -94,7 +139,10 @@ module Hyperstack
94
139
  end
95
140
 
96
141
  def component_did_update(prev_props, prev_state)
97
- observing(update_objects: true) { run_callback(:after_update, prev_props, prev_state) }
142
+ observing(update_objects: true) do
143
+ run_callback(:after_update, prev_props, prev_state)
144
+ Hyperstack::Internal::Component::RenderingContext.quiet_test(self)
145
+ end
98
146
  end
99
147
 
100
148
  def component_will_unmount
@@ -106,7 +154,9 @@ module Hyperstack
106
154
  end
107
155
 
108
156
  def component_did_catch(error, info)
109
- observing { run_callback(:after_error, error, info) }
157
+ observing do
158
+ run_callback(:after_error, error, info)
159
+ end
110
160
  end
111
161
 
112
162
  def mutations(_objects)
@@ -138,9 +188,27 @@ module Hyperstack
138
188
  @__hyperstack_component_waiting_on_resources
139
189
  end
140
190
 
191
+ def __hyperstack_component_run_post_render_hooks(element)
192
+ run_callback(:__hyperstack_component_after_render_hook, element).first
193
+ end
194
+
195
+ def _run_before_render_callbacks
196
+ # eventually add before_update if @__component_mounted
197
+ # but that will not perfectly match the current React behavior.
198
+ # However that behavior is deprecated, and so once we have
199
+ # given a chance for the code to be updated we can switch this over
200
+ # and switch the deprecation notice to an error.
201
+ component_will_mount unless @__component_mounted
202
+ @__component_mounted = true
203
+ end
204
+
205
+
141
206
  def _render_wrapper
207
+ _run_before_render_callbacks
142
208
  observing(rendering: true) do
143
- element = Hyperstack::Internal::Component::RenderingContext.render(nil) { render || '' }
209
+ element = Hyperstack::Internal::Component::RenderingContext.render(nil) do
210
+ render || ""
211
+ end
144
212
  @__hyperstack_component_waiting_on_resources =
145
213
  element.waiting_on_resources if element.respond_to? :waiting_on_resources
146
214
  element
@@ -22,7 +22,7 @@ module Hyperstack
22
22
  %x{
23
23
  React.Children.forEach(#{@children}, function(context){
24
24
  #{
25
- element = Element.new(`context`)
25
+ element = Element.new(`context`, :wrap_child)
26
26
  block.call(element)
27
27
  collection << element
28
28
  }
@@ -15,29 +15,89 @@ module Hyperstack
15
15
  # by using method missing
16
16
  #
17
17
  class Element
18
- include Native
19
18
 
20
- alias_native :element_type, :type
21
- alias_native :props, :props
19
+ # $$typeof: Symbol(react.element)
20
+ # key: null
21
+ # props: {}
22
+ # ref: null
23
+ # type: "div"
24
+ # _ _owner: null
22
25
 
23
- attr_reader :type
26
+ attr_reader :type
27
+
28
+ attr_reader :element_type # change this so name does not conflict - change to element type
24
29
  attr_reader :properties
25
30
  attr_reader :block
31
+ attr_reader :to_n
26
32
 
27
33
  attr_accessor :waiting_on_resources
28
34
 
29
- def initialize(native_element, type = nil, properties = {}, block = nil)
30
- @type = type
35
+ def set_native_attributes(native_element)
36
+ @key = `native_element.key`
37
+ @props = `native_element.props`
38
+ @ref = `native_element.ref`
39
+ @type = `native_element.type`
40
+ @_owner = `native_element._owner`
41
+ @_props_as_hash = Hash.new(@props)
42
+ end
43
+
44
+ def props
45
+ @_props_as_hash
46
+ end
47
+
48
+ def convert_string(native_element, element_type, props, block)
49
+ return native_element unless `native_element['$is_a?']`
50
+ return native_element unless native_element.is_a? String
51
+ raise "Internal Error Element.new called with string, but non-nil props or block" if !props.empty? || block
52
+
53
+ if element_type == :wrap_child
54
+ `React.createElement(React.Fragment, null, [native_element])`
55
+ else
56
+ `React.createElement(native_element, null)`
57
+ end
58
+ end
59
+
60
+ def initialize(native_element, element_type = nil, properties = {}, block = nil)
61
+
62
+ native_element = convert_string(native_element, element_type, properties, block)
63
+ @element_type = element_type unless element_type == :wrap_child
31
64
  @properties = (`typeof #{properties} === 'undefined'` ? nil : properties) || {}
32
65
  @block = block
33
- @native = native_element
66
+ `#{self}.$$typeof = native_element.$$typeof`
67
+ @to_n = self
68
+ set_native_attributes(native_element)
69
+ rescue Exception
34
70
  end
35
71
 
36
- # Attach event handlers.
72
+ def children
73
+ `#{@props}.children`
74
+ end
75
+
76
+ def _update_ref(x)
77
+ @_ref = x
78
+ @_child_element._update_ref(x) if @_child_element
79
+ end
80
+
81
+ def ref # this will not conflict with React's on ref attribute okay because its $ref!!!
82
+ return @_ref if @_ref
83
+ raise("The instance of #{self.element_type} has not been mounted yet") if properties[:ref]
84
+ raise("Attempt to get a ref on #{self.element_type} which is a static component.")
85
+ end
86
+
87
+ def dom_node
88
+ `typeof #{ref}.$dom_node == 'function'` ? ref.dom_node : ref
89
+ end
90
+
91
+ # Attach event handlers. skip false, nil and blank event names
37
92
 
38
93
  def on(*event_names, &block)
39
- event_names.each { |event_name| merge_event_prop!(event_name, &block) }
40
- @native = `React.cloneElement(#{@native}, #{@properties.shallow_to_n})`
94
+ any_found = false
95
+ event_names.each do |event_name|
96
+ next unless event_name && event_name.strip != ''
97
+ merge_event_prop!(event_name, &block)
98
+ any_found = true
99
+ end
100
+ set_native_attributes(`React.cloneElement(#{self}, #{@properties.shallow_to_n})`) if any_found
41
101
  self
42
102
  end
43
103
 
@@ -45,14 +105,14 @@ module Hyperstack
45
105
  # Used for elements that are not yet in DOM, i.e. they are provided as children
46
106
  # or they have been explicitly removed from the rendering context using the delete method.
47
107
 
48
- def render(props = {}, &new_block)
108
+ def render(*props)
49
109
  if props.empty?
50
110
  Hyperstack::Internal::Component::RenderingContext.render(self)
51
111
  else
52
- props = Hyperstack::Internal::Component::ReactWrapper.convert_props(props)
53
- Hyperstack::Internal::Component::RenderingContext.render(
54
- Element.new(`React.cloneElement(#{@native}, #{props.shallow_to_n})`,
55
- type, @properties.merge(props), block)
112
+ props = Hyperstack::Internal::Component::ReactWrapper.convert_props(element_type, @properties, *props)
113
+ @_child_element = Hyperstack::Internal::Component::RenderingContext.render(
114
+ Element.new(`React.cloneElement(#{self}, #{props.shallow_to_n})`,
115
+ element_type, props, block)
56
116
  )
57
117
  end
58
118
  end
@@ -60,11 +120,12 @@ module Hyperstack
60
120
  # Delete (remove) element from rendering context, the element may later be added back in
61
121
  # using the render method.
62
122
 
63
- def delete
123
+ def ~
64
124
  Hyperstack::Internal::Component::RenderingContext.delete(self)
65
125
  end
66
126
  # Deprecated version of delete method
67
- alias as_node delete
127
+ alias as_node ~
128
+ alias delete ~
68
129
 
69
130
  private
70
131
 
@@ -89,26 +150,37 @@ module Hyperstack
89
150
  merge_built_in_event_prop! name, &block
90
151
  elsif event_name == :enter
91
152
  merge_built_in_event_prop!('onKeyDown') { |evt| yield(evt) if evt.key_code == 13 }
92
- elsif @type.instance_variable_get('@native_import')
153
+ elsif element_type.instance_variable_get('@native_import')
93
154
  merge_component_event_prop! name, &block
94
155
  else
95
156
  merge_component_event_prop! "on_#{event_name}", &block
96
157
  end
97
158
  end
98
159
 
99
- def merge_built_in_event_prop!(prop_name)
160
+ def merge_built_in_event_prop!(prop_name, &block)
100
161
  @properties.merge!(
101
162
  prop_name => %x{
102
163
  function(){
103
164
  var react_event = arguments[0];
165
+ if (arguments.length == 0 || !react_event.nativeEvent) {
166
+ return #{yield(*Array(`arguments`))}
167
+ }
104
168
  var all_args;
105
169
  var other_args;
106
170
  if (arguments.length > 1) {
107
171
  all_args = Array.prototype.slice.call(arguments);
108
172
  other_args = all_args.slice(1, arguments.length);
109
- return #{yield(Event.new(`react_event`), *(`other_args`))};
173
+ return #{
174
+ Internal::State::Mapper.ignore_bulk_updates(
175
+ Event.new(`react_event`), *(`other_args`), &block
176
+ )
177
+ };
110
178
  } else {
111
- return #{yield(Event.new(`react_event`))};
179
+ return #{
180
+ Internal::State::Mapper.ignore_bulk_updates(
181
+ Event.new(`react_event`), &block
182
+ )
183
+ };
112
184
  }
113
185
  }
114
186
  }
@@ -1,7 +1,7 @@
1
1
  module Hyperstack
2
2
  module Component
3
3
  class Event
4
- include Native
4
+ include Native::Wrapper
5
5
  alias_native :bubbles, :bubbles
6
6
  alias_native :cancelable, :cancelable
7
7
  alias_native :current_target, :currentTarget
@@ -0,0 +1,21 @@
1
+ module Hyperstack
2
+ module Component
3
+ module FreeRender
4
+ def self.included(base)
5
+ base.instance_eval do
6
+ alias :hyperstack_component_original_meth_missing method_missing
7
+ def method_missing(name, *args, &block)
8
+ if const_defined?(name) &&
9
+ (klass = const_get(name)) &&
10
+ ((klass.is_a?(Class) && klass.method_defined?(:render)) ||
11
+ Hyperstack::Internal::Component::Tags::HTML_TAGS.include?(klass))
12
+ render(klass, *args, &block)
13
+ else
14
+ hyperstack_component_original_meth_missing(name, *args, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end