stimeo-ui 0.17.0 → 0.18.0
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/CHANGELOG.md +106 -1
- data/README.md +19 -10
- data/dist/cable/index.js +22 -14
- data/dist/controllers/accordion_controller.js +14 -6
- data/dist/controllers/alert_dialog_controller.js +145 -118
- data/dist/controllers/announcer_controller.js +113 -17
- data/dist/controllers/auto_submit_controller.js +185 -60
- data/dist/controllers/avatar_controller.js +55 -43
- data/dist/controllers/bulk_select_controller.js +64 -50
- data/dist/controllers/calendar_controller.js +55 -53
- data/dist/controllers/carousel_controller.js +124 -86
- data/dist/controllers/character_counter_controller.js +291 -95
- data/dist/controllers/checkbox_controller.js +6 -0
- data/dist/controllers/clipboard_controller.js +118 -27
- data/dist/controllers/collapsible_controller.js +52 -49
- data/dist/controllers/color_picker_controller.js +29 -7
- data/dist/controllers/combobox_controller.js +210 -71
- data/dist/controllers/command_palette_controller.js +289 -140
- data/dist/controllers/conditional_fields_controller.js +145 -58
- data/dist/controllers/confirm_controller.js +145 -118
- data/dist/controllers/context_menu_controller.js +138 -73
- data/dist/controllers/countdown_controller.js +109 -21
- data/dist/controllers/currency_input_controller.js +446 -72
- data/dist/controllers/data_grid_controller.js +134 -28
- data/dist/controllers/date_range_picker_controller.js +93 -72
- data/dist/controllers/dialog_controller.js +145 -118
- data/dist/controllers/direct_upload_controller.js +24 -14
- data/dist/controllers/dirty_form_controller.js +68 -31
- data/dist/controllers/dismissible_controller.js +64 -46
- data/dist/controllers/drawer_controller.js +145 -118
- data/dist/controllers/dropdown_controller.js +76 -63
- data/dist/controllers/editable_controller.js +199 -62
- data/dist/controllers/empty_state_controller.js +13 -6
- data/dist/controllers/file_dropzone_controller.js +55 -48
- data/dist/controllers/filter_controller.js +9 -2
- data/dist/controllers/flash_controller.js +66 -33
- data/dist/controllers/focus_controller.js +39 -14
- data/dist/controllers/form_field_controller.js +52 -43
- data/dist/controllers/form_validation_controller.js +18 -11
- data/dist/controllers/frame_loading_controller.js +55 -48
- data/dist/controllers/hover_card_controller.js +67 -61
- data/dist/controllers/idle_controller.js +10 -6
- data/dist/controllers/input_mask_controller.js +411 -24
- data/dist/controllers/intersection_controller.js +3 -0
- data/dist/controllers/lazy_frame_controller.js +15 -3
- data/dist/controllers/listbox_controller.js +260 -79
- data/dist/controllers/local_time_controller.js +48 -13
- data/dist/controllers/masonry_controller.js +11 -4
- data/dist/controllers/menu_controller.js +138 -82
- data/dist/controllers/menubar_controller.js +65 -19
- data/dist/controllers/meter_controller.js +270 -24
- data/dist/controllers/multi_select_controller.js +251 -94
- data/dist/controllers/navigation_menu_controller.js +67 -19
- data/dist/controllers/nested_form_controller.js +98 -64
- data/dist/controllers/network_status_controller.js +70 -52
- data/dist/controllers/number_input_controller.js +344 -94
- data/dist/controllers/optimistic_controller.js +29 -11
- data/dist/controllers/otp_controller.js +502 -104
- data/dist/controllers/overflow_indicator_controller.js +6 -0
- data/dist/controllers/overflow_menu_controller.js +136 -21
- data/dist/controllers/pagination_controller.js +47 -14
- data/dist/controllers/password_reveal_controller.js +52 -49
- data/dist/controllers/password_strength_controller.js +13 -6
- data/dist/controllers/persist_controller.js +48 -128
- data/dist/controllers/pointer_drag_controller.js +249 -23
- data/dist/controllers/popover_controller.js +82 -65
- data/dist/controllers/portal_controller.js +6 -0
- data/dist/controllers/preview_guard_controller.js +14 -3
- data/dist/controllers/progress_controller.js +271 -27
- data/dist/controllers/radio_group_controller.js +167 -80
- data/dist/controllers/range_slider_controller.js +27 -7
- data/dist/controllers/rating_controller.js +93 -57
- data/dist/controllers/read_more_controller.js +10 -6
- data/dist/controllers/reading_progress_controller.js +22 -5
- data/dist/controllers/relative_time_controller.js +75 -11
- data/dist/controllers/reset_on_restore_controller.js +6 -0
- data/dist/controllers/resizable_controller.js +55 -22
- data/dist/controllers/roving_controller.js +10 -4
- data/dist/controllers/scroll_area_controller.js +129 -88
- data/dist/controllers/scroll_restore_controller.js +14 -2
- data/dist/controllers/scroll_visibility_controller.js +93 -60
- data/dist/controllers/scrollspy_controller.js +11 -5
- data/dist/controllers/separator_controller.js +59 -43
- data/dist/controllers/sidebar_controller.js +150 -122
- data/dist/controllers/skeleton_controller.js +55 -48
- data/dist/controllers/slider_controller.js +27 -6
- data/dist/controllers/smart_sticky_header_controller.js +13 -5
- data/dist/controllers/sortable_controller.js +27 -14
- data/dist/controllers/spinner_controller.js +55 -48
- data/dist/controllers/step_indicator_controller.js +6 -0
- data/dist/controllers/stepper_controller.js +6 -0
- data/dist/controllers/stick_to_bottom_controller.js +19 -8
- data/dist/controllers/sticky_observer_controller.js +49 -43
- data/dist/controllers/submit_once_controller.js +145 -85
- data/dist/controllers/switch_controller.js +172 -28
- data/dist/controllers/tabs_controller.js +6 -1
- data/dist/controllers/tags_input_controller.js +157 -28
- data/dist/controllers/textarea_autosize_controller.js +40 -19
- data/dist/controllers/theme_controller.js +10 -4
- data/dist/controllers/time_picker_controller.js +192 -54
- data/dist/controllers/toast_controller.js +58 -27
- data/dist/controllers/toggle_group_controller.js +158 -56
- data/dist/controllers/toolbar_controller.js +150 -21
- data/dist/controllers/tree_view_controller.js +134 -25
- data/dist/index.js +2451 -1312
- data/dist/positioning/index.js +6 -0
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -4,6 +4,9 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
4
4
|
if (!event.key.startsWith("Arrow")) return false;
|
|
5
5
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
6
6
|
}
|
|
7
|
+
function hasModifierChord(event) {
|
|
8
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
9
|
+
}
|
|
7
10
|
|
|
8
11
|
function ownerIndex(candidates, node) {
|
|
9
12
|
if (!(node instanceof Node)) return -1;
|
|
@@ -37,6 +40,9 @@ var MicrotaskCoalescer = class {
|
|
|
37
40
|
this.#queued = false;
|
|
38
41
|
this.#generation += 1;
|
|
39
42
|
}
|
|
43
|
+
get pending() {
|
|
44
|
+
return this.#queued;
|
|
45
|
+
}
|
|
40
46
|
schedule() {
|
|
41
47
|
if (!this.#active || this.#queued) return;
|
|
42
48
|
this.#queued = true;
|
|
@@ -67,6 +73,9 @@ var MorphRenderWatcher = class {
|
|
|
67
73
|
schedule() {
|
|
68
74
|
this.#pass.schedule();
|
|
69
75
|
}
|
|
76
|
+
get pending() {
|
|
77
|
+
return this.#pass.pending;
|
|
78
|
+
}
|
|
70
79
|
disconnect() {
|
|
71
80
|
this.#listeners.dispose();
|
|
72
81
|
this.#pass.cancel();
|
|
@@ -119,6 +128,18 @@ var NumberValueReader = class {
|
|
|
119
128
|
}
|
|
120
129
|
};
|
|
121
130
|
|
|
131
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
132
|
+
if (element.getAttribute(name) === value) return false;
|
|
133
|
+
if (value === null) element.removeAttribute(name);
|
|
134
|
+
else element.setAttribute(name, value);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
function deepActiveElement(ownerDocument) {
|
|
138
|
+
let active = ownerDocument.activeElement;
|
|
139
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
140
|
+
return active;
|
|
141
|
+
}
|
|
142
|
+
|
|
122
143
|
function queryOpenDescendants(root, selector) {
|
|
123
144
|
const matches = [];
|
|
124
145
|
const roots = [root];
|
|
@@ -145,6 +166,9 @@ function sharedRegistry(key, create) {
|
|
|
145
166
|
return registry;
|
|
146
167
|
}
|
|
147
168
|
|
|
169
|
+
function holdsFocus(element) {
|
|
170
|
+
return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
|
|
171
|
+
}
|
|
148
172
|
var TabindexLoan = class _TabindexLoan {
|
|
149
173
|
static #registry = sharedRegistry(
|
|
150
174
|
"stimeo-ui.tabindex-loan.registry.v1",
|
|
@@ -152,19 +176,22 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
152
176
|
);
|
|
153
177
|
#value;
|
|
154
178
|
#record;
|
|
179
|
+
#preserveFocus;
|
|
155
180
|
#lent = /* @__PURE__ */ new Set();
|
|
156
|
-
constructor(value, owner) {
|
|
181
|
+
constructor(value, owner, options = {}) {
|
|
157
182
|
this.#value = value;
|
|
158
183
|
this.#record = `data-${owner}-tabindex-loan`;
|
|
184
|
+
this.#preserveFocus = options.preserveFocus === true;
|
|
159
185
|
_TabindexLoan.#registry.names.add(this.#record);
|
|
160
186
|
}
|
|
161
|
-
lend(element) {
|
|
187
|
+
lend(element, options = {}) {
|
|
162
188
|
const stack = this.#stack(element);
|
|
163
189
|
const current = element.getAttribute("tabindex");
|
|
164
190
|
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
165
191
|
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
166
192
|
);
|
|
167
193
|
const linked = stack.at(-1)?.value === current;
|
|
194
|
+
if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
|
|
168
195
|
if (!held && current !== null && !linked) return;
|
|
169
196
|
this.#lent.add(element);
|
|
170
197
|
if (held) held.loan = this;
|
|
@@ -174,19 +201,28 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
174
201
|
element.setAttribute("tabindex", this.#value);
|
|
175
202
|
}
|
|
176
203
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
204
|
+
return(element) {
|
|
205
|
+
const stack = this.#stack(element);
|
|
206
|
+
const hold = stack.find((held) => held.loan === this) ?? stack.find(
|
|
207
|
+
(held) => held.loan === null && held.record === this.#record && held.value === this.#value
|
|
208
|
+
);
|
|
209
|
+
if (!hold) return false;
|
|
210
|
+
if (!this.#release(element, stack, hold, this.#preserveFocus)) {
|
|
211
|
+
hold.loan = this;
|
|
212
|
+
this.#lent.add(element);
|
|
213
|
+
return false;
|
|
182
214
|
}
|
|
183
|
-
this.#lent.
|
|
215
|
+
this.#lent.delete(element);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
returnAll() {
|
|
219
|
+
for (const element of Array.from(this.#lent)) this.return(element);
|
|
184
220
|
}
|
|
185
221
|
reclaim(element) {
|
|
186
222
|
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
187
223
|
const stack = this.#stack(element);
|
|
188
224
|
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
189
|
-
if (hold && !element
|
|
225
|
+
if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
|
|
190
226
|
}
|
|
191
227
|
reclaimWithin(root) {
|
|
192
228
|
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
@@ -210,26 +246,26 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
210
246
|
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
211
247
|
return holds;
|
|
212
248
|
}
|
|
213
|
-
#release(element, stack, hold) {
|
|
249
|
+
#release(element, stack, hold, preserveFocus = false) {
|
|
214
250
|
const current = element.getAttribute("tabindex");
|
|
215
251
|
const index = stack.indexOf(hold);
|
|
216
252
|
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
253
|
+
if (owns && preserveFocus && holdsFocus(element)) return false;
|
|
217
254
|
stack.splice(index, 1);
|
|
218
255
|
const above = stack[index];
|
|
219
256
|
if (above) above.linked &&= hold.linked;
|
|
220
257
|
this.#mark(element, stack);
|
|
221
|
-
if (!owns) return;
|
|
258
|
+
if (!owns) return true;
|
|
222
259
|
const below = hold.linked ? stack[index - 1] : void 0;
|
|
223
260
|
if (!below) element.removeAttribute("tabindex");
|
|
224
261
|
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
262
|
+
return true;
|
|
225
263
|
}
|
|
226
264
|
#mark(element, stack) {
|
|
227
265
|
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
228
266
|
for (const hold of stack) records.set(hold.record, hold.value);
|
|
229
267
|
for (const [name, recorded] of records) {
|
|
230
|
-
|
|
231
|
-
if (recorded === null) element.removeAttribute(name);
|
|
232
|
-
else element.setAttribute(name, recorded);
|
|
268
|
+
setAttributeIfChanged(element, name, recorded);
|
|
233
269
|
}
|
|
234
270
|
}
|
|
235
271
|
};
|
|
@@ -260,22 +296,18 @@ var ResizableController = class _ResizableController extends Controller {
|
|
|
260
296
|
#confirmed = DEFAULT_VALUE;
|
|
261
297
|
#drag = null;
|
|
262
298
|
#paneTabindex = new TabindexLoan("-1", this.identifier);
|
|
263
|
-
#
|
|
299
|
+
#listeners = new ListenerSet();
|
|
264
300
|
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
265
301
|
connect() {
|
|
266
302
|
this.#paneTabindex.reclaimWithin(this.element);
|
|
267
303
|
this.#repaint.observe(this.element);
|
|
268
304
|
this.#confirmed = this.#position;
|
|
269
305
|
this.#render();
|
|
270
|
-
this.#
|
|
271
|
-
this.element.addEventListener("keydown", this.#onCycleKeydown, {
|
|
272
|
-
signal: this.#cycleAbort.signal
|
|
273
|
-
});
|
|
306
|
+
this.#listeners.add(this.element, "keydown", this.#onCycleKeydown);
|
|
274
307
|
}
|
|
275
308
|
disconnect() {
|
|
276
309
|
this.#endDrag();
|
|
277
|
-
this.#
|
|
278
|
-
this.#cycleAbort = null;
|
|
310
|
+
this.#listeners.dispose();
|
|
279
311
|
this.#repaint.disconnect();
|
|
280
312
|
this.#paneTabindex.returnAll();
|
|
281
313
|
}
|
|
@@ -283,7 +315,7 @@ var ResizableController = class _ResizableController extends Controller {
|
|
|
283
315
|
if (event.key !== "F6") return;
|
|
284
316
|
if (event.isComposing) return;
|
|
285
317
|
if (event.defaultPrevented) return;
|
|
286
|
-
if (event
|
|
318
|
+
if (hasModifierChord(event)) return;
|
|
287
319
|
const panes = [];
|
|
288
320
|
if (this.hasPrimaryTarget) panes.push(this.primaryTarget);
|
|
289
321
|
if (this.hasSecondaryTarget) panes.push(this.secondaryTarget);
|
|
@@ -329,6 +361,7 @@ var ResizableController = class _ResizableController extends Controller {
|
|
|
329
361
|
}
|
|
330
362
|
onKeydown(event) {
|
|
331
363
|
if (isReservedArrowChord(event)) return;
|
|
364
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
332
365
|
if (!this.hasSeparatorTarget) return;
|
|
333
366
|
const { min, max } = this.#range;
|
|
334
367
|
const isVertical = this.#isVertical;
|
|
@@ -54,6 +54,9 @@ var MicrotaskCoalescer = class {
|
|
|
54
54
|
this.#queued = false;
|
|
55
55
|
this.#generation += 1;
|
|
56
56
|
}
|
|
57
|
+
get pending() {
|
|
58
|
+
return this.#queued;
|
|
59
|
+
}
|
|
57
60
|
schedule() {
|
|
58
61
|
if (!this.#active || this.#queued) return;
|
|
59
62
|
this.#queued = true;
|
|
@@ -84,6 +87,9 @@ var MorphRenderWatcher = class {
|
|
|
84
87
|
schedule() {
|
|
85
88
|
this.#pass.schedule();
|
|
86
89
|
}
|
|
90
|
+
get pending() {
|
|
91
|
+
return this.#pass.pending;
|
|
92
|
+
}
|
|
87
93
|
disconnect() {
|
|
88
94
|
this.#listeners.dispose();
|
|
89
95
|
this.#pass.cancel();
|
|
@@ -128,10 +134,11 @@ var RovingController = class extends Controller {
|
|
|
128
134
|
#reconcile = new MorphRenderWatcher(() => this.#ensureTabStop(true));
|
|
129
135
|
#connected = false;
|
|
130
136
|
#observer = null;
|
|
137
|
+
#listeners = new ListenerSet();
|
|
131
138
|
connect() {
|
|
132
139
|
this.#ensureTabStop(false);
|
|
133
|
-
this.element
|
|
134
|
-
this.element
|
|
140
|
+
this.#listeners.add(this.element, "keydown", this.#onKeydown);
|
|
141
|
+
this.#listeners.add(this.element, "focusin", this.#onFocusin);
|
|
135
142
|
this.#watchState();
|
|
136
143
|
this.#connected = true;
|
|
137
144
|
this.#reconcile.observe(this.element);
|
|
@@ -139,8 +146,7 @@ var RovingController = class extends Controller {
|
|
|
139
146
|
disconnect() {
|
|
140
147
|
this.#connected = false;
|
|
141
148
|
this.#reconcile.disconnect();
|
|
142
|
-
this.
|
|
143
|
-
this.element.removeEventListener("focusin", this.#onFocusin);
|
|
149
|
+
this.#listeners.dispose();
|
|
144
150
|
this.#observer?.disconnect();
|
|
145
151
|
this.#observer = null;
|
|
146
152
|
}
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
4
|
+
if (element.getAttribute(name) === value) return false;
|
|
5
|
+
if (value === null) element.removeAttribute(name);
|
|
6
|
+
else element.setAttribute(name, value);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
function sharedRegistry(key, create) {
|
|
4
11
|
const symbol = Symbol.for(key);
|
|
5
12
|
const scope = globalThis;
|
|
6
13
|
const existing = scope[symbol];
|
|
7
14
|
if (existing !== void 0) return existing;
|
|
8
|
-
const
|
|
9
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
10
|
-
return
|
|
15
|
+
const registry2 = create();
|
|
16
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
17
|
+
return registry2;
|
|
11
18
|
}
|
|
12
19
|
|
|
20
|
+
var registry = sharedRegistry(
|
|
21
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
22
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
23
|
+
);
|
|
13
24
|
function isAttributeValue(value) {
|
|
14
25
|
return value === null || typeof value === "string";
|
|
15
26
|
}
|
|
@@ -26,24 +37,46 @@ function readLeaseRecord(element, record) {
|
|
|
26
37
|
return void 0;
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
40
|
+
function attributeStack(element, attribute) {
|
|
41
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
42
|
+
registry.stacks.set(element, byAttribute);
|
|
43
|
+
const known = byAttribute.get(attribute);
|
|
44
|
+
if (known) return known;
|
|
45
|
+
const found = [];
|
|
46
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
47
|
+
const read = readLeaseRecord(element, record);
|
|
48
|
+
if (read) found.push({ ...read, record });
|
|
49
|
+
}
|
|
50
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
51
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
52
|
+
lease: null,
|
|
53
|
+
record,
|
|
54
|
+
written,
|
|
55
|
+
original,
|
|
56
|
+
linked: linked && index > 0
|
|
57
|
+
}));
|
|
58
|
+
byAttribute.set(attribute, stack);
|
|
59
|
+
return stack;
|
|
60
|
+
}
|
|
61
|
+
function findHold(stack, lease, record) {
|
|
62
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
63
|
+
}
|
|
64
|
+
var AttributeLease = class {
|
|
34
65
|
#attribute;
|
|
35
66
|
#record;
|
|
36
67
|
#base;
|
|
68
|
+
#preserveAuthor;
|
|
37
69
|
#held = /* @__PURE__ */ new Set();
|
|
38
70
|
constructor(attribute, owner, options = {}) {
|
|
39
71
|
this.#attribute = attribute;
|
|
40
72
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
41
73
|
this.#base = options.base === true;
|
|
42
|
-
|
|
74
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
75
|
+
const names = registry.names;
|
|
43
76
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
44
77
|
}
|
|
45
78
|
write(element, value) {
|
|
46
|
-
const stack = this.#
|
|
79
|
+
const stack = attributeStack(element, this.#attribute);
|
|
47
80
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
48
81
|
this.#held.add(element);
|
|
49
82
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -53,8 +86,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
53
86
|
}
|
|
54
87
|
return(element) {
|
|
55
88
|
const held = this.#held.delete(element);
|
|
56
|
-
const known =
|
|
57
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
89
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
90
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
58
91
|
const hold = this.#take(stack);
|
|
59
92
|
if (!hold) return false;
|
|
60
93
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -72,31 +105,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
72
105
|
returnAll() {
|
|
73
106
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
74
107
|
}
|
|
75
|
-
#stack(element) {
|
|
76
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
77
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
78
|
-
stacks.set(element, byAttribute);
|
|
79
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
80
|
-
byAttribute.set(this.#attribute, stack);
|
|
81
|
-
return stack;
|
|
82
|
-
}
|
|
83
|
-
#seed(element) {
|
|
84
|
-
const found = [];
|
|
85
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
86
|
-
const read = readLeaseRecord(element, record);
|
|
87
|
-
if (read) found.push({ ...read, record });
|
|
88
|
-
}
|
|
89
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
90
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
91
|
-
lease: null,
|
|
92
|
-
record,
|
|
93
|
-
written,
|
|
94
|
-
original,
|
|
95
|
-
linked: linked && index > 0
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
108
|
#take(stack) {
|
|
99
|
-
const hold = stack
|
|
109
|
+
const hold = findHold(stack, this, this.#record);
|
|
100
110
|
if (hold) hold.lease = this;
|
|
101
111
|
return hold;
|
|
102
112
|
}
|
|
@@ -105,7 +115,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
105
115
|
const top = stack.at(-1);
|
|
106
116
|
const bottom = stack[0];
|
|
107
117
|
const over = top?.written === current ? top : void 0;
|
|
108
|
-
const from = this.#base ? bottom : over;
|
|
118
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
109
119
|
const hold = {
|
|
110
120
|
lease: this,
|
|
111
121
|
record: this.#record,
|
|
@@ -135,15 +145,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
135
145
|
);
|
|
136
146
|
});
|
|
137
147
|
for (const [name, recorded] of records) {
|
|
138
|
-
|
|
139
|
-
if (recorded === null) element.removeAttribute(name);
|
|
140
|
-
else element.setAttribute(name, recorded);
|
|
148
|
+
setAttributeIfChanged(element, name, recorded);
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
151
|
#reflect(element, value) {
|
|
144
|
-
|
|
145
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
146
|
-
else element.setAttribute(this.#attribute, value);
|
|
152
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
147
153
|
}
|
|
148
154
|
};
|
|
149
155
|
|
|
@@ -234,6 +240,44 @@ function firstTabStop(root) {
|
|
|
234
240
|
function hasTabStop(root) {
|
|
235
241
|
return firstTabStop(root) !== null;
|
|
236
242
|
}
|
|
243
|
+
function deepActiveElement(ownerDocument) {
|
|
244
|
+
let active = ownerDocument.activeElement;
|
|
245
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
246
|
+
return active;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var ListenerSet = class {
|
|
250
|
+
#abort = new AbortController();
|
|
251
|
+
add(target, type, handler, options) {
|
|
252
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
253
|
+
}
|
|
254
|
+
dispose() {
|
|
255
|
+
this.#abort.abort();
|
|
256
|
+
this.#abort = new AbortController();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
var FontLoadWatcher = class {
|
|
261
|
+
#listeners = new ListenerSet();
|
|
262
|
+
#onSettled;
|
|
263
|
+
#fonts = null;
|
|
264
|
+
constructor(run) {
|
|
265
|
+
this.#onSettled = () => run();
|
|
266
|
+
}
|
|
267
|
+
observe(element) {
|
|
268
|
+
const fonts = element.ownerDocument.fonts ?? null;
|
|
269
|
+
if (fonts === this.#fonts) return;
|
|
270
|
+
this.disconnect();
|
|
271
|
+
if (!fonts) return;
|
|
272
|
+
this.#fonts = fonts;
|
|
273
|
+
this.#listeners.add(fonts, "loadingdone", this.#onSettled);
|
|
274
|
+
this.#listeners.add(fonts, "loadingerror", this.#onSettled);
|
|
275
|
+
}
|
|
276
|
+
disconnect() {
|
|
277
|
+
this.#listeners.dispose();
|
|
278
|
+
this.#fonts = null;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
237
281
|
|
|
238
282
|
var FrameCoalescer = class {
|
|
239
283
|
#frame = null;
|
|
@@ -319,17 +363,6 @@ function logicalScrollMetrics(element, horizontal) {
|
|
|
319
363
|
return { position: Math.min(max, Math.max(0, position)), max };
|
|
320
364
|
}
|
|
321
365
|
|
|
322
|
-
var ListenerSet = class {
|
|
323
|
-
#abort = new AbortController();
|
|
324
|
-
add(target, type, handler, options) {
|
|
325
|
-
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
326
|
-
}
|
|
327
|
-
dispose() {
|
|
328
|
-
this.#abort.abort();
|
|
329
|
-
this.#abort = new AbortController();
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
366
|
var MicrotaskCoalescer = class {
|
|
334
367
|
#run;
|
|
335
368
|
#queued = false;
|
|
@@ -346,6 +379,9 @@ var MicrotaskCoalescer = class {
|
|
|
346
379
|
this.#queued = false;
|
|
347
380
|
this.#generation += 1;
|
|
348
381
|
}
|
|
382
|
+
get pending() {
|
|
383
|
+
return this.#queued;
|
|
384
|
+
}
|
|
349
385
|
schedule() {
|
|
350
386
|
if (!this.#active || this.#queued) return;
|
|
351
387
|
this.#queued = true;
|
|
@@ -376,6 +412,9 @@ var MorphRenderWatcher = class {
|
|
|
376
412
|
schedule() {
|
|
377
413
|
this.#pass.schedule();
|
|
378
414
|
}
|
|
415
|
+
get pending() {
|
|
416
|
+
return this.#pass.pending;
|
|
417
|
+
}
|
|
379
418
|
disconnect() {
|
|
380
419
|
this.#listeners.dispose();
|
|
381
420
|
this.#pass.cancel();
|
|
@@ -512,9 +551,7 @@ var StylePropertyLease = class _StylePropertyLease {
|
|
|
512
551
|
);
|
|
513
552
|
});
|
|
514
553
|
for (const [name, recorded] of records) {
|
|
515
|
-
|
|
516
|
-
if (recorded === null) element.removeAttribute(name);
|
|
517
|
-
else element.setAttribute(name, recorded);
|
|
554
|
+
setAttributeIfChanged(element, name, recorded);
|
|
518
555
|
}
|
|
519
556
|
}
|
|
520
557
|
#carries(element, hold) {
|
|
@@ -547,6 +584,9 @@ function queryOpenDescendants(root, selector) {
|
|
|
547
584
|
return matches;
|
|
548
585
|
}
|
|
549
586
|
|
|
587
|
+
function holdsFocus(element) {
|
|
588
|
+
return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
|
|
589
|
+
}
|
|
550
590
|
var TabindexLoan = class _TabindexLoan {
|
|
551
591
|
static #registry = sharedRegistry(
|
|
552
592
|
"stimeo-ui.tabindex-loan.registry.v1",
|
|
@@ -554,19 +594,22 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
554
594
|
);
|
|
555
595
|
#value;
|
|
556
596
|
#record;
|
|
597
|
+
#preserveFocus;
|
|
557
598
|
#lent = /* @__PURE__ */ new Set();
|
|
558
|
-
constructor(value, owner) {
|
|
599
|
+
constructor(value, owner, options = {}) {
|
|
559
600
|
this.#value = value;
|
|
560
601
|
this.#record = `data-${owner}-tabindex-loan`;
|
|
602
|
+
this.#preserveFocus = options.preserveFocus === true;
|
|
561
603
|
_TabindexLoan.#registry.names.add(this.#record);
|
|
562
604
|
}
|
|
563
|
-
lend(element) {
|
|
605
|
+
lend(element, options = {}) {
|
|
564
606
|
const stack = this.#stack(element);
|
|
565
607
|
const current = element.getAttribute("tabindex");
|
|
566
608
|
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
567
609
|
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
568
610
|
);
|
|
569
611
|
const linked = stack.at(-1)?.value === current;
|
|
612
|
+
if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
|
|
570
613
|
if (!held && current !== null && !linked) return;
|
|
571
614
|
this.#lent.add(element);
|
|
572
615
|
if (held) held.loan = this;
|
|
@@ -576,19 +619,28 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
576
619
|
element.setAttribute("tabindex", this.#value);
|
|
577
620
|
}
|
|
578
621
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
622
|
+
return(element) {
|
|
623
|
+
const stack = this.#stack(element);
|
|
624
|
+
const hold = stack.find((held) => held.loan === this) ?? stack.find(
|
|
625
|
+
(held) => held.loan === null && held.record === this.#record && held.value === this.#value
|
|
626
|
+
);
|
|
627
|
+
if (!hold) return false;
|
|
628
|
+
if (!this.#release(element, stack, hold, this.#preserveFocus)) {
|
|
629
|
+
hold.loan = this;
|
|
630
|
+
this.#lent.add(element);
|
|
631
|
+
return false;
|
|
584
632
|
}
|
|
585
|
-
this.#lent.
|
|
633
|
+
this.#lent.delete(element);
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
returnAll() {
|
|
637
|
+
for (const element of Array.from(this.#lent)) this.return(element);
|
|
586
638
|
}
|
|
587
639
|
reclaim(element) {
|
|
588
640
|
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
589
641
|
const stack = this.#stack(element);
|
|
590
642
|
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
591
|
-
if (hold && !element
|
|
643
|
+
if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
|
|
592
644
|
}
|
|
593
645
|
reclaimWithin(root) {
|
|
594
646
|
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
@@ -612,26 +664,26 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
612
664
|
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
613
665
|
return holds;
|
|
614
666
|
}
|
|
615
|
-
#release(element, stack, hold) {
|
|
667
|
+
#release(element, stack, hold, preserveFocus = false) {
|
|
616
668
|
const current = element.getAttribute("tabindex");
|
|
617
669
|
const index = stack.indexOf(hold);
|
|
618
670
|
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
671
|
+
if (owns && preserveFocus && holdsFocus(element)) return false;
|
|
619
672
|
stack.splice(index, 1);
|
|
620
673
|
const above = stack[index];
|
|
621
674
|
if (above) above.linked &&= hold.linked;
|
|
622
675
|
this.#mark(element, stack);
|
|
623
|
-
if (!owns) return;
|
|
676
|
+
if (!owns) return true;
|
|
624
677
|
const below = hold.linked ? stack[index - 1] : void 0;
|
|
625
678
|
if (!below) element.removeAttribute("tabindex");
|
|
626
679
|
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
680
|
+
return true;
|
|
627
681
|
}
|
|
628
682
|
#mark(element, stack) {
|
|
629
683
|
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
630
684
|
for (const hold of stack) records.set(hold.record, hold.value);
|
|
631
685
|
for (const [name, recorded] of records) {
|
|
632
|
-
|
|
633
|
-
if (recorded === null) element.removeAttribute(name);
|
|
634
|
-
else element.setAttribute(name, recorded);
|
|
686
|
+
setAttributeIfChanged(element, name, recorded);
|
|
635
687
|
}
|
|
636
688
|
}
|
|
637
689
|
};
|
|
@@ -654,12 +706,12 @@ var ScrollAreaController = class extends Controller {
|
|
|
654
706
|
this.identifier
|
|
655
707
|
);
|
|
656
708
|
#viewport = null;
|
|
709
|
+
#viewportListeners = new ListenerSet();
|
|
657
710
|
#content = null;
|
|
658
711
|
#nameSources = null;
|
|
659
712
|
#nameGraph = null;
|
|
660
713
|
#observedNameIds = /* @__PURE__ */ new Set();
|
|
661
714
|
#observedNameSources = [];
|
|
662
|
-
#fonts = null;
|
|
663
715
|
#frames = new FrameCoalescer();
|
|
664
716
|
#overflowing = false;
|
|
665
717
|
#lastEdge = null;
|
|
@@ -671,17 +723,18 @@ var ScrollAreaController = class extends Controller {
|
|
|
671
723
|
#onFontsSettled = () => {
|
|
672
724
|
this.#refresh();
|
|
673
725
|
};
|
|
726
|
+
#fonts = new FontLoadWatcher(this.#onFontsSettled);
|
|
674
727
|
connect() {
|
|
675
728
|
this.#rebind.observe(this.element);
|
|
676
729
|
this.#layout.observeViewport();
|
|
677
|
-
this.#
|
|
730
|
+
this.#fonts.observe(this.element);
|
|
678
731
|
this.#syncViewport();
|
|
679
732
|
}
|
|
680
733
|
disconnect() {
|
|
681
734
|
this.#rebind.disconnect();
|
|
682
735
|
if (this.#viewport) this.#unbindViewport(this.#viewport);
|
|
683
736
|
this.#layout.disconnect();
|
|
684
|
-
this.#
|
|
737
|
+
this.#fonts.disconnect();
|
|
685
738
|
this.#clearHostState();
|
|
686
739
|
}
|
|
687
740
|
orientationValueChanged() {
|
|
@@ -705,7 +758,7 @@ var ScrollAreaController = class extends Controller {
|
|
|
705
758
|
this.#clearHostState();
|
|
706
759
|
return;
|
|
707
760
|
}
|
|
708
|
-
|
|
761
|
+
this.#viewportListeners.add(next, "scroll", this.#onScroll, { passive: true });
|
|
709
762
|
this.#layout.observeDescendantLoads(next);
|
|
710
763
|
this.#layout.observe(next);
|
|
711
764
|
this.#bindContentObserver(next);
|
|
@@ -739,7 +792,7 @@ var ScrollAreaController = class extends Controller {
|
|
|
739
792
|
}
|
|
740
793
|
#unbindViewport(viewport) {
|
|
741
794
|
this.#frames.cancel();
|
|
742
|
-
|
|
795
|
+
this.#viewportListeners.dispose();
|
|
743
796
|
this.#layout.unobserveDescendantLoads();
|
|
744
797
|
this.#layout.unobserve(viewport);
|
|
745
798
|
this.#content?.disconnect();
|
|
@@ -935,18 +988,6 @@ var ScrollAreaController = class extends Controller {
|
|
|
935
988
|
this.#observedNameIds.clear();
|
|
936
989
|
this.#observedNameSources = [];
|
|
937
990
|
}
|
|
938
|
-
#bindFonts() {
|
|
939
|
-
const fonts = this.element.ownerDocument.fonts;
|
|
940
|
-
if (!fonts || this.#fonts === fonts) return;
|
|
941
|
-
this.#fonts = fonts;
|
|
942
|
-
fonts.addEventListener("loadingdone", this.#onFontsSettled);
|
|
943
|
-
fonts.addEventListener("loadingerror", this.#onFontsSettled);
|
|
944
|
-
}
|
|
945
|
-
#unbindFonts() {
|
|
946
|
-
this.#fonts?.removeEventListener("loadingdone", this.#onFontsSettled);
|
|
947
|
-
this.#fonts?.removeEventListener("loadingerror", this.#onFontsSettled);
|
|
948
|
-
this.#fonts = null;
|
|
949
|
-
}
|
|
950
991
|
};
|
|
951
992
|
|
|
952
993
|
export { ScrollAreaController };
|
|
@@ -16,6 +16,17 @@ var FrameCoalescer = class {
|
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
var ListenerSet = class {
|
|
20
|
+
#abort = new AbortController();
|
|
21
|
+
add(target, type, handler, options) {
|
|
22
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
23
|
+
}
|
|
24
|
+
dispose() {
|
|
25
|
+
this.#abort.abort();
|
|
26
|
+
this.#abort = new AbortController();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
19
30
|
function parseStored(raw) {
|
|
20
31
|
if (raw === null) return null;
|
|
21
32
|
try {
|
|
@@ -46,15 +57,16 @@ var ScrollRestoreController = class extends Controller {
|
|
|
46
57
|
this.#capture();
|
|
47
58
|
this.#frames.schedule(() => this.#persist());
|
|
48
59
|
};
|
|
60
|
+
#listeners = new ListenerSet();
|
|
49
61
|
connect() {
|
|
50
62
|
this.#connected = true;
|
|
51
63
|
this.#storageKey = this.#resolveKey();
|
|
52
|
-
this.element
|
|
64
|
+
this.#listeners.add(this.element, "scroll", this.#onScroll, { passive: true });
|
|
53
65
|
if (this.#storageKey) this.#restore();
|
|
54
66
|
}
|
|
55
67
|
disconnect() {
|
|
56
68
|
this.#connected = false;
|
|
57
|
-
this.
|
|
69
|
+
this.#listeners.dispose();
|
|
58
70
|
this.#frames.cancel();
|
|
59
71
|
this.#persist();
|
|
60
72
|
}
|