ruflet 0.0.4 → 0.0.5
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f114a59fe4013e5313b51fc7b8b3373dfa7120e57422bc33abb2e224f9724d6
|
|
4
|
+
data.tar.gz: fe0b491c3eff667a72c31521209953a2f7ba8249c1d16f6ec9ac08243099f850
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cbf26f1c4752d3fdd13600ff8b0bd305e3e669165c9cda9d094d4e4687f801adcb1df015230a509c016f8305f195c32459af74eeacdc8829475f62ac8c7af591
|
|
7
|
+
data.tar.gz: a59f249b3b71255a4b499e8e87c69216c3e61d50c974a786cc1d926b7d502b500effe4161b5c2d8c3c384078ead1cfe1194106c872ca4a17bdafb05f180e36f7
|
data/lib/ruflet/version.rb
CHANGED
data/lib/ruflet_ui/ruflet/dsl.rb
CHANGED
|
@@ -5,6 +5,15 @@ require_relative "ui/control_factory"
|
|
|
5
5
|
|
|
6
6
|
module Ruflet
|
|
7
7
|
module DSL
|
|
8
|
+
DURATION_FACTORS_MS = {
|
|
9
|
+
days: 86_400_000.0,
|
|
10
|
+
hours: 3_600_000.0,
|
|
11
|
+
minutes: 60_000.0,
|
|
12
|
+
seconds: 1_000.0,
|
|
13
|
+
milliseconds: 1.0,
|
|
14
|
+
microseconds: 0.001
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
8
17
|
module_function
|
|
9
18
|
|
|
10
19
|
def _pending_app
|
|
@@ -95,6 +104,7 @@ module Ruflet
|
|
|
95
104
|
def cupertinodialogaction(**props) = _pending_app.cupertinodialogaction(**props)
|
|
96
105
|
def cupertino_navigation_bar(**props) = _pending_app.cupertino_navigation_bar(**props)
|
|
97
106
|
def cupertinonavigationbar(**props) = _pending_app.cupertinonavigationbar(**props)
|
|
107
|
+
def duration(**parts) = duration_in_milliseconds(parts)
|
|
98
108
|
|
|
99
109
|
class App
|
|
100
110
|
include UI::ControlMethods
|
|
@@ -146,6 +156,10 @@ module Ruflet
|
|
|
146
156
|
c
|
|
147
157
|
end
|
|
148
158
|
|
|
159
|
+
def duration(**parts)
|
|
160
|
+
DSL.duration(**parts)
|
|
161
|
+
end
|
|
162
|
+
|
|
149
163
|
def run
|
|
150
164
|
app_roots = @roots
|
|
151
165
|
page_props = @page_props.dup
|
|
@@ -177,6 +191,27 @@ module Ruflet
|
|
|
177
191
|
"#{type}_#{@seq}"
|
|
178
192
|
end
|
|
179
193
|
|
|
194
|
+
def duration_in_milliseconds(parts)
|
|
195
|
+
DSL.send(:duration_in_milliseconds, parts)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def duration_in_milliseconds(parts)
|
|
201
|
+
return 0 if parts.nil? || parts.empty?
|
|
202
|
+
|
|
203
|
+
DURATION_FACTORS_MS.reduce(0.0) do |sum, (key, factor)|
|
|
204
|
+
sum + read_duration_part(parts, key) * factor
|
|
205
|
+
end.round
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def read_duration_part(parts, key)
|
|
209
|
+
raw = parts[key] || parts[key.to_s]
|
|
210
|
+
return 0.0 if raw.nil?
|
|
211
|
+
return raw.to_f if raw.is_a?(Numeric)
|
|
212
|
+
return raw.to_f if raw.is_a?(String) && raw.match?(/\A-?\d+(\.\d+)?\z/)
|
|
213
|
+
|
|
214
|
+
0.0
|
|
180
215
|
end
|
|
181
216
|
end
|
|
182
217
|
end
|
|
@@ -3,49 +3,29 @@
|
|
|
3
3
|
require_relative "event"
|
|
4
4
|
require "ruflet_protocol"
|
|
5
5
|
require_relative "control"
|
|
6
|
-
require_relative "ui/control_methods"
|
|
7
6
|
require_relative "ui/widget_builder"
|
|
8
7
|
require_relative "icons/material_icon_lookup"
|
|
9
8
|
require_relative "icons/cupertino_icon_lookup"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
rescue LoadError
|
|
13
|
-
class Set
|
|
14
|
-
def initialize
|
|
15
|
-
@index = {}
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def include?(value)
|
|
19
|
-
@index.key?(value)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def <<(value)
|
|
23
|
-
@index[value] = true
|
|
24
|
-
self
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
begin
|
|
30
|
-
require "cgi"
|
|
31
|
-
rescue LoadError
|
|
32
|
-
module CGI
|
|
33
|
-
module_function
|
|
34
|
-
|
|
35
|
-
def escape(text)
|
|
36
|
-
value = text.to_s
|
|
37
|
-
value.gsub(/[^a-zA-Z0-9_.~-]/) { |ch| "%%%02X" % ch.ord }
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
9
|
+
require "set"
|
|
10
|
+
require "cgi"
|
|
41
11
|
|
|
42
12
|
module Ruflet
|
|
43
13
|
class Page
|
|
44
|
-
include UI::ControlMethods
|
|
45
|
-
|
|
46
14
|
PAGE_PROP_KEYS = %w[route title vertical_alignment horizontal_alignment scroll].freeze
|
|
47
15
|
DIALOG_PROP_KEYS = %w[dialog snack_bar bottom_sheet].freeze
|
|
48
16
|
BUTTON_TEXT_TYPES = %w[button elevatedbutton textbutton filledbutton].freeze
|
|
17
|
+
DEPRECATED_PAGE_WIDGET_METHODS = %i[
|
|
18
|
+
control widget view column center row stack container gesture_detector gesturedetector draggable
|
|
19
|
+
drag_target dragtarget text button elevated_button elevatedbutton text_button textbutton filled_button
|
|
20
|
+
filledbutton icon_button iconbutton text_field textfield checkbox radio radio_group radiogroup
|
|
21
|
+
alert_dialog alertdialog markdown icon image app_bar appbar floating_action_button snack_bar snackbar
|
|
22
|
+
bottom_sheet bottomsheet tabs tab tab_bar tabbar tab_bar_view tabbarview navigation_bar navigationbar
|
|
23
|
+
navigation_bar_destination navigationbardestination fab cupertino_button
|
|
24
|
+
cupertinobutton cupertino_filled_button cupertinofilledbutton cupertino_text_field cupertinotextfield
|
|
25
|
+
cupertino_switch cupertinoswitch cupertino_slider cupertinoslider cupertino_alert_dialog
|
|
26
|
+
cupertinoalertdialog cupertino_action_sheet cupertinoactionsheet cupertino_dialog_action
|
|
27
|
+
cupertinodialogaction cupertino_navigation_bar cupertinonavigationbar
|
|
28
|
+
].freeze
|
|
49
29
|
|
|
50
30
|
attr_reader :session_id, :client_details, :views
|
|
51
31
|
|
|
@@ -104,16 +84,6 @@ module Ruflet
|
|
|
104
84
|
@page_props["route"] = value
|
|
105
85
|
end
|
|
106
86
|
|
|
107
|
-
def scroll
|
|
108
|
-
@page_props["scroll"]
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def scroll=(value)
|
|
112
|
-
v = normalize_value("scroll", value)
|
|
113
|
-
@page_props["scroll"] = v
|
|
114
|
-
@view_props["scroll"] = v
|
|
115
|
-
end
|
|
116
|
-
|
|
117
87
|
def vertical_alignment
|
|
118
88
|
@page_props["vertical_alignment"] || @view_props["vertical_alignment"]
|
|
119
89
|
end
|
|
@@ -163,6 +133,11 @@ module Ruflet
|
|
|
163
133
|
self
|
|
164
134
|
end
|
|
165
135
|
|
|
136
|
+
def views=(value)
|
|
137
|
+
@views = Array(value).compact
|
|
138
|
+
self
|
|
139
|
+
end
|
|
140
|
+
|
|
166
141
|
def services
|
|
167
142
|
@services_container.props["_services"] ||= []
|
|
168
143
|
end
|
|
@@ -181,11 +156,6 @@ module Ruflet
|
|
|
181
156
|
self
|
|
182
157
|
end
|
|
183
158
|
|
|
184
|
-
def views=(value)
|
|
185
|
-
@views = Array(value).compact
|
|
186
|
-
self
|
|
187
|
-
end
|
|
188
|
-
|
|
189
159
|
def go(route, **query_params)
|
|
190
160
|
@page_props["route"] = build_route(route, query_params)
|
|
191
161
|
dispatch_page_event(name: "route_change", data: @page_props["route"])
|
|
@@ -212,22 +182,10 @@ module Ruflet
|
|
|
212
182
|
add(*builder.children)
|
|
213
183
|
end
|
|
214
184
|
|
|
215
|
-
def appbar(**props, &block)
|
|
216
|
-
return @view_props["appbar"] if props.empty? && !block
|
|
217
|
-
|
|
218
|
-
WidgetBuilder.new.appbar(**props, &block)
|
|
219
|
-
end
|
|
220
|
-
|
|
221
185
|
def appbar=(value)
|
|
222
186
|
@view_props["appbar"] = value
|
|
223
187
|
end
|
|
224
188
|
|
|
225
|
-
def floating_action_button(**props, &block)
|
|
226
|
-
return @view_props["floating_action_button"] if props.empty? && !block
|
|
227
|
-
|
|
228
|
-
WidgetBuilder.new.floating_action_button(**props, &block)
|
|
229
|
-
end
|
|
230
|
-
|
|
231
189
|
def floating_action_button=(value)
|
|
232
190
|
@view_props["floating_action_button"] = value
|
|
233
191
|
end
|
|
@@ -239,40 +197,20 @@ module Ruflet
|
|
|
239
197
|
refresh_dialogs_container!
|
|
240
198
|
end
|
|
241
199
|
|
|
242
|
-
def snack_bar(**props, &block)
|
|
243
|
-
return @snack_bar if props.empty? && !block
|
|
244
|
-
|
|
245
|
-
super
|
|
246
|
-
end
|
|
247
|
-
|
|
248
200
|
def snack_bar=(value)
|
|
249
201
|
@snack_bar = value
|
|
250
202
|
refresh_dialogs_container!
|
|
251
203
|
end
|
|
252
204
|
|
|
253
|
-
def snackbar(**props, &block)
|
|
254
|
-
snack_bar(**props, &block)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
205
|
def snackbar=(value)
|
|
258
206
|
self.snack_bar = value
|
|
259
207
|
end
|
|
260
208
|
|
|
261
|
-
def bottom_sheet(**props, &block)
|
|
262
|
-
return @bottom_sheet if props.empty? && !block
|
|
263
|
-
|
|
264
|
-
super
|
|
265
|
-
end
|
|
266
|
-
|
|
267
209
|
def bottom_sheet=(value)
|
|
268
210
|
@bottom_sheet = value
|
|
269
211
|
refresh_dialogs_container!
|
|
270
212
|
end
|
|
271
213
|
|
|
272
|
-
def bottomsheet(**props, &block)
|
|
273
|
-
bottom_sheet(**props, &block)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
214
|
def bottomsheet=(value)
|
|
277
215
|
self.bottom_sheet = value
|
|
278
216
|
end
|
|
@@ -290,12 +228,48 @@ module Ruflet
|
|
|
290
228
|
self
|
|
291
229
|
end
|
|
292
230
|
|
|
231
|
+
def invoke(control_or_id, method_name, args: nil, timeout: 10)
|
|
232
|
+
control = resolve_control(control_or_id)
|
|
233
|
+
return nil unless control
|
|
234
|
+
|
|
235
|
+
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
236
|
+
send_message(Protocol::ACTIONS[:invoke_control_method], {
|
|
237
|
+
"control_id" => control.wire_id,
|
|
238
|
+
"call_id" => call_id,
|
|
239
|
+
"name" => method_name.to_s,
|
|
240
|
+
"args" => args,
|
|
241
|
+
"timeout" => timeout
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
call_id
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def launch_url(url, mode: "external_application", web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10)
|
|
248
|
+
launcher = ensure_url_launcher_service
|
|
249
|
+
invoke(
|
|
250
|
+
launcher,
|
|
251
|
+
"launch_url",
|
|
252
|
+
args: {
|
|
253
|
+
"url" => url,
|
|
254
|
+
"mode" => mode,
|
|
255
|
+
"web_view_configuration" => web_view_configuration,
|
|
256
|
+
"browser_configuration" => browser_configuration,
|
|
257
|
+
"web_only_window_name" => web_only_window_name
|
|
258
|
+
}.compact,
|
|
259
|
+
timeout: timeout
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def can_launch_url(url, timeout: 10)
|
|
264
|
+
launcher = ensure_url_launcher_service
|
|
265
|
+
invoke(launcher, "can_launch_url", args: { "url" => url }, timeout: timeout)
|
|
266
|
+
end
|
|
267
|
+
|
|
293
268
|
def pop_dialog
|
|
294
269
|
dialog_control = latest_open_dialog
|
|
295
270
|
return nil unless dialog_control
|
|
296
271
|
|
|
297
272
|
dialog_control.props["open"] = false
|
|
298
|
-
@dialogs.delete(dialog_control)
|
|
299
273
|
refresh_dialogs_container!
|
|
300
274
|
push_dialogs_update!
|
|
301
275
|
dialog_control
|
|
@@ -316,21 +290,7 @@ module Ruflet
|
|
|
316
290
|
control = resolve_control(control_or_id)
|
|
317
291
|
return self unless control
|
|
318
292
|
|
|
319
|
-
|
|
320
|
-
props.each_value { |value| register_embedded_value(value, visited) }
|
|
321
|
-
|
|
322
|
-
raw_props = props.dup
|
|
323
|
-
if BUTTON_TEXT_TYPES.include?(control.type)
|
|
324
|
-
if raw_props.key?(:text) || raw_props.key?("text")
|
|
325
|
-
text_value = raw_props.key?(:text) ? raw_props.delete(:text) : raw_props.delete("text")
|
|
326
|
-
raw_props[:content] = text_value unless raw_props.key?(:content) || raw_props.key?("content")
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
normalized_control_props = control.send(:normalize_props, raw_props)
|
|
331
|
-
normalized_control_props.each { |k, v| control.props[k] = v }
|
|
332
|
-
|
|
333
|
-
patch = normalize_props(raw_props)
|
|
293
|
+
patch = normalize_props(props)
|
|
334
294
|
if BUTTON_TEXT_TYPES.include?(control.type) && patch.key?("text")
|
|
335
295
|
patch["content"] = patch.delete("text")
|
|
336
296
|
end
|
|
@@ -345,22 +305,6 @@ module Ruflet
|
|
|
345
305
|
self
|
|
346
306
|
end
|
|
347
307
|
|
|
348
|
-
def invoke(control_or_id, method_name, args: nil, timeout: 10)
|
|
349
|
-
control = resolve_control(control_or_id)
|
|
350
|
-
return nil unless control
|
|
351
|
-
|
|
352
|
-
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
353
|
-
send_message(Protocol::ACTIONS[:invoke_control_method], {
|
|
354
|
-
"control_id" => control.wire_id,
|
|
355
|
-
"call_id" => call_id,
|
|
356
|
-
"name" => method_name.to_s,
|
|
357
|
-
"args" => args,
|
|
358
|
-
"timeout" => timeout
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
call_id
|
|
362
|
-
end
|
|
363
|
-
|
|
364
308
|
def patch_page(control_id, **props)
|
|
365
309
|
update(control_id, **props)
|
|
366
310
|
end
|
|
@@ -398,6 +342,45 @@ module Ruflet
|
|
|
398
342
|
end
|
|
399
343
|
end
|
|
400
344
|
|
|
345
|
+
def method_missing(name, *args, &block)
|
|
346
|
+
method_name = name.to_s
|
|
347
|
+
prop_name = method_name.delete_suffix("=")
|
|
348
|
+
|
|
349
|
+
if method_name.end_with?("=")
|
|
350
|
+
if DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym)
|
|
351
|
+
Kernel.warn("[DEPRECATION] `page.#{prop_name}(...)` is no longer supported.")
|
|
352
|
+
raise NoMethodError, "Use `#{prop_name}(...)` as a free widget helper, then attach with `page.add(...)`."
|
|
353
|
+
end
|
|
354
|
+
assign_split_prop(prop_name, normalize_value(prop_name, args.first))
|
|
355
|
+
return args.first
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
if args.empty? && !block
|
|
359
|
+
return @page_props[method_name] if @page_props.key?(method_name)
|
|
360
|
+
return @view_props[method_name] if @view_props.key?(method_name)
|
|
361
|
+
return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
if DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym)
|
|
365
|
+
Kernel.warn("[DEPRECATION] `page.#{name}(...)` is no longer supported.")
|
|
366
|
+
raise NoMethodError, "Use `#{name}(...)` as a free widget helper, then attach with `page.add(...)`."
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
super
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def respond_to_missing?(name, include_private = false)
|
|
373
|
+
method_name = name.to_s
|
|
374
|
+
prop_name = method_name.delete_suffix("=")
|
|
375
|
+
DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym) ||
|
|
376
|
+
DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym) ||
|
|
377
|
+
method_name.end_with?("=") ||
|
|
378
|
+
@page_props.key?(method_name) ||
|
|
379
|
+
@view_props.key?(method_name) ||
|
|
380
|
+
DIALOG_PROP_KEYS.include?(method_name) ||
|
|
381
|
+
super
|
|
382
|
+
end
|
|
383
|
+
|
|
401
384
|
private
|
|
402
385
|
|
|
403
386
|
def build_widget(type, **props, &block) = WidgetBuilder.new.control(type, **props, &block)
|
|
@@ -489,7 +472,7 @@ module Ruflet
|
|
|
489
472
|
def resolve_control(control_or_id)
|
|
490
473
|
if control_or_id.respond_to?(:wire_id)
|
|
491
474
|
control_or_id
|
|
492
|
-
elsif
|
|
475
|
+
elsif control_or_id.to_s.match?(/^\d+$/)
|
|
493
476
|
@wire_index[control_or_id.to_i]
|
|
494
477
|
else
|
|
495
478
|
@control_index[control_or_id.to_s]
|
|
@@ -509,32 +492,10 @@ module Ruflet
|
|
|
509
492
|
return codepoint unless codepoint.nil?
|
|
510
493
|
end
|
|
511
494
|
|
|
512
|
-
if value.is_a?(Ruflet::Control)
|
|
513
|
-
register_control_tree(value, Set.new)
|
|
514
|
-
return value.to_patch
|
|
515
|
-
end
|
|
516
|
-
return serialize_value(value) if value.is_a?(Array) || value.is_a?(Hash)
|
|
517
|
-
|
|
518
495
|
return value.value if value.is_a?(Ruflet::IconData)
|
|
519
496
|
value.is_a?(Symbol) ? value.to_s : value
|
|
520
497
|
end
|
|
521
498
|
|
|
522
|
-
def serialize_value(value)
|
|
523
|
-
case value
|
|
524
|
-
when Ruflet::Control
|
|
525
|
-
register_control_tree(value, Set.new)
|
|
526
|
-
value.to_patch
|
|
527
|
-
when Ruflet::IconData
|
|
528
|
-
value.value
|
|
529
|
-
when Array
|
|
530
|
-
value.map { |v| serialize_value(v) }
|
|
531
|
-
when Hash
|
|
532
|
-
value.transform_values { |v| serialize_value(v) }
|
|
533
|
-
else
|
|
534
|
-
value
|
|
535
|
-
end
|
|
536
|
-
end
|
|
537
|
-
|
|
538
499
|
def build_route(route, query_params = {})
|
|
539
500
|
base = route.to_s
|
|
540
501
|
return base if query_params.nil? || query_params.empty?
|
|
@@ -586,12 +547,6 @@ module Ruflet
|
|
|
586
547
|
key == "icon" || key.end_with?("_icon")
|
|
587
548
|
end
|
|
588
549
|
|
|
589
|
-
def numeric_string?(value)
|
|
590
|
-
return false if value.empty?
|
|
591
|
-
value.each_byte { |b| return false unless b >= 0x30 && b <= 0x39 }
|
|
592
|
-
true
|
|
593
|
-
end
|
|
594
|
-
|
|
595
550
|
def refresh_dialogs_container!
|
|
596
551
|
dialog_controls = (@dialogs + dialog_slots).uniq
|
|
597
552
|
@dialogs_container.props["controls"] = dialog_controls
|
|
@@ -679,25 +634,20 @@ module Ruflet
|
|
|
679
634
|
end
|
|
680
635
|
|
|
681
636
|
def resolve_icon_codepoint(value)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
begin
|
|
686
|
-
codepoint = Ruflet::MaterialIconLookup.codepoint_for(value)
|
|
687
|
-
rescue NameError
|
|
688
|
-
codepoint = nil
|
|
637
|
+
codepoint = Ruflet::MaterialIconLookup.codepoint_for(value)
|
|
638
|
+
if codepoint.nil? || codepoint == value
|
|
639
|
+
codepoint = Ruflet::CupertinoIconLookup.codepoint_for(value)
|
|
689
640
|
end
|
|
641
|
+
codepoint
|
|
642
|
+
end
|
|
690
643
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
codepoint = cupertino unless cupertino.nil?
|
|
695
|
-
rescue NameError
|
|
696
|
-
codepoint = nil
|
|
697
|
-
end
|
|
698
|
-
end
|
|
644
|
+
def ensure_url_launcher_service
|
|
645
|
+
launcher = services.find { |service| service.is_a?(Control) && service.type == "url_launcher" }
|
|
646
|
+
return launcher if launcher
|
|
699
647
|
|
|
700
|
-
|
|
648
|
+
launcher = build_widget(:url_launcher)
|
|
649
|
+
add_service(launcher)
|
|
650
|
+
launcher
|
|
701
651
|
end
|
|
702
652
|
end
|
|
703
653
|
end
|
|
@@ -7,6 +7,61 @@ module Ruflet
|
|
|
7
7
|
def initialize(id: nil, **props)
|
|
8
8
|
super(type: "snackbar", id: id, **props)
|
|
9
9
|
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def preprocess_props(props)
|
|
14
|
+
mapped = props.dup
|
|
15
|
+
key = if mapped.key?(:duration)
|
|
16
|
+
:duration
|
|
17
|
+
elsif mapped.key?("duration")
|
|
18
|
+
"duration"
|
|
19
|
+
end
|
|
20
|
+
return mapped unless key
|
|
21
|
+
|
|
22
|
+
mapped[key] = normalize_duration_ms(mapped[key])
|
|
23
|
+
mapped
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def normalize_duration_ms(value)
|
|
27
|
+
return value.to_i if value.is_a?(Numeric)
|
|
28
|
+
return value.to_i if value.is_a?(String) && value.match?(/\A\d+(\.\d+)?\z/)
|
|
29
|
+
|
|
30
|
+
parts =
|
|
31
|
+
if value.is_a?(Hash)
|
|
32
|
+
value
|
|
33
|
+
elsif value.respond_to?(:to_h)
|
|
34
|
+
value.to_h
|
|
35
|
+
else
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
return value unless parts.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
days = read_number(parts, "days")
|
|
41
|
+
hours = read_number(parts, "hours")
|
|
42
|
+
minutes = read_number(parts, "minutes")
|
|
43
|
+
seconds = read_number(parts, "seconds")
|
|
44
|
+
milliseconds = read_number(parts, "milliseconds")
|
|
45
|
+
microseconds = read_number(parts, "microseconds")
|
|
46
|
+
|
|
47
|
+
total_ms = 0.0
|
|
48
|
+
total_ms += days * 86_400_000.0
|
|
49
|
+
total_ms += hours * 3_600_000.0
|
|
50
|
+
total_ms += minutes * 60_000.0
|
|
51
|
+
total_ms += seconds * 1_000.0
|
|
52
|
+
total_ms += milliseconds
|
|
53
|
+
total_ms += microseconds / 1000.0
|
|
54
|
+
total_ms.round
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def read_number(parts, key)
|
|
58
|
+
raw = parts[key] || parts[key.to_sym]
|
|
59
|
+
return 0.0 if raw.nil?
|
|
60
|
+
return raw.to_f if raw.is_a?(Numeric)
|
|
61
|
+
return raw.to_f if raw.is_a?(String) && raw.match?(/\A-?\d+(\.\d+)?\z/)
|
|
62
|
+
|
|
63
|
+
0.0
|
|
64
|
+
end
|
|
10
65
|
end
|
|
11
66
|
end
|
|
12
67
|
end
|
|
@@ -74,6 +74,7 @@ module Ruflet
|
|
|
74
74
|
def cupertinodialogaction(**props) = control_delegate.cupertinodialogaction(**props)
|
|
75
75
|
def cupertino_navigation_bar(**props) = control_delegate.cupertino_navigation_bar(**props)
|
|
76
76
|
def cupertinonavigationbar(**props) = control_delegate.cupertinonavigationbar(**props)
|
|
77
|
+
def duration(**parts) = control_delegate.duration(**parts)
|
|
77
78
|
|
|
78
79
|
private
|
|
79
80
|
|