isomorfeus-preact 10.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +62 -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-preact.rb +109 -0
  12. data/lib/isomorfeus/preact/config.rb +189 -0
  13. data/lib/isomorfeus/preact/memcached_component_cache.rb +19 -0
  14. data/lib/isomorfeus/preact/redis_component_cache.rb +19 -0
  15. data/lib/isomorfeus/preact/thread_local_component_cache.rb +17 -0
  16. data/lib/isomorfeus/preact_view_helper.rb +188 -0
  17. data/lib/isomorfeus/props/validate_hash_proxy.rb +186 -0
  18. data/lib/isomorfeus/props/validator.rb +159 -0
  19. data/lib/isomorfeus/top_level.rb +101 -0
  20. data/lib/isomorfeus/top_level_ssr.rb +27 -0
  21. data/lib/isomorfeus_preact/lucid_app/api.rb +22 -0
  22. data/lib/isomorfeus_preact/lucid_app/base.rb +7 -0
  23. data/lib/isomorfeus_preact/lucid_app/mixin.rb +16 -0
  24. data/lib/isomorfeus_preact/lucid_app/native_component_constructor.rb +91 -0
  25. data/lib/isomorfeus_preact/lucid_component/api.rb +68 -0
  26. data/lib/isomorfeus_preact/lucid_component/app_store_proxy.rb +37 -0
  27. data/lib/isomorfeus_preact/lucid_component/base.rb +7 -0
  28. data/lib/isomorfeus_preact/lucid_component/class_store_proxy.rb +44 -0
  29. data/lib/isomorfeus_preact/lucid_component/initializer.rb +14 -0
  30. data/lib/isomorfeus_preact/lucid_component/instance_store_proxy.rb +44 -0
  31. data/lib/isomorfeus_preact/lucid_component/mixin.rb +15 -0
  32. data/lib/isomorfeus_preact/lucid_component/native_component_constructor.rb +84 -0
  33. data/lib/isomorfeus_preact/lucid_component/styles_api.rb +31 -0
  34. data/lib/isomorfeus_preact/lucid_component/styles_wrapper.rb +40 -0
  35. data/lib/isomorfeus_preact/lucid_func/base.rb +7 -0
  36. data/lib/isomorfeus_preact/lucid_func/initializer.rb +11 -0
  37. data/lib/isomorfeus_preact/lucid_func/mixin.rb +12 -0
  38. data/lib/isomorfeus_preact/lucid_func/native_component_constructor.rb +55 -0
  39. data/lib/isomorfeus_preact/preact/function_component/api.rb +123 -0
  40. data/lib/isomorfeus_preact/preact/function_component/base.rb +9 -0
  41. data/lib/isomorfeus_preact/preact/function_component/initializer.rb +10 -0
  42. data/lib/isomorfeus_preact/preact/function_component/mixin.rb +12 -0
  43. data/lib/isomorfeus_preact/preact/function_component/native_component_constructor.rb +48 -0
  44. data/lib/lucid_app/context.rb +24 -0
  45. data/lib/lucid_prop_declaration/mixin.rb +126 -0
  46. data/lib/preact.rb +309 -0
  47. data/lib/preact/component/api.rb +124 -0
  48. data/lib/preact/component/base.rb +9 -0
  49. data/lib/preact/component/callbacks.rb +102 -0
  50. data/lib/preact/component/elements.rb +64 -0
  51. data/lib/preact/component/initializer.rb +11 -0
  52. data/lib/preact/component/mixin.rb +15 -0
  53. data/lib/preact/component/native_component_constructor.rb +65 -0
  54. data/lib/preact/component/params.rb +18 -0
  55. data/lib/preact/component/props.rb +55 -0
  56. data/lib/preact/component/resolution.rb +97 -0
  57. data/lib/preact/component/state.rb +58 -0
  58. data/lib/preact/context_wrapper.rb +46 -0
  59. data/lib/preact/native_constant_wrapper.rb +29 -0
  60. data/lib/preact/options.rb +98 -0
  61. data/lib/preact/ref.rb +17 -0
  62. data/lib/preact/version.rb +3 -0
  63. metadata +301 -0
@@ -0,0 +1,44 @@
1
+ module LucidComponent
2
+ class InstanceStoreProxy
3
+ def initialize(component_instance)
4
+ @native = component_instance.to_n
5
+ @component_instance = component_instance
6
+ @component_object_id = component_instance.object_id.to_s
7
+ end
8
+
9
+ def [](key)
10
+ method_missing(key)
11
+ end
12
+
13
+ def []=(key, value)
14
+ method_missing(key, value)
15
+ end
16
+
17
+ def method_missing(key, *args, &block)
18
+ if `args.length > 0`
19
+ # set instance state, simply a dispatch
20
+
21
+ action = { type: 'INSTANCE_STATE', object_id: @component_object_id, name: (`key.endsWith('=')` ? key.chop : key), value: args[0] }
22
+ Isomorfeus.store.collect_and_defer_dispatch(action)
23
+
24
+ else
25
+ # get instance state
26
+ if @native.JS[:props].JS[:iso_store]
27
+ if @native.JS[:props].JS[:iso_store].JS[:instance_state] &&
28
+ @native.JS[:props].JS[:iso_store].JS[:instance_state].JS[@component_object_id] &&
29
+ @native.JS[:props].JS[:iso_store].JS[:instance_state].JS[@component_object_id].JS.hasOwnProperty(key)
30
+ # check if we have a component local state value
31
+ return @native.JS[:props].JS[:iso_store].JS[:instance_state].JS[@component_object_id].JS[key]
32
+ end
33
+ else
34
+ a_state = Isomorfeus.store.get_state
35
+ if a_state.key?(:instance_state) && a_state[:instance_state].key?(key)
36
+ return a_state[:instance_state][key]
37
+ end
38
+ end
39
+ # otherwise return nil
40
+ return nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module LucidComponent
2
+ module Mixin
3
+ def self.included(base)
4
+ base.include(::Native::Wrapper)
5
+ base.extend(::LucidComponent::NativeComponentConstructor)
6
+ base.include(::Preact::Component::Elements)
7
+ base.extend(::LucidPropDeclaration::Mixin)
8
+ base.include(::Preact::Component::Api)
9
+ base.include(::Preact::Component::Callbacks)
10
+ base.include(::LucidComponent::Api)
11
+ base.include(::LucidComponent::StylesApi)
12
+ base.include(::LucidComponent::Initializer)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,84 @@
1
+ module LucidComponent
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
+ wrapper_name = component_name + 'Wrapper'
9
+ %x{
10
+ base.css_styles = null;
11
+ base.preload_block = null;
12
+ base.while_loading_block = null;
13
+
14
+ base.preact_component = function(props) {
15
+ let value = Opal.global.PreactHooks.useContext(Opal.global.LucidApplicationContext);
16
+ return Opal.global.Preact.createElement(base.lucid_preact_component, Object.assign({}, props, value));
17
+ };
18
+ base.preact_component.displayName = #{wrapper_name};
19
+
20
+ base.lucid_preact_component = class extends Opal.global.Preact.Component {
21
+ constructor(props) {
22
+ super(props);
23
+ const oper = Opal.Preact;
24
+ if (base.$default_state_defined()) {
25
+ this.state = base.$state().$to_n();
26
+ } else {
27
+ this.state = {};
28
+ };
29
+ this.__ruby_instance = base.$new(this);
30
+ var defined_refs = base.$defined_refs();
31
+ for (var ref in defined_refs) {
32
+ if (defined_refs[ref] != null) {
33
+ this[ref] = function(element) {
34
+ element = oper.native_element_or_component_to_ruby(element);
35
+ #{`this.__ruby_instance`.instance_exec(`element`, &`defined_refs[ref]`)}
36
+ }
37
+ this[ref] = this[ref].bind(this);
38
+ } else {
39
+ this[ref] = Opal.global.Preact.createRef();
40
+ }
41
+ }
42
+ if (base.preload_block) {
43
+ oper.active_redux_components.push(this);
44
+ this.state.preloaded = this.__ruby_instance.$execute_preload_block();
45
+ oper.active_redux_components.pop();
46
+ }
47
+ }
48
+ static get displayName() {
49
+ return #{component_name};
50
+ }
51
+ render(props, state) {
52
+ const oper = Opal.Preact;
53
+ oper.render_buffer.push([]);
54
+ // console.log("lucid component pushed", oper.render_buffer, oper.render_buffer.toString());
55
+ oper.active_components.push(this);
56
+ oper.active_redux_components.push(this);
57
+ let block_result;
58
+ if (base.while_loading_block && !state.preloaded) { block_result = #{`this.__ruby_instance`.instance_exec(&`base.while_loading_block`)}; }
59
+ else { block_result = #{`this.__ruby_instance`.instance_exec(&`base.render_block`)}; }
60
+ if (block_result && block_result !== nil) { oper.render_block_result(block_result); }
61
+ oper.active_redux_components.pop();
62
+ oper.active_components.pop();
63
+ // console.log("lucid component popping", oper.render_buffer, oper.render_buffer.toString());
64
+ let result = oper.render_buffer.pop();
65
+ return (result.length === 1) ? result[0] : result;
66
+ }
67
+ data_access() {
68
+ return this.props.iso_store;
69
+ }
70
+ shouldComponentUpdate(next_props, next_state) {
71
+ if (!Opal.Preact.props_are_equal(this.props, next_props)) { return true; }
72
+ if (Opal.Preact.state_is_not_equal(this.state, next_state)) { return true; }
73
+ return false;
74
+ }
75
+ validateProp(props, propName, componentName) {
76
+ try { base.$validate_prop(propName, props[propName]) }
77
+ catch (e) { return new Error(componentName + " Error: prop validation failed: " + e.message); }
78
+ return null;
79
+ }
80
+ };
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ module LucidComponent
2
+ module StylesApi
3
+ def self.included(base)
4
+ base.instance_exec do
5
+ def styles(styles_hash = nil, &block)
6
+ component_name = self.to_s
7
+ styles_hash = block.call if block_given?
8
+ if styles_hash
9
+ %x{
10
+ if (typeof styles_hash.$is_wrapped_style !== 'undefined') {
11
+ base.css_styles = styles_hash;
12
+ } else {
13
+ let css;
14
+ if (typeof styles_hash.$to_n === 'function') { css = styles_hash.$to_n(); }
15
+ else { css = styles_hash; }
16
+ let nano_styles = Opal.global.NanoCSSInstance.sheet(css, component_name.replace(/:/g, '_'));
17
+ base.css_styles = #{::LucidComponent::StylesWrapper.new(`nano_styles`)};
18
+ }
19
+ }
20
+ end
21
+ `base.css_styles`
22
+ end
23
+ alias_method :styles=, :styles
24
+ end
25
+
26
+ def styles
27
+ `self.$class().css_styles`
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ module LucidComponent
2
+ class StylesWrapper
3
+ include ::Native::Wrapper
4
+
5
+ def is_wrapped_style
6
+ true
7
+ end
8
+
9
+ def fade_in
10
+ 'fadeIn'
11
+ end
12
+
13
+ def fade_out
14
+ 'fadeOut'
15
+ end
16
+
17
+ def [](prop)
18
+ method_missing(prop)
19
+ end
20
+
21
+ def method_missing(prop, *args, &block)
22
+ %x{
23
+ let value;
24
+ value = #@native[prop];
25
+ if (value) { return value; }
26
+ else {
27
+ console.warn("Style/Theme key '" + prop + "' returning nil!");
28
+ return #{nil};
29
+ }
30
+ }
31
+ end
32
+
33
+ def to_h
34
+ %x{
35
+ if (#@props_prop) { return Opal.Hash.$new(#@native.props[#@props_prop]); }
36
+ else { return Opal.Hash.$new(#@native); }
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module LucidFunc
2
+ class Base
3
+ def self.inherited(base)
4
+ base.include(::LucidFunc::Mixin)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module LucidFunc
2
+ module Initializer
3
+ def initialize
4
+ self.JS[:native_props] = `{ props: null }`
5
+ @native_props = `Opal.Preact.Component.Props.$new(#{self})`
6
+ @app_store = LucidComponent::AppStoreProxy.new(self)
7
+ @class_store = LucidComponent::ClassStoreProxy.new(self.class.to_s, self, self)
8
+ @store = LucidComponent::InstanceStoreProxy.new(self)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module LucidFunc
2
+ module Mixin
3
+ def self.included(base)
4
+ base.include(::LucidFunc::Initializer)
5
+ base.include(::Preact::FunctionComponent::Api)
6
+ base.extend(::LucidFunc::NativeComponentConstructor)
7
+ base.include(::Preact::Component::Elements)
8
+ base.include(::LucidComponent::Api)
9
+ base.include(::LucidComponent::StylesApi)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,55 @@
1
+ module LucidFunc
2
+ module NativeComponentConstructor
3
+ def self.extended(base)
4
+ component_name = base.to_s
5
+ %x{
6
+ base.css_styles = null;
7
+ base.instance_init = function(initial) {
8
+ let ruby_state = { instance: #{base.new(`{}`)} };
9
+ ruby_state.instance.__ruby_instance = ruby_state.instance;
10
+ ruby_state.instance.data_access = function() { return this.props.iso_store; }
11
+ ruby_state.instance.data_access.bind(ruby_state.instance);
12
+ return ruby_state;
13
+ }
14
+ base.instance_reducer = function(state, action) { return state; }
15
+ base.preact_component = function(props) {
16
+ const og = Opal.global;
17
+ const oper = Opal.Preact;
18
+ oper.render_buffer.push([]);
19
+ // console.log("function pushed", oper.render_buffer, oper.render_buffer.toString());
20
+ // Lucid functionality
21
+ let context = og.PreactHooks.useContext(og.LucidApplicationContext);
22
+ // prepare Ruby instance
23
+ const [__ruby_state, __ruby_dispatch] = og.PreactHooks.useReducer(base.instance_reducer, null, base.instance_init);
24
+ const __ruby_instance = __ruby_state.instance;
25
+ __ruby_instance.props = Object.assign({}, props, context);
26
+ oper.active_components.push(__ruby_instance);
27
+ oper.active_redux_components.push(__ruby_instance);
28
+ let block_result = #{`__ruby_instance`.instance_exec(&`base.render_block`)};
29
+ if (block_result && block_result !== nil) { oper.render_block_result(block_result); }
30
+ oper.active_redux_components.pop();
31
+ oper.active_components.pop();
32
+ // console.log("function popping", oper.render_buffer, oper.render_buffer.toString());
33
+ let result = oper.render_buffer.pop();
34
+ return (result.length === 1) ? result[0] : result;
35
+ };
36
+ base.preact_component.displayName = #{component_name};
37
+ }
38
+
39
+ base_module = base.to_s.deconstantize
40
+ if base_module != ''
41
+ base_module.constantize.define_singleton_method(base.to_s.demodulize) do |*args, &block|
42
+ `Opal.Preact.internal_prepare_args_and_render(#{base}.preact_component, args, block)`
43
+ end
44
+ else
45
+ Object.define_method(base.to_s) do |*args, &block|
46
+ `Opal.Preact.internal_prepare_args_and_render(#{base}.preact_component, args, block)`
47
+ end
48
+ end
49
+
50
+ def render(&block)
51
+ `base.render_block = #{block}`
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,123 @@
1
+ module Preact
2
+ module FunctionComponent
3
+ module Api
4
+ def props
5
+ @native_props
6
+ end
7
+
8
+ def use_callback(*deps, &block)
9
+ `Opal.global.PreactHooks.useCallback(function() { #{block.call} }, deps)`
10
+ end
11
+
12
+ def use_context(context)
13
+ native_context = `(typeof context.$is_wrapped_context !== 'undefined')` ? context.to_n : context
14
+ `Opal.global.PreactHooks.useContext(native_context)`
15
+ end
16
+
17
+ def use_debug_value(value, formatter)
18
+ formatter = `null` unless formatter
19
+ `Opal.global.PreactHooks.useDebugValue(value, formatter)`
20
+ end
21
+
22
+ def use_effect(*args, &block)
23
+ `Opal.global.PreactHooks.useEffect(function() { #{block.call} }, args)`
24
+ end
25
+
26
+ def use_error_boundary(&block)
27
+ error = nil
28
+ reset_error = nil
29
+ %x{
30
+ let _error;
31
+ let _reset_error;
32
+ if (block) {
33
+ [_error, reset_error] = Opal.global.PreactHooks.useErrorBoundary(function() { #{block.call(Error(_error))} });
34
+ } else {
35
+ [_error, reset_error] = Opal.global.PreactHooks.useErrorBoundary();
36
+ }
37
+ error = #{Error(e)};
38
+ }
39
+ [error, reset_error]
40
+ end
41
+
42
+ def use_imperative_handle(ruby_ref, *args, &block)
43
+ ref = ruby_ref.to_n
44
+ args = `null` if args.empty?
45
+ `Opal.global.PreactHooks.useImperativeHandle(ref, function() { #{block.call} }, args)`
46
+ end
47
+
48
+ def use_layout_effect(&block)
49
+ `Opal.global.PreactHooks.useLayoutEffect(function() { #{block.call} })`
50
+ end
51
+
52
+ def use_memo(*deps, &block)
53
+ `Opal.global.PreactHooks.useMemo(function() { #{block.call} }, deps)`
54
+ end
55
+
56
+ def use_reducer(inital_state, &block)
57
+ state = nil
58
+ dispatcher = nil
59
+ %x{
60
+ [state, dispatcher] = Opal.global.PreactHooks.useReducer(function(state, action) {
61
+ #{block.call(state, action)}
62
+ }, initial_state);
63
+ }
64
+ [state, proc { |arg| `dispatcher(arg)` }]
65
+ end
66
+
67
+ def use_ref(native_ref)
68
+ Preact::Ref.new(`Opal.global.PreactHooks.useRef(native_ref)`)
69
+ end
70
+
71
+ def use_state(initial_value)
72
+ initial = nil
73
+ setter = nil
74
+ `[initial, setter] = Opal.global.PreactHooks.useState(initial_value);`
75
+ [initial, proc { |arg| `setter(arg)` }]
76
+ end
77
+
78
+ def get_preact_element(arg, &block)
79
+ if block_given?
80
+ # execute block, fetch last element from buffer
81
+ %x{
82
+ let last_buffer_length = Opal.Preact.render_buffer[Opal.Preact.render_buffer.length - 1].length;
83
+ let last_buffer_element = Opal.Preact.render_buffer[Opal.Preact.render_buffer.length - 1][last_buffer_length - 1];
84
+ block.$call();
85
+ // console.log("get_preact_element popping", Opal.Preact.render_buffer, Opal.Preact.render_buffer.toString())
86
+ let new_element = Opal.Preact.render_buffer[Opal.Preact.render_buffer.length - 1].pop();
87
+ if (last_buffer_element === new_element) { #{Isomorfeus.raise_error(message: "Block did not create any Preact element!")} }
88
+ return new_element;
89
+ }
90
+ else
91
+ # element was rendered before being passed as arg
92
+ # fetch last element from buffer
93
+ # `console.log("get_preact_element popping", Opal.Preact.render_buffer, Opal.Preact.render_buffer.toString())`
94
+ `Opal.Preact.render_buffer[Opal.Preact.render_buffer.length - 1].pop()`
95
+ end
96
+ end
97
+ alias gpe get_preact_element
98
+
99
+ def render_preact_element(el)
100
+ # push el to buffer
101
+ `Opal.Preact.render_buffer[Opal.Preact.render_buffer.length - 1].push(el)`
102
+ # `console.log("render_preact_element pushed", Opal.Preact.render_buffer, Opal.Preact.render_buffer.toString())`
103
+ nil
104
+ end
105
+ alias rpe render_preact_element
106
+
107
+ def method_ref(method_symbol, *args)
108
+ method_key = "#{method_symbol}#{args}"
109
+ %x{
110
+ if (#{self}.method_refs && #{self}.method_refs[#{method_key}]) { return #{self}.method_refs[#{method_key}]; }
111
+ if (!#{self}.method_refs) { #{self}.method_refs = {}; }
112
+ #{self}.method_refs[#{method_key}] = { m: #{method(method_symbol)}, a: args };
113
+ return #{self}.method_refs[#{method_key}];
114
+ }
115
+ end
116
+ alias m_ref method_ref
117
+
118
+ def to_n
119
+ self
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,9 @@
1
+ module Preact
2
+ module FunctionComponent
3
+ class Base
4
+ def self.inherited(base)
5
+ base.include(::Preact::FunctionComponent::Mixin)
6
+ end
7
+ end
8
+ end
9
+ end