stimeo-ui 0.13.0 → 0.15.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 +73 -5
- data/dist/cable/index.js +60 -14
- data/dist/controllers/announcer_controller.js +88 -61
- data/dist/controllers/auto_submit_controller.js +40 -5
- data/dist/controllers/breadcrumb_controller.js +29 -3
- data/dist/controllers/calendar_controller.js +51 -10
- data/dist/controllers/carousel_controller.js +8 -6
- data/dist/controllers/checkbox_controller.js +27 -2
- data/dist/controllers/conditional_fields_controller.js +36 -7
- data/dist/controllers/currency_input_controller.js +77 -25
- data/dist/controllers/date_range_picker_controller.js +43 -10
- data/dist/controllers/direct_upload_controller.js +21 -10
- data/dist/controllers/dirty_form_controller.js +32 -12
- data/dist/controllers/flash_controller.js +145 -43
- data/dist/controllers/form_field_controller.js +7 -1
- data/dist/controllers/frame_loading_controller.js +29 -6
- data/dist/controllers/highlight_controller.js +63 -23
- data/dist/controllers/input_mask_controller.js +33 -12
- data/dist/controllers/intersection_controller.js +10 -5
- data/dist/controllers/lazy_frame_controller.js +10 -5
- data/dist/controllers/local_time_controller.js +43 -11
- data/dist/controllers/masonry_controller.js +32 -14
- data/dist/controllers/menu_controller.js +10 -2
- data/dist/controllers/menubar_controller.js +10 -2
- data/dist/controllers/navigation_menu_controller.js +8 -2
- data/dist/controllers/optimistic_controller.js +28 -4
- data/dist/controllers/otp_controller.js +23 -10
- data/dist/controllers/overflow_indicator_controller.js +31 -8
- data/dist/controllers/overflow_menu_controller.js +39 -4
- data/dist/controllers/pointer_drag_controller.js +10 -3
- data/dist/controllers/portal_controller.js +21 -10
- data/dist/controllers/radio_group_controller.js +11 -3
- data/dist/controllers/read_more_controller.js +31 -5
- data/dist/controllers/reading_progress_controller.js +65 -19
- data/dist/controllers/relative_time_controller.js +40 -9
- data/dist/controllers/reset_before_cache_controller.js +23 -10
- data/dist/controllers/resizable_controller.js +7 -2
- data/dist/controllers/roving_controller.js +7 -3
- data/dist/controllers/scroll_area_controller.js +70 -26
- data/dist/controllers/scroll_restore_controller.js +37 -16
- data/dist/controllers/scroll_visibility_controller.js +71 -42
- data/dist/controllers/scrollspy_controller.js +76 -32
- data/dist/controllers/smart_sticky_header_controller.js +72 -37
- data/dist/controllers/stick_to_bottom_controller.js +29 -3
- data/dist/controllers/sticky_observer_controller.js +10 -5
- data/dist/controllers/submit_once_controller.js +29 -6
- data/dist/controllers/textarea_autosize_controller.js +29 -3
- data/dist/controllers/theme_controller.js +23 -13
- data/dist/controllers/toast_controller.js +136 -73
- data/dist/controllers/toggle_group_controller.js +11 -3
- data/dist/controllers/toolbar_controller.js +7 -3
- data/dist/index.js +629 -597
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -8,10 +8,15 @@ var LayoutObserver = class {
|
|
|
8
8
|
#resizeObserverFactory;
|
|
9
9
|
#resizeObserver = null;
|
|
10
10
|
#observingViewport = false;
|
|
11
|
+
#loadContainer = null;
|
|
11
12
|
/** Stable bound handler so add/removeEventListener target the same reference. */
|
|
12
13
|
#handleViewportResize = () => {
|
|
13
14
|
this.#callback();
|
|
14
15
|
};
|
|
16
|
+
/** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
|
|
17
|
+
#handleDescendantLoad = () => {
|
|
18
|
+
this.#callback();
|
|
19
|
+
};
|
|
15
20
|
constructor(callback, options = {}) {
|
|
16
21
|
this.#callback = callback;
|
|
17
22
|
this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
|
|
@@ -47,14 +52,35 @@ var LayoutObserver = class {
|
|
|
47
52
|
window.removeEventListener("resize", this.#handleViewportResize);
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
55
|
+
* Starts reporting a `load` from anywhere inside `container` — an image or a
|
|
56
|
+
* frame settling changes the box it sits in, and it measures as zero high until
|
|
57
|
+
* then. `load` does not bubble, so the subscription is a capture-phase listener
|
|
58
|
+
* on the container itself and nothing the caller spells.
|
|
59
|
+
*
|
|
60
|
+
* **One container at a time.** A further call moves the observation, so a widget
|
|
61
|
+
* whose content element is swapped at runtime releases the element it let go by
|
|
62
|
+
* naming the new one — there is no second place for the release to drift from.
|
|
63
|
+
*/
|
|
64
|
+
observeDescendantLoads(container) {
|
|
65
|
+
this.unobserveDescendantLoads();
|
|
66
|
+
this.#loadContainer = container;
|
|
67
|
+
container.addEventListener("load", this.#handleDescendantLoad, true);
|
|
68
|
+
}
|
|
69
|
+
/** Stops reporting descendant loads without affecting element or viewport observation. */
|
|
70
|
+
unobserveDescendantLoads() {
|
|
71
|
+
this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
|
|
72
|
+
this.#loadContainer = null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Releases every observation: disconnects the {@link ResizeObserver} and removes
|
|
76
|
+
* the viewport and descendant-load listeners. Safe to call multiple times. Call
|
|
77
|
+
* this from a controller's `disconnect()`.
|
|
53
78
|
*/
|
|
54
79
|
disconnect() {
|
|
55
80
|
this.#resizeObserver?.disconnect();
|
|
56
81
|
this.#resizeObserver = null;
|
|
57
82
|
this.unobserveViewport();
|
|
83
|
+
this.unobserveDescendantLoads();
|
|
58
84
|
}
|
|
59
85
|
};
|
|
60
86
|
|
|
@@ -23,6 +23,26 @@ function hasModifierChord(event) {
|
|
|
23
23
|
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// src/utils/declared_value.ts
|
|
27
|
+
function parseDeclared(raw, parse, fallback) {
|
|
28
|
+
try {
|
|
29
|
+
return parse(raw);
|
|
30
|
+
} catch {
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validSelector(element, raw, fallback) {
|
|
35
|
+
if (raw.length === 0) return fallback;
|
|
36
|
+
return parseDeclared(
|
|
37
|
+
raw,
|
|
38
|
+
(selector) => {
|
|
39
|
+
element.matches(selector);
|
|
40
|
+
return selector;
|
|
41
|
+
},
|
|
42
|
+
fallback
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
// src/utils/roving_tabindex.ts
|
|
27
47
|
var RovingTabindex = class {
|
|
28
48
|
/** Returns the current ordered item elements; called on every operation. */
|
|
@@ -167,16 +187,7 @@ var ThemeController = class extends Controller {
|
|
|
167
187
|
}
|
|
168
188
|
/** Validates the `target` declaration once, so the render path never parses. */
|
|
169
189
|
targetValueChanged() {
|
|
170
|
-
|
|
171
|
-
if (selector.length > 0) {
|
|
172
|
-
try {
|
|
173
|
-
this.element.matches(selector);
|
|
174
|
-
this.#targetSelector = selector;
|
|
175
|
-
return;
|
|
176
|
-
} catch {
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
this.#targetSelector = DEFAULT_TARGET;
|
|
190
|
+
this.#targetSelector = validSelector(this.element, this.targetValue, DEFAULT_TARGET);
|
|
180
191
|
}
|
|
181
192
|
/** Re-derives the single Tab stop and ARIA for an option set that changed. */
|
|
182
193
|
optionTargetConnected() {
|
|
@@ -189,9 +200,8 @@ var ThemeController = class extends Controller {
|
|
|
189
200
|
/**
|
|
190
201
|
* Selects the mode the activated option declares.
|
|
191
202
|
*
|
|
192
|
-
* Read through
|
|
193
|
-
*
|
|
194
|
-
* declares.
|
|
203
|
+
* Read through the same lane that decides which option is checked, so the two
|
|
204
|
+
* can never disagree about what an option declares.
|
|
195
205
|
*/
|
|
196
206
|
set(event) {
|
|
197
207
|
const option = event.currentTarget;
|
|
@@ -55,6 +55,121 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// src/utils/keyed_timers.ts
|
|
59
|
+
var KeyedTimers = class {
|
|
60
|
+
#timers = new SafeTimeout();
|
|
61
|
+
/** The pending timer of each key; an entry lives exactly as long as its timer. */
|
|
62
|
+
#pending = /* @__PURE__ */ new Map();
|
|
63
|
+
/**
|
|
64
|
+
* Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
|
|
65
|
+
* pending. The entry is dropped before the callback runs, so the callback sees
|
|
66
|
+
* the key unarmed and may arm it again for the next round.
|
|
67
|
+
*/
|
|
68
|
+
set(key, callback, delay) {
|
|
69
|
+
this.clear(key);
|
|
70
|
+
const id = this.#timers.set(() => {
|
|
71
|
+
this.#pending.delete(key);
|
|
72
|
+
callback();
|
|
73
|
+
}, delay);
|
|
74
|
+
this.#pending.set(key, id);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Cancels `key`'s pending timer, if it has one.
|
|
78
|
+
*
|
|
79
|
+
* A timer id is a positive integer, so `-1` stands for "nothing pending" and
|
|
80
|
+
* the registry ignores an id it does not own — the unarmed case needs no
|
|
81
|
+
* branch of its own, and no other key's timer can be reached from here.
|
|
82
|
+
*/
|
|
83
|
+
clear(key) {
|
|
84
|
+
this.#timers.clear(this.#pending.get(key) ?? -1);
|
|
85
|
+
this.#pending.delete(key);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Cancels every pending timer and forgets every key. Call this from a
|
|
89
|
+
* controller's `disconnect()` so no timer, and no entry, outlives the element.
|
|
90
|
+
*/
|
|
91
|
+
clearAll() {
|
|
92
|
+
this.#timers.clearAll();
|
|
93
|
+
this.#pending.clear();
|
|
94
|
+
}
|
|
95
|
+
/** Whether `key` has a timer pending. */
|
|
96
|
+
has(key) {
|
|
97
|
+
return this.#pending.has(key);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/utils/pausable_timers.ts
|
|
102
|
+
var PausableTimers = class {
|
|
103
|
+
#timers = new KeyedTimers();
|
|
104
|
+
/** Every key armed or held; an entry goes as its timer fires, or with the key. */
|
|
105
|
+
#entries = /* @__PURE__ */ new Map();
|
|
106
|
+
/**
|
|
107
|
+
* Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
|
|
108
|
+
* A held key stays held and banks `delay` for its resume.
|
|
109
|
+
*/
|
|
110
|
+
set(key, callback, delay) {
|
|
111
|
+
const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
|
|
112
|
+
const entry = { callback, startedAt: 0, remaining: delay, reasons };
|
|
113
|
+
this.#entries.set(key, entry);
|
|
114
|
+
if (reasons.size === 0) this.#arm(key, entry);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Holds `key` for `reason` and banks the time left, with a floor of one
|
|
118
|
+
* millisecond. Reports whether this call is the one that stopped a running
|
|
119
|
+
* timer, so the caller can write its held-state hook exactly once.
|
|
120
|
+
*/
|
|
121
|
+
pause(key, reason) {
|
|
122
|
+
const entry = this.#entries.get(key);
|
|
123
|
+
if (entry === void 0) return false;
|
|
124
|
+
entry.reasons.add(reason);
|
|
125
|
+
if (!this.#timers.has(key)) return false;
|
|
126
|
+
this.#timers.clear(key);
|
|
127
|
+
entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Releases `reason` on `key`, arming the banked time again once no reason is
|
|
132
|
+
* left. Reports whether this call is the one that started the timer again.
|
|
133
|
+
*/
|
|
134
|
+
resume(key, reason) {
|
|
135
|
+
const entry = this.#entries.get(key);
|
|
136
|
+
if (entry === void 0) return false;
|
|
137
|
+
entry.reasons.delete(reason);
|
|
138
|
+
if (entry.reasons.size > 0 || this.#timers.has(key)) return false;
|
|
139
|
+
this.#arm(key, entry);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
/** Whether `key` is armed or held — that is, whether this registry drives it at all. */
|
|
143
|
+
tracks(key) {
|
|
144
|
+
return this.#entries.has(key);
|
|
145
|
+
}
|
|
146
|
+
/** Cancels `key`'s timer and drops its hold. */
|
|
147
|
+
clear(key) {
|
|
148
|
+
this.#timers.clear(key);
|
|
149
|
+
this.#entries.delete(key);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Cancels every timer and forgets every key. Call this from a controller's
|
|
153
|
+
* `disconnect()` so neither a timer nor a hold outlives the element.
|
|
154
|
+
*/
|
|
155
|
+
clearAll() {
|
|
156
|
+
this.#timers.clearAll();
|
|
157
|
+
this.#entries.clear();
|
|
158
|
+
}
|
|
159
|
+
/** Starts `entry`'s banked time running for `key`, and drops it as it fires. */
|
|
160
|
+
#arm(key, entry) {
|
|
161
|
+
entry.startedAt = Date.now();
|
|
162
|
+
this.#timers.set(
|
|
163
|
+
key,
|
|
164
|
+
() => {
|
|
165
|
+
this.#entries.delete(key);
|
|
166
|
+
entry.callback();
|
|
167
|
+
},
|
|
168
|
+
entry.remaining
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
58
173
|
// src/utils/transition_completion.ts
|
|
59
174
|
function timeMs(value) {
|
|
60
175
|
const trimmed = value.trim();
|
|
@@ -99,30 +214,23 @@ var ToastController = class extends Controller {
|
|
|
99
214
|
};
|
|
100
215
|
static actions = ["dismiss", "onKeydown", "pause", "resume", "show"];
|
|
101
216
|
static events = ["dismiss", "show"];
|
|
102
|
-
/**
|
|
103
|
-
* Registry for every auto-dismiss and transition-finalize timer the controller
|
|
104
|
-
* schedules. `SafeTimeout` owns *registration and teardown only*; the
|
|
105
|
-
* pause/resume remaining-time accounting stays in `#activeTimeouts` so the
|
|
106
|
-
* per-widget WCAG 2.2.1 semantics are not flattened into the helper.
|
|
107
|
-
*/
|
|
217
|
+
/** Removal timers for the leaving transition; the auto-dismiss ones live below. */
|
|
108
218
|
#timers = new SafeTimeout();
|
|
219
|
+
/** Per-toast auto-dismiss, held open while the toast is hovered or focused. */
|
|
220
|
+
#dismiss = new PausableTimers();
|
|
109
221
|
/**
|
|
110
222
|
* Pending one-shot `requestAnimationFrame` handles (the entering→visible flip).
|
|
111
223
|
* Tracked so {@link disconnect} can cancel any that have not fired, preventing a
|
|
112
224
|
* detached element from being mutated after it leaves the DOM (Turbo).
|
|
113
225
|
*/
|
|
114
226
|
#rafHandles = /* @__PURE__ */ new Map();
|
|
115
|
-
/** Track active timeouts mapped by each toast element for safe cancellation. */
|
|
116
|
-
#activeTimeouts = /* @__PURE__ */ new Map();
|
|
117
|
-
/** Track active pause reasons (hover/focus) per toast for WCAG 2.2.1 pause/resume. */
|
|
118
|
-
#pauseReasons = /* @__PURE__ */ new Map();
|
|
119
227
|
/** The stable list that owns delegated listeners for dynamically added items. */
|
|
120
228
|
#delegatedList = null;
|
|
121
229
|
connect() {
|
|
122
230
|
this.#connectDelegatedEvents();
|
|
123
231
|
this.enforceMaxLimit();
|
|
124
232
|
for (const item of this.itemTargets) {
|
|
125
|
-
if (!this.#
|
|
233
|
+
if (!this.#dismiss.tracks(item) && item.dataset.state !== "leaving") {
|
|
126
234
|
this.#startTimer(item);
|
|
127
235
|
}
|
|
128
236
|
}
|
|
@@ -130,12 +238,11 @@ var ToastController = class extends Controller {
|
|
|
130
238
|
disconnect() {
|
|
131
239
|
this.#disconnectDelegatedEvents();
|
|
132
240
|
this.#timers.clearAll();
|
|
241
|
+
this.#dismiss.clearAll();
|
|
133
242
|
for (const handle of this.#rafHandles.values()) {
|
|
134
243
|
window.cancelAnimationFrame(handle);
|
|
135
244
|
}
|
|
136
245
|
this.#rafHandles.clear();
|
|
137
|
-
this.#activeTimeouts.clear();
|
|
138
|
-
this.#pauseReasons.clear();
|
|
139
246
|
}
|
|
140
247
|
/** Rebinds delegated interaction when Turbo replaces the list target in place. */
|
|
141
248
|
listTargetConnected(element) {
|
|
@@ -148,18 +255,9 @@ var ToastController = class extends Controller {
|
|
|
148
255
|
durationValueChanged() {
|
|
149
256
|
for (const item of this.itemTargets) {
|
|
150
257
|
if (item.dataset.state === "leaving") continue;
|
|
151
|
-
const pauseReasons = this.#pauseReasons.get(item);
|
|
152
|
-
this.#clearTimer(item);
|
|
153
258
|
if (this.durationValue <= 0) {
|
|
259
|
+
this.#dismiss.clear(item);
|
|
154
260
|
item.removeAttribute("data-paused");
|
|
155
|
-
} else if (pauseReasons && pauseReasons.size > 0) {
|
|
156
|
-
this.#pauseReasons.set(item, pauseReasons);
|
|
157
|
-
this.#activeTimeouts.set(item, {
|
|
158
|
-
id: 0,
|
|
159
|
-
startedAt: 0,
|
|
160
|
-
remaining: this.durationValue
|
|
161
|
-
});
|
|
162
|
-
item.setAttribute("data-paused", "true");
|
|
163
261
|
} else {
|
|
164
262
|
this.#startTimer(item);
|
|
165
263
|
}
|
|
@@ -188,7 +286,7 @@ var ToastController = class extends Controller {
|
|
|
188
286
|
}
|
|
189
287
|
/** Clears any active timer when a toast is removed from the DOM. */
|
|
190
288
|
itemTargetDisconnected(element) {
|
|
191
|
-
this.#
|
|
289
|
+
this.#dismiss.clear(element);
|
|
192
290
|
this.#cancelAnimation(element);
|
|
193
291
|
}
|
|
194
292
|
/**
|
|
@@ -260,35 +358,16 @@ var ToastController = class extends Controller {
|
|
|
260
358
|
*/
|
|
261
359
|
pause(event) {
|
|
262
360
|
const item = this.#itemFromEvent(event);
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
if (!timeout) return;
|
|
266
|
-
const reasons = this.#pauseReasonsFor(item);
|
|
267
|
-
const wasActive = reasons.size > 0;
|
|
268
|
-
reasons.add(this.#pauseReason(event));
|
|
269
|
-
if (wasActive || timeout.id === 0) return;
|
|
270
|
-
this.#timers.clear(timeout.id);
|
|
271
|
-
const elapsed = Date.now() - timeout.startedAt;
|
|
272
|
-
const remaining = Math.max(0, timeout.remaining - elapsed);
|
|
273
|
-
if (remaining <= 0) {
|
|
274
|
-
this.#removeWithTransition(item, "timeout");
|
|
275
|
-
return;
|
|
361
|
+
if (item && this.#dismiss.pause(item, this.#pauseReason(event))) {
|
|
362
|
+
item.setAttribute("data-paused", "true");
|
|
276
363
|
}
|
|
277
|
-
this.#activeTimeouts.set(item, { id: 0, startedAt: 0, remaining });
|
|
278
|
-
item.setAttribute("data-paused", "true");
|
|
279
364
|
}
|
|
280
365
|
/** Resumes the auto-dismiss timer once both hover and focus have been released. */
|
|
281
366
|
resume(event) {
|
|
282
367
|
const item = this.#itemFromEvent(event);
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (reasons.size > 0) return;
|
|
287
|
-
const timeout = this.#activeTimeouts.get(item);
|
|
288
|
-
if (!timeout) return;
|
|
289
|
-
if (timeout.id !== 0 || timeout.remaining <= 0) return;
|
|
290
|
-
item.removeAttribute("data-paused");
|
|
291
|
-
this.#startTimer(item, timeout.remaining);
|
|
368
|
+
if (item && this.#dismiss.resume(item, this.#pauseReason(event))) {
|
|
369
|
+
item.removeAttribute("data-paused");
|
|
370
|
+
}
|
|
292
371
|
}
|
|
293
372
|
/** Resolves the toast item element a pause/resume event targets. */
|
|
294
373
|
#itemFromEvent(event) {
|
|
@@ -301,36 +380,20 @@ var ToastController = class extends Controller {
|
|
|
301
380
|
#pauseReason(event) {
|
|
302
381
|
return event.type === "focusin" || event.type === "focusout" ? "focus" : "hover";
|
|
303
382
|
}
|
|
304
|
-
/**
|
|
305
|
-
#
|
|
306
|
-
|
|
307
|
-
if (!reasons) {
|
|
308
|
-
reasons = /* @__PURE__ */ new Set();
|
|
309
|
-
this.#pauseReasons.set(item, reasons);
|
|
310
|
-
}
|
|
311
|
-
return reasons;
|
|
312
|
-
}
|
|
313
|
-
#startTimer(element, duration = this.durationValue) {
|
|
314
|
-
if (duration <= 0 || element.dataset.state === "leaving" || element.parentNode !== this.listTarget) {
|
|
383
|
+
/** Arms a toast's auto-dismiss; a non-positive `duration` means it never expires. */
|
|
384
|
+
#startTimer(element) {
|
|
385
|
+
if (this.durationValue <= 0 || element.dataset.state === "leaving" || element.parentNode !== this.listTarget) {
|
|
315
386
|
return;
|
|
316
387
|
}
|
|
317
|
-
this.#
|
|
318
|
-
|
|
319
|
-
this.#removeWithTransition(element, "timeout")
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
#clearTimer(element) {
|
|
324
|
-
const timeout = this.#activeTimeouts.get(element);
|
|
325
|
-
if (timeout) {
|
|
326
|
-
if (timeout.id) this.#timers.clear(timeout.id);
|
|
327
|
-
this.#activeTimeouts.delete(element);
|
|
328
|
-
}
|
|
329
|
-
this.#pauseReasons.delete(element);
|
|
388
|
+
this.#dismiss.set(
|
|
389
|
+
element,
|
|
390
|
+
() => this.#removeWithTransition(element, "timeout"),
|
|
391
|
+
this.durationValue
|
|
392
|
+
);
|
|
330
393
|
}
|
|
331
394
|
#removeWithTransition(element, reason) {
|
|
332
395
|
if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
|
|
333
|
-
this.#
|
|
396
|
+
this.#dismiss.clear(element);
|
|
334
397
|
this.#cancelAnimation(element);
|
|
335
398
|
element.setAttribute("data-state", "leaving");
|
|
336
399
|
const finalize = () => {
|
|
@@ -20,6 +20,15 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
20
20
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// src/utils/event_owner.ts
|
|
24
|
+
function ownerIndex(candidates, node) {
|
|
25
|
+
if (!(node instanceof Node)) return -1;
|
|
26
|
+
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
27
|
+
}
|
|
28
|
+
function ownerOf(candidates, node) {
|
|
29
|
+
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
// src/utils/focus_candidate.ts
|
|
24
33
|
function inheritsFieldsetDisabled(control) {
|
|
25
34
|
let fieldset = control.closest("fieldset[disabled]");
|
|
@@ -363,9 +372,8 @@ var ToggleGroupController = class extends Controller {
|
|
|
363
372
|
}
|
|
364
373
|
/** Finds this group's item containing an event target, excluding nested groups. */
|
|
365
374
|
#itemForEventTarget(target) {
|
|
366
|
-
if (!this.#ownsEventTarget(target)) return
|
|
367
|
-
|
|
368
|
-
return this.itemTargets.find((item) => item === node || item.contains(node));
|
|
375
|
+
if (!this.#ownsEventTarget(target)) return null;
|
|
376
|
+
return ownerOf(this.itemTargets, target);
|
|
369
377
|
}
|
|
370
378
|
/** Whether the closest Toggle Group scope around a target is this instance. */
|
|
371
379
|
#ownsEventTarget(target) {
|
|
@@ -60,6 +60,12 @@ var CompositionTracker = class {
|
|
|
60
60
|
};
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
// src/utils/event_owner.ts
|
|
64
|
+
function ownerIndex(candidates, node) {
|
|
65
|
+
if (!(node instanceof Node)) return -1;
|
|
66
|
+
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
// src/utils/focus_candidate.ts
|
|
64
70
|
function inheritsFieldsetDisabled(control) {
|
|
65
71
|
let fieldset = control.closest("fieldset[disabled]");
|
|
@@ -268,9 +274,7 @@ var ToolbarController = class extends Controller {
|
|
|
268
274
|
}
|
|
269
275
|
/** Index in `controlTargets` of the control owning `target` (it or a descendant). */
|
|
270
276
|
#indexOf(target) {
|
|
271
|
-
|
|
272
|
-
if (!node) return -1;
|
|
273
|
-
return this.controlTargets.findIndex((control) => control === node || control.contains(node));
|
|
277
|
+
return ownerIndex(this.controlTargets, target);
|
|
274
278
|
}
|
|
275
279
|
/** Controls eligible for the roving tab stop (excludes native disabled / hidden). */
|
|
276
280
|
get #navigableControls() {
|