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
@@ -18,16 +18,22 @@ module Hyperstack
18
18
  def self.load_context(unique_id = nil, name = nil)
19
19
  # can be called on the client to force re-initialization for testing purposes
20
20
  if !unique_id || !@context || @context.unique_id != unique_id
21
- if on_opal_server?
22
- `console.history = []` rescue nil
23
- message = "************************ React Prerendering Context Initialized #{name} ***********************"
24
- else
25
- message = "************************ React Browser Context Initialized ****************************"
26
- end
21
+ message =
22
+ if on_opal_server?
23
+ `console.history = []` rescue nil
24
+ "************************ React Prerendering Context Initialized #{name} ***********************"
25
+ else
26
+ '************************ React Browser Context Initialized ****************************'
27
+ end
28
+
27
29
  log(message)
30
+
28
31
  @context = Context.new(unique_id)
29
32
  end
30
- @context
33
+
34
+ # True is returned here because this method is evaluated by MiniRacer,
35
+ # and can cause TypeError: Converting circular structure to JSON to raise
36
+ true
31
37
  end
32
38
  end
33
39
 
@@ -139,7 +145,6 @@ module Hyperstack
139
145
 
140
146
  def send_to_opal(method_name, *args)
141
147
  return unless @ctx
142
- args = [1] if args.length == 0
143
148
  Hyperstack::Internal::Component::Rails::ComponentLoader.new(@ctx).load!
144
149
  method_args = args.collect do |arg|
145
150
  quarg = "#{arg}".tr('"', "'")
@@ -1,5 +1,5 @@
1
1
  module Hyperstack
2
2
  module Component
3
- VERSION = '1.0.alpha1.1' # '1.0.alpha1.1'
3
+ VERSION = '1.0.alpha1.6' # '1.0.alpha1.5'
4
4
  end
5
5
  end
@@ -0,0 +1,27 @@
1
+ module Hyperstack
2
+ module Component
3
+ module WhileLoading
4
+ def __hyperstack_component_rescue_wrapper(child)
5
+ Hyperstack::Internal::Component::WhileLoadingWrapper(child: self, children_elements: child)
6
+ end
7
+
8
+ def resources_loading?
9
+ @__hyperstack_while_loading_waiting_on_resources
10
+ end
11
+
12
+ def resources_loaded?
13
+ !@__hyperstack_while_loading_waiting_on_resources
14
+ end
15
+
16
+ if Hyperstack::Component::IsomorphicHelpers.on_opal_client?
17
+ %x{
18
+ function onError(event) {
19
+ if (event.message.match(/^Uncaught NotQuiet: /)) event.preventDefault();
20
+ }
21
+
22
+ window.addEventListener('error', onError);
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # from Rails 6.0 activesupport. Remove once defined by Opal activesupport
2
+ class Array
3
+ # Removes and returns the elements for which the block returns a true value.
4
+ # If no block is given, an Enumerator is returned instead.
5
+ #
6
+ # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7
+ # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
8
+ # numbers # => [0, 2, 4, 6, 8]
9
+ def extract!
10
+ return to_enum(:extract!) { size } unless block_given?
11
+
12
+ extracted_elements = []
13
+
14
+ reject! do |element|
15
+ extracted_elements << element if yield(element)
16
+ end
17
+
18
+ extracted_elements
19
+ end
20
+ end
@@ -1,11 +1,9 @@
1
1
  Element.instance_eval do
2
2
  def self.find(selector)
3
- selector = begin
4
- selector.dom_node
5
- rescue
6
- selector
7
- end if `#{selector}.$dom_node !== undefined`
8
- `$(#{selector})`
3
+ if `typeof #{selector}['$respond_to?'] == 'function'` && selector.respond_to?(:dom_node)
4
+ selector = selector.dom_node
5
+ end
6
+ `jQuery(#{selector})`
9
7
  end
10
8
 
11
9
  def self.[](selector)
@@ -37,7 +35,7 @@ Element.instance_eval do
37
35
  # see react-rails documentation for more details
38
36
 
39
37
  %x{
40
- $.fn.mount_components = function() {
38
+ jQuery.fn.mount_components = function() {
41
39
  this.each(function(e) { ReactRailsUJS.mountComponents(e[0]) })
42
40
  return this;
43
41
  }
@@ -45,4 +43,20 @@ Element.instance_eval do
45
43
  Element.expose :mount_components
46
44
  end
47
45
 
48
- DOM = Element
46
+ module Hyperstack
47
+ module Internal
48
+ module Component
49
+ module InstanceMethods
50
+ def set_jq(var)
51
+ ->(val) { set(var).call(jQ[val]) }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class Object
59
+ def jQ
60
+ Element
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ # from Rails 6.0 activesupport. Remove once defined by Opal activesupport
2
+ module Enumerable
3
+ INDEX_WITH_DEFAULT = Object.new
4
+ private_constant :INDEX_WITH_DEFAULT
5
+ def index_with(default = INDEX_WITH_DEFAULT)
6
+ if block_given?
7
+ result = {}
8
+ each { |elem| result[elem] = yield(elem) }
9
+ result
10
+ elsif default != INDEX_WITH_DEFAULT
11
+ result = {}
12
+ each { |elem| result[elem] = default }
13
+ result
14
+ else
15
+ to_enum(:index_with) { size if respond_to?(:size) }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module Kernel
2
+ def pause(s, &block)
3
+ Promise.new.tap { |p| after(s) { p.resolve(*(block && [block.call])) }}
4
+ end
5
+ alias busy_sleep sleep
6
+ alias sleep pause
7
+ end
@@ -5,4 +5,4 @@ class String
5
5
  return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
6
6
  })`
7
7
  end
8
- end
8
+ end
@@ -1,7 +1,13 @@
1
1
  require 'hyperstack-config'
2
2
 
3
3
  module Hyperstack
4
-
5
4
  define_setting :prerendering, :off if RUBY_ENGINE != 'opal'
6
5
 
6
+ module Internal
7
+ module Component
8
+ class << self
9
+ attr_accessor :after_error_args
10
+ end
11
+ end
12
+ end
7
13
  end
@@ -20,6 +20,10 @@ module Hyperstack
20
20
  true
21
21
  end
22
22
 
23
+ def allow_deprecated_render_definition?
24
+ false
25
+ end
26
+
23
27
  def mounted_components
24
28
  Hyperstack::Component.mounted_components.select { |c| c.class <= self }
25
29
  end
@@ -38,17 +42,74 @@ module Hyperstack
38
42
  backtrace[1..-1].each { |line| message_array << line }
39
43
  end
40
44
 
45
+ def before_receive_props(*args, &block)
46
+ deprecation_warning "'before_receive_props' is deprecated. Use the 'before_new_params' macro instead."
47
+ before_new_params(*args, &block)
48
+ end
49
+
41
50
  def render(container = nil, params = {}, &block)
51
+ Tags.included(self)
42
52
  if container
43
- container = container.type if container.is_a? Hyperstack::Component::Element
44
- define_method :render do
45
- RenderingContext.render(container, params) { instance_eval(&block) if block }
53
+ container = container.element_type if container.is_a? Hyperstack::Component::Element
54
+ define_method(:__hyperstack_component_render) do
55
+ __hyperstack_component_select_wrappers do
56
+ RenderingContext.render(container, params) do
57
+ instance_eval(&block) if block
58
+ end
59
+ end
46
60
  end
47
61
  else
48
- define_method(:render) { instance_eval(&block) }
62
+ define_method(:__hyperstack_component_render) do
63
+ __hyperstack_component_select_wrappers do
64
+ instance_eval(&block)
65
+ end
66
+ end
49
67
  end
50
68
  end
51
69
 
70
+ # def while_loading(*args, &block)
71
+ # __hyperstack_component_after_render_hook do |element|
72
+ # element.while_loading(*args) { |*aargs| instance_exec(*aargs, &block) }
73
+ # end
74
+ # end
75
+
76
+ def on(*args, &block)
77
+ __hyperstack_component_after_render_hook do |element|
78
+ element.on(*args) { |*aargs| instance_exec(*aargs, &block) }
79
+ end
80
+ end
81
+
82
+ def rescues(*klasses, &block)
83
+ klasses = [StandardError] if klasses.empty?
84
+ __hyperstack_component_rescue_hook do |found, *args|
85
+ next [found, *args] if found || !klasses.detect { |klass| args[0].is_a? klass }
86
+ instance_exec(*args, &block)
87
+ [true, *args]
88
+ end
89
+ end
90
+
91
+ def before_render(*args, &block)
92
+ before_mount(*args, &block)
93
+ before_update(*args, &block)
94
+ end
95
+
96
+ def after_render(*args, &block)
97
+ after_mount(*args, &block)
98
+ after_update(*args, &block)
99
+ end
100
+
101
+ # [:while_loading, :on].each do |method|
102
+ # define_method(method) do |*args, &block|
103
+ # @@alias_id ||= 0
104
+ # @@alias_id += 1
105
+ # alias_name = "__hyperstack_attach_post_render_hook_#{@@alias_id}"
106
+ # alias_method alias_name, :__hyperstack_attach_post_render_hook
107
+ # define_method(:__hyperstack_attach_post_render_hook) do |element|
108
+ # send(alias_name, element.while_loading(*args) { instance_eval(&block) })
109
+ # end
110
+ # end
111
+ # end
112
+
52
113
  # method missing will assume the method is a class name, and will treat this a render of
53
114
  # of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
54
115
 
@@ -125,6 +186,10 @@ module Hyperstack
125
186
  options[:default] ||= nil
126
187
  options[:allow_nil] = true unless options.key?(:allow_nil)
127
188
  end
189
+ if name == :class
190
+ name = :className
191
+ options[:alias] ||= :Class
192
+ end
128
193
  if options[:default]
129
194
  validator.optional(name, options)
130
195
  else
@@ -138,14 +203,24 @@ module Hyperstack
138
203
 
139
204
  alias other_params collect_other_params_as
140
205
  alias others collect_other_params_as
206
+ alias other collect_other_params_as
207
+ alias opts collect_other_params_as
141
208
 
142
- def triggers(name, opts = {})
209
+ def fires(name, opts = {})
143
210
  aka = opts[:alias] || "#{name}!"
144
- name = name =~ /^<(.+)>$/ ? name.gsub(/^<(.+)>$/, '\1') : "on_#{name}"
211
+ name = if name =~ /^<(.+)>$/
212
+ name.gsub(/^<(.+)>$/, '\1')
213
+ elsif Hyperstack::Component::Event::BUILT_IN_EVENTS.include?("on#{name.event_camelize}")
214
+ "on#{name.event_camelize}"
215
+ else
216
+ "on_#{name}"
217
+ end
145
218
  validator.event(name)
146
219
  define_method(aka) { |*args| props[name]&.call(*args) }
147
220
  end
148
221
 
222
+ alias triggers fires
223
+
149
224
  def define_state(*states, &block)
150
225
  deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
151
226
  default_initial_value = (block && block.arity == 0) ? yield : nil
@@ -196,7 +271,8 @@ module Hyperstack
196
271
  ReactWrapper.import_native_component(
197
272
  self, ReactWrapper.eval_native_react_component(component_name)
198
273
  )
199
- define_method(:render) {} # define a dummy render method - will never be called...
274
+ render {}
275
+ #define_method(:render) {} # define a dummy render method - will never be called...
200
276
  rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
201
277
  raise "#{self} cannot import '#{component_name}': #{e.message}."
202
278
  # rubocop:enable Lint/RescueException
@@ -216,7 +292,7 @@ module Hyperstack
216
292
  end
217
293
 
218
294
  def to_n
219
- ReactWrapper.class_eval('@@component_classes')[self]
295
+ Hyperstack::Internal::Component::ReactWrapper.create_native_react_class(self)
220
296
  end
221
297
  end
222
298
  end
@@ -27,7 +27,9 @@ module Hyperstack
27
27
  Hyperstack::Internal::Component::RenderingContext.replace(
28
28
  self,
29
29
  Hyperstack::Internal::Component::RenderingContext.build do
30
- Hyperstack::Internal::Component::RenderingContext.render(type, build_new_properties(class_name, args), &new_block)
30
+ Hyperstack::Internal::Component::RenderingContext.render(
31
+ element_type, @properties, args, class: haml_class_name(class_name), &new_block
32
+ )
31
33
  end
32
34
  )
33
35
  end
@@ -39,17 +41,6 @@ module Hyperstack
39
41
  def haml_class_name(class_name)
40
42
  class_name.gsub(/__|_/, '__' => '_', '_' => '-')
41
43
  end
42
-
43
- private
44
-
45
- def build_new_properties(class_name, args)
46
- class_name = haml_class_name(class_name)
47
- new_props = @properties.dup
48
- new_props[:className] = "\
49
- #{class_name} #{new_props[:className]} #{args.delete(:class)} #{args.delete(:className)}\
50
- ".split(' ').uniq.join(' ')
51
- new_props.merge! args
52
- end
53
44
  end
54
45
  end
55
46
  end
@@ -9,7 +9,7 @@ module Hyperstack
9
9
  end
10
10
 
11
11
  def params
12
- if @__hyperstack_component_params_wrapper.param_accessor_style == :hyperstack
12
+ if [:hyperstack, :accessors].include? @__hyperstack_component_params_wrapper.param_accessor_style
13
13
  raise "params are now directly accessible via instance variables.\n"\
14
14
  ' to access the legacy behavior add `param_accessor_style = :legacy` '\
15
15
  "to your component class\n"\
@@ -23,18 +23,28 @@ module Hyperstack
23
23
  Hash.new(`#{@__hyperstack_component_native}.props`)
24
24
  end
25
25
 
26
- def refs
27
- Hash.new(`#{@__hyperstack_component_native}.refs`)
28
- end
29
-
30
26
  def dom_node
31
27
  `ReactDOM.findDOMNode(#{self}.__hyperstack_component_native)` # react >= v0.15.0
32
28
  end
33
29
 
30
+ def jq_node
31
+ ::Element[dom_node]
32
+ end
33
+
34
34
  def mounted?
35
35
  `(#{self}.__hyperstack_component_is_mounted === undefined) ? false : #{self}.__hyperstack_component_is_mounted`
36
36
  end
37
37
 
38
+ def pluralize(count, singular, plural = nil)
39
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
40
+ singular
41
+ else
42
+ plural || singular.pluralize
43
+ end
44
+
45
+ "#{count || 0} #{word}"
46
+ end
47
+
38
48
  def force_update!
39
49
  `#{self}.__hyperstack_component_native.forceUpdate()`
40
50
  self
@@ -51,6 +61,29 @@ module Hyperstack
51
61
 
52
62
  private
53
63
 
64
+ # can be overriden by the Router include
65
+ def __hyperstack_router_wrapper(&block)
66
+ ->() { instance_eval(&block) }
67
+ end
68
+
69
+ # can be overriden by including WhileLoading include
70
+ def __hyperstack_component_rescue_wrapper(child)
71
+ if self.class.callbacks?(:__hyperstack_component_rescue_hook)
72
+ Hyperstack::Internal::Component::RescueWrapper(child: self, children_elements: child)
73
+ else
74
+ child.call
75
+ end
76
+ end
77
+
78
+ def __hyperstack_component_select_wrappers(&block)
79
+ RescueWrapper.after_error_args = nil
80
+ __hyperstack_component_run_post_render_hooks(
81
+ __hyperstack_component_rescue_wrapper(
82
+ __hyperstack_router_wrapper(&block)
83
+ )
84
+ )
85
+ end
86
+
54
87
  def set_or_replace_state_or_prop(state_or_prop, method, &block)
55
88
  raise "No native ReactComponent associated" unless @__hyperstack_component_native
56
89
  `var state_prop_n = #{state_or_prop.shallow_to_n}`
@@ -9,7 +9,7 @@ module Hyperstack
9
9
  def instance_var_name_for(name)
10
10
  case Hyperstack.naming_convention
11
11
  when :camelize_params
12
- name.camelize
12
+ fix_suffix(name.camelize)
13
13
  when :prefix_params
14
14
  "_#{name}"
15
15
  else
@@ -17,6 +17,17 @@ module Hyperstack
17
17
  end
18
18
  end
19
19
 
20
+ def fix_suffix(name)
21
+ return unless name
22
+ if name =~ /\?$/
23
+ name[0..-2] + '_q'
24
+ elsif name =~ /\!$/
25
+ name[0..-2] + '_b'
26
+ else
27
+ name
28
+ end
29
+ end
30
+
20
31
  def param_accessor_style(style = nil)
21
32
  @param_accessor_style = style if style
22
33
  @param_accessor_style ||=
@@ -37,17 +48,23 @@ module Hyperstack
37
48
  end
38
49
 
39
50
  def define_param(name, param_type, aka = nil)
40
- meth_name = aka || name
41
- var_name = aka || instance_var_name_for(name)
42
- param_definitions[name] = lambda do |props|
43
- @component.instance_variable_set :"@#{var_name}", fetch_from_cache(name, param_type, props)
51
+ if param_accessor_style != :legacy || aka
52
+ meth_name = aka || name
53
+ var_name = fix_suffix(aka) || instance_var_name_for(name)
54
+ param_definitions[name] = lambda do |props|
55
+ @component.instance_variable_set :"@#{var_name}", val = fetch_from_cache(name, param_type, props)
56
+ next unless param_accessor_style == :accessors
57
+ `#{@component}[#{"$#{meth_name}"}] = function() { return #{val} }`
58
+ # @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
59
+ end
60
+ return if %i[hyperstack accessors].include? param_accessor_style
44
61
  end
45
62
  if param_type == Proc
46
- define_method(meth_name.to_sym) do |*args, &block|
63
+ define_method(name.to_sym) do |*args, &block|
47
64
  props[name].call(*args, &block) if props[name]
48
65
  end
49
66
  else
50
- define_method(meth_name.to_sym) do
67
+ define_method(name.to_sym) do
51
68
  fetch_from_cache(name, param_type, props)
52
69
  end
53
70
  end
@@ -56,7 +73,10 @@ module Hyperstack
56
73
  def define_all_others(name)
57
74
  var_name = instance_var_name_for(name)
58
75
  param_definitions[name] = lambda do |props|
59
- @component.instance_variable_set :"@#{var_name}", yield(props)
76
+ @component.instance_variable_set :"@#{var_name}", val = yield(props)
77
+ next unless param_accessor_style == :accessors
78
+ `#{@component}[#{"$#{name}"}] = function() { return #{val} }`
79
+ # @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
60
80
  end
61
81
  define_method(name.to_sym) do
62
82
  @_all_others_cache ||= yield(props)
@@ -70,7 +90,7 @@ module Hyperstack
70
90
 
71
91
  def initialize(component, incoming = nil)
72
92
  @component = component
73
- return if param_accessor_style == :legacy
93
+ #return if param_accessor_style == :legacy
74
94
  self.class.param_definitions.each_value do |initializer|
75
95
  instance_exec(incoming || props, &initializer)
76
96
  end