hyper-component 0.12.3 → 0.99.0

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 (79) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +42 -41
  4. data/.travis.yml +29 -0
  5. data/CHANGELOG.md +143 -0
  6. data/DOCS.md +1515 -0
  7. data/Gemfile +5 -2
  8. data/Gemfile.lock +244 -193
  9. data/LICENSE +5 -7
  10. data/README.md +49 -0
  11. data/Rakefile +40 -0
  12. data/hyper-component.gemspec +41 -31
  13. data/lib/hyper-component.rb +44 -9
  14. data/lib/rails-helpers/top_level_rails_component.rb +79 -0
  15. data/lib/react/api.rb +270 -0
  16. data/lib/react/callbacks.rb +42 -0
  17. data/lib/react/children.rb +38 -0
  18. data/lib/react/component.rb +189 -0
  19. data/lib/react/component/api.rb +70 -0
  20. data/lib/react/component/base.rb +13 -0
  21. data/lib/react/component/class_methods.rb +175 -0
  22. data/lib/react/component/dsl_instance_methods.rb +23 -0
  23. data/lib/react/component/params.rb +6 -0
  24. data/lib/react/component/props_wrapper.rb +90 -0
  25. data/lib/react/component/should_component_update.rb +99 -0
  26. data/lib/react/component/tags.rb +116 -0
  27. data/lib/react/config.rb +5 -0
  28. data/lib/react/element.rb +159 -0
  29. data/lib/react/event.rb +76 -0
  30. data/lib/react/ext/hash.rb +9 -0
  31. data/lib/react/ext/opal-jquery/element.rb +37 -0
  32. data/lib/react/ext/string.rb +8 -0
  33. data/lib/react/native_library.rb +87 -0
  34. data/lib/react/object.rb +15 -0
  35. data/lib/react/react-source-server.rb +3 -0
  36. data/lib/react/react-source.rb +17 -0
  37. data/lib/react/ref_callback.rb +31 -0
  38. data/lib/react/rendering_context.rb +149 -0
  39. data/lib/react/server.rb +19 -0
  40. data/lib/react/state_wrapper.rb +23 -0
  41. data/lib/react/test.rb +16 -0
  42. data/lib/react/test/dsl.rb +17 -0
  43. data/lib/react/test/matchers/render_html_matcher.rb +56 -0
  44. data/lib/react/test/rspec.rb +15 -0
  45. data/lib/react/test/session.rb +37 -0
  46. data/lib/react/test/utils.rb +71 -0
  47. data/lib/react/to_key.rb +26 -0
  48. data/lib/react/top_level.rb +110 -0
  49. data/lib/react/top_level_render.rb +28 -0
  50. data/lib/react/validator.rb +132 -0
  51. data/lib/reactive-ruby/component_loader.rb +43 -0
  52. data/lib/reactive-ruby/isomorphic_helpers.rb +233 -0
  53. data/lib/reactive-ruby/rails.rb +8 -0
  54. data/lib/reactive-ruby/rails/component_mount.rb +48 -0
  55. data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
  56. data/lib/reactive-ruby/rails/railtie.rb +20 -0
  57. data/lib/reactive-ruby/serializers.rb +23 -0
  58. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +46 -0
  59. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
  60. data/lib/{hyperloop/component → reactive-ruby}/version.rb +1 -1
  61. data/lib/reactrb/auto-import.rb +27 -0
  62. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
  63. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
  64. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  65. data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  66. data/misc/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  67. data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  68. data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  69. data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  70. data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  71. data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +121 -0
  72. data/misc/how-component-name-lookup-works.md +145 -0
  73. data/misc/hyperloop-logo-small-pink.png +0 -0
  74. data/misc/logo1.png +0 -0
  75. data/misc/logo2.png +0 -0
  76. data/misc/logo3.png +0 -0
  77. data/path_release_steps.md +9 -0
  78. metadata +260 -37
  79. data/CODE_OF_CONDUCT.md +0 -49
@@ -0,0 +1,76 @@
1
+ module React
2
+ class Event
3
+ include Native
4
+ alias_native :bubbles, :bubbles
5
+ alias_native :cancelable, :cancelable
6
+ alias_native :current_target, :currentTarget
7
+ alias_native :default_prevented, :defaultPrevented
8
+ alias_native :event_phase, :eventPhase
9
+ alias_native :is_trusted?, :isTrusted
10
+ alias_native :native_event, :nativeEvent
11
+ alias_native :target, :target
12
+ alias_native :timestamp, :timeStamp
13
+ alias_native :event_type, :type
14
+ alias_native :prevent_default, :preventDefault
15
+ alias_native :stop_propagation, :stopPropagation
16
+ # Clipboard
17
+ alias_native :clipboard_data, :clipboardData
18
+ # Keyboard
19
+ alias_native :alt_key, :altKey
20
+ alias_native :char_code, :charCode
21
+ alias_native :ctrl_key, :ctrlKey
22
+ alias_native :get_modifier_state, :getModifierState
23
+ alias_native :key, :key
24
+ alias_native :key_code, :keyCode
25
+ alias_native :locale, :locale
26
+ alias_native :location, :location
27
+ alias_native :meta_key, :metaKey
28
+ alias_native :repeat, :repeat
29
+ alias_native :shift_key, :shiftKey
30
+ alias_native :which, :which
31
+ # Focus
32
+ alias_native :related_target, :relatedTarget
33
+ # Mouse
34
+ # aliased above: alias_native :alt_key, :altKey
35
+ alias_native :button, :button
36
+ alias_native :buttons, :buttons
37
+ alias_native :client_x, :clientX
38
+ alias_native :client_y, :clientY
39
+ # aliased above: alias_native :ctrl_key, :ctrlKey
40
+ alias_native :get_modifier_state, :getModifierState
41
+ # aliased above: alias_native :meta_key, :metaKey
42
+ alias_native :page_x, :pageX
43
+ alias_native :page_y, :pageY
44
+ # aliased above: alias_native :related_target, :relatedTarget
45
+ alias_native :screen_x, :screen_x
46
+ alias_native :screen_y, :screen_y
47
+ # aliased above: alias_native :shift_key, :shift_key
48
+ # Touch
49
+ # aliased above: alias_native :alt_key, :altKey
50
+ alias_native :changed_touches, :changedTouches
51
+ # aliased above: alias_native :ctrl_key, :ctrlKey
52
+ # aliased above: alias_native :get_modifier_state, :getModifierState
53
+ # aliased above: alias_native :meta_key, :metaKey
54
+ # aliased above: alias_native :shift_key, :shiftKey
55
+ alias_native :target_touches, :targetTouches
56
+ alias_native :touches, :touches
57
+ # UI
58
+ alias_native :detail, :detail
59
+ alias_native :view, :view
60
+ # Wheel
61
+ alias_native :delta_mode, :deltaMode
62
+ alias_native :delta_x, :deltaX
63
+ alias_native :delta_y, :deltaY
64
+ alias_native :delta_z, :deltaZ
65
+
66
+ BUILT_IN_EVENTS = %w{onCopy onCut onPaste onKeyDown onKeyPress onKeyUp
67
+ onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick onDrag
68
+ onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop
69
+ onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver
70
+ onMouseUp onSelect onTouchCancel onTouchEnd onTouchMove onTouchStart onScroll onWheel}
71
+
72
+ def initialize(native_event)
73
+ @native = native_event
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def shallow_to_n
3
+ hash = `{}`
4
+ self.each do |key, value|
5
+ `hash[#{key}] = #{value}`
6
+ end
7
+ hash
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ Element.instance_eval do
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})`
9
+ end
10
+
11
+ def self.[](selector)
12
+ find(selector)
13
+ end
14
+
15
+ define_method :render do |container = nil, params = {}, &block|
16
+ if `#{self.to_n}._reactrb_component_class === undefined`
17
+ `#{self.to_n}._reactrb_component_class = #{Class.new(Hyperloop::Component)}`
18
+ end
19
+ klass = `#{self.to_n}._reactrb_component_class`
20
+ klass.class_eval do
21
+ render(container, params, &block)
22
+ end
23
+
24
+ React.render(React.create_element(`#{self.to_n}._reactrb_component_class`), self)
25
+ end
26
+
27
+ # mount_components is useful for dynamically generated page segments for example
28
+ # see react-rails documentation for more details
29
+
30
+ %x{
31
+ $.fn.mount_components = function() {
32
+ this.each(function(e) { ReactRailsUJS.mountComponents(e[0]) })
33
+ return this;
34
+ }
35
+ }
36
+ Element.expose :mount_components
37
+ end if Object.const_defined?('Element')
@@ -0,0 +1,8 @@
1
+ class String
2
+ def event_camelize
3
+ `return #{self}.replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
4
+ var capitalize = true;
5
+ return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
6
+ })`
7
+ end
8
+ end
@@ -0,0 +1,87 @@
1
+ module React
2
+ # NativeLibrary handles importing JS libraries. Importing native components is handled
3
+ # by the React::Base. It also provides several methods used by auto-import.rb
4
+
5
+ # A NativeLibrary is simply a wrapper that holds the name of the native js library.
6
+ # It responds to const_missing and method_missing by looking up objects within the js library.
7
+ # If the object is a react component it is wrapped by a reactrb component class, otherwise
8
+ # a nested NativeLibrary is returned.
9
+
10
+ # Two macros are provided: imports (for naming the native library) and renames which allows
11
+ # the members of a library to be given different names within the ruby name space.
12
+
13
+ # Public methods used by auto-import.rb are import_const_from_native and find_and_render_component
14
+ class NativeLibrary
15
+ class << self
16
+ def imports(native_name)
17
+ @native_prefix = "#{native_name}."
18
+ self
19
+ end
20
+
21
+ def rename(rename_list)
22
+ # rename_list is a hash in the form: native_name => ruby_name, native_name => ruby_name
23
+ rename_list.each do |js_name, ruby_name|
24
+ native_name = lookup_native_name(js_name)
25
+ if lookup_native_name(js_name)
26
+ create_component_wrapper(self, native_name, ruby_name) ||
27
+ create_library_wrapper(self, native_name, ruby_name)
28
+ else
29
+ raise "class #{name} < React::NativeLibrary could not import #{js_name}. "\
30
+ "Native value #{scope_native_name(js_name)} is undefined."
31
+ end
32
+ end
33
+ end
34
+
35
+ def import_const_from_native(klass, const_name, create_library)
36
+ native_name = lookup_native_name(const_name) ||
37
+ lookup_native_name(const_name[0].downcase + const_name[1..-1])
38
+ native_name && (
39
+ create_component_wrapper(klass, native_name, const_name) || (
40
+ create_library &&
41
+ create_library_wrapper(klass, native_name, const_name)))
42
+ end
43
+
44
+ def const_missing(const_name)
45
+ import_const_from_native(self, const_name, true) || super
46
+ end
47
+
48
+ def method_missing(method, *args, &block)
49
+ component_class = const_get(method) if const_defined?(method, false)
50
+ component_class ||= import_const_from_native(self, method, false)
51
+ raise 'could not import a react component named: '\
52
+ "#{scope_native_name method}" unless component_class
53
+ React::RenderingContext.render(component_class, *args, &block)
54
+ end
55
+
56
+ private
57
+
58
+ def lookup_native_name(js_name)
59
+ native_name = scope_native_name(js_name)
60
+ `eval(#{native_name}) !== undefined && native_name`
61
+ # rubocop:disable Lint/RescueException # that is what eval raises in Opal >= 0.10.
62
+ rescue Exception
63
+ nil
64
+ # rubocop:enable Lint/RescueException
65
+ end
66
+
67
+ def scope_native_name(js_name)
68
+ "#{@native_prefix}#{js_name}"
69
+ end
70
+
71
+ def create_component_wrapper(klass, native_name, ruby_name)
72
+ if React::API.native_react_component?(native_name)
73
+ new_klass = klass.const_set ruby_name, Class.new
74
+ new_klass.class_eval do
75
+ include Hyperloop::Component::Mixin
76
+ imports native_name
77
+ end
78
+ new_klass
79
+ end
80
+ end
81
+
82
+ def create_library_wrapper(klass, native_name, ruby_name)
83
+ klass.const_set ruby_name, Class.new(React::NativeLibrary).imports(native_name)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,15 @@
1
+ # Lazy load HTML tag constants in the form DIV or A
2
+ # This is needed to allow for a HAML expression like this DIV.my_class
3
+ class Object
4
+ class << self
5
+ alias _reactrb_tag_original_const_missing const_missing
6
+
7
+ def const_missing(const_name)
8
+ # Opal uses const_missing to initially define things,
9
+ # so we always call the original, and respond to the exception
10
+ _reactrb_tag_original_const_missing(const_name)
11
+ rescue StandardError => e
12
+ React::Component::Tags.html_tag_class_for(const_name) || raise(e)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ if RUBY_ENGINE == 'opal'
2
+ require 'react-server.js'
3
+ end
@@ -0,0 +1,17 @@
1
+ if RUBY_ENGINE == 'opal'
2
+ %x{
3
+ var ms = [
4
+ "Warning: `react/react-source` is deprecated, ",
5
+ "use `react/react-source-browser` or `react/react-source-server` instead."
6
+ ]
7
+ console.error(ms.join(""));
8
+ }
9
+ require 'react.js'
10
+ require "react-server.js"
11
+ else
12
+ require "react/config"
13
+ require "react/rails/asset_variant"
14
+ variant = Hyperloop.env.production? ? 'production' : 'development'
15
+ react_directory = React::Rails::AssetVariant.new({environment: variant}).react_directory
16
+ Opal.append_path react_directory.untaint
17
+ end
@@ -0,0 +1,31 @@
1
+ require 'react/native_library'
2
+
3
+ module React
4
+ module RefsCallbackExtension
5
+ end
6
+
7
+ class API
8
+ class << self
9
+ alias :orig_convert_props :convert_props
10
+ end
11
+
12
+ def self.convert_props(properties)
13
+ props = self.orig_convert_props(properties)
14
+ props.map do |key, value|
15
+ if key == "ref" && value.is_a?(Proc)
16
+ new_proc = Proc.new do |native_inst|
17
+ if `#{native_inst} !== null && #{native_inst}.__opalInstance !== undefined && #{native_inst}.__opalInstance !== null`
18
+ value.call(`#{native_inst}.__opalInstance`)
19
+ elsif `#{native_inst} !== null && ReactDOM.findDOMNode !== undefined && #{native_inst}.nodeType === undefined`
20
+ value.call(`ReactDOM.findDOMNode(#{native_inst})`) # react >= v0.15.`)
21
+ else
22
+ value.call(native_inst)
23
+ end
24
+ end
25
+ props[key] = new_proc
26
+ end
27
+ end
28
+ props
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,149 @@
1
+ module React
2
+ class RenderingContext
3
+ class << self
4
+ attr_accessor :waiting_on_resources
5
+
6
+ def render(name, *args, &block)
7
+ was_outer_most = !@not_outer_most
8
+ @not_outer_most = true
9
+ remove_nodes_from_args(args)
10
+ @buffer ||= [] unless @buffer
11
+ if block
12
+ element = build do
13
+ saved_waiting_on_resources = waiting_on_resources
14
+ self.waiting_on_resources = nil
15
+ run_child_block(name.nil?, &block)
16
+ if name
17
+ buffer = @buffer.dup
18
+ React::API.create_element(name, *args) { buffer }.tap do |element|
19
+ element.waiting_on_resources = saved_waiting_on_resources || !!buffer.detect { |e| e.waiting_on_resources if e.respond_to?(:waiting_on_resources) }
20
+ element.waiting_on_resources ||= waiting_on_resources if buffer.last.is_a?(String)
21
+ end
22
+ elsif @buffer.last.is_a? React::Element
23
+ @buffer.last.tap { |element| element.waiting_on_resources ||= saved_waiting_on_resources }
24
+ else
25
+ buffer_s = @buffer.last.to_s
26
+ React::RenderingContext.render(:span) { buffer_s }.tap { |element| element.waiting_on_resources = saved_waiting_on_resources }
27
+ end
28
+ end
29
+ elsif name.is_a? React::Element
30
+ element = name
31
+ else
32
+ element = React::API.create_element(name, *args)
33
+ element.waiting_on_resources = waiting_on_resources
34
+ end
35
+ @buffer << element
36
+ self.waiting_on_resources = nil
37
+ element
38
+ ensure
39
+ @not_outer_most = @buffer = nil if was_outer_most
40
+ end
41
+
42
+ def build
43
+ current = @buffer
44
+ @buffer = []
45
+ return_val = yield @buffer
46
+ @buffer = current
47
+ return_val
48
+ end
49
+
50
+ def delete(element)
51
+ @buffer.delete(element)
52
+ element
53
+ end
54
+ alias as_node delete
55
+
56
+ def rendered?(element)
57
+ @buffer.include? element
58
+ end
59
+
60
+ def replace(e1, e2)
61
+ @buffer[@buffer.index(e1)] = e2
62
+ end
63
+
64
+ def remove_nodes_from_args(args)
65
+ args[0].each do |key, value|
66
+ begin
67
+ value.delete if value.is_a?(Element) # deletes Element from buffer
68
+ rescue Exception
69
+ end
70
+ end if args[0] && args[0].is_a?(Hash)
71
+ end
72
+
73
+ # run_child_block gathers the element(s) generated by a child block.
74
+ # for example when rendering this div: div { "hello".span; "goodby".span }
75
+ # two child Elements will be generated.
76
+ #
77
+ # the final value of the block should either be
78
+ # 1 an object that responds to :acts_as_string?
79
+ # 2 a string,
80
+ # 3 an element that is NOT yet pushed on the rendering buffer
81
+ # 4 or the last element pushed on the buffer
82
+ #
83
+ # in case 1 we render a span
84
+ # in case 2 we automatically push the string onto the buffer
85
+ # in case 3 we also push the Element onto the buffer IF the buffer is empty
86
+ # case 4 requires no special processing
87
+ #
88
+ # Once we have taken care of these special cases we do a check IF we are in an
89
+ # outer rendering scope. In this case react only allows us to generate 1 Element
90
+ # so we insure that is the case, and also check to make sure that element in the buffer
91
+ # is the element returned
92
+
93
+ def run_child_block(is_outer_scope)
94
+ result = yield
95
+ if result.respond_to?(:acts_as_string?) && result.acts_as_string?
96
+ # hyper-mesh DummyValues respond to acts_as_string, and must
97
+ # be converted to spans INSIDE the parent, otherwise the waiting_on_resources
98
+ # flag will get set in the wrong context
99
+ React::RenderingContext.render(:span) { result.to_s }
100
+ elsif result.is_a?(String) || (result.is_a?(React::Element) && @buffer.empty?)
101
+ @buffer << result
102
+ end
103
+ raise_render_error(result) if is_outer_scope && @buffer != [result]
104
+ end
105
+
106
+ # heurestically raise a meaningful error based on the situation
107
+
108
+ def raise_render_error(result)
109
+ improper_render 'A different element was returned than was generated within the DSL.',
110
+ 'Possibly improper use of Element#delete.' if @buffer.count == 1
111
+ improper_render "Instead #{@buffer.count} elements were generated.",
112
+ 'Do you want to wrap your elements in a div?' if @buffer.count > 1
113
+ improper_render "Instead the component #{result} was returned.",
114
+ "Did you mean #{result}()?" if result.try :reactrb_component?
115
+ improper_render "Instead the #{result.class} #{result} was returned.",
116
+ 'You may need to convert this to a string.'
117
+ end
118
+
119
+ def improper_render(message, solution)
120
+ raise "a component's render method must generate and return exactly 1 element or a string.\n"\
121
+ " #{message} #{solution}"
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ class Object
128
+ [:span, :td, :th, :while_loading].each do |tag|
129
+ define_method(tag) do |*args, &block|
130
+ args.unshift(tag)
131
+ return send(*args, &block) if is_a? React::Component
132
+ React::RenderingContext.render(*args) { to_s }
133
+ end
134
+ end
135
+
136
+ def para(*args, &block)
137
+ args.unshift(:p)
138
+ return send(*args, &block) if is_a? React::Component
139
+ React::RenderingContext.render(*args) { to_s }
140
+ end
141
+
142
+ def br
143
+ return send(:br) if is_a? React::Component
144
+ React::RenderingContext.render(:span) do
145
+ React::RenderingContext.render(to_s)
146
+ React::RenderingContext.render(:br)
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,19 @@
1
+ module React
2
+ module Server
3
+ def self.render_to_string(element)
4
+ if !(`typeof ReactDOMServer === 'undefined'`)
5
+ React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
6
+ else
7
+ raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
8
+ end
9
+ end
10
+
11
+ def self.render_to_static_markup(element)
12
+ if !(`typeof ReactDOMServer === 'undefined'`)
13
+ React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
14
+ else
15
+ raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
16
+ end
17
+ end
18
+ end
19
+ end