ruflet 0.0.3 → 0.0.4
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/manifest_compiler.rb +62 -0
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet.rb +6 -6
- data/lib/ruflet_ui/ruflet/app.rb +11 -0
- data/lib/ruflet_ui/ruflet/colors.rb +50 -16
- data/lib/ruflet_ui/ruflet/control.rb +16 -2
- data/lib/ruflet_ui/ruflet/icon_data.rb +8 -3
- data/lib/ruflet_ui/ruflet/page.rb +161 -8
- data/lib/ruflet_ui/ruflet/ui/control_factory.rb +3 -2
- data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_dialog_action_control.rb +11 -0
- data/lib/ruflet_ui/ruflet/ui/cupertino_control_registry.rb +14 -0
- data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +11 -2
- data/lib/ruflet_ui/ruflet/ui/material_control_registry.rb +64 -1
- data/lib/ruflet_ui.rb +3 -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: 01d8a3779e874187cf24c4373efa47dbb8541c9495e8b54534d1c4028bf0b90c
|
|
4
|
+
data.tar.gz: 81d1d6959b1d6e144f24e3c6586f35b6c32fc5f971f039b702a607fe3ef1ea15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 303761d68b2adda5d36edf77e6270c6668ca166defbc71035df969e012c7539bb6c39b9cd69a20df3ab2c849f1fa20cdb857b663db59eb274bcb36cae206661e
|
|
7
|
+
data.tar.gz: b0892bfbb584db2187782958b4d67c86ffbf2ed2899974389ac79b89d770d9898f099eef8ec42477d19b8104a4b5496ed7655b2882f4b7e149cd7d5f0a4b2428
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module Ruflet
|
|
7
|
+
module ManifestCompiler
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def compile_app(app, route: "/")
|
|
11
|
+
messages = []
|
|
12
|
+
sender = lambda do |action, payload|
|
|
13
|
+
messages << { "action" => action, "payload" => payload }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
page = Ruflet::Page.new(
|
|
17
|
+
session_id: "manifest",
|
|
18
|
+
client_details: { "route" => route.to_s.empty? ? "/" : route.to_s },
|
|
19
|
+
sender: sender
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
app.view(page)
|
|
23
|
+
page.update
|
|
24
|
+
|
|
25
|
+
compacted = compact_messages(messages)
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
"schema" => "ruflet_manifest/v1",
|
|
29
|
+
"generated_at" => Time.now.utc.iso8601,
|
|
30
|
+
"route" => page.route || "/",
|
|
31
|
+
"messages" => compacted
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def write_file(path, manifest)
|
|
36
|
+
File.write(path, JSON.pretty_generate(manifest))
|
|
37
|
+
path
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def read_file(path)
|
|
41
|
+
JSON.parse(File.read(path.to_s))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def compact_messages(messages)
|
|
45
|
+
full_patch_index = nil
|
|
46
|
+
messages.each_with_index do |message, idx|
|
|
47
|
+
next unless message["action"] == Ruflet::Protocol::ACTIONS[:patch_control]
|
|
48
|
+
|
|
49
|
+
payload = message["payload"] || {}
|
|
50
|
+
next unless payload["id"] == 1
|
|
51
|
+
|
|
52
|
+
patch = payload["patch"]
|
|
53
|
+
next unless patch.is_a?(Array) && patch.any? { |op| op.is_a?(Array) && op[2] == "views" }
|
|
54
|
+
|
|
55
|
+
full_patch_index = idx
|
|
56
|
+
end
|
|
57
|
+
return messages if full_patch_index.nil?
|
|
58
|
+
|
|
59
|
+
[messages[full_patch_index]]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/ruflet/version.rb
CHANGED
data/lib/ruflet.rb
CHANGED
|
@@ -11,12 +11,6 @@ module Ruflet
|
|
|
11
11
|
module_function
|
|
12
12
|
|
|
13
13
|
def run(entrypoint = nil, host: "0.0.0.0", port: 8550, &block)
|
|
14
|
-
begin
|
|
15
|
-
require "ruflet_server"
|
|
16
|
-
rescue LoadError => e
|
|
17
|
-
raise LoadError, "Ruflet.run requires the 'ruflet_server' gem. Add it to your Gemfile.", e.backtrace
|
|
18
|
-
end
|
|
19
|
-
|
|
20
14
|
callback = entrypoint || block
|
|
21
15
|
raise ArgumentError, "Ruflet.run requires a callable entrypoint or block" unless callback.respond_to?(:call)
|
|
22
16
|
|
|
@@ -26,6 +20,12 @@ module Ruflet
|
|
|
26
20
|
return result unless result == :pass
|
|
27
21
|
end
|
|
28
22
|
|
|
23
|
+
begin
|
|
24
|
+
require "ruflet_server"
|
|
25
|
+
rescue LoadError => e
|
|
26
|
+
raise LoadError, "Ruflet.run requires the 'ruflet_server' gem unless a run interceptor handles execution.", e.backtrace
|
|
27
|
+
end
|
|
28
|
+
|
|
29
29
|
Server.new(host: host, port: port) do |page|
|
|
30
30
|
callback.call(page)
|
|
31
31
|
end.start
|
data/lib/ruflet_ui/ruflet/app.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require_relative "../../ruflet/manifest_compiler"
|
|
2
3
|
|
|
3
4
|
module Ruflet
|
|
4
5
|
class App
|
|
@@ -8,6 +9,16 @@ module Ruflet
|
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def run
|
|
12
|
+
manifest_out = ENV["RUFLET_MANIFEST_OUT"].to_s
|
|
13
|
+
unless manifest_out.empty?
|
|
14
|
+
route = ENV["RUFLET_MANIFEST_ROUTE"].to_s
|
|
15
|
+
route = "/" if route.empty?
|
|
16
|
+
manifest = Ruflet::ManifestCompiler.compile_app(self, route: route)
|
|
17
|
+
Ruflet::ManifestCompiler.write_file(manifest_out, manifest)
|
|
18
|
+
puts manifest_out
|
|
19
|
+
return manifest_out
|
|
20
|
+
end
|
|
21
|
+
|
|
11
22
|
Ruflet.run(host: @host, port: @port) do |page|
|
|
12
23
|
view(page)
|
|
13
24
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module Ruflet
|
|
4
4
|
module Colors
|
|
5
|
-
module_function
|
|
6
|
-
|
|
7
5
|
SEMANTIC_COLORS = {
|
|
8
6
|
PRIMARY: "primary",
|
|
9
7
|
ON_PRIMARY: "onprimary",
|
|
@@ -127,14 +125,32 @@ module Ruflet
|
|
|
127
125
|
end
|
|
128
126
|
|
|
129
127
|
def all_values
|
|
130
|
-
@all_values
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
return @all_values if @all_values
|
|
129
|
+
|
|
130
|
+
values = []
|
|
131
|
+
SEMANTIC_COLORS.each_value { |v| values << v }
|
|
132
|
+
FIXED_COLORS.each_value { |v| values << v }
|
|
133
|
+
|
|
134
|
+
BASE_PRIMARY.each do |base|
|
|
135
|
+
values << base
|
|
136
|
+
PRIMARY_SHADES.each do |shade|
|
|
137
|
+
values << "#{base}#{shade}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
BASE_ACCENT.each do |base|
|
|
142
|
+
values << "#{base}accent"
|
|
143
|
+
ACCENT_SHADES.each do |shade|
|
|
144
|
+
values << "#{base}accent#{shade}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
uniq_map = {}
|
|
149
|
+
values.each { |v| uniq_map[v] = true }
|
|
150
|
+
@all_values = uniq_map.keys.freeze
|
|
135
151
|
end
|
|
136
152
|
|
|
137
|
-
def normalize_color(color)
|
|
153
|
+
def self.normalize_color(color)
|
|
138
154
|
return color.to_s if color.is_a?(Symbol)
|
|
139
155
|
return color if color.is_a?(String)
|
|
140
156
|
return color.to_s unless color.respond_to?(:to_s)
|
|
@@ -164,15 +180,17 @@ module Ruflet
|
|
|
164
180
|
"yellow" => "YELLOW"
|
|
165
181
|
}.freeze
|
|
166
182
|
|
|
167
|
-
def constant_prefix_for(base_name)
|
|
168
|
-
|
|
183
|
+
def self.constant_prefix_for(base_name)
|
|
184
|
+
key = base_name.to_s
|
|
185
|
+
return BASE_PREFIX[key] if BASE_PREFIX.key?(key)
|
|
186
|
+
key.upcase
|
|
169
187
|
end
|
|
170
188
|
|
|
171
189
|
SEMANTIC_COLORS.each { |k, v| const_set(k, v) }
|
|
172
190
|
FIXED_COLORS.each { |k, v| const_set(k, v) }
|
|
173
191
|
|
|
174
192
|
BASE_PRIMARY.each do |base|
|
|
175
|
-
prefix = constant_prefix_for(base)
|
|
193
|
+
prefix = self.constant_prefix_for(base)
|
|
176
194
|
const_set(prefix, base)
|
|
177
195
|
PRIMARY_SHADES.each do |shade|
|
|
178
196
|
const_set("#{prefix}_#{shade}", "#{base}#{shade}")
|
|
@@ -180,7 +198,7 @@ module Ruflet
|
|
|
180
198
|
end
|
|
181
199
|
|
|
182
200
|
BASE_ACCENT.each do |base|
|
|
183
|
-
prefix = "#{constant_prefix_for(base)}_ACCENT"
|
|
201
|
+
prefix = "#{self.constant_prefix_for(base)}_ACCENT"
|
|
184
202
|
const_set(prefix, "#{base}accent")
|
|
185
203
|
ACCENT_SHADES.each do |shade|
|
|
186
204
|
const_set("#{prefix}_#{shade}", "#{base}accent#{shade}")
|
|
@@ -191,10 +209,26 @@ module Ruflet
|
|
|
191
209
|
const_set(alias_name, const_get(target))
|
|
192
210
|
end
|
|
193
211
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
constant_names = []
|
|
213
|
+
SEMANTIC_COLORS.each_key { |k| constant_names << k }
|
|
214
|
+
FIXED_COLORS.each_key { |k| constant_names << k }
|
|
215
|
+
BASE_PRIMARY.each do |base|
|
|
216
|
+
constant_names << constant_prefix_for(base).to_sym
|
|
217
|
+
PRIMARY_SHADES.each { |shade| constant_names << "#{constant_prefix_for(base)}_#{shade}".to_sym }
|
|
218
|
+
end
|
|
219
|
+
BASE_ACCENT.each do |base|
|
|
220
|
+
constant_names << "#{constant_prefix_for(base)}_ACCENT".to_sym
|
|
221
|
+
ACCENT_SHADES.each { |shade| constant_names << "#{constant_prefix_for(base)}_ACCENT_#{shade}".to_sym }
|
|
222
|
+
end
|
|
223
|
+
DEPRECATED_ALIASES.each_key { |k| constant_names << k }
|
|
224
|
+
|
|
225
|
+
uniq_constants = {}
|
|
226
|
+
constant_names.each { |n| uniq_constants[n] = true }
|
|
227
|
+
if respond_to?(:define_singleton_method)
|
|
228
|
+
uniq_constants.keys.each do |name|
|
|
229
|
+
next if respond_to?(name)
|
|
230
|
+
define_singleton_method(name) { const_get(name) }
|
|
231
|
+
end
|
|
198
232
|
end
|
|
199
233
|
end
|
|
200
234
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
begin
|
|
4
|
+
require "securerandom"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
nil
|
|
7
|
+
end
|
|
4
8
|
require_relative "ui/control_registry"
|
|
5
9
|
require_relative "icon_data"
|
|
6
10
|
require_relative "icons/material_icon_lookup"
|
|
@@ -17,7 +21,7 @@ module Ruflet
|
|
|
17
21
|
|
|
18
22
|
def initialize(type:, id: nil, **props)
|
|
19
23
|
@type = type.to_s.downcase
|
|
20
|
-
@id = (id || props.delete(:id) || "ctrl_#{
|
|
24
|
+
@id = (id || props.delete(:id) || "ctrl_#{self.class.generate_id}").to_s
|
|
21
25
|
@children = []
|
|
22
26
|
@handlers = {}
|
|
23
27
|
@wire_id = nil
|
|
@@ -62,6 +66,16 @@ module Ruflet
|
|
|
62
66
|
|
|
63
67
|
private
|
|
64
68
|
|
|
69
|
+
class << self
|
|
70
|
+
def generate_id
|
|
71
|
+
if defined?(SecureRandom) && SecureRandom.respond_to?(:hex)
|
|
72
|
+
SecureRandom.hex(4)
|
|
73
|
+
else
|
|
74
|
+
format("%08x", rand(0..0xffff_ffff))
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
65
79
|
def serialize_value(value)
|
|
66
80
|
case value
|
|
67
81
|
when Control
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require_relative "icons/
|
|
3
|
+
begin
|
|
4
|
+
require_relative "icons/material_icon_lookup"
|
|
5
|
+
require_relative "icons/cupertino_icon_lookup"
|
|
6
|
+
rescue StandardError
|
|
7
|
+
nil
|
|
8
|
+
end
|
|
5
9
|
|
|
6
10
|
module Ruflet
|
|
7
11
|
class IconData
|
|
@@ -44,7 +48,8 @@ module Ruflet
|
|
|
44
48
|
return codepoint
|
|
45
49
|
end
|
|
46
50
|
|
|
47
|
-
raw = input.to_s
|
|
51
|
+
raw = input.to_s
|
|
52
|
+
raw = raw.strip if raw.respond_to?(:strip)
|
|
48
53
|
return raw if raw.empty?
|
|
49
54
|
|
|
50
55
|
codepoint = Ruflet::MaterialIconLookup.codepoint_for(raw)
|
|
@@ -7,14 +7,43 @@ require_relative "ui/control_methods"
|
|
|
7
7
|
require_relative "ui/widget_builder"
|
|
8
8
|
require_relative "icons/material_icon_lookup"
|
|
9
9
|
require_relative "icons/cupertino_icon_lookup"
|
|
10
|
-
|
|
11
|
-
require "
|
|
10
|
+
begin
|
|
11
|
+
require "set"
|
|
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
|
|
12
41
|
|
|
13
42
|
module Ruflet
|
|
14
43
|
class Page
|
|
15
44
|
include UI::ControlMethods
|
|
16
45
|
|
|
17
|
-
PAGE_PROP_KEYS = %w[route title vertical_alignment horizontal_alignment].freeze
|
|
46
|
+
PAGE_PROP_KEYS = %w[route title vertical_alignment horizontal_alignment scroll].freeze
|
|
18
47
|
DIALOG_PROP_KEYS = %w[dialog snack_bar bottom_sheet].freeze
|
|
19
48
|
BUTTON_TEXT_TYPES = %w[button elevatedbutton textbutton filledbutton].freeze
|
|
20
49
|
|
|
@@ -39,12 +68,18 @@ module Ruflet
|
|
|
39
68
|
id: "_overlay",
|
|
40
69
|
controls: []
|
|
41
70
|
)
|
|
71
|
+
@services_container = Ruflet::Control.new(
|
|
72
|
+
type: "service_registry",
|
|
73
|
+
id: "_services",
|
|
74
|
+
"_services": []
|
|
75
|
+
)
|
|
42
76
|
@dialogs_container = Ruflet::Control.new(
|
|
43
77
|
type: "dialogs",
|
|
44
78
|
id: "_dialogs",
|
|
45
79
|
controls: []
|
|
46
80
|
)
|
|
47
81
|
refresh_overlay_container!
|
|
82
|
+
refresh_services_container!
|
|
48
83
|
refresh_dialogs_container!
|
|
49
84
|
end
|
|
50
85
|
|
|
@@ -69,6 +104,16 @@ module Ruflet
|
|
|
69
104
|
@page_props["route"] = value
|
|
70
105
|
end
|
|
71
106
|
|
|
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
|
+
|
|
72
117
|
def vertical_alignment
|
|
73
118
|
@page_props["vertical_alignment"] || @view_props["vertical_alignment"]
|
|
74
119
|
end
|
|
@@ -118,6 +163,24 @@ module Ruflet
|
|
|
118
163
|
self
|
|
119
164
|
end
|
|
120
165
|
|
|
166
|
+
def services
|
|
167
|
+
@services_container.props["_services"] ||= []
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def services=(value)
|
|
171
|
+
@services_container.props["_services"] = Array(value).compact
|
|
172
|
+
refresh_services_container!
|
|
173
|
+
push_services_update!
|
|
174
|
+
self
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def add_service(*value)
|
|
178
|
+
@services_container.props["_services"] = services + value.flatten.compact
|
|
179
|
+
refresh_services_container!
|
|
180
|
+
push_services_update!
|
|
181
|
+
self
|
|
182
|
+
end
|
|
183
|
+
|
|
121
184
|
def views=(value)
|
|
122
185
|
@views = Array(value).compact
|
|
123
186
|
self
|
|
@@ -232,6 +295,7 @@ module Ruflet
|
|
|
232
295
|
return nil unless dialog_control
|
|
233
296
|
|
|
234
297
|
dialog_control.props["open"] = false
|
|
298
|
+
@dialogs.delete(dialog_control)
|
|
235
299
|
refresh_dialogs_container!
|
|
236
300
|
push_dialogs_update!
|
|
237
301
|
dialog_control
|
|
@@ -252,7 +316,21 @@ module Ruflet
|
|
|
252
316
|
control = resolve_control(control_or_id)
|
|
253
317
|
return self unless control
|
|
254
318
|
|
|
255
|
-
|
|
319
|
+
visited = Set.new
|
|
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)
|
|
256
334
|
if BUTTON_TEXT_TYPES.include?(control.type) && patch.key?("text")
|
|
257
335
|
patch["content"] = patch.delete("text")
|
|
258
336
|
end
|
|
@@ -267,6 +345,22 @@ module Ruflet
|
|
|
267
345
|
self
|
|
268
346
|
end
|
|
269
347
|
|
|
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
|
+
|
|
270
364
|
def patch_page(control_id, **props)
|
|
271
365
|
update(control_id, **props)
|
|
272
366
|
end
|
|
@@ -395,7 +489,7 @@ module Ruflet
|
|
|
395
489
|
def resolve_control(control_or_id)
|
|
396
490
|
if control_or_id.respond_to?(:wire_id)
|
|
397
491
|
control_or_id
|
|
398
|
-
elsif control_or_id.to_s
|
|
492
|
+
elsif numeric_string?(control_or_id.to_s)
|
|
399
493
|
@wire_index[control_or_id.to_i]
|
|
400
494
|
else
|
|
401
495
|
@control_index[control_or_id.to_s]
|
|
@@ -415,10 +509,32 @@ module Ruflet
|
|
|
415
509
|
return codepoint unless codepoint.nil?
|
|
416
510
|
end
|
|
417
511
|
|
|
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
|
+
|
|
418
518
|
return value.value if value.is_a?(Ruflet::IconData)
|
|
419
519
|
value.is_a?(Symbol) ? value.to_s : value
|
|
420
520
|
end
|
|
421
521
|
|
|
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
|
+
|
|
422
538
|
def build_route(route, query_params = {})
|
|
423
539
|
base = route.to_s
|
|
424
540
|
return base if query_params.nil? || query_params.empty?
|
|
@@ -470,6 +586,12 @@ module Ruflet
|
|
|
470
586
|
key == "icon" || key.end_with?("_icon")
|
|
471
587
|
end
|
|
472
588
|
|
|
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
|
+
|
|
473
595
|
def refresh_dialogs_container!
|
|
474
596
|
dialog_controls = (@dialogs + dialog_slots).uniq
|
|
475
597
|
@dialogs_container.props["controls"] = dialog_controls
|
|
@@ -480,6 +602,23 @@ module Ruflet
|
|
|
480
602
|
@page_props["_overlay"] = @overlay_container
|
|
481
603
|
end
|
|
482
604
|
|
|
605
|
+
def refresh_services_container!
|
|
606
|
+
@page_props["_services"] = @services_container
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def push_services_update!
|
|
610
|
+
refresh_control_indexes!
|
|
611
|
+
|
|
612
|
+
if @services_container.wire_id
|
|
613
|
+
send_message(Protocol::ACTIONS[:patch_control], {
|
|
614
|
+
"id" => @services_container.wire_id,
|
|
615
|
+
"patch" => [[0], [0, 0, "_services", serialize_patch_value(@services_container.props["_services"])]]
|
|
616
|
+
})
|
|
617
|
+
else
|
|
618
|
+
send_view_patch
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
|
|
483
622
|
def push_dialogs_update!
|
|
484
623
|
refresh_control_indexes!
|
|
485
624
|
|
|
@@ -540,10 +679,24 @@ module Ruflet
|
|
|
540
679
|
end
|
|
541
680
|
|
|
542
681
|
def resolve_icon_codepoint(value)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
682
|
+
return nil unless value.is_a?(Integer) || value.is_a?(Symbol) || value.is_a?(String)
|
|
683
|
+
|
|
684
|
+
codepoint = nil
|
|
685
|
+
begin
|
|
686
|
+
codepoint = Ruflet::MaterialIconLookup.codepoint_for(value)
|
|
687
|
+
rescue NameError
|
|
688
|
+
codepoint = nil
|
|
546
689
|
end
|
|
690
|
+
|
|
691
|
+
if codepoint.nil? || (value.is_a?(Integer) && codepoint == value)
|
|
692
|
+
begin
|
|
693
|
+
cupertino = Ruflet::CupertinoIconLookup.codepoint_for(value)
|
|
694
|
+
codepoint = cupertino unless cupertino.nil?
|
|
695
|
+
rescue NameError
|
|
696
|
+
codepoint = nil
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
547
700
|
codepoint
|
|
548
701
|
end
|
|
549
702
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "material_control_factory"
|
|
4
4
|
require_relative "cupertino_control_factory"
|
|
5
|
+
require_relative "../control"
|
|
5
6
|
|
|
6
7
|
module Ruflet
|
|
7
8
|
module UI
|
|
@@ -13,9 +14,9 @@ module Ruflet
|
|
|
13
14
|
def build(type, id: nil, **props)
|
|
14
15
|
normalized_type = type.to_s.downcase
|
|
15
16
|
klass = CLASS_MAP[normalized_type]
|
|
16
|
-
|
|
17
|
+
return klass.new(id: id, **props) if klass
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Ruflet::Control.new(type: normalized_type, id: id, **props)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
@@ -7,6 +7,17 @@ module Ruflet
|
|
|
7
7
|
def initialize(id: nil, **props)
|
|
8
8
|
super(type: "cupertino_dialog_action", id: id, **props)
|
|
9
9
|
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def preprocess_props(props)
|
|
14
|
+
mapped = props.dup
|
|
15
|
+
if mapped.key?(:text) || mapped.key?("text")
|
|
16
|
+
value = mapped.key?(:text) ? mapped.delete(:text) : mapped.delete("text")
|
|
17
|
+
mapped[:content] = value unless mapped.key?(:content) || mapped.key?("content")
|
|
18
|
+
end
|
|
19
|
+
mapped
|
|
20
|
+
end
|
|
10
21
|
end
|
|
11
22
|
end
|
|
12
23
|
end
|
|
@@ -8,18 +8,32 @@ module Ruflet
|
|
|
8
8
|
"cupertinobutton" => "CupertinoButton",
|
|
9
9
|
"cupertino_filled_button" => "CupertinoFilledButton",
|
|
10
10
|
"cupertinofilledbutton" => "CupertinoFilledButton",
|
|
11
|
+
"cupertino_tinted_button" => "CupertinoTintedButton",
|
|
12
|
+
"cupertinotintedbutton" => "CupertinoTintedButton",
|
|
11
13
|
"cupertino_text_field" => "CupertinoTextField",
|
|
12
14
|
"cupertinotextfield" => "CupertinoTextField",
|
|
15
|
+
"cupertino_checkbox" => "CupertinoCheckbox",
|
|
16
|
+
"cupertinocheckbox" => "CupertinoCheckbox",
|
|
13
17
|
"cupertino_switch" => "CupertinoSwitch",
|
|
14
18
|
"cupertinoswitch" => "CupertinoSwitch",
|
|
15
19
|
"cupertino_slider" => "CupertinoSlider",
|
|
16
20
|
"cupertinoslider" => "CupertinoSlider",
|
|
21
|
+
"cupertino_radio" => "CupertinoRadio",
|
|
22
|
+
"cupertinoradio" => "CupertinoRadio",
|
|
17
23
|
"cupertino_alert_dialog" => "CupertinoAlertDialog",
|
|
18
24
|
"cupertinoalertdialog" => "CupertinoAlertDialog",
|
|
19
25
|
"cupertino_action_sheet" => "CupertinoActionSheet",
|
|
20
26
|
"cupertinoactionsheet" => "CupertinoActionSheet",
|
|
21
27
|
"cupertino_dialog_action" => "CupertinoDialogAction",
|
|
22
28
|
"cupertinodialogaction" => "CupertinoDialogAction",
|
|
29
|
+
"cupertino_bottom_sheet" => "CupertinoBottomSheet",
|
|
30
|
+
"cupertinobottomsheet" => "CupertinoBottomSheet",
|
|
31
|
+
"cupertino_picker" => "CupertinoPicker",
|
|
32
|
+
"cupertinopicker" => "CupertinoPicker",
|
|
33
|
+
"cupertino_date_picker" => "CupertinoDatePicker",
|
|
34
|
+
"cupertinodatepicker" => "CupertinoDatePicker",
|
|
35
|
+
"cupertino_timer_picker" => "CupertinoTimerPicker",
|
|
36
|
+
"cupertinotimerpicker" => "CupertinoTimerPicker",
|
|
23
37
|
"cupertino_navigation_bar" => "CupertinoNavigationBar",
|
|
24
38
|
"cupertinonavigationbar" => "CupertinoNavigationBar"
|
|
25
39
|
}.freeze
|
|
@@ -71,14 +71,17 @@ module Ruflet
|
|
|
71
71
|
|
|
72
72
|
def icon(**props) = build_widget(:icon, **props)
|
|
73
73
|
|
|
74
|
-
def image(src = nil, **props)
|
|
74
|
+
def image(src = nil, src_base64: nil, placeholder_src: nil, **props)
|
|
75
75
|
mapped = props.dup
|
|
76
|
-
mapped[:src] = src unless src.nil?
|
|
76
|
+
mapped[:src] = normalize_image_source(src) unless src.nil?
|
|
77
|
+
mapped[:src] = normalize_image_source(src_base64) if mapped[:src].nil? && !src_base64.nil?
|
|
78
|
+
mapped[:placeholder_src] = normalize_image_source(placeholder_src) unless placeholder_src.nil?
|
|
77
79
|
build_widget(:image, **mapped)
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def app_bar(**props) = build_widget(:appbar, **props)
|
|
81
83
|
def appbar(**props) = app_bar(**props)
|
|
84
|
+
def url_launcher(**props) = build_widget(:url_launcher, **props)
|
|
82
85
|
def floating_action_button(**props) = build_widget(:floatingactionbutton, **props)
|
|
83
86
|
def floatingactionbutton(**props) = floating_action_button(**props)
|
|
84
87
|
def tabs(**props, &block) = build_widget(:tabs, **props, &block)
|
|
@@ -100,6 +103,12 @@ module Ruflet
|
|
|
100
103
|
|
|
101
104
|
private
|
|
102
105
|
|
|
106
|
+
def normalize_image_source(value)
|
|
107
|
+
return value unless value.is_a?(Array)
|
|
108
|
+
return value.pack("C*") if value.all? { |v| v.is_a?(Integer) }
|
|
109
|
+
value
|
|
110
|
+
end
|
|
111
|
+
|
|
103
112
|
# Flet container alignment expects a vector-like object ({x:, y:}),
|
|
104
113
|
# not a plain string. Keep common shorthand compatible.
|
|
105
114
|
def normalize_container_props(props)
|
|
@@ -52,7 +52,55 @@ module Ruflet
|
|
|
52
52
|
"navigationbar" => "NavigationBar",
|
|
53
53
|
"navigation_bar" => "NavigationBar",
|
|
54
54
|
"navigationbardestination" => "NavigationBarDestination",
|
|
55
|
-
"navigation_bar_destination" => "NavigationBarDestination"
|
|
55
|
+
"navigation_bar_destination" => "NavigationBarDestination",
|
|
56
|
+
"switch" => "Switch",
|
|
57
|
+
"slider" => "Slider",
|
|
58
|
+
"dropdown" => "DropdownM2",
|
|
59
|
+
"dropdownm2" => "DropdownM2",
|
|
60
|
+
"dropdown_m2" => "DropdownM2",
|
|
61
|
+
"option" => "Option",
|
|
62
|
+
"card" => "Card",
|
|
63
|
+
"banner" => "Banner",
|
|
64
|
+
"datepicker" => "DatePicker",
|
|
65
|
+
"date_picker" => "DatePicker",
|
|
66
|
+
"timepicker" => "TimePicker",
|
|
67
|
+
"time_picker" => "TimePicker",
|
|
68
|
+
"filledtonalbutton" => "FilledTonalButton",
|
|
69
|
+
"filled_tonal_button" => "FilledTonalButton",
|
|
70
|
+
"outlinedbutton" => "OutlinedButton",
|
|
71
|
+
"outlined_button" => "OutlinedButton",
|
|
72
|
+
"listtile" => "ListTile",
|
|
73
|
+
"list_tile" => "ListTile",
|
|
74
|
+
"progressbar" => "ProgressBar",
|
|
75
|
+
"progress_bar" => "ProgressBar",
|
|
76
|
+
"safearea" => "SafeArea",
|
|
77
|
+
"safe_area" => "SafeArea",
|
|
78
|
+
"canvas" => "Canvas",
|
|
79
|
+
"line" => "Line",
|
|
80
|
+
"service_registry" => "ServiceRegistry",
|
|
81
|
+
"url_launcher" => "UrlLauncher",
|
|
82
|
+
"audio" => "Audio",
|
|
83
|
+
"video" => "Video",
|
|
84
|
+
"flashlight" => "Flashlight",
|
|
85
|
+
"barchart" => "BarChart",
|
|
86
|
+
"barchartgroup" => "BarChartGroup",
|
|
87
|
+
"barchartrod" => "BarChartRod",
|
|
88
|
+
"barchartrodstackitem" => "BarChartRodStackItem",
|
|
89
|
+
"linechart" => "LineChart",
|
|
90
|
+
"linechartdata" => "LineChartData",
|
|
91
|
+
"linechartdatapoint" => "LineChartDataPoint",
|
|
92
|
+
"piechart" => "PieChart",
|
|
93
|
+
"piechartsection" => "PieChartSection",
|
|
94
|
+
"candlestickchart" => "CandlestickChart",
|
|
95
|
+
"candlestickchartspot" => "CandlestickChartSpot",
|
|
96
|
+
"radarchart" => "RadarChart",
|
|
97
|
+
"radarcharttitle" => "RadarChartTitle",
|
|
98
|
+
"radardataset" => "RadarDataSet",
|
|
99
|
+
"radardatasetentry" => "RadarDataSetEntry",
|
|
100
|
+
"scatterchart" => "ScatterChart",
|
|
101
|
+
"scatterchartspot" => "ScatterChartSpot",
|
|
102
|
+
"chartaxis" => "ChartAxis",
|
|
103
|
+
"chartaxislabel" => "ChartAxisLabel"
|
|
56
104
|
}.freeze
|
|
57
105
|
|
|
58
106
|
EVENT_PROPS = {
|
|
@@ -77,6 +125,21 @@ module Ruflet
|
|
|
77
125
|
on_horizontal_drag_start: "horizontal_drag_start",
|
|
78
126
|
on_horizontal_drag_update: "horizontal_drag_update",
|
|
79
127
|
on_horizontal_drag_end: "horizontal_drag_end",
|
|
128
|
+
on_tap_down: "tap_down",
|
|
129
|
+
on_long_press_start: "long_press_start",
|
|
130
|
+
on_right_pan_start: "right_pan_start",
|
|
131
|
+
on_event: "event",
|
|
132
|
+
on_load: "load",
|
|
133
|
+
on_loaded: "loaded",
|
|
134
|
+
on_enter_fullscreen: "enter_fullscreen",
|
|
135
|
+
on_exit_fullscreen: "exit_fullscreen",
|
|
136
|
+
on_duration_change: "duration_change",
|
|
137
|
+
on_position_change: "position_change",
|
|
138
|
+
on_state_change: "state_change",
|
|
139
|
+
on_seek_complete: "seek_complete",
|
|
140
|
+
on_complete: "complete",
|
|
141
|
+
on_track_change: "track_change",
|
|
142
|
+
on_error: "error",
|
|
80
143
|
on_accept: "accept",
|
|
81
144
|
on_will_accept: "will_accept",
|
|
82
145
|
on_accept_with_details: "accept_with_details",
|
data/lib/ruflet_ui.rb
CHANGED
|
@@ -105,5 +105,7 @@ module Kernel
|
|
|
105
105
|
Ruflet::DSL
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
if Ruflet::UI::SharedControlForwarders.respond_to?(:instance_methods)
|
|
109
|
+
private(*Ruflet::UI::SharedControlForwarders.instance_methods(false))
|
|
110
|
+
end
|
|
109
111
|
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.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AdamMusa
|
|
@@ -19,6 +19,7 @@ extra_rdoc_files: []
|
|
|
19
19
|
files:
|
|
20
20
|
- README.md
|
|
21
21
|
- lib/ruflet.rb
|
|
22
|
+
- lib/ruflet/manifest_compiler.rb
|
|
22
23
|
- lib/ruflet/version.rb
|
|
23
24
|
- lib/ruflet_protocol.rb
|
|
24
25
|
- lib/ruflet_protocol/ruflet/protocol.rb
|