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.
- checksums.yaml +4 -4
- data/README.md +64 -0
- data/lib/browser/delegate_native.rb +70 -0
- data/lib/browser/element.rb +176 -0
- data/lib/browser/element/canvas.rb +17 -0
- data/lib/browser/element/media.rb +78 -0
- data/lib/browser/event.rb +92 -0
- data/lib/browser/event_target.rb +39 -0
- data/lib/browser/file_list.rb +125 -0
- data/lib/browser/iterable.rb +15 -0
- data/lib/isomorfeus-react-material-ui.rb +10 -0
- data/lib/isomorfeus-react.rb +145 -0
- data/lib/isomorfeus/config.rb +130 -0
- data/lib/isomorfeus/props/validate_hash_proxy.rb +178 -0
- data/lib/isomorfeus/props/validator.rb +131 -0
- data/lib/isomorfeus/react_view_helper.rb +130 -0
- data/lib/isomorfeus/top_level.rb +86 -0
- data/lib/isomorfeus/top_level_ssr.rb +28 -0
- data/lib/lucid_app/api.rb +30 -0
- data/lib/lucid_app/base.rb +7 -0
- data/lib/lucid_app/context.rb +7 -0
- data/lib/lucid_app/mixin.rb +20 -0
- data/lib/lucid_app/native_component_constructor.rb +105 -0
- data/lib/lucid_component/app_store_defaults.rb +36 -0
- data/lib/lucid_component/app_store_proxy.rb +38 -0
- data/lib/lucid_component/base.rb +7 -0
- data/lib/lucid_component/class_store_proxy.rb +41 -0
- data/lib/lucid_component/component_class_store_defaults.rb +38 -0
- data/lib/lucid_component/component_instance_store_defaults.rb +35 -0
- data/lib/lucid_component/event_handler.rb +17 -0
- data/lib/lucid_component/initializer.rb +12 -0
- data/lib/lucid_component/instance_store_proxy.rb +45 -0
- data/lib/lucid_component/mixin.rb +18 -0
- data/lib/lucid_component/native_component_constructor.rb +116 -0
- data/lib/lucid_component/reducers.rb +48 -0
- data/lib/lucid_component/store_api.rb +38 -0
- data/lib/lucid_component/styles_support.rb +37 -0
- data/lib/lucid_material/app/base.rb +9 -0
- data/lib/lucid_material/app/mixin.rb +22 -0
- data/lib/lucid_material/app/native_component_constructor.rb +107 -0
- data/lib/lucid_material/component/base.rb +9 -0
- data/lib/lucid_material/component/mixin.rb +20 -0
- data/lib/lucid_material/component/native_component_constructor.rb +118 -0
- data/lib/lucid_prop_declaration/mixin.rb +91 -0
- data/lib/react.rb +195 -0
- data/lib/react/active_support_support.rb +13 -0
- data/lib/react/children.rb +35 -0
- data/lib/react/component/api.rb +80 -0
- data/lib/react/component/base.rb +9 -0
- data/lib/react/component/callbacks.rb +106 -0
- data/lib/react/component/elements.rb +60 -0
- data/lib/react/component/event_handler.rb +19 -0
- data/lib/react/component/features.rb +47 -0
- data/lib/react/component/history.rb +36 -0
- data/lib/react/component/initializer.rb +11 -0
- data/lib/react/component/location.rb +15 -0
- data/lib/react/component/match.rb +31 -0
- data/lib/react/component/mixin.rb +19 -0
- data/lib/react/component/native_component_constructor.rb +93 -0
- data/lib/react/component/props.rb +59 -0
- data/lib/react/component/resolution.rb +70 -0
- data/lib/react/component/should_component_update.rb +14 -0
- data/lib/react/component/state.rb +52 -0
- data/lib/react/component/styles.rb +27 -0
- data/lib/react/component/unsafe_api.rb +33 -0
- data/lib/react/context_wrapper.rb +46 -0
- data/lib/react/function_component/api.rb +63 -0
- data/lib/react/function_component/base.rb +9 -0
- data/lib/react/function_component/creator.rb +32 -0
- data/lib/react/function_component/event_handler.rb +13 -0
- data/lib/react/function_component/mixin.rb +14 -0
- data/lib/react/function_component/resolution.rb +62 -0
- data/lib/react/memo_component/base.rb +9 -0
- data/lib/react/memo_component/creator.rb +32 -0
- data/lib/react/memo_component/mixin.rb +14 -0
- data/lib/react/native_constant_wrapper.rb +26 -0
- data/lib/react/pure_component/base.rb +9 -0
- data/lib/react/pure_component/mixin.rb +18 -0
- data/lib/react/ref.rb +13 -0
- data/lib/react/synthetic_event.rb +53 -0
- data/lib/react/version.rb +3 -0
- data/lib/react_dom.rb +47 -0
- data/lib/react_dom_server.rb +19 -0
- metadata +84 -2
@@ -0,0 +1,178 @@
|
|
1
|
+
module Isomorfeus
|
2
|
+
module Props
|
3
|
+
class ValidateHashProxy
|
4
|
+
def initialize
|
5
|
+
@validation_hash = { required: true, validate: {} }
|
6
|
+
end
|
7
|
+
|
8
|
+
def is
|
9
|
+
self
|
10
|
+
end
|
11
|
+
alias_method :and, :is
|
12
|
+
alias_method :has, :is
|
13
|
+
alias_method :with, :is
|
14
|
+
|
15
|
+
def cast
|
16
|
+
@validation_hash[:cast] = true
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def default(v)
|
21
|
+
@validation_hash[:required] = false
|
22
|
+
@validation_hash[:default] = v
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure(v = nil, &block)
|
27
|
+
if block_given?
|
28
|
+
@validation_hash[:ensure_block] = block
|
29
|
+
else
|
30
|
+
@validation_hash[:ensure] = v
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def exact_class(t_class)
|
36
|
+
@validation_hash[:class] = t_class
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def greater_than(v)
|
41
|
+
@validation_hash[:validate][:gt] = v
|
42
|
+
self
|
43
|
+
end
|
44
|
+
alias_method :gt, :greater_than
|
45
|
+
|
46
|
+
def is_a(i_class)
|
47
|
+
@validation_hash[:is_a] = i_class
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def keys(*keys)
|
52
|
+
@validation_hash[:validate][:hash_keys] = keys
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def size(l)
|
57
|
+
@validation_hash[:validate][:size] = v
|
58
|
+
self
|
59
|
+
end
|
60
|
+
alias_method :length, :size
|
61
|
+
|
62
|
+
def less_than(v)
|
63
|
+
@validation_hash[:validate][:lt] = v
|
64
|
+
self
|
65
|
+
end
|
66
|
+
alias_method :lt, :less_than
|
67
|
+
|
68
|
+
def matches(regexp)
|
69
|
+
@validation_hash[:validate][:matches] = regexp
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def max(l)
|
74
|
+
@validation_hash[:validate][:max] = l
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def max_size(l)
|
79
|
+
@validation_hash[:validate][:max_size] = l
|
80
|
+
self
|
81
|
+
end
|
82
|
+
alias_method :max_length, :max_size
|
83
|
+
|
84
|
+
def min(l)
|
85
|
+
@validation_hash[:validate][:min] = l
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def min_size(l)
|
90
|
+
@validation_hash[:validate][:min_size] = l
|
91
|
+
self
|
92
|
+
end
|
93
|
+
alias_method :min_length, :min_size
|
94
|
+
|
95
|
+
def negative
|
96
|
+
@validation_hash[:validate][:direction] = :negative
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def optional
|
101
|
+
@validation_hash[:required] = false
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def positive
|
106
|
+
@validation_hash[:validate][:direction] = :positive
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def required
|
111
|
+
@validation_hash[:required] = true
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def test(&block)
|
116
|
+
@validation_hash[:validate][:test] = block
|
117
|
+
self
|
118
|
+
end
|
119
|
+
alias_method :condition, :test
|
120
|
+
alias_method :check, :test
|
121
|
+
|
122
|
+
# types
|
123
|
+
|
124
|
+
def Array
|
125
|
+
@validation_hash[:class] = Array
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def Boolean
|
130
|
+
@validation_hash[:type] = :boolean
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
def Enumerable
|
135
|
+
@validation_hash[:is_a] = Enumerable
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
def Float
|
140
|
+
@validation_hash[:class] = Float
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
def Hash
|
145
|
+
@validation_hash[:class] = Hash
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
def Integer
|
150
|
+
@validation_hash[:class] = Integer
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def String
|
155
|
+
@validation_hash[:class] = String
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# sub types
|
160
|
+
|
161
|
+
def Email
|
162
|
+
@validation_hash[:type] = :string
|
163
|
+
@validation_hash[:validate][:sub_type] = :email
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
def Url
|
168
|
+
@validation_hash[:type] = :string
|
169
|
+
@validation_hash[:validate][:sub_type] = :url
|
170
|
+
self
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_h
|
174
|
+
@validation_hash
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Isomorfeus
|
2
|
+
module Props
|
3
|
+
class Validator
|
4
|
+
def initialize(source_class, prop, value, options)
|
5
|
+
@c = source_class
|
6
|
+
@p = prop
|
7
|
+
@v = value
|
8
|
+
@o = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate!
|
12
|
+
ensured = ensure!
|
13
|
+
unless ensured
|
14
|
+
cast!
|
15
|
+
type!
|
16
|
+
end
|
17
|
+
run_checks!
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# basic tests
|
24
|
+
|
25
|
+
def cast!
|
26
|
+
if @o.key?(:cast)
|
27
|
+
begin
|
28
|
+
@v = case @o[:class]
|
29
|
+
when Integer then @v.to_i
|
30
|
+
when String then @v.to_s
|
31
|
+
when Float then @v.to_f
|
32
|
+
when Array then @v.to_a
|
33
|
+
when Hash then @v.to_h
|
34
|
+
end
|
35
|
+
@v = !!@v if @o[:type] == :boolean
|
36
|
+
rescue
|
37
|
+
raise "#{@c}: #{@p} cast failed" unless @v.class == @o[:class]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ensure!
|
43
|
+
if @o.key?(:ensure)
|
44
|
+
@v = @o[:ensure] unless @v
|
45
|
+
true
|
46
|
+
elsif @o.key?(:ensure_block)
|
47
|
+
@v = @o[:ensure_block].call(@v)
|
48
|
+
true
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def type!
|
55
|
+
if @o.key?(:class)
|
56
|
+
raise "#{@c}: #{@p} class not #{@o[:class]}" unless @v.class == @o[:class]
|
57
|
+
elsif @o.key?(:is_a)
|
58
|
+
raise "#{@c}: #{@p} is not a #{@o[:is_a]}" unless @v.is_a?(@o[:is_a])
|
59
|
+
elsif @o.key?(:type)
|
60
|
+
case @o[:type]
|
61
|
+
when :boolean
|
62
|
+
raise "#{@c}: #{@p} is not a boolean" unless @v.class == TrueClass || @v.class == FalseClass
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# all other checks
|
68
|
+
|
69
|
+
def run_checks!
|
70
|
+
if @o.key?(:validate)
|
71
|
+
@o[:validate].each do |m, l|
|
72
|
+
send('c_' + m, l)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# specific validations
|
78
|
+
def c_gt(v)
|
79
|
+
raise "#{@c}: #{@p} not greater than #{v}!" unless @v > v
|
80
|
+
end
|
81
|
+
|
82
|
+
def c_lt(v)
|
83
|
+
raise "#{@c}: #{@p} not less than #{v}!" unless @v < v
|
84
|
+
end
|
85
|
+
|
86
|
+
def c_keys(v)
|
87
|
+
raise "#{@c}: #{@p} keys dont fit!" unless @v.keys.sort == v.sort
|
88
|
+
end
|
89
|
+
|
90
|
+
def c_size(v)
|
91
|
+
raise "#{@c}: #{@p} length/size is not #{v}" unless @v.size == v
|
92
|
+
end
|
93
|
+
|
94
|
+
def c_matches(v)
|
95
|
+
raise "#{@c}: #{@p} does not match #{v}" unless v.match?(@v)
|
96
|
+
end
|
97
|
+
|
98
|
+
def c_max(v)
|
99
|
+
raise "#{@c}: #{@p} is larger than #{v}" unless @v <= v
|
100
|
+
end
|
101
|
+
|
102
|
+
def c_min(v)
|
103
|
+
raise "#{@c}: #{@p} is smaller than #{v}" unless @v >= v
|
104
|
+
end
|
105
|
+
|
106
|
+
def c_max_size(v)
|
107
|
+
raise "#{@c}: #{@p} is larger than #{v}" unless @v.size <= v
|
108
|
+
end
|
109
|
+
|
110
|
+
def c_min_size(v)
|
111
|
+
raise "#{@c}: #{@p} is smaller than #{v}" unless @v.size >= v
|
112
|
+
end
|
113
|
+
|
114
|
+
def c_direction(v)
|
115
|
+
raise "#{@c}: #{@p} is positive" if v == :negative && @v >= 0
|
116
|
+
raise "#{@c}: #{@p} is negative" if v == :positive && @v < 0
|
117
|
+
end
|
118
|
+
|
119
|
+
def c_test
|
120
|
+
raise "#{@c}: #{@p} test condition check failed" unless @o[:test].call(@v)
|
121
|
+
end
|
122
|
+
|
123
|
+
def c_sub_type(v)
|
124
|
+
case v
|
125
|
+
when :email
|
126
|
+
when :url
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Isomorfeus
|
2
|
+
module ReactViewHelper
|
3
|
+
def mount_component(component_name, props = {}, asset = 'application_ssr.js')
|
4
|
+
@ssr_response_status = nil
|
5
|
+
@ssr_styles = nil
|
6
|
+
thread_id_asset = "#{Thread.current.object_id}#{asset}"
|
7
|
+
render_result = "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'"
|
8
|
+
if Isomorfeus.server_side_rendering
|
9
|
+
|
10
|
+
# initialize speednode context
|
11
|
+
unless Isomorfeus.ssr_contexts.key?(thread_id_asset)
|
12
|
+
asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
|
13
|
+
asset_path = File.join('public', asset_file_name)
|
14
|
+
Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(File.read(asset_path))
|
15
|
+
end
|
16
|
+
|
17
|
+
# build javascript for rendering first pass
|
18
|
+
javascript = <<~JAVASCRIPT
|
19
|
+
global.FirstPassFinished = false;
|
20
|
+
global.Opal.Isomorfeus['$env=']('#{Isomorfeus.env}');
|
21
|
+
if (typeof global.Opal.Isomorfeus.$negotiated_locale === 'function') {
|
22
|
+
global.Opal.Isomorfeus["$negotiated_locale="]('#{props[:locale]}');
|
23
|
+
}
|
24
|
+
global.Opal.Isomorfeus['$force_init!']();
|
25
|
+
global.Opal.Isomorfeus['$ssr_response_status='](200);
|
26
|
+
global.Opal.Isomorfeus.TopLevel['$ssr_route_path=']('#{props[:location]}');
|
27
|
+
JAVASCRIPT
|
28
|
+
|
29
|
+
# if location_host and scheme are given and if Transport is loaded, connect and then render, otherwise do not render
|
30
|
+
ws_scheme = props[:location_scheme] == 'https:' ? 'wss:' : 'ws:'
|
31
|
+
location_host = props[:location_host] ? props[:location_host] : 'localhost'
|
32
|
+
api_ws_path = Isomorfeus.respond_to?(:api_websocket_path) ? Isomorfeus.api_websocket_path : ''
|
33
|
+
transport_ws_url = ws_scheme + location_host + api_ws_path
|
34
|
+
javascript << <<~JAVASCRIPT
|
35
|
+
var api_ws_path = '#{api_ws_path}';
|
36
|
+
if (typeof global.Opal.Isomorfeus.Transport !== 'undefined' && api_ws_path !== '') {
|
37
|
+
global.Opal.Isomorfeus.TopLevel["$transport_ws_url="]("#{transport_ws_url}");
|
38
|
+
global.Opal.send(global.Opal.Isomorfeus.Transport.$promise_connect(), 'then', [], ($$1 = function(){
|
39
|
+
try {
|
40
|
+
global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)});
|
41
|
+
global.FirstPassFinished = 'transport';
|
42
|
+
} catch (e) { global.FirstPassFinished = 'transport'; }
|
43
|
+
}, $$1.$$s = this, $$1.$$arity = 0, $$1))
|
44
|
+
} else { return global.FirstPassFinished = true; };
|
45
|
+
JAVASCRIPT
|
46
|
+
|
47
|
+
# execute first render pass
|
48
|
+
first_pass_skipped = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
|
49
|
+
|
50
|
+
# wait for first pass to finish
|
51
|
+
unless first_pass_skipped
|
52
|
+
first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
|
53
|
+
unless first_pass_finished
|
54
|
+
start_time = Time.now
|
55
|
+
while !first_pass_finished
|
56
|
+
break if (Time.now - start_time) > 10
|
57
|
+
sleep 0.01
|
58
|
+
first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# wait for transport requests to finish
|
63
|
+
if first_pass_finished == 'transport'
|
64
|
+
transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
|
65
|
+
if transport_busy
|
66
|
+
start_time = Time.now
|
67
|
+
while transport_busy
|
68
|
+
break if (Time.now - start_time) > 10
|
69
|
+
sleep 0.01
|
70
|
+
transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# build javascript for second render pass
|
77
|
+
javascript = <<~JAVASCRIPT
|
78
|
+
let rendered_tree;
|
79
|
+
let ssr_styles;
|
80
|
+
let component;
|
81
|
+
if (typeof global.Opal.global.MuiStyles !== 'undefined' && typeof global.Opal.global.MuiStyles.ServerStyleSheets !== 'undefined') {
|
82
|
+
component = '#{component_name}'.split(".").reduce(function(o, x) {
|
83
|
+
return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
|
84
|
+
}, global.Opal.global)
|
85
|
+
if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
|
86
|
+
let sheets = new global.Opal.global.MuiStyles.ServerStyleSheets();
|
87
|
+
let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
|
88
|
+
rendered_tree = global.Opal.global.ReactDOMServer.renderToString(sheets.collect(app));
|
89
|
+
ssr_styles = sheets.toString();
|
90
|
+
} else if (typeof global.Opal.global.ReactJSS !== 'undefined' && typeof global.Opal.global.ReactJSS.SheetsRegistry !== 'undefined') {
|
91
|
+
component = '#{component_name}'.split(".").reduce(function(o, x) {
|
92
|
+
return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
|
93
|
+
}, global.Opal.global)
|
94
|
+
if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
|
95
|
+
let sheets = new global.Opal.global.ReactJSS.SheetsRegistry();
|
96
|
+
let generate_id = global.Opal.global.ReactJSS.createGenerateId();
|
97
|
+
let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
|
98
|
+
let element = global.Opal.global.React.createElement(global.Opal.global.ReactJSS.JssProvider, { registry: sheets, generateId: generate_id }, app);
|
99
|
+
rendered_tree = global.Opal.global.ReactDOMServer.renderToString(element);
|
100
|
+
ssr_styles = sheets.toString();
|
101
|
+
} else {
|
102
|
+
rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)});
|
103
|
+
}
|
104
|
+
let application_state = global.Opal.Isomorfeus.store.native.getState();
|
105
|
+
if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); }
|
106
|
+
return [rendered_tree, application_state, ssr_styles, global.Opal.Isomorfeus['$ssr_response_status']()];
|
107
|
+
JAVASCRIPT
|
108
|
+
|
109
|
+
# execute second render pass
|
110
|
+
rendered_tree, application_state, @ssr_styles, @ssr_response_status = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
|
111
|
+
|
112
|
+
# build result
|
113
|
+
render_result << " data-iso-nloc='#{props[:locale]}' data-iso-state='#{Oj.dump(application_state, mode: :strict)}'>"
|
114
|
+
render_result << rendered_tree
|
115
|
+
else
|
116
|
+
render_result << " data-iso-nloc='#{props[:locale]}'>"
|
117
|
+
end
|
118
|
+
render_result << '</div>'
|
119
|
+
render_result
|
120
|
+
end
|
121
|
+
|
122
|
+
def ssr_response_status
|
123
|
+
@ssr_response_status || 200
|
124
|
+
end
|
125
|
+
|
126
|
+
def ssr_styles
|
127
|
+
@ssr_styles || ''
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Isomorfeus
|
2
|
+
class TopLevel
|
3
|
+
class << self
|
4
|
+
def mount!
|
5
|
+
Isomorfeus.init
|
6
|
+
Isomorfeus::TopLevel.on_ready do
|
7
|
+
root_element = `document.querySelector('div[data-iso-root]')`
|
8
|
+
component_name = root_element.JS.getAttribute('data-iso-root')
|
9
|
+
Isomorfeus.env = root_element.JS.getAttribute('data-iso-env')
|
10
|
+
component = nil
|
11
|
+
begin
|
12
|
+
component = component_name.constantize
|
13
|
+
rescue Exception
|
14
|
+
@timeout_start = Time.now unless @timeout_start
|
15
|
+
if (Time.now - @timeout_start) < 10
|
16
|
+
`setTimeout(Opal.Isomorfeus.TopLevel['$mount!'], 100)`
|
17
|
+
else
|
18
|
+
`console.error("Unable to mount '" + #{component_name} + "'!")`
|
19
|
+
end
|
20
|
+
end
|
21
|
+
if component
|
22
|
+
props_json = root_element.JS.getAttribute('data-iso-props')
|
23
|
+
props = `Opal.Hash.$new(JSON.parse(props_json))`
|
24
|
+
hydrated = root_element.JS.getAttribute('data-iso-hydrated')
|
25
|
+
state_json = root_element.JS.getAttribute('data-iso-state')
|
26
|
+
if state_json
|
27
|
+
%x{
|
28
|
+
var state = JSON.parse(state_json);
|
29
|
+
var keys = Object.keys(state);
|
30
|
+
for(var i=0; i < keys.length; i++) {
|
31
|
+
global.Opal.Isomorfeus.store.native.dispatch({ type: keys[i].toUpperCase(), set_state: state[keys[i]] });
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
begin
|
36
|
+
Isomorfeus::TopLevel.mount_component(component, props, root_element, hydrated)
|
37
|
+
rescue Exception
|
38
|
+
@timeout_start = Time.now unless @timeout_start
|
39
|
+
if (Time.now - @timeout_start) < 10
|
40
|
+
`setTimeout(Opal.Isomorfeus.TopLevel['$mount!'], 100)`
|
41
|
+
else
|
42
|
+
`console.error("Unable to mount '" + #{component_name} + "'!")`
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_ready(&block)
|
50
|
+
# this looks a bit odd but works across _all_ browsers, and no event handler mess
|
51
|
+
%x{
|
52
|
+
function run() { block.$call() };
|
53
|
+
function ready_fun() {
|
54
|
+
/in/.test(document.readyState) ? setTimeout(ready_fun,5) : run();
|
55
|
+
}
|
56
|
+
ready_fun();
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_ready_mount(component, props = nil, element_query = nil)
|
61
|
+
# init in case it hasn't been run yet
|
62
|
+
Isomorfeus.init
|
63
|
+
on_ready do
|
64
|
+
Isomorfeus::TopLevel.mount_component(component, props, element_query)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def mount_component(component, props, element_or_query, hydrated = false)
|
69
|
+
if `(typeof element_or_query === 'string')` || (`(typeof element_or_query.$class === 'function')` && element_or_query.class == String)
|
70
|
+
element = `document.body.querySelector(element_or_query)`
|
71
|
+
elsif `(typeof element_or_query.$is_a === 'function')` && element_or_query.is_a?(Browser::Element)
|
72
|
+
element = element_or_query.to_n
|
73
|
+
else
|
74
|
+
element = element_or_query
|
75
|
+
end
|
76
|
+
|
77
|
+
top = if hydrated
|
78
|
+
ReactDOM.hydrate(React.create_element(component, props), element)
|
79
|
+
else
|
80
|
+
ReactDOM.render(React.create_element(component, props), element)
|
81
|
+
end
|
82
|
+
Isomorfeus.top_component = top if top
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|