symbiote-ui 0.3.0-alpha.39 → 0.3.0-alpha.40
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.
- package/custom-elements.json +2 -2
- package/manifest/component-registry.js +1 -1
- package/package.json +1 -1
- package/themes/CascadeThemeWidget/CascadeThemeWidget.css.js +200 -158
- package/themes/CascadeThemeWidget/CascadeThemeWidget.js +120 -6
- package/themes/CascadeThemeWidget/CascadeThemeWidget.tpl.js +8 -1
package/custom-elements.json
CHANGED
|
@@ -3657,9 +3657,9 @@
|
|
|
3657
3657
|
}
|
|
3658
3658
|
],
|
|
3659
3659
|
"agent": {
|
|
3660
|
-
"componentDescription": "Generic hierarchical graph overview/read renderer with force layout, semantic navigation, and
|
|
3660
|
+
"componentDescription": "Generic hierarchical graph overview/read renderer with force layout, semantic navigation, selection events, and optional device-orientation parallax. Use this interactive visual graph surface when composing canvas UI. Capabilities: hierarchical-graph, overview-read-renderer, force-layout, semantic-clusters, focus-selection, layout-snapshot, device-orientation-parallax. Data ownership: host-owned model; component renders and emits intent events. WebMCP tools: canvas_graph_set_model, canvas_graph_focus_node, canvas_graph_focus_nodes, canvas_graph_set_path.",
|
|
3661
3661
|
"semanticRole": "interactive visual graph surface",
|
|
3662
|
-
"usage": "Render or compose <canvas-graph> when the host needs generic hierarchical graph overview/read renderer with force layout, semantic navigation, and
|
|
3662
|
+
"usage": "Render or compose <canvas-graph> when the host needs generic hierarchical graph overview/read renderer with force layout, semantic navigation, selection events, and optional device-orientation parallax.",
|
|
3663
3663
|
"dataOwnership": "host-owned model; component renders and emits intent events",
|
|
3664
3664
|
"webmcp": {
|
|
3665
3665
|
"mode": "explicit-descriptor",
|
|
@@ -3259,7 +3259,7 @@ export let COMPONENTS = [
|
|
|
3259
3259
|
className: 'CanvasGraph',
|
|
3260
3260
|
module: 'canvas/CanvasGraph/CanvasGraph.js',
|
|
3261
3261
|
category: 'canvas',
|
|
3262
|
-
description: 'Generic hierarchical graph overview/read renderer with force layout, semantic navigation, and
|
|
3262
|
+
description: 'Generic hierarchical graph overview/read renderer with force layout, semantic navigation, selection events, and optional device-orientation parallax.',
|
|
3263
3263
|
contract: {
|
|
3264
3264
|
status: 'draft',
|
|
3265
3265
|
schemaVersion: 'component-descriptor-v2',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiote-ui",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Symbiote provider UI, Web Components, themes, manifests, schemas, graph layout helpers, WebMCP metadata, and SSR-safe UI contracts.",
|
|
6
6
|
"main": "index.js",
|
|
@@ -7,189 +7,231 @@ export default css`
|
|
|
7
7
|
align-items: center;
|
|
8
8
|
color: var(--sn-text);
|
|
9
9
|
font-family: var(--sn-font);
|
|
10
|
+
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
cascade-theme-widget[hidden] {
|
|
13
|
+
display: none !important;
|
|
14
|
+
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
cascade-theme-widget .ctw-trigger {
|
|
17
|
+
flex: 0 0 auto;
|
|
18
|
+
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
cascade-theme-widget .ctw-trigger .material-symbols-outlined,
|
|
21
|
+
cascade-theme-widget .ctw-header-actions .material-symbols-outlined,
|
|
22
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions .material-symbols-outlined {
|
|
23
|
+
font-size: var(--sn-shell-menu-action-icon-size, var(--sn-layout-header-icon-size, 16px));
|
|
24
|
+
}
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
cascade-theme-widget .ctw-popover,
|
|
27
|
+
.ctw-popover[data-overlay-portal] {
|
|
28
|
+
z-index: var(--sn-theme-widget-z, var(--sn-overlay-z-base, 20000));
|
|
29
|
+
display: grid;
|
|
30
|
+
grid-template-rows: auto auto auto;
|
|
31
|
+
gap: var(--sn-theme-widget-gap, calc(8px * var(--sn-theme-density, 1)));
|
|
32
|
+
width: min(92vw, var(--sn-theme-widget-width, 320px));
|
|
33
|
+
max-width: calc(100vw - 16px);
|
|
34
|
+
padding: var(--sn-theme-widget-padding, calc(10px * var(--sn-theme-density, 1)));
|
|
35
|
+
border: var(--sn-node-border-width, 1px) solid var(--sn-node-border);
|
|
36
|
+
border-radius: var(--sn-node-radius, 8px);
|
|
37
|
+
background: var(--sn-panel-bg);
|
|
38
|
+
box-shadow: var(--sn-panel-shadow, 0 16px 48px hsl(0 0% 0% / 0.28));
|
|
39
|
+
color: var(--sn-text);
|
|
40
|
+
font-family: var(--sn-font);
|
|
41
|
+
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
cascade-theme-widget .ctw-popover {
|
|
44
|
+
position: absolute;
|
|
45
|
+
top: calc(100% + var(--sn-theme-widget-offset, 8px));
|
|
46
|
+
right: 0;
|
|
47
|
+
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
align-items: center;
|
|
52
|
-
min-width: 0;
|
|
53
|
-
}
|
|
49
|
+
.ctw-popover[data-overlay-portal] {
|
|
50
|
+
position: fixed;
|
|
51
|
+
top: 0;
|
|
52
|
+
left: 0;
|
|
53
|
+
right: auto;
|
|
54
|
+
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
cascade-theme-widget .ctw-popover[hidden],
|
|
57
|
+
.ctw-popover[data-overlay-portal][hidden] {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
cascade-theme-widget .ctw-header,
|
|
62
|
+
cascade-theme-widget .ctw-header-actions,
|
|
63
|
+
cascade-theme-widget .ctw-control,
|
|
64
|
+
cascade-theme-widget .ctw-control-head,
|
|
65
|
+
cascade-theme-widget .ctw-mode,
|
|
66
|
+
.ctw-popover[data-overlay-portal] .ctw-header,
|
|
67
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions,
|
|
68
|
+
.ctw-popover[data-overlay-portal] .ctw-control,
|
|
69
|
+
.ctw-popover[data-overlay-portal] .ctw-control-head,
|
|
70
|
+
.ctw-popover[data-overlay-portal] .ctw-mode {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
min-width: 0;
|
|
74
|
+
}
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
cascade-theme-widget .ctw-header,
|
|
77
|
+
.ctw-popover[data-overlay-portal] .ctw-header {
|
|
78
|
+
justify-content: space-between;
|
|
79
|
+
gap: var(--sn-theme-widget-gap, 8px);
|
|
80
|
+
}
|
|
71
81
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
color: var(--sn-button-color, var(--sn-text));
|
|
81
|
-
font: inherit;
|
|
82
|
-
cursor: pointer;
|
|
83
|
-
}
|
|
82
|
+
cascade-theme-widget .ctw-header strong,
|
|
83
|
+
.ctw-popover[data-overlay-portal] .ctw-header strong {
|
|
84
|
+
min-width: 0;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
font-size: var(--sn-theme-widget-title-size, var(--sn-app-title-size, 13px));
|
|
87
|
+
text-overflow: ellipsis;
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
}
|
|
84
90
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
padding: 0;
|
|
90
|
-
}
|
|
91
|
+
cascade-theme-widget .ctw-header-actions,
|
|
92
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions {
|
|
93
|
+
gap: var(--sn-theme-widget-action-gap, 4px);
|
|
94
|
+
}
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
cascade-theme-widget .ctw-header-actions button,
|
|
97
|
+
cascade-theme-widget .ctw-mode button,
|
|
98
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions button,
|
|
99
|
+
.ctw-popover[data-overlay-portal] .ctw-mode button {
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
border: 1px solid var(--sn-button-border, var(--sn-node-border));
|
|
104
|
+
border-radius: var(--sn-button-radius, 6px);
|
|
105
|
+
background: var(--sn-button-bg, var(--sn-node-bg));
|
|
106
|
+
color: var(--sn-button-color, var(--sn-text));
|
|
107
|
+
font: inherit;
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
}
|
|
101
110
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
cascade-theme-widget .ctw-header-actions button,
|
|
112
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions button {
|
|
113
|
+
width: var(--sn-theme-widget-icon-button-size, var(--sn-theme-editor-icon-button-size, 28px));
|
|
114
|
+
min-width: var(--sn-theme-widget-icon-button-size, var(--sn-theme-editor-icon-button-size, 28px));
|
|
115
|
+
height: var(--sn-theme-widget-icon-button-size, var(--sn-theme-editor-icon-button-size, 28px));
|
|
116
|
+
padding: 0;
|
|
117
|
+
}
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
cascade-theme-widget .ctw-mode,
|
|
120
|
+
.ctw-popover[data-overlay-portal] .ctw-mode {
|
|
121
|
+
display: grid;
|
|
122
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
123
|
+
gap: var(--sn-theme-widget-mode-gap, 4px);
|
|
124
|
+
padding: var(--sn-theme-widget-mode-padding, 3px);
|
|
125
|
+
border: 1px solid var(--sn-node-border);
|
|
126
|
+
border-radius: var(--sn-node-radius, 8px);
|
|
127
|
+
background: var(--sn-bg);
|
|
128
|
+
}
|
|
115
129
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
cascade-theme-widget .ctw-mode button,
|
|
131
|
+
.ctw-popover[data-overlay-portal] .ctw-mode button {
|
|
132
|
+
min-width: 0;
|
|
133
|
+
min-height: var(--sn-theme-widget-mode-height, var(--sn-button-min-height, 28px));
|
|
134
|
+
padding: var(--sn-theme-widget-mode-button-padding, 5px 10px);
|
|
135
|
+
color: var(--sn-text-dim);
|
|
136
|
+
font-size: var(--sn-theme-widget-control-size, var(--sn-button-font-size, 12px));
|
|
137
|
+
}
|
|
121
138
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
139
|
+
cascade-theme-widget .ctw-mode button[aria-pressed="true"],
|
|
140
|
+
.ctw-popover[data-overlay-portal] .ctw-mode button[aria-pressed="true"] {
|
|
141
|
+
border-color: var(--sn-button-primary-border, var(--sn-node-selected));
|
|
142
|
+
background: var(--sn-button-primary-bg, var(--sn-node-selected));
|
|
143
|
+
color: var(--sn-button-primary-color, var(--sn-bg));
|
|
144
|
+
}
|
|
129
145
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
cascade-theme-widget .ctw-controls,
|
|
147
|
+
.ctw-popover[data-overlay-portal] .ctw-controls {
|
|
148
|
+
display: grid;
|
|
149
|
+
gap: var(--sn-theme-widget-control-gap, 7px);
|
|
150
|
+
min-width: 0;
|
|
151
|
+
}
|
|
133
152
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
cascade-theme-widget .ctw-control,
|
|
154
|
+
.ctw-popover[data-overlay-portal] .ctw-control {
|
|
155
|
+
display: grid;
|
|
156
|
+
grid-template-columns: minmax(82px, 0.52fr) minmax(96px, 1fr) minmax(34px, auto);
|
|
157
|
+
gap: var(--sn-theme-widget-control-gap, 7px);
|
|
158
|
+
color: var(--sn-text-dim);
|
|
159
|
+
font-size: var(--sn-theme-widget-control-size, var(--sn-theme-editor-control-size, 12px));
|
|
160
|
+
}
|
|
141
161
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
162
|
+
cascade-theme-widget .ctw-control-head,
|
|
163
|
+
.ctw-popover[data-overlay-portal] .ctw-control-head {
|
|
164
|
+
gap: var(--sn-theme-widget-control-gap, 7px);
|
|
165
|
+
}
|
|
147
166
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
cascade-theme-widget .ctw-control-icon,
|
|
168
|
+
.ctw-popover[data-overlay-portal] .ctw-control-icon {
|
|
169
|
+
width: var(--sn-theme-widget-control-icon-box, calc(18px * var(--sn-theme-density, 1)));
|
|
170
|
+
color: var(--sn-node-selected);
|
|
171
|
+
font-size: var(--sn-theme-widget-control-icon-size, var(--sn-layout-header-icon-size, 16px));
|
|
172
|
+
line-height: 1;
|
|
173
|
+
text-align: center;
|
|
174
|
+
}
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
176
|
+
cascade-theme-widget .ctw-control label,
|
|
177
|
+
.ctw-popover[data-overlay-portal] .ctw-control label {
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
text-overflow: ellipsis;
|
|
180
|
+
white-space: nowrap;
|
|
181
|
+
}
|
|
160
182
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
cascade-theme-widget .ctw-control input,
|
|
184
|
+
.ctw-popover[data-overlay-portal] .ctw-control input {
|
|
185
|
+
width: 100%;
|
|
186
|
+
min-width: 0;
|
|
187
|
+
height: var(--sn-theme-widget-range-hit-size, 34px);
|
|
188
|
+
accent-color: var(--sn-node-selected);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
cascade-theme-widget .ctw-control output,
|
|
192
|
+
.ctw-popover[data-overlay-portal] .ctw-control output {
|
|
193
|
+
color: var(--sn-text);
|
|
194
|
+
font-variant-numeric: tabular-nums;
|
|
195
|
+
text-align: right;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
cascade-theme-widget .ctw-header-actions button:hover,
|
|
199
|
+
cascade-theme-widget .ctw-mode button:hover,
|
|
200
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions button:hover,
|
|
201
|
+
.ctw-popover[data-overlay-portal] .ctw-mode button:hover {
|
|
202
|
+
background: var(--sn-button-hover-bg, var(--sn-node-hover));
|
|
203
|
+
}
|
|
165
204
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
205
|
+
cascade-theme-widget .ctw-header-actions button:focus-visible,
|
|
206
|
+
cascade-theme-widget .ctw-mode button:focus-visible,
|
|
207
|
+
cascade-theme-widget .ctw-control input:focus-visible,
|
|
208
|
+
cascade-theme-widget .ctw-trigger:focus-visible,
|
|
209
|
+
.ctw-popover[data-overlay-portal] .ctw-header-actions button:focus-visible,
|
|
210
|
+
.ctw-popover[data-overlay-portal] .ctw-mode button:focus-visible,
|
|
211
|
+
.ctw-popover[data-overlay-portal] .ctw-control input:focus-visible {
|
|
212
|
+
outline: var(--sn-effect-focus-ring, 2px solid var(--sn-node-selected));
|
|
213
|
+
outline-offset: 2px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@media (max-width: 820px) {
|
|
217
|
+
cascade-theme-widget .ctw-trigger-label {
|
|
218
|
+
display: none;
|
|
172
219
|
}
|
|
173
220
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
max(var(--sn-theme-widget-mobile-inset, 8px), env(safe-area-inset-bottom))
|
|
189
|
-
);
|
|
190
|
-
overflow: auto;
|
|
191
|
-
transform: none;
|
|
192
|
-
}
|
|
221
|
+
cascade-theme-widget .ctw-popover,
|
|
222
|
+
.ctw-popover[data-overlay-portal] {
|
|
223
|
+
position: fixed;
|
|
224
|
+
top: var(--sn-theme-widget-mobile-top, calc(env(safe-area-inset-top) + 88px));
|
|
225
|
+
right: max(var(--sn-theme-widget-mobile-inset, 8px), env(safe-area-inset-right));
|
|
226
|
+
left: max(var(--sn-theme-widget-mobile-inset, 8px), env(safe-area-inset-left));
|
|
227
|
+
width: auto;
|
|
228
|
+
max-width: none;
|
|
229
|
+
max-height: calc(
|
|
230
|
+
100dvh - var(--sn-theme-widget-mobile-top, calc(env(safe-area-inset-top) + 88px)) -
|
|
231
|
+
max(var(--sn-theme-widget-mobile-inset, 8px), env(safe-area-inset-bottom))
|
|
232
|
+
);
|
|
233
|
+
overflow: auto;
|
|
234
|
+
transform: none;
|
|
193
235
|
}
|
|
194
236
|
}
|
|
195
237
|
`;
|
|
@@ -6,6 +6,12 @@ import {
|
|
|
6
6
|
getCascadeThemeControls,
|
|
7
7
|
normalizeCascadeThemeOptions,
|
|
8
8
|
} from '../cascade-theme.js';
|
|
9
|
+
import {
|
|
10
|
+
bringOverlayToFront,
|
|
11
|
+
mountOverlayToDocument,
|
|
12
|
+
restoreOverlayHome,
|
|
13
|
+
} from '../../ui/overlay-stack.js';
|
|
14
|
+
import { positionOverlay } from '../../ui/overlay-positioner.js';
|
|
9
15
|
import css from './CascadeThemeWidget.css.js';
|
|
10
16
|
import tpl from './CascadeThemeWidget.tpl.js';
|
|
11
17
|
|
|
@@ -47,13 +53,15 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
47
53
|
#controls = getCascadeThemeControls().filter((control) => COMPACT_CONTROLS.includes(control.name));
|
|
48
54
|
#state = normalizeCascadeThemeOptions(CASCADE_THEME_DEFAULTS);
|
|
49
55
|
#ready = false;
|
|
56
|
+
#popoverBound = false;
|
|
57
|
+
#overlayListenersBound = false;
|
|
50
58
|
|
|
51
59
|
init$ = {
|
|
52
60
|
isOpen: false,
|
|
53
61
|
triggerTitle: 'Theme quick controls',
|
|
54
62
|
|
|
55
63
|
onToggle: () => {
|
|
56
|
-
this
|
|
64
|
+
this.#setOpen(!this.$.isOpen);
|
|
57
65
|
},
|
|
58
66
|
};
|
|
59
67
|
|
|
@@ -62,8 +70,8 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
62
70
|
this.addEventListener('input', this.#onInput);
|
|
63
71
|
this.addEventListener('click', this.#onClick);
|
|
64
72
|
this._onDocumentPointerDown = (event) => {
|
|
65
|
-
if (!this.$.isOpen || this
|
|
66
|
-
this
|
|
73
|
+
if (!this.$.isOpen || this.#eventTargetsWidget(event)) return;
|
|
74
|
+
this.#setOpen(false);
|
|
67
75
|
};
|
|
68
76
|
if (typeof document !== 'undefined') {
|
|
69
77
|
document.addEventListener('pointerdown', this._onDocumentPointerDown);
|
|
@@ -73,6 +81,8 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
73
81
|
disconnectedCallback() {
|
|
74
82
|
this.removeEventListener('input', this.#onInput);
|
|
75
83
|
this.removeEventListener('click', this.#onClick);
|
|
84
|
+
this.#unbindPopoverEvents();
|
|
85
|
+
this.#setOpen(false);
|
|
76
86
|
if (typeof document !== 'undefined') {
|
|
77
87
|
document.removeEventListener('pointerdown', this._onDocumentPointerDown);
|
|
78
88
|
}
|
|
@@ -90,9 +100,11 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
90
100
|
renderCallback() {
|
|
91
101
|
if (this.#ready) return;
|
|
92
102
|
this.#ready = true;
|
|
103
|
+
this.#bindPopoverEvents();
|
|
93
104
|
this.#renderControls();
|
|
94
105
|
this.#loadStoredState();
|
|
95
106
|
this.#apply('init');
|
|
107
|
+
if (this.$.isOpen) this.#openPopover();
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
get state() {
|
|
@@ -137,7 +149,7 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
137
149
|
|
|
138
150
|
#onInput = (event) => {
|
|
139
151
|
let input = event.target.closest?.('[data-theme-control]');
|
|
140
|
-
if (!input || !this
|
|
152
|
+
if (!input || !this.#elementTargetsWidget(input)) return;
|
|
141
153
|
this.#state = normalizeCascadeThemeOptions({
|
|
142
154
|
...this.#state,
|
|
143
155
|
[input.dataset.themeControl]: Number(input.value),
|
|
@@ -147,7 +159,7 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
147
159
|
|
|
148
160
|
#onClick = (event) => {
|
|
149
161
|
let modeButton = event.target.closest?.('[data-theme-mode]');
|
|
150
|
-
if (modeButton && this
|
|
162
|
+
if (modeButton && this.#elementTargetsWidget(modeButton)) {
|
|
151
163
|
this.#state = normalizeCascadeThemeOptions({
|
|
152
164
|
...this.#state,
|
|
153
165
|
mode: modeButton.dataset.themeMode,
|
|
@@ -163,7 +175,7 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
163
175
|
} else if (action === 'reset') {
|
|
164
176
|
this.reset();
|
|
165
177
|
} else if (action === 'open-full') {
|
|
166
|
-
this
|
|
178
|
+
this.#setOpen(false);
|
|
167
179
|
this.dispatchEvent(new CustomEvent('cascade-theme-open-full', {
|
|
168
180
|
bubbles: true,
|
|
169
181
|
composed: true,
|
|
@@ -172,6 +184,108 @@ export class CascadeThemeWidget extends Symbiote {
|
|
|
172
184
|
}
|
|
173
185
|
};
|
|
174
186
|
|
|
187
|
+
#bindPopoverEvents() {
|
|
188
|
+
let popover = this.ref.popover;
|
|
189
|
+
if (!popover || this.#popoverBound) return;
|
|
190
|
+
popover.addEventListener('input', this.#onInput);
|
|
191
|
+
popover.addEventListener('click', this.#onClick);
|
|
192
|
+
this.#popoverBound = true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#unbindPopoverEvents() {
|
|
196
|
+
let popover = this.ref.popover;
|
|
197
|
+
if (!popover || !this.#popoverBound) return;
|
|
198
|
+
popover.removeEventListener('input', this.#onInput);
|
|
199
|
+
popover.removeEventListener('click', this.#onClick);
|
|
200
|
+
this.#popoverBound = false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#setOpen(open) {
|
|
204
|
+
let nextOpen = Boolean(open);
|
|
205
|
+
if (nextOpen === this.$.isOpen) {
|
|
206
|
+
if (nextOpen) this.#openPopover();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.$.isOpen = nextOpen;
|
|
210
|
+
if (nextOpen) {
|
|
211
|
+
this.#openPopover();
|
|
212
|
+
} else {
|
|
213
|
+
this.#closePopover();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#openPopover() {
|
|
218
|
+
let popover = this.ref.popover;
|
|
219
|
+
if (!popover) return;
|
|
220
|
+
this.#bindPopoverEvents();
|
|
221
|
+
popover.hidden = false;
|
|
222
|
+
mountOverlayToDocument(popover, this);
|
|
223
|
+
bringOverlayToFront(popover);
|
|
224
|
+
this.#positionPopover();
|
|
225
|
+
this.#bindOverlayListeners();
|
|
226
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
227
|
+
requestAnimationFrame(() => {
|
|
228
|
+
if (this.$.isOpen) this.#positionPopover();
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#closePopover() {
|
|
234
|
+
let popover = this.ref.popover;
|
|
235
|
+
this.#unbindOverlayListeners();
|
|
236
|
+
if (!popover) return;
|
|
237
|
+
popover.hidden = true;
|
|
238
|
+
popover.style.removeProperty('top');
|
|
239
|
+
popover.style.removeProperty('left');
|
|
240
|
+
restoreOverlayHome(popover);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#bindOverlayListeners() {
|
|
244
|
+
if (this.#overlayListenersBound || typeof window === 'undefined') return;
|
|
245
|
+
window.addEventListener('resize', this.#onOverlayReposition);
|
|
246
|
+
window.addEventListener('scroll', this.#onOverlayReposition, true);
|
|
247
|
+
this.#overlayListenersBound = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#unbindOverlayListeners() {
|
|
251
|
+
if (!this.#overlayListenersBound || typeof window === 'undefined') return;
|
|
252
|
+
window.removeEventListener('resize', this.#onOverlayReposition);
|
|
253
|
+
window.removeEventListener('scroll', this.#onOverlayReposition, true);
|
|
254
|
+
this.#overlayListenersBound = false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#onOverlayReposition = () => {
|
|
258
|
+
if (this.$.isOpen) this.#positionPopover();
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
#positionPopover() {
|
|
262
|
+
let popover = this.ref.popover;
|
|
263
|
+
let trigger = this.ref.trigger || this.querySelector('.ctw-trigger');
|
|
264
|
+
if (!popover || !trigger || typeof window === 'undefined') return;
|
|
265
|
+
if (window.matchMedia?.('(max-width: 820px)')?.matches) {
|
|
266
|
+
popover.style.removeProperty('top');
|
|
267
|
+
popover.style.removeProperty('left');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let offset = Number.parseFloat(
|
|
271
|
+
getComputedStyle(trigger).getPropertyValue('--sn-theme-widget-offset') || '8'
|
|
272
|
+
);
|
|
273
|
+
positionOverlay(trigger, popover, 'bottom-end', {
|
|
274
|
+
offset: Number.isFinite(offset) ? offset : 8,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
#eventTargetsWidget(event) {
|
|
279
|
+
let path = event.composedPath?.() || [];
|
|
280
|
+
let popover = this.ref.popover;
|
|
281
|
+
return path.includes(this) || (popover && (path.includes(popover) || popover.contains(event.target)));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#elementTargetsWidget(element) {
|
|
285
|
+
let popover = this.ref.popover;
|
|
286
|
+
return this.contains(element) || Boolean(popover?.contains(element));
|
|
287
|
+
}
|
|
288
|
+
|
|
175
289
|
#renderControls() {
|
|
176
290
|
let controls = this.ref.controls;
|
|
177
291
|
if (!controls) return;
|
|
@@ -2,6 +2,7 @@ import { html } from '@symbiotejs/symbiote';
|
|
|
2
2
|
|
|
3
3
|
export default html`
|
|
4
4
|
<button
|
|
5
|
+
ref="trigger"
|
|
5
6
|
class="ctw-trigger shell-action"
|
|
6
7
|
type="button"
|
|
7
8
|
aria-haspopup="dialog"
|
|
@@ -15,7 +16,13 @@ export default html`
|
|
|
15
16
|
<span class="material-symbols-outlined" aria-hidden="true">palette</span>
|
|
16
17
|
<span class="ctw-trigger-label">Theme</span>
|
|
17
18
|
</button>
|
|
18
|
-
<section
|
|
19
|
+
<section
|
|
20
|
+
ref="popover"
|
|
21
|
+
class="ctw-popover"
|
|
22
|
+
role="dialog"
|
|
23
|
+
aria-label="Theme quick controls"
|
|
24
|
+
${{ '@hidden': '!isOpen' }}
|
|
25
|
+
>
|
|
19
26
|
<header class="ctw-header">
|
|
20
27
|
<strong>Theme</strong>
|
|
21
28
|
<div class="ctw-header-actions">
|