isomorfeus-react 16.10.0 → 16.10.1

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +64 -0
  3. data/lib/browser/delegate_native.rb +70 -0
  4. data/lib/browser/element.rb +176 -0
  5. data/lib/browser/element/canvas.rb +17 -0
  6. data/lib/browser/element/media.rb +78 -0
  7. data/lib/browser/event.rb +92 -0
  8. data/lib/browser/event_target.rb +39 -0
  9. data/lib/browser/file_list.rb +125 -0
  10. data/lib/browser/iterable.rb +15 -0
  11. data/lib/isomorfeus-react-material-ui.rb +10 -0
  12. data/lib/isomorfeus-react.rb +145 -0
  13. data/lib/isomorfeus/config.rb +130 -0
  14. data/lib/isomorfeus/props/validate_hash_proxy.rb +178 -0
  15. data/lib/isomorfeus/props/validator.rb +131 -0
  16. data/lib/isomorfeus/react_view_helper.rb +130 -0
  17. data/lib/isomorfeus/top_level.rb +86 -0
  18. data/lib/isomorfeus/top_level_ssr.rb +28 -0
  19. data/lib/lucid_app/api.rb +30 -0
  20. data/lib/lucid_app/base.rb +7 -0
  21. data/lib/lucid_app/context.rb +7 -0
  22. data/lib/lucid_app/mixin.rb +20 -0
  23. data/lib/lucid_app/native_component_constructor.rb +105 -0
  24. data/lib/lucid_component/app_store_defaults.rb +36 -0
  25. data/lib/lucid_component/app_store_proxy.rb +38 -0
  26. data/lib/lucid_component/base.rb +7 -0
  27. data/lib/lucid_component/class_store_proxy.rb +41 -0
  28. data/lib/lucid_component/component_class_store_defaults.rb +38 -0
  29. data/lib/lucid_component/component_instance_store_defaults.rb +35 -0
  30. data/lib/lucid_component/event_handler.rb +17 -0
  31. data/lib/lucid_component/initializer.rb +12 -0
  32. data/lib/lucid_component/instance_store_proxy.rb +45 -0
  33. data/lib/lucid_component/mixin.rb +18 -0
  34. data/lib/lucid_component/native_component_constructor.rb +116 -0
  35. data/lib/lucid_component/reducers.rb +48 -0
  36. data/lib/lucid_component/store_api.rb +38 -0
  37. data/lib/lucid_component/styles_support.rb +37 -0
  38. data/lib/lucid_material/app/base.rb +9 -0
  39. data/lib/lucid_material/app/mixin.rb +22 -0
  40. data/lib/lucid_material/app/native_component_constructor.rb +107 -0
  41. data/lib/lucid_material/component/base.rb +9 -0
  42. data/lib/lucid_material/component/mixin.rb +20 -0
  43. data/lib/lucid_material/component/native_component_constructor.rb +118 -0
  44. data/lib/lucid_prop_declaration/mixin.rb +91 -0
  45. data/lib/react.rb +195 -0
  46. data/lib/react/active_support_support.rb +13 -0
  47. data/lib/react/children.rb +35 -0
  48. data/lib/react/component/api.rb +80 -0
  49. data/lib/react/component/base.rb +9 -0
  50. data/lib/react/component/callbacks.rb +106 -0
  51. data/lib/react/component/elements.rb +60 -0
  52. data/lib/react/component/event_handler.rb +19 -0
  53. data/lib/react/component/features.rb +47 -0
  54. data/lib/react/component/history.rb +36 -0
  55. data/lib/react/component/initializer.rb +11 -0
  56. data/lib/react/component/location.rb +15 -0
  57. data/lib/react/component/match.rb +31 -0
  58. data/lib/react/component/mixin.rb +19 -0
  59. data/lib/react/component/native_component_constructor.rb +93 -0
  60. data/lib/react/component/props.rb +59 -0
  61. data/lib/react/component/resolution.rb +70 -0
  62. data/lib/react/component/should_component_update.rb +14 -0
  63. data/lib/react/component/state.rb +52 -0
  64. data/lib/react/component/styles.rb +27 -0
  65. data/lib/react/component/unsafe_api.rb +33 -0
  66. data/lib/react/context_wrapper.rb +46 -0
  67. data/lib/react/function_component/api.rb +63 -0
  68. data/lib/react/function_component/base.rb +9 -0
  69. data/lib/react/function_component/creator.rb +32 -0
  70. data/lib/react/function_component/event_handler.rb +13 -0
  71. data/lib/react/function_component/mixin.rb +14 -0
  72. data/lib/react/function_component/resolution.rb +62 -0
  73. data/lib/react/memo_component/base.rb +9 -0
  74. data/lib/react/memo_component/creator.rb +32 -0
  75. data/lib/react/memo_component/mixin.rb +14 -0
  76. data/lib/react/native_constant_wrapper.rb +26 -0
  77. data/lib/react/pure_component/base.rb +9 -0
  78. data/lib/react/pure_component/mixin.rb +18 -0
  79. data/lib/react/ref.rb +13 -0
  80. data/lib/react/synthetic_event.rb +53 -0
  81. data/lib/react/version.rb +3 -0
  82. data/lib/react_dom.rb +47 -0
  83. data/lib/react_dom_server.rb +19 -0
  84. metadata +84 -2
@@ -0,0 +1,28 @@
1
+ module Isomorfeus
2
+ class TopLevel
3
+ class << self
4
+ attr_accessor :ssr_route_path
5
+ attr_accessor :transport_ws_url
6
+
7
+ def mount!
8
+ # nothing, but keep it for compatibility with browser
9
+ end
10
+
11
+ def render_component_to_string(component_name, props)
12
+ component = nil
13
+ %x{
14
+ if (typeof component_name === 'string' || component_name instanceof String) {
15
+ component = component_name.split(".").reduce(function(o, x) {
16
+ return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
17
+ }, Opal.global)
18
+ } else {
19
+ component = component_name;
20
+ }
21
+ }
22
+ component = Isomorfeus.cached_component_class(component_name) unless component
23
+
24
+ ReactDOMServer.render_to_string(React.create_element(component, `Opal.Hash.$new(props)`))
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module LucidApp
2
+ module API
3
+ def self.included(base)
4
+ base.instance_exec do
5
+ def theme(theme_hash = nil, &block)
6
+ if block_given?
7
+ %x{
8
+ let result = block.$call(Opal.Hash.$new(base.jss_theme));
9
+ if (typeof result.$to_n === 'function') { base.jss_theme = result.$to_n(); }
10
+ else { base.jss_theme = result; }
11
+ return result;
12
+ }
13
+ elsif theme_hash
14
+ `base.jss_theme = #{theme_hash.to_n}` if theme_hash
15
+ theme_hash
16
+ elsif `typeof base.jss_theme === 'object'`
17
+ `Opal.Hash.$new(base.jss_theme)`
18
+ else
19
+ nil
20
+ end
21
+ end
22
+ alias_method :theme=, :theme
23
+ end
24
+
25
+ def context
26
+ @native.JS[:context]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module LucidApp
2
+ class Base
3
+ def self.inherited(base)
4
+ base.include(::LucidApp::Mixin)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module LucidApp
2
+ module Context
3
+ def self.create_application_context
4
+ React.create_context('LucidApplicationContext', Isomorfeus.store)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ module LucidApp
2
+ module Mixin
3
+ def self.included(base)
4
+ base.include(::Native::Wrapper)
5
+ base.extend(::LucidApp::NativeComponentConstructor)
6
+ base.extend(::LucidPropDeclaration::Mixin)
7
+ base.extend(::React::Component::ShouldComponentUpdate)
8
+ base.extend(::LucidComponent::EventHandler)
9
+ base.include(::React::Component::Elements)
10
+ base.include(::React::Component::API)
11
+ base.include(::React::Component::Callbacks)
12
+ base.include(::LucidComponent::StoreAPI)
13
+ base.include(::LucidComponent::StylesSupport)
14
+ base.include(::LucidApp::API)
15
+ base.include(::LucidComponent::Initializer)
16
+ base.include(::React::Component::Features)
17
+ base.include(::React::Component::Resolution)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,105 @@
1
+ module LucidApp
2
+ module NativeComponentConstructor
3
+ # for should_component_update we apply ruby semantics for comparing props
4
+ # to do so, we convert the props to ruby hashes and then compare
5
+ # this makes sure, that for example rubys Nil object gets handled properly
6
+ def self.extended(base)
7
+ component_name = base.to_s
8
+ # language=JS
9
+ %x{
10
+ base.lucid_react_component = class extends Opal.global.React.Component {
11
+ constructor(props) {
12
+ super(props);
13
+ if (base.$default_state_defined()) {
14
+ this.state = base.$state().$to_n();
15
+ } else {
16
+ this.state = {};
17
+ };
18
+ this.state.isomorfeus_store_state = Opal.Isomorfeus.store.native.getState();
19
+ var current_store_state = this.state.isomorfeus_store_state;
20
+ if (typeof current_store_state.component_class_state[#{component_name}] !== "undefined") {
21
+ this.state.component_class_state = {};
22
+ this.state.component_class_state[#{component_name}] = current_store_state.component_class_state[#{component_name}];
23
+ } else {
24
+ this.state.component_class_state = {};
25
+ this.state.component_class_state[#{component_name}] = {};
26
+ };
27
+ this.__ruby_instance = base.$new(this);
28
+ this.__object_id = this.__ruby_instance.$object_id().$to_s();
29
+ if (Opal.Isomorfeus.$top_component() == nil) { Opal.Isomorfeus['$top_component='](this); }
30
+ if (!this.state.component_state) {
31
+ this.state.component_state = {};
32
+ this.state.component_state[this.__object_id] = {};
33
+ };
34
+ var event_handlers = #{base.event_handlers};
35
+ for (var i = 0; i < event_handlers.length; i++) {
36
+ this[event_handlers[i]] = this[event_handlers[i]].bind(this);
37
+ }
38
+ var defined_refs = #{base.defined_refs};
39
+ for (var ref in defined_refs) {
40
+ if (defined_refs[ref] != null) {
41
+ this[ref] = function(element) {
42
+ element = Opal.React.native_element_or_component_to_ruby(element);
43
+ #{`this.__ruby_instance`.instance_exec(`element`, &`defined_refs[ref]`)}
44
+ }
45
+ this[ref] = this[ref].bind(this);
46
+ } else {
47
+ this[ref] = Opal.global.React.createRef();
48
+ }
49
+ }
50
+ this.listener = this.listener.bind(this);
51
+ this.unsubscriber = Opal.Isomorfeus.store.native.subscribe(this.listener);
52
+ }
53
+ static get displayName() {
54
+ return #{component_name};
55
+ }
56
+ render() {
57
+ Opal.React.render_buffer.push([]);
58
+ Opal.React.active_components.push(this);
59
+ Opal.React.active_redux_components.push(this.__ruby_instance);
60
+ #{`this.__ruby_instance`.instance_exec(&`base.render_block`)};
61
+ Opal.React.active_redux_components.pop();
62
+ Opal.React.active_components.pop();
63
+ let children = Opal.React.render_buffer.pop();
64
+ return Opal.global.React.createElement(Opal.global.LucidApplicationContext.Provider, { value: this.state.isomorfeus_store_state }, children);
65
+ }
66
+ listener() {
67
+ let next_state = Opal.Isomorfeus.store.native.getState();
68
+ this.setState({ isomorfeus_store_state: next_state });
69
+ }
70
+ componentWillUnmount() {
71
+ if (typeof this.unsubscriber === "function") { this.unsubscriber(); };
72
+ }
73
+ validateProp(props, propName, componentName) {
74
+ try { base.$validate_prop(propName, props[propName]) }
75
+ catch (e) { return new Error(componentName + ": Error: prop validation failed: " + e.message); }
76
+ return null;
77
+ }
78
+ }
79
+ base.jss_styles = null;
80
+ base.jss_styles_used = null;
81
+ base.jss_theme = {};
82
+ base.use_styles = null;
83
+ base.themed_react_component = function(props) {
84
+ let classes = null;
85
+ let theme = Opal.global.ReactJSS.useTheme();
86
+ if (base.jss_styles) {
87
+ if (!base.use_styles || (Opal.Isomorfeus["$development?"]() && !Object.is(base.jss_styles, base.jss_styles_used))) {
88
+ base.jss_styles_used = base.jss_styles;
89
+ let styles = base.jss_styles
90
+ if (typeof styles === 'function') { styles = styles(theme); }
91
+ base.use_styles = Opal.global.ReactJSS.createUseStyles(styles);
92
+ }
93
+ classes = base.use_styles();
94
+ }
95
+ let themed_classes_props = Object.assign({}, props, { classes: classes, theme: theme });
96
+ return Opal.global.React.createElement(base.lucid_react_component, themed_classes_props);
97
+ }
98
+ base.react_component = function(props) {
99
+ let themed_component = Opal.global.React.createElement(base.themed_react_component, props);
100
+ return Opal.global.React.createElement(Opal.global.ReactJSS.ThemeProvider, { theme: base.jss_theme }, themed_component);
101
+ }
102
+ }
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,36 @@
1
+ module LucidComponent
2
+ class AppStoreDefaults
3
+ def initialize(state, component_name)
4
+ @state = state
5
+ if @state.isomorfeus_store
6
+ @state.isomorfeus_store.merge!(application_state: {})
7
+ else
8
+ @state.isomorfeus_store = { application_state: {}}
9
+ end
10
+ end
11
+
12
+ def method_missing(key, *args, &block)
13
+ if `args.length > 0`
14
+ # set initial class state
15
+ key = key.chop if `key.endsWith('=')`
16
+ @state.isomorfeus_store[:application_state][key] = args[0]
17
+ current_state = Isomorfeus.store.get_state
18
+ if !(current_state[:application_state].key?(key))
19
+ Isomorfeus.store.dispatch(type: 'APPLICATION_STATE', name: key, value: args[0])
20
+ end
21
+ else
22
+ # get class state
23
+
24
+ # check if we have a component local state value
25
+ if @state.isomorfeus_store[:application_state].key?(key)
26
+ return @state.isomorfeus_store[:application_state][key]
27
+ end
28
+ end
29
+ nil
30
+ end
31
+
32
+ def to_h
33
+ @state.isomorfeus_store[:application_state]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ module LucidComponent
2
+ class AppStoreProxy
3
+ def initialize(component_instance)
4
+ @native_component_instance = component_instance.to_n
5
+ @component_instance = component_instance
6
+ end
7
+
8
+ def method_missing(key, *args, &block)
9
+ if `args.length > 0`
10
+ # set class state, simply a dispatch
11
+ action = { type: 'APPLICATION_STATE', name: (`key.endsWith('=')` ? key.chop : key), value: args[0] }
12
+ Isomorfeus.store.dispatch(action)
13
+ else
14
+ # check if we have a component local state value
15
+ if `this.native_component_instance.context.application_state.hasOwnProperty(key)`
16
+ return @native_component_instance.JS['context'].JS[:application_state].JS[key]
17
+ elsif @component_instance.class.default_app_store_defined && @component_instance.class.app_store.to_h.key?(key)
18
+ # check if a default value was given
19
+ return @component_instance.class.app_store.to_h[key]
20
+ end
21
+ # otherwise return nil
22
+ return nil
23
+ end
24
+ end
25
+
26
+ def dispatch(action)
27
+ Isomorfeus.store.dispatch(action)
28
+ end
29
+
30
+ def subscribe(&block)
31
+ Isomorfeus.store.subscribe(&block)
32
+ end
33
+
34
+ def unsubscribe(unsubscriber)
35
+ `unsubscriber()`
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ module LucidComponent
2
+ class Base
3
+ def self.inherited(base)
4
+ base.include(::LucidComponent::Mixin)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module LucidComponent
2
+ class ClassStoreProxy
3
+ def initialize(component_instance)
4
+ @native_component_instance = component_instance.to_n
5
+ @component_instance = component_instance
6
+ @component_name = component_instance.class.to_s
7
+ end
8
+
9
+ def method_missing(key, *args, &block)
10
+ if `args.length > 0`
11
+ # set class state, simply a dispatch
12
+ action = { type: 'COMPONENT_CLASS_STATE', class: @component_name, name: (`key.endsWith('=')` ? key.chop : key), value: args[0] }
13
+ Isomorfeus.store.dispatch(action)
14
+ else
15
+ # get class state
16
+ # check if we have a component local state value
17
+ if @native_component_instance.JS['context'].JS[:component_class_state].JS[@component_name] &&
18
+ @native_component_instance.JS['context'].JS[:component_class_state].JS[@component_name].JS.hasOwnProperty(key)
19
+ return @native_component_instance.JS['context'].JS[:component_class_state].JS[@component_name].JS[key]
20
+ elsif @component_instance.class.default_class_store_defined && @component_instance.class.class_store.to_h.key?(key)
21
+ # check if a default value was given
22
+ return @component_instance.class.class_store.to_h[key]
23
+ end
24
+ # otherwise return nil
25
+ return nil
26
+ end
27
+ end
28
+
29
+ def dispatch(action)
30
+ Isomorfeus.store.dispatch(action)
31
+ end
32
+
33
+ def subscribe(&block)
34
+ Isomorfeus.store.subscribe(&block)
35
+ end
36
+
37
+ def unsubscribe(unsubscriber)
38
+ `unsubscriber()`
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ module LucidComponent
2
+ class ComponentClassStoreDefaults
3
+ def initialize(state, component_name)
4
+ @state = state
5
+ @component_name = component_name
6
+ if @state.isomorfeus_store
7
+ @state.isomorfeus_store.merge!(component_class_state: { @component_name => {} })
8
+ else
9
+ @state.isomorfeus_store = { component_class_state: { @component_name => {} } }
10
+ end
11
+ end
12
+
13
+ def method_missing(key, *args, &block)
14
+ if `args.length > 0`
15
+ # set initial class state
16
+ key = key.chop if `key.endsWith('=')`
17
+ @state.isomorfeus_store[:component_class_state][@component_name][key] = args[0]
18
+ current_state = Isomorfeus.store.get_state
19
+ if !(current_state[:component_class_state].key?(@component_name) && current_state[:component_class_state][@component_name].key?(key))
20
+ Isomorfeus.store.dispatch(type: 'COMPONENT_CLASS_STATE', class: @component_name, name: key, value: args[0])
21
+ end
22
+ else
23
+ # get class state
24
+
25
+ # check if we have a component local state value
26
+
27
+ if @state.isomorfeus_store[:component_class_state][@component_name].key?(key)
28
+ return @state.isomorfeus_store[:component_class_state][@component_name][key]
29
+ end
30
+ end
31
+ nil
32
+ end
33
+
34
+ def to_h
35
+ @state.isomorfeus_store[:component_class_state][@component_name]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module LucidComponent
2
+ class ComponentInstanceStoreDefaults
3
+ def initialize(state, component_name)
4
+ @state = {}
5
+ @component_name = component_name
6
+ end
7
+
8
+ def method_missing(key, *args, &block)
9
+ if `args.length > 0`
10
+ # set initial class state
11
+ key = key.chop if `key.endsWith('=')`
12
+ @state[key] = args[0]
13
+ current_state = Isomorfeus.store.get_state
14
+ if !(current_state[:component_state].key?(@component_name) &&
15
+ current_state[:component_state][@component_name].key?(:instance_defaults) &&
16
+ current_state[:component_state][@component_name][:instance_defaults].key?(key))
17
+ Isomorfeus.store.dispatch(type: 'COMPONENT_CLASS_STATE', class: @component_name, name: :instance_defaults, value: { key => args[0]})
18
+ end
19
+ else
20
+ # get class state
21
+
22
+ # check if we have a component local state value
23
+
24
+ if @state.key?(key)
25
+ return @state[key]
26
+ end
27
+ end
28
+ nil
29
+ end
30
+
31
+ def to_h
32
+ @state
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module LucidComponent
2
+ module EventHandler
3
+ def event_handlers
4
+ @event_handlers ||= []
5
+ end
6
+
7
+ def event_handler(name, &block)
8
+ event_handlers << name
9
+ %x{
10
+ self.lucid_react_component.prototype[name] = function(event, info) {
11
+ #{ruby_event = ::React::SyntheticEvent.new(`event`)};
12
+ #{`this.__ruby_instance`.instance_exec(ruby_event, `info`, &block)};
13
+ }
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module LucidComponent
2
+ module Initializer
3
+ def initialize(native_component)
4
+ @native = native_component
5
+ @app_store = `Opal.LucidComponent.AppStoreProxy.$new(#{self})`
6
+ @class_store = `Opal.LucidComponent.ClassStoreProxy.$new(#{self})`
7
+ @props = `Opal.React.Component.Props.$new(#@native)`
8
+ @state = `Opal.React.Component.State.$new(#@native)`
9
+ @store = `Opal.LucidComponent.InstanceStoreProxy.$new(#{self})`
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ module LucidComponent
2
+ class InstanceStoreProxy
3
+ def initialize(component_instance)
4
+ @native_component_instance = component_instance.to_n
5
+ @component_instance = component_instance
6
+ @component_object_id = component_instance.object_id.to_s
7
+ end
8
+
9
+ def method_missing(key, *args, &block)
10
+ if `args.length > 0`
11
+ # set instance state, simply a dispatch
12
+
13
+ action = { type: 'COMPONENT_STATE', object_id: @component_object_id, name: (`key.endsWith('=')` ? key.chop : key), value: args[0] }
14
+ Isomorfeus.store.dispatch(action)
15
+
16
+ else
17
+ # get instance state
18
+
19
+ if @native_component_instance.JS[:context].JS[:component_state].JS[@component_object_id] &&
20
+ @native_component_instance.JS[:context].JS[:component_state].JS[@component_object_id].JS.hasOwnProperty(key)
21
+ # check if we have a component local state value
22
+ return @native_component_instance.JS[:context].JS[:component_state].JS[@component_object_id].JS[key]
23
+ elsif @component_instance.class.default_instance_store_defined && @component_instance.class.store.to_h.key?(key)
24
+ # check if a default value was given
25
+ return @component_instance.class.store.to_h[key]
26
+ end
27
+
28
+ # otherwise return nil
29
+ return nil
30
+ end
31
+ end
32
+
33
+ def dispatch(action)
34
+ Isomorfeus.store.dispatch(action)
35
+ end
36
+
37
+ def subscribe(&block)
38
+ Isomorfeus.store.subscribe(&block)
39
+ end
40
+
41
+ def unsubscribe(unsubscriber)
42
+ `unsubscriber()`
43
+ end
44
+ end
45
+ end