ruflet_core 0.0.18 → 0.0.19
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 +30 -2
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_ui/ruflet/page.rb +8 -0
- data/lib/ruflet_ui/ruflet/ui/control_factory.rb +4 -4
- data/lib/ruflet_ui/ruflet/ui/controls/materials/webview_control.rb +113 -1
- data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +1 -1
- data/lib/ruflet_ui/ruflet/ui/shared_control_forwarders.rb +5 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 412cca0911d7e0d196902336dff3da369a776e146713f511b4571fe3e693f99e
|
|
4
|
+
data.tar.gz: 9f07083cd60681b95aec47ec0a269c9c0ef42365ee23f8f627f2ea6795e82a66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77edfb41b260ca90eaa82cc02fd63d81f5c8212845eba2fec5289d31d8d45d1b6c6cf5b02ebf911fbce91bc070a3f591f60d4c9d6a2906f5c87f7456fed1d83c
|
|
7
|
+
data.tar.gz: 70606ebdd3a54ea6d10924b742a2821f5ce05b6ddf0ae0e8c0c23ed69d6042a40452aca569dd48d43b36fa7f85c99c96f674f22ad2722dc075bbdb5fa55f4d5f
|
data/README.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ruflet_core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`ruflet_core` provides Ruflet's Ruby UI API: controls, control builders, page
|
|
4
|
+
operations, events, services, and application lifecycle behavior.
|
|
5
|
+
|
|
6
|
+
Applications normally receive this package through a generated Ruflet project
|
|
7
|
+
or through `ruflet_rails`; it is not a standalone application server.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
require "ruflet"
|
|
11
|
+
|
|
12
|
+
Ruflet.run do |page|
|
|
13
|
+
page.title = "Hello"
|
|
14
|
+
page.add(
|
|
15
|
+
container(
|
|
16
|
+
bgcolor: :surface_container_high,
|
|
17
|
+
padding: 24,
|
|
18
|
+
content: text("Hello Ruflet", color: "DeepOrange500")
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
```
|
|
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
|
+
|
|
30
|
+
Use `ruflet_server` to run a standalone server-driven application. Use
|
|
31
|
+
`ruflet_rails` when the UI is hosted by Rails.
|
data/lib/ruflet/version.rb
CHANGED
|
@@ -590,11 +590,13 @@ module Ruflet
|
|
|
590
590
|
def dialog=(value)
|
|
591
591
|
@dialog = value
|
|
592
592
|
refresh_dialogs_container!
|
|
593
|
+
push_dialogs_update! if @dialogs_container_mounted
|
|
593
594
|
end
|
|
594
595
|
|
|
595
596
|
def snack_bar=(value)
|
|
596
597
|
@snack_bar = value
|
|
597
598
|
refresh_dialogs_container!
|
|
599
|
+
push_dialogs_update! if @dialogs_container_mounted
|
|
598
600
|
end
|
|
599
601
|
|
|
600
602
|
def snack_bar
|
|
@@ -608,6 +610,7 @@ module Ruflet
|
|
|
608
610
|
def bottom_sheet=(value)
|
|
609
611
|
@bottom_sheet = value
|
|
610
612
|
refresh_dialogs_container!
|
|
613
|
+
push_dialogs_update! if @dialogs_container_mounted
|
|
611
614
|
end
|
|
612
615
|
|
|
613
616
|
def bottom_sheet
|
|
@@ -656,6 +659,9 @@ module Ruflet
|
|
|
656
659
|
return nil unless target
|
|
657
660
|
|
|
658
661
|
target.props["open"] = false
|
|
662
|
+
@dialog = nil if @dialog.equal?(target)
|
|
663
|
+
@snack_bar = nil if @snack_bar.equal?(target)
|
|
664
|
+
@bottom_sheet = nil if @bottom_sheet.equal?(target)
|
|
659
665
|
refresh_dialogs_container!
|
|
660
666
|
push_dialogs_update!
|
|
661
667
|
target
|
|
@@ -1192,6 +1198,8 @@ module Ruflet
|
|
|
1192
1198
|
patch["content"] = patch.delete("text")
|
|
1193
1199
|
end
|
|
1194
1200
|
|
|
1201
|
+
patch.each { |key, value| control.props[key] = value }
|
|
1202
|
+
|
|
1195
1203
|
# Keep runtime control tree aligned with incremental patches.
|
|
1196
1204
|
if patch.key?("controls")
|
|
1197
1205
|
control.children.clear
|
|
@@ -15,8 +15,8 @@ module Ruflet
|
|
|
15
15
|
.freeze
|
|
16
16
|
CONSTRUCTOR_KEYWORDS_CACHE = {}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
COMMON_ATTRIBUTES = %i[flip ref transform].freeze
|
|
19
|
+
ATTRIBUTE_OVERRIDES = {
|
|
20
20
|
"expansionpanellist" => %i[auto_scroll on_scroll scroll scroll_interval],
|
|
21
21
|
"fletapp" => %i[assets_dir on_python_output],
|
|
22
22
|
"image" => %i[
|
|
@@ -64,7 +64,7 @@ module Ruflet
|
|
|
64
64
|
|
|
65
65
|
def normalize_constructor_props(klass, type, props)
|
|
66
66
|
keywords = constructor_keywords(klass)
|
|
67
|
-
return [props, {}] if keywords.empty? &&
|
|
67
|
+
return [props, {}] if keywords.empty? && ATTRIBUTE_OVERRIDES[type].nil?
|
|
68
68
|
|
|
69
69
|
allowed = keywords.map(&:to_s)
|
|
70
70
|
mapped = props.each_with_object({}) { |(k, v), out| out[k.to_sym] = v }
|
|
@@ -74,7 +74,7 @@ module Ruflet
|
|
|
74
74
|
if mapped.key?(:value) && !allowed.include?("value") && allowed.include?("text") && !mapped.key?(:text)
|
|
75
75
|
mapped[:text] = mapped.delete(:value)
|
|
76
76
|
end
|
|
77
|
-
supplemental_names =
|
|
77
|
+
supplemental_names = COMMON_ATTRIBUTES + ATTRIBUTE_OVERRIDES.fetch(type, [])
|
|
78
78
|
supplemental = mapped.slice(*supplemental_names)
|
|
79
79
|
[mapped.reject { |key, _| supplemental_names.include?(key) }, supplemental]
|
|
80
80
|
end
|
|
@@ -4,11 +4,32 @@ module Ruflet
|
|
|
4
4
|
module UI
|
|
5
5
|
module Controls
|
|
6
6
|
module RufletComponents
|
|
7
|
+
# WebView control — parity with Flet's WebView
|
|
8
|
+
# (https://flet.dev/docs/controls/webview/).
|
|
9
|
+
#
|
|
10
|
+
# Properties: url, bgcolor, prevent_links, plus the usual layout props.
|
|
11
|
+
# Events: on_page_started, on_page_ended, on_web_resource_error,
|
|
12
|
+
# on_progress, on_url_change, on_scroll, on_console_message,
|
|
13
|
+
# on_javascript_alert_dialog.
|
|
14
|
+
# Methods (invoked over the wire on a mounted control): reload, go_back,
|
|
15
|
+
# go_forward, can_go_back, can_go_forward, run_javascript, load_html,
|
|
16
|
+
# load_request, load_file, scroll_to, scroll_by, clear_cache,
|
|
17
|
+
# clear_local_storage, enable_zoom, disable_zoom, set_javascript_mode,
|
|
18
|
+
# get_current_url, get_title, get_user_agent.
|
|
19
|
+
#
|
|
20
|
+
# Platform note: the native webview runs on iOS, Android, macOS, Windows,
|
|
21
|
+
# and Linux. Linux distributions must provide WebKitGTK 4.1. On web it
|
|
22
|
+
# uses an iframe, so browser cross-origin restrictions still apply.
|
|
7
23
|
class WebViewControl < Ruflet::Control
|
|
8
24
|
TYPE = "WebView".freeze
|
|
9
25
|
WIRE = "WebView".freeze
|
|
10
26
|
|
|
11
|
-
def initialize(id: nil, bgcolor: nil, data: nil, enable_javascript: nil, expand: nil,
|
|
27
|
+
def initialize(id: nil, bgcolor: nil, data: nil, enable_javascript: nil, expand: nil,
|
|
28
|
+
height: nil, key: nil, method: nil, opacity: nil, prevent_links: nil,
|
|
29
|
+
rtl: nil, tooltip: nil, url: nil, visible: nil, width: nil,
|
|
30
|
+
on_page_ended: nil, on_page_started: nil, on_web_resource_error: nil,
|
|
31
|
+
on_progress: nil, on_url_change: nil, on_scroll: nil,
|
|
32
|
+
on_console_message: nil, on_javascript_alert_dialog: nil)
|
|
12
33
|
props = {}
|
|
13
34
|
props[:bgcolor] = bgcolor unless bgcolor.nil?
|
|
14
35
|
props[:data] = data unless data.nil?
|
|
@@ -18,6 +39,7 @@ module Ruflet
|
|
|
18
39
|
props[:key] = key unless key.nil?
|
|
19
40
|
props[:method] = method unless method.nil?
|
|
20
41
|
props[:opacity] = opacity unless opacity.nil?
|
|
42
|
+
props[:prevent_links] = prevent_links unless prevent_links.nil?
|
|
21
43
|
props[:rtl] = rtl unless rtl.nil?
|
|
22
44
|
props[:tooltip] = tooltip unless tooltip.nil?
|
|
23
45
|
props[:url] = url unless url.nil?
|
|
@@ -26,8 +48,98 @@ module Ruflet
|
|
|
26
48
|
props[:on_page_ended] = on_page_ended unless on_page_ended.nil?
|
|
27
49
|
props[:on_page_started] = on_page_started unless on_page_started.nil?
|
|
28
50
|
props[:on_web_resource_error] = on_web_resource_error unless on_web_resource_error.nil?
|
|
51
|
+
props[:on_progress] = on_progress unless on_progress.nil?
|
|
52
|
+
props[:on_url_change] = on_url_change unless on_url_change.nil?
|
|
53
|
+
props[:on_scroll] = on_scroll unless on_scroll.nil?
|
|
54
|
+
props[:on_console_message] = on_console_message unless on_console_message.nil?
|
|
55
|
+
props[:on_javascript_alert_dialog] = on_javascript_alert_dialog unless on_javascript_alert_dialog.nil?
|
|
29
56
|
super(type: TYPE, id: id, **props)
|
|
30
57
|
end
|
|
58
|
+
|
|
59
|
+
# --- Navigation --------------------------------------------------
|
|
60
|
+
|
|
61
|
+
def reload = invoke_webview_method("reload")
|
|
62
|
+
def go_back = invoke_webview_method("go_back")
|
|
63
|
+
def go_forward = invoke_webview_method("go_forward")
|
|
64
|
+
|
|
65
|
+
def can_go_back(timeout: 10, &on_result)
|
|
66
|
+
invoke_webview_method("can_go_back", timeout: timeout, on_result: on_result)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def can_go_forward(timeout: 10, &on_result)
|
|
70
|
+
invoke_webview_method("can_go_forward", timeout: timeout, on_result: on_result)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# --- Loading content ---------------------------------------------
|
|
74
|
+
|
|
75
|
+
def load_request(url, method: "get")
|
|
76
|
+
invoke_webview_method("load_request", { "url" => url.to_s, "method" => method.to_s })
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def load_html(value, base_url: nil)
|
|
80
|
+
args = { "value" => value.to_s }
|
|
81
|
+
args["base_url"] = base_url.to_s unless base_url.nil?
|
|
82
|
+
invoke_webview_method("load_html", args)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def load_file(path)
|
|
86
|
+
invoke_webview_method("load_file", { "path" => path.to_s })
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# --- JavaScript injection ---------------------------------------
|
|
90
|
+
|
|
91
|
+
# Run arbitrary JS inside the page — e.g. hide a node so a native
|
|
92
|
+
# control can take its place:
|
|
93
|
+
# webview.run_javascript("document.getElementById('banner').remove()")
|
|
94
|
+
def run_javascript(value)
|
|
95
|
+
invoke_webview_method("run_javascript", { "value" => value.to_s })
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def set_javascript_mode(mode)
|
|
99
|
+
invoke_webview_method("set_javascript_mode", { "mode" => mode.to_s })
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# --- Scrolling ---------------------------------------------------
|
|
103
|
+
|
|
104
|
+
def scroll_to(x, y)
|
|
105
|
+
invoke_webview_method("scroll_to", { "x" => x.to_i, "y" => y.to_i })
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def scroll_by(x, y)
|
|
109
|
+
invoke_webview_method("scroll_by", { "x" => x.to_i, "y" => y.to_i })
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# --- Storage / zoom ----------------------------------------------
|
|
113
|
+
|
|
114
|
+
def clear_cache = invoke_webview_method("clear_cache")
|
|
115
|
+
def clear_local_storage = invoke_webview_method("clear_local_storage")
|
|
116
|
+
def enable_zoom = invoke_webview_method("enable_zoom")
|
|
117
|
+
def disable_zoom = invoke_webview_method("disable_zoom")
|
|
118
|
+
|
|
119
|
+
# --- Introspection (result delivered to the block) ---------------
|
|
120
|
+
|
|
121
|
+
def get_current_url(timeout: 10, &on_result)
|
|
122
|
+
invoke_webview_method("get_current_url", timeout: timeout, on_result: on_result)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def get_title(timeout: 10, &on_result)
|
|
126
|
+
invoke_webview_method("get_title", timeout: timeout, on_result: on_result)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def get_user_agent(timeout: 10, &on_result)
|
|
130
|
+
invoke_webview_method("get_user_agent", timeout: timeout, on_result: on_result)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def invoke_webview_method(name, args = nil, timeout: 10, on_result: nil)
|
|
136
|
+
page = runtime_page
|
|
137
|
+
unless page && wire_id
|
|
138
|
+
raise "WebView ##{id} is not mounted yet — add it to the page before calling #{name}."
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
page.invoke(self, name, args: args, timeout: timeout, on_result: on_result)
|
|
142
|
+
end
|
|
31
143
|
end
|
|
32
144
|
end
|
|
33
145
|
end
|
|
@@ -21,7 +21,7 @@ module Ruflet
|
|
|
21
21
|
|
|
22
22
|
def view(children = nil, **props, &block)
|
|
23
23
|
mapped = props.dup
|
|
24
|
-
mapped[:
|
|
24
|
+
mapped[:controls] = children unless children.nil?
|
|
25
25
|
build_widget(:view, **mapped, &block)
|
|
26
26
|
end
|
|
27
27
|
def column(children = nil, **props, &block)
|
|
@@ -6,7 +6,11 @@ module Ruflet
|
|
|
6
6
|
def control(type, **props, &block) = control_delegate.control(type, **props, &block)
|
|
7
7
|
def widget(type, **props, &block) = control_delegate.widget(type, **props, &block)
|
|
8
8
|
def service(type, **props, &block) = control_delegate.service(type, **props, &block)
|
|
9
|
-
def view(children = nil, **props, &block)
|
|
9
|
+
def view(children = nil, **props, &block)
|
|
10
|
+
mapped = props.dup
|
|
11
|
+
mapped[:controls] = children unless children.nil?
|
|
12
|
+
control_delegate.control(:view, **mapped, &block)
|
|
13
|
+
end
|
|
10
14
|
def column(children = nil, **props, &block) = control_delegate.column(children, **props, &block)
|
|
11
15
|
def center(**props, &block) = control_delegate.center(**props, &block)
|
|
12
16
|
def row(children = nil, **props, &block) = control_delegate.row(children, **props, &block)
|