ruflet 0.0.4 → 0.0.6
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/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_ui/ruflet/dsl.rb +46 -1
- data/lib/ruflet_ui/ruflet/page.rb +151 -159
- data/lib/ruflet_ui/ruflet/ui/controls/material/grid_view_control.rb +13 -0
- data/lib/ruflet_ui/ruflet/ui/controls/material/snack_bar_control.rb +55 -0
- data/lib/ruflet_ui/ruflet/ui/material_control_factory.rb +3 -0
- data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +3 -0
- data/lib/ruflet_ui/ruflet/ui/material_control_registry.rb +3 -0
- data/lib/ruflet_ui/ruflet/ui/shared_control_forwarders.rb +4 -0
- data/lib/ruflet_ui/ruflet/ui/widget_builder.rb +8 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71ccb066d48adaf26ea46834bd630001c3a144aca86d5891f1f5c4646755029c
|
|
4
|
+
data.tar.gz: 133976a5d04e617e8faefba38b42d802d308df2d6c920c23d04d906508f0a040
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e26a8c097c5f85de3fa2e56655503b19a2f54da7b1a285d0b5e7c5094c6d4cc4b6ce3f452460531a958ae915c2408f6a8ccc185d773a4d148e4f3c2b9356e70f
|
|
7
|
+
data.tar.gz: 2fb16bbdd819e73446492e188dc371383387b26a37306512256a431722dabce9695b88d6d66f0f02852e59918e5e7e75e5b4b6a776637718ff0f326eca3d6a4d
|
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
|
|
@@ -31,6 +40,8 @@ module Ruflet
|
|
|
31
40
|
def center(**props, &block) = _pending_app.center(**props, &block)
|
|
32
41
|
def row(**props, &block) = _pending_app.row(**props, &block)
|
|
33
42
|
def stack(**props, &block) = _pending_app.stack(**props, &block)
|
|
43
|
+
def grid_view(**props, &block) = _pending_app.grid_view(**props, &block)
|
|
44
|
+
def gridview(**props, &block) = _pending_app.gridview(**props, &block)
|
|
34
45
|
def container(**props, &block) = _pending_app.container(**props, &block)
|
|
35
46
|
def gesture_detector(**props, &block) = _pending_app.gesture_detector(**props, &block)
|
|
36
47
|
def gesturedetector(**props, &block) = _pending_app.gesturedetector(**props, &block)
|
|
@@ -49,6 +60,7 @@ module Ruflet
|
|
|
49
60
|
def iconbutton(**props) = _pending_app.iconbutton(**props)
|
|
50
61
|
def app_bar(**props) = _pending_app.app_bar(**props)
|
|
51
62
|
def appbar(**props) = _pending_app.appbar(**props)
|
|
63
|
+
def clipboard(**props) = _pending_app.clipboard(**props)
|
|
52
64
|
def text_button(**props) = _pending_app.text_button(**props)
|
|
53
65
|
def textbutton(**props) = _pending_app.textbutton(**props)
|
|
54
66
|
def filled_button(**props) = _pending_app.filled_button(**props)
|
|
@@ -95,6 +107,7 @@ module Ruflet
|
|
|
95
107
|
def cupertinodialogaction(**props) = _pending_app.cupertinodialogaction(**props)
|
|
96
108
|
def cupertino_navigation_bar(**props) = _pending_app.cupertino_navigation_bar(**props)
|
|
97
109
|
def cupertinonavigationbar(**props) = _pending_app.cupertinonavigationbar(**props)
|
|
110
|
+
def duration(**parts) = duration_in_milliseconds(parts)
|
|
98
111
|
|
|
99
112
|
class App
|
|
100
113
|
include UI::ControlMethods
|
|
@@ -127,7 +140,7 @@ module Ruflet
|
|
|
127
140
|
|
|
128
141
|
def control(type, **props, &block)
|
|
129
142
|
mapped_props = props.dup
|
|
130
|
-
prop_children =
|
|
143
|
+
prop_children = extract_children_prop(mapped_props)
|
|
131
144
|
|
|
132
145
|
id = mapped_props.delete(:id)&.to_s || next_id(type)
|
|
133
146
|
c = Ruflet::UI::ControlFactory.build(type.to_s, id: id, **normalize_props(mapped_props))
|
|
@@ -146,6 +159,10 @@ module Ruflet
|
|
|
146
159
|
c
|
|
147
160
|
end
|
|
148
161
|
|
|
162
|
+
def duration(**parts)
|
|
163
|
+
DSL.duration(**parts)
|
|
164
|
+
end
|
|
165
|
+
|
|
149
166
|
def run
|
|
150
167
|
app_roots = @roots
|
|
151
168
|
page_props = @page_props.dup
|
|
@@ -172,11 +189,39 @@ module Ruflet
|
|
|
172
189
|
hash.transform_keys(&:to_s).transform_values { |v| v.is_a?(Symbol) ? v.to_s : v }
|
|
173
190
|
end
|
|
174
191
|
|
|
192
|
+
def extract_children_prop(props)
|
|
193
|
+
props.delete(:children) ||
|
|
194
|
+
props.delete("children") ||
|
|
195
|
+
props.delete(:controls) ||
|
|
196
|
+
props.delete("controls")
|
|
197
|
+
end
|
|
198
|
+
|
|
175
199
|
def next_id(type)
|
|
176
200
|
@seq += 1
|
|
177
201
|
"#{type}_#{@seq}"
|
|
178
202
|
end
|
|
179
203
|
|
|
204
|
+
def duration_in_milliseconds(parts)
|
|
205
|
+
DSL.send(:duration_in_milliseconds, parts)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def duration_in_milliseconds(parts)
|
|
211
|
+
return 0 if parts.nil? || parts.empty?
|
|
212
|
+
|
|
213
|
+
DURATION_FACTORS_MS.reduce(0.0) do |sum, (key, factor)|
|
|
214
|
+
sum + read_duration_part(parts, key) * factor
|
|
215
|
+
end.round
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def read_duration_part(parts, key)
|
|
219
|
+
raw = parts[key] || parts[key.to_s]
|
|
220
|
+
return 0.0 if raw.nil?
|
|
221
|
+
return raw.to_f if raw.is_a?(Numeric)
|
|
222
|
+
return raw.to_f if raw.is_a?(String) && raw.match?(/\A-?\d+(\.\d+)?\z/)
|
|
223
|
+
|
|
224
|
+
0.0
|
|
180
225
|
end
|
|
181
226
|
end
|
|
182
227
|
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,78 @@ 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
|
+
|
|
268
|
+
def set_clipboard(value, timeout: 10)
|
|
269
|
+
clipboard = ensure_clipboard_service
|
|
270
|
+
invoke(clipboard, "set", args: { "data" => value.to_s }, timeout: timeout)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def get_clipboard(timeout: 10)
|
|
274
|
+
clipboard = ensure_clipboard_service
|
|
275
|
+
invoke(clipboard, "get", timeout: timeout)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def set_clipboard_files(files, timeout: 10)
|
|
279
|
+
clipboard = ensure_clipboard_service
|
|
280
|
+
invoke(clipboard, "set_files", args: { "files" => Array(files).map(&:to_s) }, timeout: timeout)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def get_clipboard_files(timeout: 10)
|
|
284
|
+
clipboard = ensure_clipboard_service
|
|
285
|
+
invoke(clipboard, "get_files", timeout: timeout)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def set_clipboard_image(value, timeout: 10)
|
|
289
|
+
clipboard = ensure_clipboard_service
|
|
290
|
+
invoke(clipboard, "set_image", args: { "data" => value }, timeout: timeout)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def get_clipboard_image(timeout: 10)
|
|
294
|
+
clipboard = ensure_clipboard_service
|
|
295
|
+
invoke(clipboard, "get_image", timeout: timeout)
|
|
296
|
+
end
|
|
297
|
+
|
|
293
298
|
def pop_dialog
|
|
294
299
|
dialog_control = latest_open_dialog
|
|
295
300
|
return nil unless dialog_control
|
|
296
301
|
|
|
297
302
|
dialog_control.props["open"] = false
|
|
298
|
-
@dialogs.delete(dialog_control)
|
|
299
303
|
refresh_dialogs_container!
|
|
300
304
|
push_dialogs_update!
|
|
301
305
|
dialog_control
|
|
@@ -316,26 +320,15 @@ module Ruflet
|
|
|
316
320
|
control = resolve_control(control_or_id)
|
|
317
321
|
return self unless control
|
|
318
322
|
|
|
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)
|
|
323
|
+
patch = normalize_props(props)
|
|
334
324
|
if BUTTON_TEXT_TYPES.include?(control.type) && patch.key?("text")
|
|
335
325
|
patch["content"] = patch.delete("text")
|
|
336
326
|
end
|
|
337
327
|
|
|
338
|
-
|
|
328
|
+
visited = Set.new
|
|
329
|
+
patch.each_value { |value| register_embedded_value(value, visited) }
|
|
330
|
+
|
|
331
|
+
patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] }
|
|
339
332
|
|
|
340
333
|
send_message(Protocol::ACTIONS[:patch_control], {
|
|
341
334
|
"id" => control.wire_id,
|
|
@@ -345,22 +338,6 @@ module Ruflet
|
|
|
345
338
|
self
|
|
346
339
|
end
|
|
347
340
|
|
|
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
341
|
def patch_page(control_id, **props)
|
|
365
342
|
update(control_id, **props)
|
|
366
343
|
end
|
|
@@ -398,6 +375,45 @@ module Ruflet
|
|
|
398
375
|
end
|
|
399
376
|
end
|
|
400
377
|
|
|
378
|
+
def method_missing(name, *args, &block)
|
|
379
|
+
method_name = name.to_s
|
|
380
|
+
prop_name = method_name.delete_suffix("=")
|
|
381
|
+
|
|
382
|
+
if method_name.end_with?("=")
|
|
383
|
+
if DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym)
|
|
384
|
+
Kernel.warn("[DEPRECATION] `page.#{prop_name}(...)` is no longer supported.")
|
|
385
|
+
raise NoMethodError, "Use `#{prop_name}(...)` as a free widget helper, then attach with `page.add(...)`."
|
|
386
|
+
end
|
|
387
|
+
assign_split_prop(prop_name, normalize_value(prop_name, args.first))
|
|
388
|
+
return args.first
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
if args.empty? && !block
|
|
392
|
+
return @page_props[method_name] if @page_props.key?(method_name)
|
|
393
|
+
return @view_props[method_name] if @view_props.key?(method_name)
|
|
394
|
+
return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
if DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym)
|
|
398
|
+
Kernel.warn("[DEPRECATION] `page.#{name}(...)` is no longer supported.")
|
|
399
|
+
raise NoMethodError, "Use `#{name}(...)` as a free widget helper, then attach with `page.add(...)`."
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
super
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def respond_to_missing?(name, include_private = false)
|
|
406
|
+
method_name = name.to_s
|
|
407
|
+
prop_name = method_name.delete_suffix("=")
|
|
408
|
+
DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym) ||
|
|
409
|
+
DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym) ||
|
|
410
|
+
method_name.end_with?("=") ||
|
|
411
|
+
@page_props.key?(method_name) ||
|
|
412
|
+
@view_props.key?(method_name) ||
|
|
413
|
+
DIALOG_PROP_KEYS.include?(method_name) ||
|
|
414
|
+
super
|
|
415
|
+
end
|
|
416
|
+
|
|
401
417
|
private
|
|
402
418
|
|
|
403
419
|
def build_widget(type, **props, &block) = WidgetBuilder.new.control(type, **props, &block)
|
|
@@ -489,7 +505,7 @@ module Ruflet
|
|
|
489
505
|
def resolve_control(control_or_id)
|
|
490
506
|
if control_or_id.respond_to?(:wire_id)
|
|
491
507
|
control_or_id
|
|
492
|
-
elsif
|
|
508
|
+
elsif control_or_id.to_s.match?(/^\d+$/)
|
|
493
509
|
@wire_index[control_or_id.to_i]
|
|
494
510
|
else
|
|
495
511
|
@control_index[control_or_id.to_s]
|
|
@@ -509,32 +525,10 @@ module Ruflet
|
|
|
509
525
|
return codepoint unless codepoint.nil?
|
|
510
526
|
end
|
|
511
527
|
|
|
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
528
|
return value.value if value.is_a?(Ruflet::IconData)
|
|
519
529
|
value.is_a?(Symbol) ? value.to_s : value
|
|
520
530
|
end
|
|
521
531
|
|
|
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
532
|
def build_route(route, query_params = {})
|
|
539
533
|
base = route.to_s
|
|
540
534
|
return base if query_params.nil? || query_params.empty?
|
|
@@ -586,12 +580,6 @@ module Ruflet
|
|
|
586
580
|
key == "icon" || key.end_with?("_icon")
|
|
587
581
|
end
|
|
588
582
|
|
|
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
583
|
def refresh_dialogs_container!
|
|
596
584
|
dialog_controls = (@dialogs + dialog_slots).uniq
|
|
597
585
|
@dialogs_container.props["controls"] = dialog_controls
|
|
@@ -679,25 +667,29 @@ module Ruflet
|
|
|
679
667
|
end
|
|
680
668
|
|
|
681
669
|
def resolve_icon_codepoint(value)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
begin
|
|
686
|
-
codepoint = Ruflet::MaterialIconLookup.codepoint_for(value)
|
|
687
|
-
rescue NameError
|
|
688
|
-
codepoint = nil
|
|
670
|
+
codepoint = Ruflet::MaterialIconLookup.codepoint_for(value)
|
|
671
|
+
if codepoint.nil? || codepoint == value
|
|
672
|
+
codepoint = Ruflet::CupertinoIconLookup.codepoint_for(value)
|
|
689
673
|
end
|
|
674
|
+
codepoint
|
|
675
|
+
end
|
|
690
676
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
codepoint = cupertino unless cupertino.nil?
|
|
695
|
-
rescue NameError
|
|
696
|
-
codepoint = nil
|
|
697
|
-
end
|
|
698
|
-
end
|
|
677
|
+
def ensure_url_launcher_service
|
|
678
|
+
launcher = services.find { |service| service.is_a?(Control) && service.type == "url_launcher" }
|
|
679
|
+
return launcher if launcher
|
|
699
680
|
|
|
700
|
-
|
|
681
|
+
launcher = build_widget(:url_launcher)
|
|
682
|
+
add_service(launcher)
|
|
683
|
+
launcher
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def ensure_clipboard_service
|
|
687
|
+
clipboard = services.find { |service| service.is_a?(Control) && service.type == "clipboard" }
|
|
688
|
+
return clipboard if clipboard
|
|
689
|
+
|
|
690
|
+
clipboard = build_widget(:clipboard)
|
|
691
|
+
add_service(clipboard)
|
|
692
|
+
clipboard
|
|
701
693
|
end
|
|
702
694
|
end
|
|
703
695
|
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
|
|
@@ -6,6 +6,7 @@ require_relative "controls/material/container_control"
|
|
|
6
6
|
require_relative "controls/material/column_control"
|
|
7
7
|
require_relative "controls/material/row_control"
|
|
8
8
|
require_relative "controls/material/stack_control"
|
|
9
|
+
require_relative "controls/material/grid_view_control"
|
|
9
10
|
require_relative "controls/material/gesture_detector_control"
|
|
10
11
|
require_relative "controls/material/draggable_control"
|
|
11
12
|
require_relative "controls/material/drag_target_control"
|
|
@@ -44,6 +45,8 @@ module Ruflet
|
|
|
44
45
|
"column" => Controls::ColumnControl,
|
|
45
46
|
"row" => Controls::RowControl,
|
|
46
47
|
"stack" => Controls::StackControl,
|
|
48
|
+
"gridview" => Controls::GridViewControl,
|
|
49
|
+
"grid_view" => Controls::GridViewControl,
|
|
47
50
|
"container" => Controls::ContainerControl,
|
|
48
51
|
"gesturedetector" => Controls::GestureDetectorControl,
|
|
49
52
|
"gesture_detector" => Controls::GestureDetectorControl,
|
|
@@ -27,6 +27,8 @@ module Ruflet
|
|
|
27
27
|
|
|
28
28
|
def row(**props, &block) = build_widget(:row, **props, &block)
|
|
29
29
|
def stack(**props, &block) = build_widget(:stack, **props, &block)
|
|
30
|
+
def grid_view(**props, &block) = build_widget(:gridview, **props, &block)
|
|
31
|
+
def gridview(**props, &block) = grid_view(**props, &block)
|
|
30
32
|
def container(**props, &block) = build_widget(:container, **normalize_container_props(props), &block)
|
|
31
33
|
def gesture_detector(**props, &block) = build_widget(:gesturedetector, **props, &block)
|
|
32
34
|
def gesturedetector(**props, &block) = gesture_detector(**props, &block)
|
|
@@ -82,6 +84,7 @@ module Ruflet
|
|
|
82
84
|
def app_bar(**props) = build_widget(:appbar, **props)
|
|
83
85
|
def appbar(**props) = app_bar(**props)
|
|
84
86
|
def url_launcher(**props) = build_widget(:url_launcher, **props)
|
|
87
|
+
def clipboard(**props) = build_widget(:clipboard, **props)
|
|
85
88
|
def floating_action_button(**props) = build_widget(:floatingactionbutton, **props)
|
|
86
89
|
def floatingactionbutton(**props) = floating_action_button(**props)
|
|
87
90
|
def tabs(**props, &block) = build_widget(:tabs, **props, &block)
|
|
@@ -8,6 +8,8 @@ module Ruflet
|
|
|
8
8
|
"column" => "Column",
|
|
9
9
|
"row" => "Row",
|
|
10
10
|
"stack" => "Stack",
|
|
11
|
+
"gridview" => "GridView",
|
|
12
|
+
"grid_view" => "GridView",
|
|
11
13
|
"view" => "View",
|
|
12
14
|
"container" => "Container",
|
|
13
15
|
"checkbox" => "Checkbox",
|
|
@@ -79,6 +81,7 @@ module Ruflet
|
|
|
79
81
|
"line" => "Line",
|
|
80
82
|
"service_registry" => "ServiceRegistry",
|
|
81
83
|
"url_launcher" => "UrlLauncher",
|
|
84
|
+
"clipboard" => "Clipboard",
|
|
82
85
|
"audio" => "Audio",
|
|
83
86
|
"video" => "Video",
|
|
84
87
|
"flashlight" => "Flashlight",
|
|
@@ -10,6 +10,8 @@ module Ruflet
|
|
|
10
10
|
def center(**props, &block) = control_delegate.center(**props, &block)
|
|
11
11
|
def row(**props, &block) = control_delegate.row(**props, &block)
|
|
12
12
|
def stack(**props, &block) = control_delegate.stack(**props, &block)
|
|
13
|
+
def grid_view(**props, &block) = control_delegate.grid_view(**props, &block)
|
|
14
|
+
def gridview(**props, &block) = control_delegate.gridview(**props, &block)
|
|
13
15
|
def container(**props, &block) = control_delegate.container(**props, &block)
|
|
14
16
|
def gesture_detector(**props, &block) = control_delegate.gesture_detector(**props, &block)
|
|
15
17
|
def gesturedetector(**props, &block) = control_delegate.gesturedetector(**props, &block)
|
|
@@ -44,6 +46,7 @@ module Ruflet
|
|
|
44
46
|
def fab(content = nil, **props) = control_delegate.fab(content, **props)
|
|
45
47
|
def app_bar(**props) = control_delegate.app_bar(**props)
|
|
46
48
|
def appbar(**props) = control_delegate.appbar(**props)
|
|
49
|
+
def clipboard(**props) = control_delegate.clipboard(**props)
|
|
47
50
|
def floating_action_button(**props) = control_delegate.floating_action_button(**props)
|
|
48
51
|
def floatingactionbutton(**props) = control_delegate.floatingactionbutton(**props)
|
|
49
52
|
def tabs(**props, &block) = control_delegate.tabs(**props, &block)
|
|
@@ -74,6 +77,7 @@ module Ruflet
|
|
|
74
77
|
def cupertinodialogaction(**props) = control_delegate.cupertinodialogaction(**props)
|
|
75
78
|
def cupertino_navigation_bar(**props) = control_delegate.cupertino_navigation_bar(**props)
|
|
76
79
|
def cupertinonavigationbar(**props) = control_delegate.cupertinonavigationbar(**props)
|
|
80
|
+
def duration(**parts) = control_delegate.duration(**parts)
|
|
77
81
|
|
|
78
82
|
private
|
|
79
83
|
|
|
@@ -19,7 +19,7 @@ module Ruflet
|
|
|
19
19
|
|
|
20
20
|
def control(type, **props, &block)
|
|
21
21
|
mapped_props = props.dup
|
|
22
|
-
prop_children =
|
|
22
|
+
prop_children = extract_children_prop(mapped_props)
|
|
23
23
|
|
|
24
24
|
node = UI::ControlFactory.build(type, **mapped_props)
|
|
25
25
|
if block
|
|
@@ -44,5 +44,12 @@ module Ruflet
|
|
|
44
44
|
def build_widget(type, **props, &block) = control(type, **props, &block)
|
|
45
45
|
|
|
46
46
|
private
|
|
47
|
+
|
|
48
|
+
def extract_children_prop(props)
|
|
49
|
+
props.delete(:children) ||
|
|
50
|
+
props.delete("children") ||
|
|
51
|
+
props.delete(:controls) ||
|
|
52
|
+
props.delete("controls")
|
|
53
|
+
end
|
|
47
54
|
end
|
|
48
55
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruflet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AdamMusa
|
|
@@ -60,6 +60,7 @@ files:
|
|
|
60
60
|
- lib/ruflet_ui/ruflet/ui/controls/material/filled_button_control.rb
|
|
61
61
|
- lib/ruflet_ui/ruflet/ui/controls/material/floating_action_button_control.rb
|
|
62
62
|
- lib/ruflet_ui/ruflet/ui/controls/material/gesture_detector_control.rb
|
|
63
|
+
- lib/ruflet_ui/ruflet/ui/controls/material/grid_view_control.rb
|
|
63
64
|
- lib/ruflet_ui/ruflet/ui/controls/material/icon_button_control.rb
|
|
64
65
|
- lib/ruflet_ui/ruflet/ui/controls/material/icon_control.rb
|
|
65
66
|
- lib/ruflet_ui/ruflet/ui/controls/material/image_control.rb
|