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

Sign up to get free protection for your applications and to get access to all the features.
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