isomorfeus-preact 10.5.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 (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