ruflet_core 0.0.16 → 0.0.17
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 +13 -1
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_ui/ruflet/colors.rb +35 -3
- data/lib/ruflet_ui/ruflet/control.rb +2 -5
- data/lib/ruflet_ui/ruflet/page.rb +67 -6
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82d5d751f2b92b2b740414e315e1b1f9e88389eb0423ec0ce7a040710e191d59
|
|
4
|
+
data.tar.gz: 728109060a0cff6ca564e2bfa5d7620e5f8182dc05cf82b571ae3280e7e0a015
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf40cdafd48b625d189f9625945350873b4936c0668916ce8556e9d07f47368584892b29e57e4fe2e4583230d7ce5c87297848b90c5d01ab1d7ddac0b60df4d2
|
|
7
|
+
data.tar.gz: 2d229699ad359fcbc5dc5640dd176227bd0dad3f01d641b9f44ea569b26ccea61926b16c465284bda7e2fb786b514215922b8fdda5aa0a6b5f6baca36c0303fe
|
data/README.md
CHANGED
|
@@ -11,9 +11,21 @@ require "ruflet"
|
|
|
11
11
|
|
|
12
12
|
Ruflet.run do |page|
|
|
13
13
|
page.title = "Hello"
|
|
14
|
-
page.add(
|
|
14
|
+
page.add(
|
|
15
|
+
container(
|
|
16
|
+
bgcolor: :surface_container_high,
|
|
17
|
+
padding: 24,
|
|
18
|
+
content: text("Hello Ruflet", color: "DeepOrange500")
|
|
19
|
+
)
|
|
20
|
+
)
|
|
15
21
|
end
|
|
16
22
|
```
|
|
17
23
|
|
|
24
|
+
Color properties accept named colors and hex values. Use Ruby-style symbols
|
|
25
|
+
like `:deep_orange_500`, compact strings like `"DeepOrange500"`, spaced names
|
|
26
|
+
like `"Deep Orange 500"`, semantic theme names like `:primary_container`, or
|
|
27
|
+
hex strings like `"#ff6d00"`. Ruflet normalizes named colors before sending
|
|
28
|
+
them to the client.
|
|
29
|
+
|
|
18
30
|
Use `ruflet_server` to run a standalone server-driven application. Use
|
|
19
31
|
`ruflet_rails` when the UI is hosted by Rails.
|
data/lib/ruflet/version.rb
CHANGED
|
@@ -158,16 +158,48 @@ module Ruflet
|
|
|
158
158
|
canonicalize(color.to_s)
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
+
def self.normalize_property(key, value)
|
|
162
|
+
normalize_value(value, color_key?(key))
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def self.color_key?(key)
|
|
166
|
+
name = key.to_s
|
|
167
|
+
name == "color" ||
|
|
168
|
+
name == "colors" ||
|
|
169
|
+
name.end_with?("bgcolor") ||
|
|
170
|
+
name.end_with?("_color") ||
|
|
171
|
+
name.end_with?("_colors") ||
|
|
172
|
+
name == "color_scheme_seed"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.normalize_value(value, color_context = false)
|
|
176
|
+
case value
|
|
177
|
+
when String
|
|
178
|
+
color_context ? canonicalize(value) : value
|
|
179
|
+
when Symbol
|
|
180
|
+
color_context ? canonicalize(value.to_s) : value
|
|
181
|
+
when Array
|
|
182
|
+
value.map { |nested| normalize_value(nested, color_context) }
|
|
183
|
+
when Hash
|
|
184
|
+
value.each_with_object({}) do |(nested_key, nested_value), result|
|
|
185
|
+
result[nested_key] = normalize_value(nested_value, color_context || color_key?(nested_key))
|
|
186
|
+
end
|
|
187
|
+
else
|
|
188
|
+
value
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
161
192
|
# Canonicalizes a named color into flet's wire format. Flet color names are
|
|
162
193
|
# lowercase with no separators ("bluegrey", "deeporange", "red500"), so we
|
|
163
|
-
# strip underscores
|
|
164
|
-
#
|
|
194
|
+
# strip underscores, hyphens, and whitespace before downcasing. Hex values
|
|
195
|
+
# (#... / 0x...) are downcased without separator stripping; the optional
|
|
196
|
+
# ",opacity" suffix is preserved untouched.
|
|
165
197
|
def self.canonicalize(value)
|
|
166
198
|
return value unless value.is_a?(String)
|
|
167
199
|
|
|
168
200
|
color, separator, opacity = value.partition(",")
|
|
169
201
|
color = color.strip.downcase
|
|
170
|
-
color = color.
|
|
202
|
+
color = color.gsub(/[_\-\s]+/, "") unless color.start_with?("#") || color.start_with?("0x")
|
|
171
203
|
|
|
172
204
|
"#{color}#{separator}#{opacity}"
|
|
173
205
|
end
|
|
@@ -198,14 +198,11 @@ module Ruflet
|
|
|
198
198
|
end
|
|
199
199
|
|
|
200
200
|
def normalize_color_prop(key, value)
|
|
201
|
-
|
|
202
|
-
return Ruflet::Colors.canonicalize(value) if color_prop_key?(key)
|
|
203
|
-
|
|
204
|
-
value
|
|
201
|
+
Ruflet::Colors.normalize_property(key, value)
|
|
205
202
|
end
|
|
206
203
|
|
|
207
204
|
def color_prop_key?(key)
|
|
208
|
-
|
|
205
|
+
Ruflet::Colors.color_key?(key)
|
|
209
206
|
end
|
|
210
207
|
|
|
211
208
|
def normalize_icon_prop(key, value)
|
|
@@ -183,6 +183,7 @@ module Ruflet
|
|
|
183
183
|
id: "_dialogs",
|
|
184
184
|
controls: []
|
|
185
185
|
)
|
|
186
|
+
@mounted_services = []
|
|
186
187
|
@invoke_waiters = {}
|
|
187
188
|
@invoke_callbacks = {}
|
|
188
189
|
@invoke_waiters_mutex = Mutex.new
|
|
@@ -540,6 +541,12 @@ module Ruflet
|
|
|
540
541
|
def bottom_sheet=(value)
|
|
541
542
|
@bottom_sheet = value
|
|
542
543
|
refresh_dialogs_container!
|
|
544
|
+
# Mirror dialog= / snack_bar=: once the dialogs container is mounted, push
|
|
545
|
+
# the change as an in-place patch. Without this, opening a bottom sheet
|
|
546
|
+
# after the container is already mounted (e.g. a prior dialog/snackbar)
|
|
547
|
+
# never reaches the client, since send_view_patch skips _dialogs once
|
|
548
|
+
# mounted. This is what made NativeApp's modal: sheets fail to open.
|
|
549
|
+
push_dialogs_update! if @dialogs_container_mounted
|
|
543
550
|
end
|
|
544
551
|
|
|
545
552
|
def bottomsheet=(value)
|
|
@@ -1567,6 +1574,7 @@ module Ruflet
|
|
|
1567
1574
|
raise ArgumentError, "page #{key} must use an icon name string, not #{value.inspect}"
|
|
1568
1575
|
end
|
|
1569
1576
|
|
|
1577
|
+
value = Ruflet::Colors.normalize_property(key, value)
|
|
1570
1578
|
return value.value if value.is_a?(Ruflet::IconData)
|
|
1571
1579
|
value.is_a?(Symbol) ? value.to_s : value
|
|
1572
1580
|
end
|
|
@@ -1592,8 +1600,14 @@ module Ruflet
|
|
|
1592
1600
|
query_string = route_value.to_s.split("?", 2)[1].to_s
|
|
1593
1601
|
return {} if query_string.empty?
|
|
1594
1602
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1603
|
+
query_string.split("&").each_with_object({}) do |pair, result|
|
|
1604
|
+
next if pair.empty?
|
|
1605
|
+
|
|
1606
|
+
key, value = pair.split("=", 2)
|
|
1607
|
+
key = CGI.unescape(key.to_s.tr("+", " "))
|
|
1608
|
+
value = CGI.unescape(value.to_s.tr("+", " "))
|
|
1609
|
+
previous = result[key]
|
|
1610
|
+
result[key] = previous.nil? ? value : Array(previous) << value
|
|
1597
1611
|
end
|
|
1598
1612
|
end
|
|
1599
1613
|
|
|
@@ -1701,14 +1715,61 @@ module Ruflet
|
|
|
1701
1715
|
def push_services_update!
|
|
1702
1716
|
refresh_control_indexes!
|
|
1703
1717
|
|
|
1704
|
-
|
|
1718
|
+
unless @services_container.wire_id
|
|
1719
|
+
# First mount: the whole list ships inside the page/view patch.
|
|
1720
|
+
send_view_patch
|
|
1721
|
+
@mounted_services = Array(@services_container.props["_services"]).dup
|
|
1722
|
+
return
|
|
1723
|
+
end
|
|
1724
|
+
|
|
1725
|
+
current = Array(@services_container.props["_services"])
|
|
1726
|
+
ops = services_patch_ops(@mounted_services, current)
|
|
1727
|
+
unless ops.empty?
|
|
1728
|
+
# Incrementally add/remove individual services. Re-sending the whole
|
|
1729
|
+
# `_services` list as full objects (a single replace op) makes the
|
|
1730
|
+
# client run Control.fromMap for EVERY service, replacing each Control
|
|
1731
|
+
# instance while ServiceRegistry only binds ids it hasn't seen — so an
|
|
1732
|
+
# already-mounted service (share, clipboard, haptic) keeps its old
|
|
1733
|
+
# binding while controlsIndex points at the fresh, listener-less
|
|
1734
|
+
# instance, and its next invoke hangs. That was the "after Copy, Share
|
|
1735
|
+
# stops working" bug. Add/remove ops leave untouched services intact.
|
|
1705
1736
|
send_message(Protocol::ACTIONS[:patch_control], {
|
|
1706
1737
|
"id" => @services_container.wire_id,
|
|
1707
|
-
"patch" => [[0
|
|
1738
|
+
"patch" => [[0, { "_services" => [1] }], *ops]
|
|
1708
1739
|
})
|
|
1709
|
-
else
|
|
1710
|
-
send_view_patch
|
|
1711
1740
|
end
|
|
1741
|
+
@mounted_services = current.dup
|
|
1742
|
+
end
|
|
1743
|
+
|
|
1744
|
+
# Diff the previously-mounted services list against the current one and emit
|
|
1745
|
+
# add (1) / remove (2) ops against the `_services` list (patch node 1).
|
|
1746
|
+
# Services are normally append-only; a moved entry is expressed as
|
|
1747
|
+
# remove + add. Existing entries are never re-serialized, so their client
|
|
1748
|
+
# Control instances (and invoke listeners) survive.
|
|
1749
|
+
def services_patch_ops(previous, current)
|
|
1750
|
+
ops = []
|
|
1751
|
+
working = previous.dup
|
|
1752
|
+
|
|
1753
|
+
(working.length - 1).downto(0) do |index|
|
|
1754
|
+
next if current.any? { |service| service.equal?(working[index]) }
|
|
1755
|
+
|
|
1756
|
+
ops << [2, 1, index]
|
|
1757
|
+
working.delete_at(index)
|
|
1758
|
+
end
|
|
1759
|
+
|
|
1760
|
+
current.each_with_index do |service, index|
|
|
1761
|
+
next if working[index]&.equal?(service)
|
|
1762
|
+
|
|
1763
|
+
existing = working.index { |candidate| candidate.equal?(service) }
|
|
1764
|
+
if existing
|
|
1765
|
+
ops << [2, 1, existing]
|
|
1766
|
+
working.delete_at(existing)
|
|
1767
|
+
end
|
|
1768
|
+
ops << [1, 1, index, serialize_patch_value(service)]
|
|
1769
|
+
working.insert(index, service)
|
|
1770
|
+
end
|
|
1771
|
+
|
|
1772
|
+
ops
|
|
1712
1773
|
end
|
|
1713
1774
|
|
|
1714
1775
|
def push_dialogs_update!
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruflet_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AdamMusa
|
|
@@ -253,7 +253,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
253
253
|
- !ruby/object:Gem::Version
|
|
254
254
|
version: '0'
|
|
255
255
|
requirements: []
|
|
256
|
-
rubygems_version:
|
|
256
|
+
rubygems_version: 4.0.11
|
|
257
257
|
specification_version: 4
|
|
258
258
|
summary: Ruflet core runtime package.
|
|
259
259
|
test_files: []
|