inclusive-elements 0.6.1 → 0.7.0-beta.1
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/CHANGELOG.md +7 -0
- package/README.md +22 -17
- package/dist/index.d.ts +230 -0
- package/dist/index.js +1524 -503
- package/package.json +20 -23
- package/src/accordion/README.md +6 -6
- package/src/alerts/README.md +16 -14
- package/src/combobox/README.md +133 -0
- package/src/dialog/README.md +15 -17
- package/src/disclosure/README.md +5 -5
- package/src/menu/README.md +19 -26
- package/src/popover/README.md +10 -10
- package/src/tabs/README.md +12 -12
- package/src/toolbar/README.md +8 -8
- package/src/tooltip/README.md +14 -14
- package/dist/types/alerts/alerts.d.ts +0 -30
- package/dist/types/index.d.ts +0 -6
- package/dist/types/menu/menu.d.ts +0 -42
- package/dist/types/tabs/tabs.d.ts +0 -24
- package/dist/types/toolbar/toolbar.d.ts +0 -11
- package/dist/types/tooltip/tooltip.d.ts +0 -36
- package/dist/types/utils.d.ts +0 -15
package/dist/index.js
CHANGED
|
@@ -30,7 +30,8 @@ function i(e, t) {
|
|
|
30
30
|
let n = e.style;
|
|
31
31
|
for (let e of Object.keys(t)) n[e] = t[e] ?? "";
|
|
32
32
|
}
|
|
33
|
-
var a = /* @__PURE__ */ new WeakMap()
|
|
33
|
+
var a = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
var o = [
|
|
34
35
|
"margin",
|
|
35
36
|
"marginTop",
|
|
36
37
|
"marginRight",
|
|
@@ -42,7 +43,8 @@ var a = /* @__PURE__ */ new WeakMap(), o = [
|
|
|
42
43
|
"marginInline",
|
|
43
44
|
"marginInlineStart",
|
|
44
45
|
"marginInlineEnd"
|
|
45
|
-
]
|
|
46
|
+
];
|
|
47
|
+
var s = {};
|
|
46
48
|
for (let e of o) s[e] = "0px";
|
|
47
49
|
async function c(r, i, o = {}) {
|
|
48
50
|
let s = e(o) + i + "-", c = Symbol(s), u = o.detachFromLayout ?? !1, f = u ? g(r) : void 0, p = u ? d(r, f) : void 0, m = i === "leave" ? _(r) : void 0, h = {
|
|
@@ -137,7 +139,9 @@ function _(e) {
|
|
|
137
139
|
e.inert = t;
|
|
138
140
|
};
|
|
139
141
|
}
|
|
140
|
-
var v = /* @__PURE__ */ new WeakMap()
|
|
142
|
+
var v = /* @__PURE__ */ new WeakMap();
|
|
143
|
+
var y = /* @__PURE__ */ new WeakMap();
|
|
144
|
+
var b = /* @__PURE__ */ new WeakMap();
|
|
141
145
|
function x(t, n, r = {}) {
|
|
142
146
|
let i = [];
|
|
143
147
|
for (let e of Array.from(t)) getComputedStyle(e).display !== "none" && i.push({
|
|
@@ -148,20 +152,20 @@ function x(t, n, r = {}) {
|
|
|
148
152
|
let a = e(r) + "move", o = [];
|
|
149
153
|
for (let { el: e, before: t } of i) {
|
|
150
154
|
if (u(e)) continue;
|
|
151
|
-
let n = e.getBoundingClientRect(), r = t.left - n.left, i = t.top - n.top, s =
|
|
152
|
-
|
|
155
|
+
let n = e.getBoundingClientRect(), r = t.left - n.left, i = t.top - n.top, s = j(e);
|
|
156
|
+
D(e, v, !1), !(!r && !i) && o.push(w(e, {
|
|
153
157
|
store: v,
|
|
154
158
|
className: a,
|
|
155
|
-
startStyles: { transform:
|
|
159
|
+
startStyles: { transform: M(`translate(${r}px, ${i}px)`, s) },
|
|
156
160
|
endStyles: { transform: e.style.transform }
|
|
157
161
|
}));
|
|
158
162
|
}
|
|
159
163
|
return Promise.all(o).then(() => void 0);
|
|
160
164
|
}
|
|
161
|
-
function
|
|
162
|
-
|
|
165
|
+
function C(e) {
|
|
166
|
+
D(e, y, !1), D(e, v, !1);
|
|
163
167
|
}
|
|
164
|
-
function
|
|
168
|
+
function w(e, a) {
|
|
165
169
|
let o = Symbol(a.className), s = {
|
|
166
170
|
transitionDuration: "0s",
|
|
167
171
|
...a.startStyles
|
|
@@ -171,7 +175,7 @@ function ee(e, a) {
|
|
|
171
175
|
}, l = Object.keys({
|
|
172
176
|
...s,
|
|
173
177
|
...c
|
|
174
|
-
}), u =
|
|
178
|
+
}), u = T(e), d, f = new Promise((e) => {
|
|
175
179
|
d = e;
|
|
176
180
|
}), p = {
|
|
177
181
|
className: a.className,
|
|
@@ -180,12 +184,12 @@ function ee(e, a) {
|
|
|
180
184
|
resolve: d
|
|
181
185
|
};
|
|
182
186
|
return "transitionDuration" in p.styles && (p.styles.transitionDuration = u), a.store.set(e, p), e.classList.add(a.className), i(e, s), e.getBoundingClientRect(), t().then(async () => {
|
|
183
|
-
|
|
187
|
+
E(e), a.store.get(e)?.token === o && (await n(e, () => {
|
|
184
188
|
i(e, c);
|
|
185
|
-
}), a.store.get(e)?.token === o &&
|
|
189
|
+
}), a.store.get(e)?.token === o && O(e, a.store, p, !0));
|
|
186
190
|
}), f;
|
|
187
191
|
}
|
|
188
|
-
function
|
|
192
|
+
function T(e) {
|
|
189
193
|
let t = b.get(e);
|
|
190
194
|
if (t) return t.count += 1, t.value;
|
|
191
195
|
let n = e.style.transitionDuration;
|
|
@@ -194,73 +198,90 @@ function te(e) {
|
|
|
194
198
|
count: 1
|
|
195
199
|
}), n;
|
|
196
200
|
}
|
|
197
|
-
function
|
|
201
|
+
function E(e) {
|
|
198
202
|
let t = b.get(e);
|
|
199
203
|
t && (--t.count, t.count <= 0 && b.delete(e));
|
|
200
204
|
}
|
|
201
|
-
function
|
|
205
|
+
function D(e, t, n) {
|
|
202
206
|
let r = t.get(e);
|
|
203
|
-
r &&
|
|
207
|
+
r && O(e, t, r, n);
|
|
204
208
|
}
|
|
205
|
-
function
|
|
206
|
-
|
|
209
|
+
function O(e, t, n, r) {
|
|
210
|
+
k(e, n), t.delete(e), n.resolve(r);
|
|
207
211
|
}
|
|
208
|
-
function
|
|
212
|
+
function k(e, t) {
|
|
209
213
|
e.classList.remove(t.className), i(e, t.styles);
|
|
210
214
|
}
|
|
211
|
-
function
|
|
215
|
+
function j(e) {
|
|
212
216
|
let t = getComputedStyle(e).transform;
|
|
213
217
|
return t === "none" ? "" : t;
|
|
214
218
|
}
|
|
215
|
-
function
|
|
219
|
+
function M(...e) {
|
|
216
220
|
return e.filter((e) => !!e && e !== "none").join(" ");
|
|
217
221
|
}
|
|
218
|
-
function
|
|
219
|
-
return
|
|
222
|
+
function N(e, t, n = {}) {
|
|
223
|
+
return I(e), c(e, t, n);
|
|
220
224
|
}
|
|
221
|
-
function
|
|
222
|
-
return
|
|
225
|
+
function P(e, t = {}) {
|
|
226
|
+
return N(e, "enter", t);
|
|
223
227
|
}
|
|
224
|
-
function
|
|
225
|
-
return
|
|
228
|
+
function F(e, t = {}) {
|
|
229
|
+
return N(e, "leave", {
|
|
226
230
|
...t,
|
|
227
231
|
detachFromLayout: t.detachFromLayout ?? !0
|
|
228
232
|
});
|
|
229
233
|
}
|
|
230
|
-
function
|
|
231
|
-
l(e),
|
|
234
|
+
function I(e) {
|
|
235
|
+
l(e), C(e);
|
|
232
236
|
}
|
|
233
237
|
//#endregion
|
|
234
238
|
//#region src/alerts/alerts.ts
|
|
235
|
-
var
|
|
239
|
+
var AlertsElement = class AlertsElement extends HTMLElement {
|
|
236
240
|
static duration = 1e4;
|
|
237
241
|
static politeness = "polite";
|
|
238
242
|
timeouts = /* @__PURE__ */ new WeakMap();
|
|
239
243
|
index = 0;
|
|
240
244
|
connectedCallback() {
|
|
241
|
-
this.addEventListener("focusin", this.pauseOnInteraction)
|
|
242
|
-
|
|
245
|
+
this.addEventListener("focusin", this.pauseOnInteraction);
|
|
246
|
+
this.addEventListener("focusout", this.resumeOnInteraction);
|
|
247
|
+
this.addEventListener("mouseover", this.pauseOnInteraction);
|
|
248
|
+
this.addEventListener("mouseout", this.resumeOnInteraction);
|
|
249
|
+
for (const alert of this.getAlerts()) {
|
|
250
|
+
alert.remove();
|
|
251
|
+
this.show(alert);
|
|
252
|
+
}
|
|
243
253
|
}
|
|
244
254
|
disconnectedCallback() {
|
|
245
|
-
this.removeEventListener("focusin", this.pauseOnInteraction)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
255
|
+
this.removeEventListener("focusin", this.pauseOnInteraction);
|
|
256
|
+
this.removeEventListener("focusout", this.resumeOnInteraction);
|
|
257
|
+
this.removeEventListener("mouseover", this.pauseOnInteraction);
|
|
258
|
+
this.removeEventListener("mouseout", this.resumeOnInteraction);
|
|
259
|
+
this.getAlerts().forEach((alert) => this.clearTimeout(alert));
|
|
260
|
+
}
|
|
261
|
+
show(el, options = {}) {
|
|
262
|
+
const key = options.key || el.dataset.key || String(this.index++);
|
|
263
|
+
el.dataset.key = key;
|
|
264
|
+
this.syncAlertPoliteness(el, options.politeness);
|
|
265
|
+
if (options.duration !== void 0) el.dataset.duration = String(options.duration);
|
|
266
|
+
const alerts = this.getAlerts().filter((alert) => alert !== el);
|
|
267
|
+
x(alerts, () => {
|
|
268
|
+
alerts.filter((alert) => alert.dataset.key === key).forEach((alert) => this.leaveAlert(alert));
|
|
269
|
+
I(el);
|
|
270
|
+
this.append(el);
|
|
271
|
+
P(el);
|
|
272
|
+
});
|
|
273
|
+
this.startTimeout(el);
|
|
274
|
+
return key;
|
|
254
275
|
}
|
|
255
|
-
dismiss(
|
|
256
|
-
(typeof
|
|
276
|
+
dismiss(elOrKey) {
|
|
277
|
+
(typeof elOrKey === "string" ? this.getAlerts().filter((alert) => alert.dataset.key === elOrKey) : [elOrKey]).forEach((alert) => this.dismissAlert(alert));
|
|
257
278
|
}
|
|
258
279
|
clear() {
|
|
259
|
-
this.getAlerts().forEach((
|
|
280
|
+
this.getAlerts().forEach((alert) => this.dismissAlert(alert));
|
|
260
281
|
}
|
|
261
|
-
speak(
|
|
262
|
-
|
|
263
|
-
Object.assign(
|
|
282
|
+
speak(message, options = {}) {
|
|
283
|
+
const el = document.createElement("div");
|
|
284
|
+
Object.assign(el.style, {
|
|
264
285
|
clip: "rect(0 0 0 0)",
|
|
265
286
|
clipPath: "inset(50%)",
|
|
266
287
|
height: "1px",
|
|
@@ -268,300 +289,1033 @@ var j = class e extends HTMLElement {
|
|
|
268
289
|
position: "absolute",
|
|
269
290
|
whiteSpace: "nowrap",
|
|
270
291
|
width: "1px"
|
|
271
|
-
})
|
|
292
|
+
});
|
|
293
|
+
el.textContent = message;
|
|
294
|
+
this.show(el, options);
|
|
272
295
|
}
|
|
273
|
-
dismissAlert(
|
|
274
|
-
x(this.getAlerts().filter((
|
|
296
|
+
dismissAlert(el) {
|
|
297
|
+
x(this.getAlerts().filter((alert) => alert !== el), () => this.leaveAlert(el));
|
|
275
298
|
}
|
|
276
|
-
leaveAlert(
|
|
277
|
-
this.clearTimeout(
|
|
278
|
-
|
|
299
|
+
leaveAlert(el) {
|
|
300
|
+
this.clearTimeout(el);
|
|
301
|
+
F(el).then((complete) => {
|
|
302
|
+
if (complete && el.parentElement === this) el.remove();
|
|
279
303
|
});
|
|
280
304
|
}
|
|
281
|
-
startTimeout(
|
|
282
|
-
this.clearTimeout(
|
|
283
|
-
|
|
284
|
-
|
|
305
|
+
startTimeout(el) {
|
|
306
|
+
this.clearTimeout(el);
|
|
307
|
+
const duration = Number(el.dataset.duration || AlertsElement.duration);
|
|
308
|
+
if (duration > 0) this.timeouts.set(el, window.setTimeout(() => this.dismiss(el), duration));
|
|
285
309
|
}
|
|
286
|
-
clearTimeout(
|
|
287
|
-
|
|
288
|
-
|
|
310
|
+
clearTimeout(el) {
|
|
311
|
+
const timeout = this.timeouts.get(el);
|
|
312
|
+
if (timeout !== void 0) {
|
|
313
|
+
clearTimeout(timeout);
|
|
314
|
+
this.timeouts.delete(el);
|
|
315
|
+
}
|
|
289
316
|
}
|
|
290
|
-
pauseOnInteraction = (
|
|
291
|
-
this.handleInteraction(
|
|
317
|
+
pauseOnInteraction = (event) => {
|
|
318
|
+
this.handleInteraction(event, (alert) => this.clearTimeout(alert));
|
|
292
319
|
};
|
|
293
|
-
resumeOnInteraction = (
|
|
294
|
-
this.handleInteraction(
|
|
320
|
+
resumeOnInteraction = (event) => {
|
|
321
|
+
this.handleInteraction(event, (alert) => this.startTimeout(alert));
|
|
295
322
|
};
|
|
296
|
-
handleInteraction(
|
|
297
|
-
|
|
298
|
-
|
|
323
|
+
handleInteraction(event, action) {
|
|
324
|
+
const alert = this.getAlertFromTarget(event.target);
|
|
325
|
+
if (!alert) return;
|
|
326
|
+
if (event.relatedTarget instanceof Node && alert.contains(event.relatedTarget)) return;
|
|
327
|
+
action(alert);
|
|
299
328
|
}
|
|
300
329
|
getAlerts() {
|
|
301
|
-
return Array.from(this.children, (
|
|
330
|
+
return Array.from(this.children, (child) => child);
|
|
302
331
|
}
|
|
303
|
-
syncAlertPoliteness(
|
|
304
|
-
if ((
|
|
305
|
-
|
|
332
|
+
syncAlertPoliteness(el, politeness) {
|
|
333
|
+
if ((politeness || this.getAuthoredPoliteness(el) || AlertsElement.politeness) === "assertive") {
|
|
334
|
+
el.setAttribute("role", "alert");
|
|
335
|
+
el.setAttribute("aria-live", "assertive");
|
|
306
336
|
return;
|
|
307
337
|
}
|
|
308
|
-
|
|
338
|
+
el.setAttribute("role", "status");
|
|
339
|
+
el.setAttribute("aria-live", "polite");
|
|
309
340
|
}
|
|
310
|
-
getAuthoredPoliteness(
|
|
311
|
-
if (
|
|
312
|
-
if (
|
|
341
|
+
getAuthoredPoliteness(el) {
|
|
342
|
+
if (el.getAttribute("role") === "alert" || el.getAttribute("aria-live") === "assertive") return "assertive";
|
|
343
|
+
if (el.getAttribute("role") === "status" || el.getAttribute("aria-live") === "polite") return "polite";
|
|
313
344
|
}
|
|
314
|
-
getAlertFromTarget(
|
|
315
|
-
|
|
345
|
+
getAlertFromTarget(target) {
|
|
346
|
+
if (!(target instanceof Node)) return null;
|
|
347
|
+
return Array.from(this.children).find((child) => child instanceof HTMLElement && child.contains(target)) || null;
|
|
316
348
|
}
|
|
317
349
|
};
|
|
318
350
|
//#endregion
|
|
319
351
|
//#region src/utils.ts
|
|
320
|
-
function
|
|
321
|
-
let
|
|
352
|
+
function createQueuedSync(element, sync) {
|
|
353
|
+
let queued = false;
|
|
354
|
+
const queue = () => {
|
|
355
|
+
if (queued) return;
|
|
356
|
+
queued = true;
|
|
357
|
+
queueMicrotask(() => {
|
|
358
|
+
queued = false;
|
|
359
|
+
if (element.isConnected) sync();
|
|
360
|
+
});
|
|
361
|
+
};
|
|
322
362
|
return {
|
|
323
|
-
queue
|
|
324
|
-
n || (n = !0, queueMicrotask(() => {
|
|
325
|
-
n = !1, e.isConnected && t();
|
|
326
|
-
}));
|
|
327
|
-
},
|
|
363
|
+
queue,
|
|
328
364
|
disconnect() {
|
|
329
|
-
|
|
365
|
+
queued = false;
|
|
330
366
|
}
|
|
331
367
|
};
|
|
332
368
|
}
|
|
333
|
-
function
|
|
334
|
-
|
|
369
|
+
function createQueuedElementSync(element, options, sync) {
|
|
370
|
+
const queuedSync = createQueuedSync(element, sync);
|
|
371
|
+
const observer = new MutationObserver(queuedSync.queue);
|
|
335
372
|
return {
|
|
336
373
|
observe() {
|
|
337
|
-
|
|
374
|
+
observer.observe(element, options);
|
|
338
375
|
},
|
|
339
376
|
disconnect() {
|
|
340
|
-
|
|
377
|
+
observer.disconnect();
|
|
378
|
+
queuedSync.disconnect();
|
|
341
379
|
},
|
|
342
|
-
queue:
|
|
380
|
+
queue: queuedSync.queue
|
|
343
381
|
};
|
|
344
382
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
347
|
-
return
|
|
383
|
+
function getLookupRoot(element) {
|
|
384
|
+
const root = element.getRootNode();
|
|
385
|
+
return root instanceof ShadowRoot ? root : element.ownerDocument;
|
|
348
386
|
}
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
387
|
+
function getRovingIndexForKey(key, options) {
|
|
388
|
+
const { currentIndex, length, vertical } = options;
|
|
389
|
+
if (length === 0 || currentIndex < 0 || currentIndex >= length) return;
|
|
390
|
+
if (key === (vertical ? "ArrowUp" : "ArrowLeft")) return (currentIndex - 1 + length) % length;
|
|
391
|
+
if (key === (vertical ? "ArrowDown" : "ArrowRight")) return (currentIndex + 1) % length;
|
|
392
|
+
if (key === "Home") return 0;
|
|
393
|
+
if (key === "End") return length - 1;
|
|
357
394
|
}
|
|
358
395
|
//#endregion
|
|
396
|
+
//#region src/combobox/combobox.ts
|
|
397
|
+
let idCounter$1 = 0;
|
|
398
|
+
const internalSourceEvents = /* @__PURE__ */ new WeakSet();
|
|
399
|
+
const transferredAttributes = [
|
|
400
|
+
"title",
|
|
401
|
+
"aria-label",
|
|
402
|
+
"aria-labelledby",
|
|
403
|
+
"aria-describedby",
|
|
404
|
+
"aria-errormessage"
|
|
405
|
+
];
|
|
406
|
+
var ComboboxElement = class extends HTMLElement {
|
|
407
|
+
idPrefix = "combobox" + ++idCounter$1;
|
|
408
|
+
suppressedListId = this.idPrefix + "_suppressed_list";
|
|
409
|
+
sync = createQueuedSync(this, () => this.sourceState ? this.render() : this.enhance());
|
|
410
|
+
observer = new MutationObserver(this.sync.queue);
|
|
411
|
+
listeners;
|
|
412
|
+
sourceState;
|
|
413
|
+
control;
|
|
414
|
+
tokens;
|
|
415
|
+
input;
|
|
416
|
+
listbox;
|
|
417
|
+
activeIndex = -1;
|
|
418
|
+
labelledBy = "";
|
|
419
|
+
generatedLabelIds = [];
|
|
420
|
+
connectedCallback() {
|
|
421
|
+
this.sync.queue();
|
|
422
|
+
}
|
|
423
|
+
disconnectedCallback() {
|
|
424
|
+
this.sync.disconnect();
|
|
425
|
+
this.teardownSource();
|
|
426
|
+
}
|
|
427
|
+
refresh() {
|
|
428
|
+
const transferFocus = this.input?.matches(":focus");
|
|
429
|
+
this.teardownSource();
|
|
430
|
+
if (this.isConnected) this.enhance(transferFocus);
|
|
431
|
+
}
|
|
432
|
+
enhance(transferFocus) {
|
|
433
|
+
if (typeof HTMLElement.prototype.showPopover !== "function" || typeof HTMLElement.prototype.hidePopover !== "function") return;
|
|
434
|
+
const source = this.discoverSource();
|
|
435
|
+
if (!source) return;
|
|
436
|
+
transferFocus ??= source.matches(":focus");
|
|
437
|
+
this.setupSource(source);
|
|
438
|
+
this.render();
|
|
439
|
+
if (transferFocus) this.input?.focus();
|
|
440
|
+
}
|
|
441
|
+
setupSource(source) {
|
|
442
|
+
const signal = (this.listeners = new AbortController()).signal;
|
|
443
|
+
if (source instanceof HTMLSelectElement) {
|
|
444
|
+
const container = document.createElement("div");
|
|
445
|
+
this.sourceState = {
|
|
446
|
+
source,
|
|
447
|
+
cssHidden: source.style.display === "none" || !source.hidden && getComputedStyle(source).display === "none",
|
|
448
|
+
container
|
|
449
|
+
};
|
|
450
|
+
container.hidden = true;
|
|
451
|
+
container.inert = true;
|
|
452
|
+
source.before(container);
|
|
453
|
+
container.append(source);
|
|
454
|
+
this.createGeneratedInput(source, signal);
|
|
455
|
+
} else {
|
|
456
|
+
this.sourceState = this.createInputState(source);
|
|
457
|
+
this.input = source;
|
|
458
|
+
this.addInputListeners(source, signal);
|
|
459
|
+
source.addEventListener("keydown", this.onWidgetKeyDown, { signal });
|
|
460
|
+
source.addEventListener("focusout", this.onFocusOut, { signal });
|
|
461
|
+
source.setAttribute("list", this.suppressedListId);
|
|
462
|
+
this.observeDatalist();
|
|
463
|
+
}
|
|
464
|
+
const listbox = this.createListbox(signal);
|
|
465
|
+
this.setInputAttribute("role", "combobox");
|
|
466
|
+
this.setInputAttribute("aria-autocomplete", "list");
|
|
467
|
+
this.setInputAttribute("aria-haspopup", "listbox");
|
|
468
|
+
this.setInputAttribute("aria-controls", listbox.id);
|
|
469
|
+
if (this.multiple) listbox.setAttribute("aria-multiselectable", "true");
|
|
470
|
+
source.addEventListener("input", this.onSourceUpdate, { signal });
|
|
471
|
+
source.addEventListener("change", this.onSourceUpdate, { signal });
|
|
472
|
+
source.addEventListener("invalid", () => this.syncValidityState(), { signal });
|
|
473
|
+
this.ownerDocument.addEventListener("pointerdown", this.onDocumentPointerDown, {
|
|
474
|
+
capture: true,
|
|
475
|
+
signal
|
|
476
|
+
});
|
|
477
|
+
this.observeSource(source);
|
|
478
|
+
source.form?.addEventListener("reset", this.onFormReset, { signal });
|
|
479
|
+
if (source instanceof HTMLSelectElement) {
|
|
480
|
+
const labels = Array.from(source.labels);
|
|
481
|
+
this.labelledBy = labels.map((label, index) => {
|
|
482
|
+
if (label.id) return label.id;
|
|
483
|
+
const id = `${this.idPrefix}_label_${index}`;
|
|
484
|
+
label.id = id;
|
|
485
|
+
this.generatedLabelIds.push([label, id]);
|
|
486
|
+
return id;
|
|
487
|
+
}).join(" ");
|
|
488
|
+
labels.forEach((label) => {
|
|
489
|
+
label.addEventListener("click", this.onLabelClick, { signal });
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
observeSource(source) {
|
|
494
|
+
this.observer.observe(source, {
|
|
495
|
+
subtree: source instanceof HTMLSelectElement,
|
|
496
|
+
childList: source instanceof HTMLSelectElement,
|
|
497
|
+
characterData: source instanceof HTMLSelectElement,
|
|
498
|
+
attributes: true,
|
|
499
|
+
attributeFilter: [
|
|
500
|
+
"disabled",
|
|
501
|
+
"hidden",
|
|
502
|
+
"label",
|
|
503
|
+
"readonly",
|
|
504
|
+
"required",
|
|
505
|
+
"selected",
|
|
506
|
+
"tabindex",
|
|
507
|
+
"value",
|
|
508
|
+
"aria-invalid",
|
|
509
|
+
...transferredAttributes
|
|
510
|
+
]
|
|
511
|
+
});
|
|
512
|
+
for (let ancestor = source.parentElement; ancestor; ancestor = ancestor.parentElement) if (ancestor instanceof HTMLFieldSetElement) this.observer.observe(ancestor, {
|
|
513
|
+
attributes: true,
|
|
514
|
+
attributeFilter: ["disabled"]
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
teardownSource() {
|
|
518
|
+
const state = this.sourceState;
|
|
519
|
+
if (!state) return;
|
|
520
|
+
this.listeners?.abort();
|
|
521
|
+
this.listeners = void 0;
|
|
522
|
+
this.observer.disconnect();
|
|
523
|
+
this.generatedLabelIds.forEach(([label, id]) => {
|
|
524
|
+
if (label.id === id) label.removeAttribute("id");
|
|
525
|
+
});
|
|
526
|
+
this.generatedLabelIds = [];
|
|
527
|
+
this.labelledBy = "";
|
|
528
|
+
if (!("cssHidden" in state)) {
|
|
529
|
+
const list = state.source.getAttribute("list");
|
|
530
|
+
if (list !== this.suppressedListId) state.list = list;
|
|
531
|
+
this.restoreInputAttributes(state.source, state.inputAttributes);
|
|
532
|
+
if (state.list !== null) state.source.setAttribute("list", state.list);
|
|
533
|
+
else state.source.removeAttribute("list");
|
|
534
|
+
} else state.container.replaceWith(...state.container.childNodes);
|
|
535
|
+
this.control?.remove();
|
|
536
|
+
this.listbox?.remove();
|
|
537
|
+
this.sourceState = void 0;
|
|
538
|
+
this.control = void 0;
|
|
539
|
+
this.tokens = void 0;
|
|
540
|
+
this.input = void 0;
|
|
541
|
+
this.listbox = void 0;
|
|
542
|
+
this.activeIndex = -1;
|
|
543
|
+
}
|
|
544
|
+
render() {
|
|
545
|
+
const state = this.sourceState;
|
|
546
|
+
if (!state) return;
|
|
547
|
+
if ("cssHidden" in state && this.control) this.control.hidden = this.sourceHidden;
|
|
548
|
+
this.syncInputState();
|
|
549
|
+
if (state.source instanceof HTMLSelectElement) {
|
|
550
|
+
this.syncInputDisplay();
|
|
551
|
+
this.renderTokens();
|
|
552
|
+
}
|
|
553
|
+
this.renderOptions();
|
|
554
|
+
}
|
|
555
|
+
createGeneratedInput(source, signal) {
|
|
556
|
+
this.control = document.createElement("div");
|
|
557
|
+
this.control.setAttribute("data-combobox-control", "");
|
|
558
|
+
this.control.addEventListener("focusout", this.onFocusOut, { signal });
|
|
559
|
+
this.control.addEventListener("mousedown", this.onControlMouseDown, { signal });
|
|
560
|
+
this.control.addEventListener("click", this.onControlClick, { signal });
|
|
561
|
+
this.control.addEventListener("keydown", this.onWidgetKeyDown, { signal });
|
|
562
|
+
this.append(this.control);
|
|
563
|
+
if (this.multiple) {
|
|
564
|
+
this.tokens = document.createElement("div");
|
|
565
|
+
this.tokens.setAttribute("data-combobox-tokens", "");
|
|
566
|
+
this.control.append(this.tokens);
|
|
567
|
+
}
|
|
568
|
+
this.input = document.createElement("input");
|
|
569
|
+
this.input.type = "text";
|
|
570
|
+
const form = source.getAttribute("form");
|
|
571
|
+
if (form !== null) this.input.setAttribute("form", form);
|
|
572
|
+
this.addInputListeners(this.input, signal);
|
|
573
|
+
this.control.append(this.input);
|
|
574
|
+
}
|
|
575
|
+
createListbox(signal) {
|
|
576
|
+
this.listbox = document.createElement("div");
|
|
577
|
+
this.listbox.id = this.idPrefix + "_listbox";
|
|
578
|
+
this.listbox.setAttribute("role", "listbox");
|
|
579
|
+
this.listbox.setAttribute("popover", "manual");
|
|
580
|
+
this.listbox.addEventListener("mousedown", (e) => {
|
|
581
|
+
if (e.target?.closest("[role=\"option\"]")) e.preventDefault();
|
|
582
|
+
}, { signal });
|
|
583
|
+
this.listbox.addEventListener("click", this.onListboxClick, { signal });
|
|
584
|
+
this.append(this.listbox);
|
|
585
|
+
return this.listbox;
|
|
586
|
+
}
|
|
587
|
+
syncInputState() {
|
|
588
|
+
const state = this.sourceState;
|
|
589
|
+
const input = this.input;
|
|
590
|
+
if (!state || !input) return;
|
|
591
|
+
this.setInputAttribute("aria-expanded", String(this.isOpen()));
|
|
592
|
+
if (state.source instanceof HTMLSelectElement) {
|
|
593
|
+
transferredAttributes.forEach((attribute) => {
|
|
594
|
+
this.setInputAttribute(attribute, state.source.getAttribute(attribute));
|
|
595
|
+
});
|
|
596
|
+
if (!state.source.hasAttribute("aria-label") && !state.source.hasAttribute("aria-labelledby") && this.labelledBy) input.setAttribute("aria-labelledby", this.labelledBy);
|
|
597
|
+
input.disabled = state.source.disabled;
|
|
598
|
+
input.tabIndex = state.source.tabIndex;
|
|
599
|
+
this.setInputAttribute("aria-required", state.source.required ? "true" : null);
|
|
600
|
+
}
|
|
601
|
+
this.syncValidityState();
|
|
602
|
+
if (!this.editable) this.close();
|
|
603
|
+
}
|
|
604
|
+
syncValidityState() {
|
|
605
|
+
const source = this.sourceState?.source;
|
|
606
|
+
if (!(source instanceof HTMLSelectElement) || !this.input) return;
|
|
607
|
+
this.input.setCustomValidity(source.validity.valid ? "" : source.validationMessage);
|
|
608
|
+
const invalid = source.getAttribute("aria-invalid") ?? (!source.validity.valid ? "true" : null);
|
|
609
|
+
this.setInputAttribute("aria-invalid", invalid);
|
|
610
|
+
}
|
|
611
|
+
addInputListeners(input, signal) {
|
|
612
|
+
input.addEventListener("input", this.onInput, { signal });
|
|
613
|
+
input.addEventListener("keydown", this.onKeyDown, { signal });
|
|
614
|
+
input.addEventListener("focus", this.onInputFocus, { signal });
|
|
615
|
+
input.addEventListener("click", this.onInputClick, { signal });
|
|
616
|
+
}
|
|
617
|
+
restoreInputAttributes(input, attributes) {
|
|
618
|
+
attributes.forEach(({ original, managed }, name) => {
|
|
619
|
+
if (input.getAttribute(name) !== managed) return;
|
|
620
|
+
if (original === null) input.removeAttribute(name);
|
|
621
|
+
else input.setAttribute(name, original);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
setInputAttribute(name, value) {
|
|
625
|
+
const input = this.input;
|
|
626
|
+
const state = this.sourceState;
|
|
627
|
+
if (!input || !state) return;
|
|
628
|
+
if (!("cssHidden" in state)) {
|
|
629
|
+
const attribute = state.inputAttributes.get(name);
|
|
630
|
+
if (attribute) attribute.managed = value;
|
|
631
|
+
}
|
|
632
|
+
if (value === null) input.removeAttribute(name);
|
|
633
|
+
else input.setAttribute(name, value);
|
|
634
|
+
}
|
|
635
|
+
renderTokens() {
|
|
636
|
+
const source = this.sourceState?.source;
|
|
637
|
+
if (!this.tokens || !(source instanceof HTMLSelectElement)) return;
|
|
638
|
+
const selectedOptions = Array.from(source.selectedOptions).filter((option) => option.value !== "");
|
|
639
|
+
this.tokens.replaceChildren(...selectedOptions.map((option) => {
|
|
640
|
+
const token = document.createElement("span");
|
|
641
|
+
token.setAttribute("data-combobox-token", "");
|
|
642
|
+
const label = this.optionLabel(option);
|
|
643
|
+
token.textContent = label + " ";
|
|
644
|
+
const remove = document.createElement("button");
|
|
645
|
+
remove.type = "button";
|
|
646
|
+
remove.setAttribute("data-combobox-token-remove", "");
|
|
647
|
+
remove.dataset.optionIndex = String(option.index);
|
|
648
|
+
remove.setAttribute("aria-label", `Remove ${label}`);
|
|
649
|
+
remove.disabled = source.disabled || this.isOptionDisabled(option);
|
|
650
|
+
remove.tabIndex = -1;
|
|
651
|
+
remove.textContent = "×";
|
|
652
|
+
token.append(remove);
|
|
653
|
+
return token;
|
|
654
|
+
}));
|
|
655
|
+
}
|
|
656
|
+
renderOptions() {
|
|
657
|
+
if (!this.listbox) return;
|
|
658
|
+
const options = this.filteredOptions();
|
|
659
|
+
if (this.activeIndex >= options.length) this.activeIndex = this.firstEnabledIndex(options);
|
|
660
|
+
const children = [];
|
|
661
|
+
const groups = /* @__PURE__ */ new Map();
|
|
662
|
+
options.forEach((option, index) => {
|
|
663
|
+
const el = document.createElement("div");
|
|
664
|
+
el.id = this.idPrefix + "_option_" + index;
|
|
665
|
+
el.dataset.index = String(index);
|
|
666
|
+
el.setAttribute("role", "option");
|
|
667
|
+
el.setAttribute("aria-selected", String(this.isSelected(option)));
|
|
668
|
+
el.textContent = this.optionLabel(option);
|
|
669
|
+
if (!this.multiple && option.value === "") el.setAttribute("data-placeholder", "");
|
|
670
|
+
if (this.isOptionDisabled(option)) el.setAttribute("aria-disabled", "true");
|
|
671
|
+
if (index === this.activeIndex) el.setAttribute("data-active", "");
|
|
672
|
+
const parent = option.parentElement;
|
|
673
|
+
if (parent instanceof HTMLOptGroupElement) {
|
|
674
|
+
let group = groups.get(parent);
|
|
675
|
+
if (!group) {
|
|
676
|
+
group = document.createElement("div");
|
|
677
|
+
group.setAttribute("role", "group");
|
|
678
|
+
group.setAttribute("aria-label", parent.label);
|
|
679
|
+
groups.set(parent, group);
|
|
680
|
+
children.push(group);
|
|
681
|
+
}
|
|
682
|
+
group.append(el);
|
|
683
|
+
} else children.push(el);
|
|
684
|
+
});
|
|
685
|
+
this.listbox.replaceChildren(...children);
|
|
686
|
+
if (!options.length) {
|
|
687
|
+
this.close();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
this.syncActiveDescendant();
|
|
691
|
+
}
|
|
692
|
+
syncInputDisplay() {
|
|
693
|
+
const state = this.sourceState;
|
|
694
|
+
if (!this.input || !(state?.source instanceof HTMLSelectElement)) return;
|
|
695
|
+
if (this.input.matches(":focus") && (this.multiple || this.isOpen())) return;
|
|
696
|
+
this.restoreCommittedText();
|
|
697
|
+
}
|
|
698
|
+
onInput = () => {
|
|
699
|
+
if (!this.editable) return;
|
|
700
|
+
this.activeIndex = this.firstEnabledIndex(this.filteredOptions());
|
|
701
|
+
this.renderOptions();
|
|
702
|
+
this.open();
|
|
703
|
+
};
|
|
704
|
+
onInputFocus = () => {
|
|
705
|
+
this.syncValidityState();
|
|
706
|
+
this.showSuggestions();
|
|
707
|
+
};
|
|
708
|
+
onInputClick = () => {
|
|
709
|
+
if (!this.isOpen()) this.showSuggestions();
|
|
710
|
+
};
|
|
711
|
+
onKeyDown = (e) => {
|
|
712
|
+
if (e.isComposing || !this.input || !this.editable) return;
|
|
713
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
714
|
+
e.preventDefault();
|
|
715
|
+
if (!this.isOpen() && this.sourceState?.source instanceof HTMLSelectElement) this.showSuggestions();
|
|
716
|
+
this.moveActive(e.key === "ArrowDown" ? 1 : -1);
|
|
717
|
+
this.open();
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (e.key === "Enter" && this.isOpen()) {
|
|
721
|
+
e.preventDefault();
|
|
722
|
+
this.chooseOption(this.filteredOptions()[this.activeIndex]);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (this.multiple && this.input.selectionStart === 0 && this.input.selectionEnd === 0 && (e.key === "ArrowLeft" || e.key === "Backspace" && this.input.value === "")) {
|
|
726
|
+
const lastToken = this.enabledTokenButtons().at(-1);
|
|
727
|
+
if (lastToken) {
|
|
728
|
+
e.preventDefault();
|
|
729
|
+
lastToken.focus();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
onControlMouseDown = (e) => {
|
|
734
|
+
if (e.target?.closest("[data-combobox-token]")) e.preventDefault();
|
|
735
|
+
};
|
|
736
|
+
onControlClick = (e) => {
|
|
737
|
+
const target = e.target;
|
|
738
|
+
const remove = target?.closest("[data-combobox-token-remove]");
|
|
739
|
+
if (remove) {
|
|
740
|
+
const option = this.optionForToken(remove);
|
|
741
|
+
if (option) this.toggleOption(option);
|
|
742
|
+
this.input?.focus();
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const token = target?.closest("[data-combobox-token]");
|
|
746
|
+
if (token) {
|
|
747
|
+
token.querySelector("[data-combobox-token-remove]")?.focus();
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
this.input?.focus();
|
|
751
|
+
if (!this.isOpen()) this.showSuggestions();
|
|
752
|
+
};
|
|
753
|
+
onWidgetKeyDown = (e) => {
|
|
754
|
+
if (e.isComposing) return;
|
|
755
|
+
if (e.key === "Escape" && this.isOpen()) {
|
|
756
|
+
e.preventDefault();
|
|
757
|
+
e.stopPropagation();
|
|
758
|
+
this.dismiss();
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const target = e.target;
|
|
762
|
+
const tokenFocused = target instanceof HTMLButtonElement && target.hasAttribute("data-combobox-token-remove");
|
|
763
|
+
if (e.key === "Tab") {
|
|
764
|
+
if (tokenFocused) this.input?.focus();
|
|
765
|
+
this.close();
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (!tokenFocused) return;
|
|
769
|
+
const tokens = this.enabledTokenButtons();
|
|
770
|
+
const index = tokens.indexOf(target);
|
|
771
|
+
if (e.key === "ArrowLeft" && index > 0) {
|
|
772
|
+
e.preventDefault();
|
|
773
|
+
tokens[index - 1].focus();
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (e.key === "ArrowRight") {
|
|
777
|
+
e.preventDefault();
|
|
778
|
+
(tokens[index + 1] ?? this.input)?.focus();
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (e.key === "Backspace" || e.key === "Delete") {
|
|
782
|
+
e.preventDefault();
|
|
783
|
+
const option = this.optionForToken(target);
|
|
784
|
+
if (!option) return;
|
|
785
|
+
this.toggleOption(option);
|
|
786
|
+
const remaining = this.enabledTokenButtons();
|
|
787
|
+
(remaining[e.key === "Backspace" ? index - 1 : Math.min(index, remaining.length - 1)] ?? this.input)?.focus();
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
onListboxClick = (e) => {
|
|
791
|
+
const optionEl = e.target?.closest("[role=\"option\"]");
|
|
792
|
+
if (!optionEl) return;
|
|
793
|
+
if (this.chooseOption(this.filteredOptions()[Number(optionEl.dataset.index)])) this.input?.focus();
|
|
794
|
+
};
|
|
795
|
+
onFocusOut = (e) => {
|
|
796
|
+
const next = e.relatedTarget;
|
|
797
|
+
if (next instanceof Node && this.contains(next)) return;
|
|
798
|
+
this.dismiss();
|
|
799
|
+
};
|
|
800
|
+
onDocumentPointerDown = (e) => {
|
|
801
|
+
if (!this.isOpen() || e.composedPath().includes(this)) return;
|
|
802
|
+
this.dismiss();
|
|
803
|
+
};
|
|
804
|
+
onLabelClick = (e) => {
|
|
805
|
+
if (e.defaultPrevented || e.target instanceof Element && !!e.target.closest("a[href], button, input, select, textarea, summary, [contenteditable]")) return;
|
|
806
|
+
e.preventDefault();
|
|
807
|
+
if (!(e.target instanceof Node && this.contains(e.target))) this.input?.focus();
|
|
808
|
+
};
|
|
809
|
+
onSourceUpdate = (event) => {
|
|
810
|
+
if (!internalSourceEvents.has(event)) this.sync.queue();
|
|
811
|
+
};
|
|
812
|
+
onFormReset = () => {
|
|
813
|
+
queueMicrotask(() => {
|
|
814
|
+
this.close();
|
|
815
|
+
this.sync.queue();
|
|
816
|
+
});
|
|
817
|
+
};
|
|
818
|
+
moveActive(delta) {
|
|
819
|
+
const options = this.filteredOptions();
|
|
820
|
+
if (!options.length) {
|
|
821
|
+
this.activeIndex = -1;
|
|
822
|
+
this.syncActiveDescendant();
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
let index = this.activeIndex === -1 && delta === -1 ? options.length : this.activeIndex;
|
|
826
|
+
for (let i = 0; i < options.length; i++) {
|
|
827
|
+
index = (index + delta + options.length) % options.length;
|
|
828
|
+
if (!this.isOptionDisabled(options[index])) {
|
|
829
|
+
this.activeIndex = index;
|
|
830
|
+
this.renderOptions();
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
showSuggestions() {
|
|
836
|
+
const state = this.sourceState;
|
|
837
|
+
if (!state || !this.input || !this.editable) return;
|
|
838
|
+
if (state.source instanceof HTMLSelectElement) this.input.value = "";
|
|
839
|
+
const options = this.filteredOptions();
|
|
840
|
+
const selectedIndex = options.findIndex((option) => this.isSelected(option) && !this.isOptionDisabled(option));
|
|
841
|
+
this.activeIndex = state.source instanceof HTMLSelectElement && selectedIndex !== -1 ? selectedIndex : this.firstEnabledIndex(options);
|
|
842
|
+
this.renderOptions();
|
|
843
|
+
this.open();
|
|
844
|
+
}
|
|
845
|
+
chooseOption(option) {
|
|
846
|
+
const state = this.sourceState;
|
|
847
|
+
if (!state || !option || !this.editable || this.isOptionDisabled(option)) return false;
|
|
848
|
+
if (this.multiple) {
|
|
849
|
+
this.toggleOption(option);
|
|
850
|
+
return true;
|
|
851
|
+
}
|
|
852
|
+
if (state.source instanceof HTMLSelectElement) {
|
|
853
|
+
if (!option.selected) {
|
|
854
|
+
option.selected = true;
|
|
855
|
+
this.emitSourceUpdate(state.source);
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
if (state.source.value !== option.value) state.source.value = option.value;
|
|
859
|
+
this.emitSourceUpdate(state.source);
|
|
860
|
+
}
|
|
861
|
+
this.syncValidityState();
|
|
862
|
+
this.dismiss();
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
toggleOption(option) {
|
|
866
|
+
const state = this.sourceState;
|
|
867
|
+
if (!(state?.source instanceof HTMLSelectElement) || !this.editable || this.isOptionDisabled(option)) return;
|
|
868
|
+
option.selected = !option.selected;
|
|
869
|
+
this.emitSourceUpdate(state.source);
|
|
870
|
+
this.syncValidityState();
|
|
871
|
+
if (this.input) this.input.value = "";
|
|
872
|
+
this.activeIndex = this.firstEnabledIndex(this.filteredOptions());
|
|
873
|
+
this.renderTokens();
|
|
874
|
+
this.renderOptions();
|
|
875
|
+
}
|
|
876
|
+
emitSourceUpdate(source) {
|
|
877
|
+
for (const type of ["input", "change"]) {
|
|
878
|
+
const event = new Event(type, {
|
|
879
|
+
bubbles: true,
|
|
880
|
+
composed: type === "input"
|
|
881
|
+
});
|
|
882
|
+
internalSourceEvents.add(event);
|
|
883
|
+
source.dispatchEvent(event);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
restoreCommittedText() {
|
|
887
|
+
const state = this.sourceState;
|
|
888
|
+
if (!this.input || !(state?.source instanceof HTMLSelectElement)) return;
|
|
889
|
+
if (this.multiple) {
|
|
890
|
+
this.input.value = "";
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const selected = state.source.options.item(state.source.selectedIndex);
|
|
894
|
+
const label = selected ? this.optionLabel(selected) : "";
|
|
895
|
+
this.input.placeholder = label;
|
|
896
|
+
this.input.value = selected?.value ? label : "";
|
|
897
|
+
}
|
|
898
|
+
open() {
|
|
899
|
+
if (!this.listbox || !this.input) return;
|
|
900
|
+
if (!this.listbox.childElementCount) {
|
|
901
|
+
this.close();
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const wasOpen = this.isOpen();
|
|
905
|
+
this.setInputAttribute("aria-expanded", "true");
|
|
906
|
+
if (!wasOpen) {
|
|
907
|
+
const source = this.control ?? this.input;
|
|
908
|
+
this.listbox.showPopover({ source });
|
|
909
|
+
}
|
|
910
|
+
this.syncActiveDescendant();
|
|
911
|
+
if (wasOpen) this.scrollActiveOptionIntoView();
|
|
912
|
+
else requestAnimationFrame(() => this.scrollActiveOptionIntoView());
|
|
913
|
+
}
|
|
914
|
+
close() {
|
|
915
|
+
if (!this.listbox || !this.input) return;
|
|
916
|
+
if (this.isOpen()) this.listbox.hidePopover();
|
|
917
|
+
this.setInputAttribute("aria-expanded", "false");
|
|
918
|
+
this.activeIndex = -1;
|
|
919
|
+
this.syncActiveDescendant();
|
|
920
|
+
}
|
|
921
|
+
dismiss() {
|
|
922
|
+
this.close();
|
|
923
|
+
this.restoreCommittedText();
|
|
924
|
+
}
|
|
925
|
+
isOpen() {
|
|
926
|
+
return this.listbox?.matches(":popover-open") ?? false;
|
|
927
|
+
}
|
|
928
|
+
get activeOption() {
|
|
929
|
+
return this.listbox?.querySelector(`[role="option"][data-index="${this.activeIndex}"]`) ?? void 0;
|
|
930
|
+
}
|
|
931
|
+
syncActiveDescendant() {
|
|
932
|
+
if (!this.input) return;
|
|
933
|
+
const option = this.activeOption;
|
|
934
|
+
if (this.isOpen() && option) this.setInputAttribute("aria-activedescendant", option.id);
|
|
935
|
+
else this.setInputAttribute("aria-activedescendant", null);
|
|
936
|
+
}
|
|
937
|
+
scrollActiveOptionIntoView() {
|
|
938
|
+
const listbox = this.listbox;
|
|
939
|
+
const option = this.activeOption;
|
|
940
|
+
if (!listbox || !option || !this.isOpen() || !listbox.clientHeight) return;
|
|
941
|
+
const listboxRect = listbox.getBoundingClientRect();
|
|
942
|
+
const optionRect = option.getBoundingClientRect();
|
|
943
|
+
const visibleTop = listboxRect.top + listbox.clientTop;
|
|
944
|
+
const visibleBottom = visibleTop + listbox.clientHeight;
|
|
945
|
+
if (optionRect.top < visibleTop) listbox.scrollTop -= visibleTop - optionRect.top;
|
|
946
|
+
else if (optionRect.bottom > visibleBottom) listbox.scrollTop += optionRect.bottom - visibleBottom;
|
|
947
|
+
}
|
|
948
|
+
enabledTokenButtons() {
|
|
949
|
+
return this.tokens ? Array.from(this.tokens.querySelectorAll("[data-combobox-token-remove]:not(:disabled)")) : [];
|
|
950
|
+
}
|
|
951
|
+
optionForToken(token) {
|
|
952
|
+
const source = this.sourceState?.source;
|
|
953
|
+
return source instanceof HTMLSelectElement ? source.options.item(Number(token.dataset.optionIndex)) : null;
|
|
954
|
+
}
|
|
955
|
+
filteredOptions() {
|
|
956
|
+
const query = this.input?.value.trim().toLowerCase() ?? "";
|
|
957
|
+
return this.options().filter((option) => {
|
|
958
|
+
if (!query) return true;
|
|
959
|
+
return this.optionLabel(option).toLowerCase().includes(query) || option.value.toLowerCase().includes(query);
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
options() {
|
|
963
|
+
const state = this.sourceState;
|
|
964
|
+
if (!state) return [];
|
|
965
|
+
if (state.source instanceof HTMLSelectElement) return Array.from(state.source.options).filter((option) => (!this.multiple || option.value !== "" && !option.selected) && !!this.optionLabel(option).trim() && !option.hidden && !(option.parentElement instanceof HTMLOptGroupElement && option.parentElement.hidden));
|
|
966
|
+
const datalist = "datalist" in state ? state.datalist : null;
|
|
967
|
+
return datalist ? Array.from(datalist.options).filter((option) => !option.hidden) : [];
|
|
968
|
+
}
|
|
969
|
+
firstEnabledIndex(options) {
|
|
970
|
+
return options.findIndex((option) => !this.isOptionDisabled(option));
|
|
971
|
+
}
|
|
972
|
+
isOptionDisabled(option) {
|
|
973
|
+
return option.disabled || option.parentElement instanceof HTMLOptGroupElement && option.parentElement.disabled;
|
|
974
|
+
}
|
|
975
|
+
optionLabel(option) {
|
|
976
|
+
return this.sourceState?.source instanceof HTMLInputElement ? option.label || option.value || option.textContent || "" : option.label || option.textContent || option.value;
|
|
977
|
+
}
|
|
978
|
+
isSelected(option) {
|
|
979
|
+
const source = this.sourceState?.source;
|
|
980
|
+
return source instanceof HTMLInputElement ? source.value === option.value : option.selected;
|
|
981
|
+
}
|
|
982
|
+
observeDatalist() {
|
|
983
|
+
const state = this.sourceState;
|
|
984
|
+
const datalist = state && "datalist" in state ? state.datalist : null;
|
|
985
|
+
if (!datalist) return;
|
|
986
|
+
this.observer.observe(datalist, {
|
|
987
|
+
subtree: true,
|
|
988
|
+
childList: true,
|
|
989
|
+
characterData: true,
|
|
990
|
+
attributes: true,
|
|
991
|
+
attributeFilter: [
|
|
992
|
+
"disabled",
|
|
993
|
+
"hidden",
|
|
994
|
+
"label",
|
|
995
|
+
"value"
|
|
996
|
+
]
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
discoverSource() {
|
|
1000
|
+
const candidates = Array.from(this.children).filter((child) => this.isSupportedSource(child));
|
|
1001
|
+
return candidates.length === 1 ? candidates[0] : void 0;
|
|
1002
|
+
}
|
|
1003
|
+
isSupportedSource(element) {
|
|
1004
|
+
if (element instanceof HTMLSelectElement) return true;
|
|
1005
|
+
return element instanceof HTMLInputElement && [
|
|
1006
|
+
"text",
|
|
1007
|
+
"search",
|
|
1008
|
+
"email",
|
|
1009
|
+
"url",
|
|
1010
|
+
"tel"
|
|
1011
|
+
].includes(element.type) && !(element.type === "email" && element.multiple) && !!element.getAttribute("list");
|
|
1012
|
+
}
|
|
1013
|
+
get multiple() {
|
|
1014
|
+
const source = this.sourceState?.source;
|
|
1015
|
+
return source instanceof HTMLSelectElement && source.multiple;
|
|
1016
|
+
}
|
|
1017
|
+
get editable() {
|
|
1018
|
+
const source = this.sourceState?.source;
|
|
1019
|
+
return !!(source && !this.sourceHidden && !source.matches(":disabled") && !(source instanceof HTMLInputElement && source.readOnly));
|
|
1020
|
+
}
|
|
1021
|
+
get sourceHidden() {
|
|
1022
|
+
const state = this.sourceState;
|
|
1023
|
+
return !!(state && (state.source.hidden || "cssHidden" in state && state.cssHidden));
|
|
1024
|
+
}
|
|
1025
|
+
snapshotInputAttributes(input) {
|
|
1026
|
+
return new Map([
|
|
1027
|
+
"role",
|
|
1028
|
+
"aria-autocomplete",
|
|
1029
|
+
"aria-haspopup",
|
|
1030
|
+
"aria-expanded",
|
|
1031
|
+
"aria-controls",
|
|
1032
|
+
"aria-activedescendant"
|
|
1033
|
+
].map((name) => {
|
|
1034
|
+
const value = input.getAttribute(name);
|
|
1035
|
+
return [name, {
|
|
1036
|
+
original: value,
|
|
1037
|
+
managed: value
|
|
1038
|
+
}];
|
|
1039
|
+
}));
|
|
1040
|
+
}
|
|
1041
|
+
createInputState(source) {
|
|
1042
|
+
const list = source.getAttribute("list");
|
|
1043
|
+
const datalist = getLookupRoot(this).querySelector(`#${CSS.escape(list)}`);
|
|
1044
|
+
return {
|
|
1045
|
+
source,
|
|
1046
|
+
list,
|
|
1047
|
+
datalist: datalist instanceof HTMLDataListElement ? datalist : null,
|
|
1048
|
+
inputAttributes: this.snapshotInputAttributes(source)
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
//#endregion
|
|
359
1053
|
//#region src/menu/menu.ts
|
|
360
|
-
var
|
|
1054
|
+
var MenuElement = class MenuElement extends HTMLElement {
|
|
361
1055
|
static searchDelay = 800;
|
|
362
1056
|
static observedAttributes = ["id", "popover"];
|
|
363
1057
|
itemSync;
|
|
364
1058
|
search = "";
|
|
365
1059
|
searchTimeout;
|
|
366
1060
|
connectedCallback() {
|
|
367
|
-
this.hasAttribute("role")
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
1061
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "menu");
|
|
1062
|
+
if (!this.hasAttribute("tabindex")) this.setAttribute("tabindex", "-1");
|
|
1063
|
+
this.addEventListener("keydown", this.onKeyDown);
|
|
1064
|
+
this.addEventListener("click", this.onClick);
|
|
1065
|
+
this.addEventListener("mouseenter", this.onMouseEnter, true);
|
|
1066
|
+
this.itemSync ??= createQueuedElementSync(this, {
|
|
1067
|
+
subtree: true,
|
|
1068
|
+
childList: true,
|
|
1069
|
+
attributes: true,
|
|
371
1070
|
attributeFilter: ["role", "tabindex"]
|
|
372
|
-
}, () => this.syncItems())
|
|
1071
|
+
}, () => this.syncItems());
|
|
1072
|
+
this.itemSync.observe();
|
|
1073
|
+
this.itemSync.queue();
|
|
1074
|
+
this.syncDocumentListeners();
|
|
373
1075
|
}
|
|
374
1076
|
disconnectedCallback() {
|
|
375
|
-
this.removeEventListener("keydown", this.onKeyDown)
|
|
1077
|
+
this.removeEventListener("keydown", this.onKeyDown);
|
|
1078
|
+
this.removeEventListener("click", this.onClick);
|
|
1079
|
+
this.removeEventListener("mouseenter", this.onMouseEnter, true);
|
|
1080
|
+
this.removeDocumentListeners();
|
|
1081
|
+
this.itemSync?.disconnect();
|
|
1082
|
+
this.closeSelfAndDescendants();
|
|
1083
|
+
clearTimeout(this.searchTimeout);
|
|
376
1084
|
}
|
|
377
|
-
attributeChangedCallback(
|
|
378
|
-
|
|
1085
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1086
|
+
if (oldValue === newValue) return;
|
|
1087
|
+
if (name !== "id" && name !== "popover") return;
|
|
1088
|
+
this.syncDocumentListeners();
|
|
379
1089
|
}
|
|
380
1090
|
get horizontal() {
|
|
381
1091
|
return this.getAttribute("aria-orientation") === "horizontal";
|
|
382
1092
|
}
|
|
383
1093
|
syncDocumentListeners() {
|
|
384
1094
|
if (this.isConnected && this.hasAttribute("popover") && !this.getParentMenuTrigger()) {
|
|
385
|
-
this.ownerDocument.addEventListener("click", this.onDocumentClick)
|
|
1095
|
+
this.ownerDocument.addEventListener("click", this.onDocumentClick);
|
|
1096
|
+
this.ownerDocument.addEventListener("keydown", this.onDocumentKeyDown);
|
|
386
1097
|
return;
|
|
387
1098
|
}
|
|
388
1099
|
this.removeDocumentListeners();
|
|
389
1100
|
}
|
|
390
1101
|
removeDocumentListeners() {
|
|
391
|
-
this.ownerDocument.removeEventListener("click", this.onDocumentClick)
|
|
1102
|
+
this.ownerDocument.removeEventListener("click", this.onDocumentClick);
|
|
1103
|
+
this.ownerDocument.removeEventListener("keydown", this.onDocumentKeyDown);
|
|
392
1104
|
}
|
|
393
1105
|
onDocumentClick = (e) => {
|
|
394
|
-
this.getRootTriggerForEvent(e)
|
|
395
|
-
|
|
1106
|
+
if (!this.getRootTriggerForEvent(e)) return;
|
|
1107
|
+
requestAnimationFrame(() => {
|
|
1108
|
+
if (!this.isActive()) return;
|
|
1109
|
+
if (e.detail === 0) this.getEnabledItems()[0]?.focus();
|
|
1110
|
+
else this.focus({ preventScroll: true });
|
|
396
1111
|
});
|
|
397
1112
|
};
|
|
398
1113
|
onDocumentKeyDown = (e) => {
|
|
399
|
-
|
|
400
|
-
if (!
|
|
401
|
-
e.
|
|
402
|
-
|
|
403
|
-
e.
|
|
1114
|
+
const trigger = this.getRootTriggerForEvent(e);
|
|
1115
|
+
if (!trigger) return;
|
|
1116
|
+
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
|
|
1117
|
+
if (this.hidden) return;
|
|
1118
|
+
e.preventDefault();
|
|
1119
|
+
if (!this.isActive()) this.showPopoverWithSource(trigger);
|
|
1120
|
+
const items = this.getEnabledItems();
|
|
1121
|
+
if (e.key === "ArrowDown") items[0]?.focus();
|
|
1122
|
+
else items[items.length - 1]?.focus();
|
|
404
1123
|
};
|
|
405
1124
|
onKeyDown = (e) => {
|
|
406
1125
|
if (!this.isActive()) return;
|
|
407
1126
|
if (e.key === "Tab") {
|
|
408
|
-
this.closeMenuTree()
|
|
1127
|
+
this.closeMenuTree();
|
|
1128
|
+
this.focusRootMenuTrigger();
|
|
409
1129
|
return;
|
|
410
1130
|
}
|
|
411
1131
|
if (e.key === "Escape") {
|
|
412
|
-
e.preventDefault()
|
|
1132
|
+
e.preventDefault();
|
|
1133
|
+
this.closeMenuTree();
|
|
1134
|
+
this.focusRootMenuTrigger();
|
|
413
1135
|
return;
|
|
414
1136
|
}
|
|
415
|
-
|
|
416
|
-
|
|
1137
|
+
const items = this.getEnabledItems();
|
|
1138
|
+
const activeItem = this.getActiveItem(items);
|
|
1139
|
+
const activeIndex = activeItem ? items.indexOf(activeItem) : -1;
|
|
1140
|
+
if (activeIndex === -1) {
|
|
417
1141
|
if (!this.horizontal && e.key === "ArrowDown" || this.horizontal && e.key === "ArrowRight") {
|
|
418
|
-
|
|
1142
|
+
items[0]?.focus();
|
|
1143
|
+
e.preventDefault();
|
|
419
1144
|
return;
|
|
420
1145
|
}
|
|
421
1146
|
if (!this.horizontal && e.key === "ArrowUp" || this.horizontal && e.key === "ArrowLeft") {
|
|
422
|
-
|
|
1147
|
+
items[items.length - 1]?.focus();
|
|
1148
|
+
e.preventDefault();
|
|
423
1149
|
return;
|
|
424
1150
|
}
|
|
425
1151
|
}
|
|
426
|
-
|
|
427
|
-
if (
|
|
1152
|
+
const activeSubmenu = activeItem ? this.getSubmenu(activeItem) : null;
|
|
1153
|
+
if (activeItem && activeSubmenu) {
|
|
428
1154
|
if (!this.horizontal && e.key === "ArrowRight" || this.horizontal && e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
429
|
-
this.openSubmenu(
|
|
1155
|
+
this.openSubmenu(activeItem);
|
|
1156
|
+
activeSubmenu.getItems()[0]?.focus();
|
|
430
1157
|
return;
|
|
431
1158
|
}
|
|
432
1159
|
if (this.horizontal && e.key === "ArrowUp") {
|
|
433
|
-
this.openSubmenu(
|
|
1160
|
+
this.openSubmenu(activeItem);
|
|
1161
|
+
activeSubmenu.getItems().at(-1)?.focus();
|
|
434
1162
|
return;
|
|
435
1163
|
}
|
|
436
1164
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
1165
|
+
const parentMenuTrigger = this.getParentMenuTrigger();
|
|
1166
|
+
const parentMenu = parentMenuTrigger?.closest("ui-menu");
|
|
1167
|
+
if (parentMenu && e.key === (parentMenu.horizontal ? "ArrowUp" : "ArrowLeft")) {
|
|
1168
|
+
this.closeSelfAndDescendants();
|
|
1169
|
+
parentMenuTrigger?.focus({ preventScroll: true });
|
|
440
1170
|
return;
|
|
441
1171
|
}
|
|
442
|
-
|
|
443
|
-
currentIndex:
|
|
444
|
-
length:
|
|
1172
|
+
const nextIndex = getRovingIndexForKey(e.key, {
|
|
1173
|
+
currentIndex: activeIndex,
|
|
1174
|
+
length: items.length,
|
|
445
1175
|
vertical: !this.horizontal
|
|
446
1176
|
});
|
|
447
|
-
if (
|
|
448
|
-
|
|
1177
|
+
if (nextIndex !== void 0) {
|
|
1178
|
+
items[nextIndex]?.focus();
|
|
1179
|
+
e.preventDefault();
|
|
449
1180
|
return;
|
|
450
1181
|
}
|
|
451
|
-
this.handleTypeahead(e,
|
|
1182
|
+
this.handleTypeahead(e, items, activeItem);
|
|
452
1183
|
};
|
|
453
1184
|
onClick = (e) => {
|
|
454
|
-
|
|
455
|
-
if (!
|
|
456
|
-
|
|
457
|
-
if (
|
|
458
|
-
e.preventDefault()
|
|
1185
|
+
const item = this.getItemForEvent(e);
|
|
1186
|
+
if (!item) return;
|
|
1187
|
+
const submenu = this.getSubmenu(item);
|
|
1188
|
+
if (submenu) {
|
|
1189
|
+
e.preventDefault();
|
|
1190
|
+
if (!submenu.isActive()) {
|
|
1191
|
+
this.openSubmenu(item);
|
|
1192
|
+
submenu.getItems()[0]?.focus();
|
|
1193
|
+
}
|
|
459
1194
|
return;
|
|
460
1195
|
}
|
|
461
1196
|
this.closeMenuTree();
|
|
462
1197
|
};
|
|
463
1198
|
onMouseEnter = (e) => {
|
|
464
1199
|
if (!this.isActive()) return;
|
|
465
|
-
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
this.closeSubmenus();
|
|
1200
|
+
const item = this.getItemForEvent(e);
|
|
1201
|
+
if (!item) return;
|
|
1202
|
+
item.focus();
|
|
1203
|
+
if (this.getSubmenu(item)) {
|
|
1204
|
+
this.openSubmenu(item);
|
|
1205
|
+
return;
|
|
472
1206
|
}
|
|
1207
|
+
this.closeSubmenus();
|
|
473
1208
|
};
|
|
474
1209
|
isActive() {
|
|
475
|
-
|
|
1210
|
+
if (this.hidden) return false;
|
|
1211
|
+
return !this.hasAttribute("popover") || this.matches(":popover-open");
|
|
476
1212
|
}
|
|
477
1213
|
syncItems() {
|
|
478
|
-
this.getItems().forEach((
|
|
479
|
-
|
|
1214
|
+
this.getItems().forEach((item) => {
|
|
1215
|
+
if (item.getAttribute("tabindex") !== "-1") item.setAttribute("tabindex", "-1");
|
|
480
1216
|
});
|
|
481
1217
|
}
|
|
482
1218
|
getItems() {
|
|
483
|
-
return Array.from(this.querySelectorAll("[role^=menuitem]")).filter((
|
|
1219
|
+
return Array.from(this.querySelectorAll("[role^=menuitem]")).filter((item) => this.belongsToThisMenu(item));
|
|
484
1220
|
}
|
|
485
1221
|
getEnabledItems() {
|
|
486
|
-
return this.getItems().filter((
|
|
1222
|
+
return this.getItems().filter((item) => this.isItemEnabled(item));
|
|
487
1223
|
}
|
|
488
|
-
isItemEnabled(
|
|
489
|
-
return !
|
|
1224
|
+
isItemEnabled(item) {
|
|
1225
|
+
return !item.hidden && !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true";
|
|
490
1226
|
}
|
|
491
|
-
belongsToThisMenu(
|
|
492
|
-
return
|
|
1227
|
+
belongsToThisMenu(item) {
|
|
1228
|
+
return item.closest("ui-menu") === this;
|
|
493
1229
|
}
|
|
494
|
-
getActiveItem(
|
|
495
|
-
|
|
496
|
-
return
|
|
1230
|
+
getActiveItem(items) {
|
|
1231
|
+
const activeElement = getLookupRoot(this).activeElement;
|
|
1232
|
+
return items.find((item) => item === activeElement);
|
|
497
1233
|
}
|
|
498
|
-
getItemForEvent(
|
|
499
|
-
|
|
500
|
-
if (!(
|
|
501
|
-
|
|
502
|
-
return
|
|
1234
|
+
getItemForEvent(event) {
|
|
1235
|
+
const target = event.target;
|
|
1236
|
+
if (!(target instanceof Element)) return null;
|
|
1237
|
+
const item = target.closest("[role^=menuitem]");
|
|
1238
|
+
return item && this.belongsToThisMenu(item) && this.isItemEnabled(item) ? item : null;
|
|
503
1239
|
}
|
|
504
|
-
getRootTriggerForEvent(
|
|
505
|
-
for (
|
|
1240
|
+
getRootTriggerForEvent(event) {
|
|
1241
|
+
for (const candidate of event.composedPath()) if (candidate instanceof HTMLElement && !candidate.closest("ui-menu") && candidate.getAttribute("popovertarget") === this.id) return candidate;
|
|
506
1242
|
return null;
|
|
507
1243
|
}
|
|
508
1244
|
getTriggers() {
|
|
509
|
-
|
|
1245
|
+
if (!this.id) return [];
|
|
1246
|
+
return Array.from(getLookupRoot(this).querySelectorAll(`[popovertarget="${CSS.escape(this.id)}"]`));
|
|
510
1247
|
}
|
|
511
1248
|
getParentMenuTrigger() {
|
|
512
|
-
return this.getTriggers().find((
|
|
1249
|
+
return this.getTriggers().find((trigger) => trigger.closest("ui-menu")) || null;
|
|
513
1250
|
}
|
|
514
1251
|
getParentMenu() {
|
|
515
1252
|
return this.getParentMenuTrigger()?.closest("ui-menu") || null;
|
|
516
1253
|
}
|
|
517
|
-
getSubmenu(
|
|
518
|
-
|
|
519
|
-
|
|
1254
|
+
getSubmenu(item) {
|
|
1255
|
+
const id = item.getAttribute("popovertarget");
|
|
1256
|
+
if (!id) return null;
|
|
1257
|
+
return getLookupRoot(this).querySelector(`ui-menu#${CSS.escape(id)}`);
|
|
520
1258
|
}
|
|
521
|
-
openSubmenu(
|
|
522
|
-
|
|
523
|
-
|
|
1259
|
+
openSubmenu(item) {
|
|
1260
|
+
const submenu = this.getSubmenu(item);
|
|
1261
|
+
if (!submenu) return;
|
|
1262
|
+
this.closeSubmenus();
|
|
1263
|
+
submenu.showPopoverWithSource(item);
|
|
524
1264
|
}
|
|
525
1265
|
closeSubmenus() {
|
|
526
|
-
this.getItems().forEach((
|
|
1266
|
+
this.getItems().forEach((item) => this.getSubmenu(item)?.closeSelfAndDescendants());
|
|
527
1267
|
}
|
|
528
1268
|
closeSelfAndDescendants() {
|
|
529
|
-
this.closeSubmenus()
|
|
1269
|
+
this.closeSubmenus();
|
|
1270
|
+
if (this.hasAttribute("popover") && this.matches(":popover-open")) this.hidePopover();
|
|
530
1271
|
}
|
|
531
1272
|
closeMenuTree() {
|
|
532
|
-
this.closeSelfAndDescendants()
|
|
1273
|
+
this.closeSelfAndDescendants();
|
|
1274
|
+
this.getParentMenu()?.closeMenuTree();
|
|
533
1275
|
}
|
|
534
1276
|
focusRootMenuTrigger() {
|
|
535
|
-
this.getRootMenuTrigger()?.focus({ preventScroll:
|
|
1277
|
+
this.getRootMenuTrigger()?.focus({ preventScroll: true });
|
|
536
1278
|
}
|
|
537
1279
|
getRootMenuTrigger() {
|
|
538
|
-
|
|
539
|
-
return
|
|
1280
|
+
const parentMenu = this.getParentMenu();
|
|
1281
|
+
return parentMenu ? parentMenu.getRootMenuTrigger() : this.getTriggers()[0] || null;
|
|
540
1282
|
}
|
|
541
|
-
handleTypeahead(e,
|
|
542
|
-
if (e.key === " "
|
|
1283
|
+
handleTypeahead(e, items, activeItem) {
|
|
1284
|
+
if (e.key === " ") return;
|
|
1285
|
+
if (e.key.length > 1 || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
1286
|
+
if (!items.length) return;
|
|
543
1287
|
e.preventDefault();
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
1288
|
+
const search = this.updateTypeaheadSearch(e.key);
|
|
1289
|
+
const startIndex = activeItem ? items.indexOf(activeItem) : -1;
|
|
1290
|
+
this.findTypeaheadMatch(items, search, startIndex)?.focus();
|
|
1291
|
+
}
|
|
1292
|
+
updateTypeaheadSearch(key) {
|
|
1293
|
+
this.search += key.toLowerCase();
|
|
1294
|
+
clearTimeout(this.searchTimeout);
|
|
1295
|
+
this.searchTimeout = window.setTimeout(() => {
|
|
549
1296
|
this.search = "";
|
|
550
|
-
},
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
1297
|
+
}, MenuElement.searchDelay);
|
|
1298
|
+
if (this.search.length > 1 && this.search.split("").every((character) => character === this.search[0])) return this.search[0];
|
|
1299
|
+
return this.search;
|
|
1300
|
+
}
|
|
1301
|
+
findTypeaheadMatch(items, search, startIndex) {
|
|
1302
|
+
for (let offset = 1; offset <= items.length; offset += 1) {
|
|
1303
|
+
const item = items[(startIndex + offset + items.length) % items.length];
|
|
1304
|
+
if (this.getItemLabel(item).startsWith(search)) return item;
|
|
556
1305
|
}
|
|
557
1306
|
}
|
|
558
|
-
getItemLabel(
|
|
559
|
-
return (
|
|
1307
|
+
getItemLabel(item) {
|
|
1308
|
+
return (item.getAttribute("aria-label") || item.textContent || "").trim().toLowerCase();
|
|
560
1309
|
}
|
|
561
|
-
showPopoverWithSource(
|
|
562
|
-
this.showPopover?.({ source
|
|
1310
|
+
showPopoverWithSource(source) {
|
|
1311
|
+
this.showPopover?.({ source });
|
|
563
1312
|
}
|
|
564
|
-
}
|
|
1313
|
+
};
|
|
1314
|
+
/*!
|
|
1315
|
+
* tabbable 6.4.0
|
|
1316
|
+
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
|
|
1317
|
+
*/
|
|
1318
|
+
var candidateSelector = /* #__PURE__ */ [
|
|
565
1319
|
"input:not([inert]):not([inert] *)",
|
|
566
1320
|
"select:not([inert]):not([inert] *)",
|
|
567
1321
|
"textarea:not([inert]):not([inert] *)",
|
|
@@ -573,305 +1327,514 @@ var I = class e extends HTMLElement {
|
|
|
573
1327
|
"[contenteditable]:not([contenteditable=\"false\"]):not([inert]):not([inert] *)",
|
|
574
1328
|
"details>summary:first-of-type:not([inert]):not([inert] *)",
|
|
575
1329
|
"details:not([inert]):not([inert] *)"
|
|
576
|
-
].join(",")
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
1330
|
+
].join(",");
|
|
1331
|
+
var NoElement = typeof Element === "undefined";
|
|
1332
|
+
var matches = NoElement ? function() {} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
|
|
1333
|
+
var getRootNode = !NoElement && Element.prototype.getRootNode ? function(element) {
|
|
1334
|
+
var _element$getRootNode;
|
|
1335
|
+
return element === null || element === void 0 ? void 0 : (_element$getRootNode = element.getRootNode) === null || _element$getRootNode === void 0 ? void 0 : _element$getRootNode.call(element);
|
|
1336
|
+
} : function(element) {
|
|
1337
|
+
return element === null || element === void 0 ? void 0 : element.ownerDocument;
|
|
1338
|
+
};
|
|
1339
|
+
/**
|
|
1340
|
+
* Determines if a node is inert or in an inert ancestor.
|
|
1341
|
+
* @param {Node} [node]
|
|
1342
|
+
* @param {boolean} [lookUp] If true and `node` is not inert, looks up at ancestors to
|
|
1343
|
+
* see if any of them are inert. If false, only `node` itself is considered.
|
|
1344
|
+
* @returns {boolean} True if inert itself or by way of being in an inert ancestor.
|
|
1345
|
+
* False if `node` is falsy.
|
|
1346
|
+
*/
|
|
1347
|
+
var _isInert = function isInert(node, lookUp) {
|
|
1348
|
+
var _node$getAttribute;
|
|
1349
|
+
if (lookUp === void 0) lookUp = true;
|
|
1350
|
+
var inertAtt = node === null || node === void 0 ? void 0 : (_node$getAttribute = node.getAttribute) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.call(node, "inert");
|
|
1351
|
+
return inertAtt === "" || inertAtt === "true" || lookUp && node && (typeof node.closest === "function" ? node.closest("[inert]") : _isInert(node.parentNode));
|
|
1352
|
+
};
|
|
1353
|
+
/**
|
|
1354
|
+
* Determines if a node's content is editable.
|
|
1355
|
+
* @param {Element} [node]
|
|
1356
|
+
* @returns True if it's content-editable; false if it's not or `node` is falsy.
|
|
1357
|
+
*/
|
|
1358
|
+
var isContentEditable = function isContentEditable(node) {
|
|
1359
|
+
var _node$getAttribute2;
|
|
1360
|
+
var attValue = node === null || node === void 0 ? void 0 : (_node$getAttribute2 = node.getAttribute) === null || _node$getAttribute2 === void 0 ? void 0 : _node$getAttribute2.call(node, "contenteditable");
|
|
1361
|
+
return attValue === "" || attValue === "true";
|
|
1362
|
+
};
|
|
1363
|
+
/**
|
|
1364
|
+
* @param {Element} el container to check in
|
|
1365
|
+
* @param {boolean} includeContainer add container to check
|
|
1366
|
+
* @param {(node: Element) => boolean} filter filter candidates
|
|
1367
|
+
* @returns {Element[]}
|
|
1368
|
+
*/
|
|
1369
|
+
var getCandidates = function getCandidates(el, includeContainer, filter) {
|
|
1370
|
+
if (_isInert(el)) return [];
|
|
1371
|
+
var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector));
|
|
1372
|
+
if (includeContainer && matches.call(el, candidateSelector)) candidates.unshift(el);
|
|
1373
|
+
candidates = candidates.filter(filter);
|
|
1374
|
+
return candidates;
|
|
1375
|
+
};
|
|
1376
|
+
/**
|
|
1377
|
+
* @callback GetShadowRoot
|
|
1378
|
+
* @param {Element} element to check for shadow root
|
|
1379
|
+
* @returns {ShadowRoot|boolean} ShadowRoot if available or boolean indicating if a shadowRoot is attached but not available.
|
|
1380
|
+
*/
|
|
1381
|
+
/**
|
|
1382
|
+
* @callback ShadowRootFilter
|
|
1383
|
+
* @param {Element} shadowHostNode the element which contains shadow content
|
|
1384
|
+
* @returns {boolean} true if a shadow root could potentially contain valid candidates.
|
|
1385
|
+
*/
|
|
1386
|
+
/**
|
|
1387
|
+
* @typedef {Object} CandidateScope
|
|
1388
|
+
* @property {Element} scopeParent contains inner candidates
|
|
1389
|
+
* @property {Element[]} candidates list of candidates found in the scope parent
|
|
1390
|
+
*/
|
|
1391
|
+
/**
|
|
1392
|
+
* @typedef {Object} IterativeOptions
|
|
1393
|
+
* @property {GetShadowRoot|boolean} getShadowRoot true if shadow support is enabled; falsy if not;
|
|
1394
|
+
* if a function, implies shadow support is enabled and either returns the shadow root of an element
|
|
1395
|
+
* or a boolean stating if it has an undisclosed shadow root
|
|
1396
|
+
* @property {(node: Element) => boolean} filter filter candidates
|
|
1397
|
+
* @property {boolean} flatten if true then result will flatten any CandidateScope into the returned list
|
|
1398
|
+
* @property {ShadowRootFilter} shadowRootFilter filter shadow roots;
|
|
1399
|
+
*/
|
|
1400
|
+
/**
|
|
1401
|
+
* @param {Element[]} elements list of element containers to match candidates from
|
|
1402
|
+
* @param {boolean} includeContainer add container list to check
|
|
1403
|
+
* @param {IterativeOptions} options
|
|
1404
|
+
* @returns {Array.<Element|CandidateScope>}
|
|
1405
|
+
*/
|
|
1406
|
+
var _getCandidatesIteratively = function getCandidatesIteratively(elements, includeContainer, options) {
|
|
1407
|
+
var candidates = [];
|
|
1408
|
+
var elementsToCheck = Array.from(elements);
|
|
1409
|
+
while (elementsToCheck.length) {
|
|
1410
|
+
var element = elementsToCheck.shift();
|
|
1411
|
+
if (_isInert(element, false)) continue;
|
|
1412
|
+
if (element.tagName === "SLOT") {
|
|
1413
|
+
var assigned = element.assignedElements();
|
|
1414
|
+
var nestedCandidates = _getCandidatesIteratively(assigned.length ? assigned : element.children, true, options);
|
|
1415
|
+
if (options.flatten) candidates.push.apply(candidates, nestedCandidates);
|
|
1416
|
+
else candidates.push({
|
|
1417
|
+
scopeParent: element,
|
|
1418
|
+
candidates: nestedCandidates
|
|
599
1419
|
});
|
|
600
1420
|
} else {
|
|
601
|
-
|
|
602
|
-
var
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
1421
|
+
if (matches.call(element, candidateSelector) && options.filter(element) && (includeContainer || !elements.includes(element))) candidates.push(element);
|
|
1422
|
+
var shadowRoot = element.shadowRoot || typeof options.getShadowRoot === "function" && options.getShadowRoot(element);
|
|
1423
|
+
var validShadowRoot = !_isInert(shadowRoot, false) && (!options.shadowRootFilter || options.shadowRootFilter(element));
|
|
1424
|
+
if (shadowRoot && validShadowRoot) {
|
|
1425
|
+
var _nestedCandidates = _getCandidatesIteratively(shadowRoot === true ? element.children : shadowRoot.children, true, options);
|
|
1426
|
+
if (options.flatten) candidates.push.apply(candidates, _nestedCandidates);
|
|
1427
|
+
else candidates.push({
|
|
1428
|
+
scopeParent: element,
|
|
1429
|
+
candidates: _nestedCandidates
|
|
608
1430
|
});
|
|
609
|
-
} else
|
|
1431
|
+
} else elementsToCheck.unshift.apply(elementsToCheck, element.children);
|
|
610
1432
|
}
|
|
611
1433
|
}
|
|
612
|
-
return
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
|
|
1434
|
+
return candidates;
|
|
1435
|
+
};
|
|
1436
|
+
/**
|
|
1437
|
+
* @private
|
|
1438
|
+
* Determines if the node has an explicitly specified `tabindex` attribute.
|
|
1439
|
+
* @param {HTMLElement} node
|
|
1440
|
+
* @returns {boolean} True if so; false if not.
|
|
1441
|
+
*/
|
|
1442
|
+
var hasTabIndex = function hasTabIndex(node) {
|
|
1443
|
+
return !isNaN(parseInt(node.getAttribute("tabindex"), 10));
|
|
1444
|
+
};
|
|
1445
|
+
/**
|
|
1446
|
+
* Determine the tab index of a given node.
|
|
1447
|
+
* @param {HTMLElement} node
|
|
1448
|
+
* @returns {number} Tab order (negative, 0, or positive number).
|
|
1449
|
+
* @throws {Error} If `node` is falsy.
|
|
1450
|
+
*/
|
|
1451
|
+
var getTabIndex = function getTabIndex(node) {
|
|
1452
|
+
if (!node) throw new Error("No node provided");
|
|
1453
|
+
if (node.tabIndex < 0) {
|
|
1454
|
+
if ((/^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || isContentEditable(node)) && !hasTabIndex(node)) return 0;
|
|
1455
|
+
}
|
|
1456
|
+
return node.tabIndex;
|
|
1457
|
+
};
|
|
1458
|
+
/**
|
|
1459
|
+
* Determine the tab index of a given node __for sort order purposes__.
|
|
1460
|
+
* @param {HTMLElement} node
|
|
1461
|
+
* @param {boolean} [isScope] True for a custom element with shadow root or slot that, by default,
|
|
1462
|
+
* has tabIndex -1, but needs to be sorted by document order in order for its content to be
|
|
1463
|
+
* inserted into the correct sort position.
|
|
1464
|
+
* @returns {number} Tab order (negative, 0, or positive number).
|
|
1465
|
+
*/
|
|
1466
|
+
var getSortOrderTabIndex = function getSortOrderTabIndex(node, isScope) {
|
|
1467
|
+
var tabIndex = getTabIndex(node);
|
|
1468
|
+
if (tabIndex < 0 && isScope && !hasTabIndex(node)) return 0;
|
|
1469
|
+
return tabIndex;
|
|
1470
|
+
};
|
|
1471
|
+
var sortOrderedTabbables = function sortOrderedTabbables(a, b) {
|
|
1472
|
+
return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;
|
|
1473
|
+
};
|
|
1474
|
+
var isInput = function isInput(node) {
|
|
1475
|
+
return node.tagName === "INPUT";
|
|
1476
|
+
};
|
|
1477
|
+
var isHiddenInput = function isHiddenInput(node) {
|
|
1478
|
+
return isInput(node) && node.type === "hidden";
|
|
1479
|
+
};
|
|
1480
|
+
var isDetailsWithSummary = function isDetailsWithSummary(node) {
|
|
1481
|
+
return node.tagName === "DETAILS" && Array.prototype.slice.apply(node.children).some(function(child) {
|
|
1482
|
+
return child.tagName === "SUMMARY";
|
|
630
1483
|
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
1484
|
+
};
|
|
1485
|
+
var getCheckedRadio = function getCheckedRadio(nodes, form) {
|
|
1486
|
+
for (var i = 0; i < nodes.length; i++) if (nodes[i].checked && nodes[i].form === form) return nodes[i];
|
|
1487
|
+
};
|
|
1488
|
+
var isTabbableRadio = function isTabbableRadio(node) {
|
|
1489
|
+
if (!node.name) return true;
|
|
1490
|
+
var radioScope = node.form || getRootNode(node);
|
|
1491
|
+
var queryRadios = function queryRadios(name) {
|
|
1492
|
+
return radioScope.querySelectorAll("input[type=\"radio\"][name=\"" + name + "\"]");
|
|
1493
|
+
};
|
|
1494
|
+
var radioSet;
|
|
1495
|
+
if (typeof window !== "undefined" && typeof window.CSS !== "undefined" && typeof window.CSS.escape === "function") radioSet = queryRadios(window.CSS.escape(node.name));
|
|
639
1496
|
else try {
|
|
640
|
-
|
|
641
|
-
} catch (
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
1497
|
+
radioSet = queryRadios(node.name);
|
|
1498
|
+
} catch (err) {
|
|
1499
|
+
console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s", err.message);
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
var checked = getCheckedRadio(radioSet, node.form);
|
|
1503
|
+
return !checked || checked === node;
|
|
1504
|
+
};
|
|
1505
|
+
var isRadio = function isRadio(node) {
|
|
1506
|
+
return isInput(node) && node.type === "radio";
|
|
1507
|
+
};
|
|
1508
|
+
var isNonTabbableRadio = function isNonTabbableRadio(node) {
|
|
1509
|
+
return isRadio(node) && !isTabbableRadio(node);
|
|
1510
|
+
};
|
|
1511
|
+
var isNodeAttached = function isNodeAttached(node) {
|
|
1512
|
+
var _nodeRoot;
|
|
1513
|
+
var nodeRoot = node && getRootNode(node);
|
|
1514
|
+
var nodeRootHost = (_nodeRoot = nodeRoot) === null || _nodeRoot === void 0 ? void 0 : _nodeRoot.host;
|
|
1515
|
+
var attached = false;
|
|
1516
|
+
if (nodeRoot && nodeRoot !== node) {
|
|
1517
|
+
var _nodeRootHost, _nodeRootHost$ownerDo, _node$ownerDocument;
|
|
1518
|
+
attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && (_nodeRootHost$ownerDo = _nodeRootHost.ownerDocument) !== null && _nodeRootHost$ownerDo !== void 0 && _nodeRootHost$ownerDo.contains(nodeRootHost) || node !== null && node !== void 0 && (_node$ownerDocument = node.ownerDocument) !== null && _node$ownerDocument !== void 0 && _node$ownerDocument.contains(node));
|
|
1519
|
+
while (!attached && nodeRootHost) {
|
|
1520
|
+
var _nodeRoot2, _nodeRootHost2, _nodeRootHost2$ownerD;
|
|
1521
|
+
nodeRoot = getRootNode(nodeRootHost);
|
|
1522
|
+
nodeRootHost = (_nodeRoot2 = nodeRoot) === null || _nodeRoot2 === void 0 ? void 0 : _nodeRoot2.host;
|
|
1523
|
+
attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && (_nodeRootHost2$ownerD = _nodeRootHost2.ownerDocument) !== null && _nodeRootHost2$ownerD !== void 0 && _nodeRootHost2$ownerD.contains(nodeRootHost));
|
|
657
1524
|
}
|
|
658
1525
|
}
|
|
659
|
-
return
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
1526
|
+
return attached;
|
|
1527
|
+
};
|
|
1528
|
+
var isZeroArea = function isZeroArea(node) {
|
|
1529
|
+
var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;
|
|
1530
|
+
return width === 0 && height === 0;
|
|
1531
|
+
};
|
|
1532
|
+
var isHidden = function isHidden(node, _ref) {
|
|
1533
|
+
var displayCheck = _ref.displayCheck, getShadowRoot = _ref.getShadowRoot;
|
|
1534
|
+
if (displayCheck === "full-native") {
|
|
1535
|
+
if ("checkVisibility" in node) return !node.checkVisibility({
|
|
1536
|
+
checkOpacity: false,
|
|
1537
|
+
opacityProperty: false,
|
|
1538
|
+
contentVisibilityAuto: true,
|
|
1539
|
+
visibilityProperty: true,
|
|
1540
|
+
checkVisibilityCSS: true
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
if (getComputedStyle(node).visibility === "hidden") return true;
|
|
1544
|
+
var nodeUnderDetails = matches.call(node, "details>summary:first-of-type") ? node.parentElement : node;
|
|
1545
|
+
if (matches.call(nodeUnderDetails, "details:not([open]) *")) return true;
|
|
1546
|
+
if (!displayCheck || displayCheck === "full" || displayCheck === "full-native" || displayCheck === "legacy-full") {
|
|
1547
|
+
if (typeof getShadowRoot === "function") {
|
|
1548
|
+
var originalNode = node;
|
|
1549
|
+
while (node) {
|
|
1550
|
+
var parentElement = node.parentElement;
|
|
1551
|
+
var rootNode = getRootNode(node);
|
|
1552
|
+
if (parentElement && !parentElement.shadowRoot && getShadowRoot(parentElement) === true) return isZeroArea(node);
|
|
1553
|
+
else if (node.assignedSlot) node = node.assignedSlot;
|
|
1554
|
+
else if (!parentElement && rootNode !== node.ownerDocument) node = rootNode.host;
|
|
1555
|
+
else node = parentElement;
|
|
681
1556
|
}
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
if (
|
|
685
|
-
if (
|
|
686
|
-
} else if (
|
|
687
|
-
return
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1557
|
+
node = originalNode;
|
|
1558
|
+
}
|
|
1559
|
+
if (isNodeAttached(node)) return !node.getClientRects().length;
|
|
1560
|
+
if (displayCheck !== "legacy-full") return true;
|
|
1561
|
+
} else if (displayCheck === "non-zero-area") return isZeroArea(node);
|
|
1562
|
+
return false;
|
|
1563
|
+
};
|
|
1564
|
+
var isDisabledFromFieldset = function isDisabledFromFieldset(node) {
|
|
1565
|
+
if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(node.tagName)) {
|
|
1566
|
+
var parentNode = node.parentElement;
|
|
1567
|
+
while (parentNode) {
|
|
1568
|
+
if (parentNode.tagName === "FIELDSET" && parentNode.disabled) {
|
|
1569
|
+
for (var i = 0; i < parentNode.children.length; i++) {
|
|
1570
|
+
var child = parentNode.children.item(i);
|
|
1571
|
+
if (child.tagName === "LEGEND") return matches.call(parentNode, "fieldset[disabled] *") ? true : !child.contains(node);
|
|
1572
|
+
}
|
|
1573
|
+
return true;
|
|
694
1574
|
}
|
|
695
|
-
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1575
|
+
parentNode = parentNode.parentElement;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return false;
|
|
1579
|
+
};
|
|
1580
|
+
var isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable(options, node) {
|
|
1581
|
+
if (node.disabled || isHiddenInput(node) || isHidden(node, options) || isDetailsWithSummary(node) || isDisabledFromFieldset(node)) return false;
|
|
1582
|
+
return true;
|
|
1583
|
+
};
|
|
1584
|
+
var isNodeMatchingSelectorTabbable = function isNodeMatchingSelectorTabbable(options, node) {
|
|
1585
|
+
if (isNonTabbableRadio(node) || getTabIndex(node) < 0 || !isNodeMatchingSelectorFocusable(options, node)) return false;
|
|
1586
|
+
return true;
|
|
1587
|
+
};
|
|
1588
|
+
var isShadowRootTabbable = function isShadowRootTabbable(shadowHostNode) {
|
|
1589
|
+
var tabIndex = parseInt(shadowHostNode.getAttribute("tabindex"), 10);
|
|
1590
|
+
if (isNaN(tabIndex) || tabIndex >= 0) return true;
|
|
1591
|
+
return false;
|
|
1592
|
+
};
|
|
1593
|
+
/**
|
|
1594
|
+
* @param {Array.<Element|CandidateScope>} candidates
|
|
1595
|
+
* @returns Element[]
|
|
1596
|
+
*/
|
|
1597
|
+
var _sortByOrder = function sortByOrder(candidates) {
|
|
1598
|
+
var regularTabbables = [];
|
|
1599
|
+
var orderedTabbables = [];
|
|
1600
|
+
candidates.forEach(function(item, i) {
|
|
1601
|
+
var isScope = !!item.scopeParent;
|
|
1602
|
+
var element = isScope ? item.scopeParent : item;
|
|
1603
|
+
var candidateTabindex = getSortOrderTabIndex(element, isScope);
|
|
1604
|
+
var elements = isScope ? _sortByOrder(item.candidates) : element;
|
|
1605
|
+
if (candidateTabindex === 0) isScope ? regularTabbables.push.apply(regularTabbables, elements) : regularTabbables.push(element);
|
|
1606
|
+
else orderedTabbables.push({
|
|
1607
|
+
documentOrder: i,
|
|
1608
|
+
tabIndex: candidateTabindex,
|
|
1609
|
+
item,
|
|
1610
|
+
isScope,
|
|
1611
|
+
content: elements
|
|
717
1612
|
});
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
1613
|
+
});
|
|
1614
|
+
return orderedTabbables.sort(sortOrderedTabbables).reduce(function(acc, sortable) {
|
|
1615
|
+
sortable.isScope ? acc.push.apply(acc, sortable.content) : acc.push(sortable.content);
|
|
1616
|
+
return acc;
|
|
1617
|
+
}, []).concat(regularTabbables);
|
|
1618
|
+
};
|
|
1619
|
+
var tabbable = function tabbable(container, options) {
|
|
1620
|
+
options = options || {};
|
|
1621
|
+
var candidates;
|
|
1622
|
+
if (options.getShadowRoot) candidates = _getCandidatesIteratively([container], options.includeContainer, {
|
|
1623
|
+
filter: isNodeMatchingSelectorTabbable.bind(null, options),
|
|
1624
|
+
flatten: false,
|
|
1625
|
+
getShadowRoot: options.getShadowRoot,
|
|
1626
|
+
shadowRootFilter: isShadowRootTabbable
|
|
1627
|
+
});
|
|
1628
|
+
else candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorTabbable.bind(null, options));
|
|
1629
|
+
return _sortByOrder(candidates);
|
|
1630
|
+
};
|
|
1631
|
+
var focusable = function focusable(container, options) {
|
|
1632
|
+
options = options || {};
|
|
1633
|
+
var candidates;
|
|
1634
|
+
if (options.getShadowRoot) candidates = _getCandidatesIteratively([container], options.includeContainer, {
|
|
1635
|
+
filter: isNodeMatchingSelectorFocusable.bind(null, options),
|
|
1636
|
+
flatten: true,
|
|
1637
|
+
getShadowRoot: options.getShadowRoot
|
|
1638
|
+
});
|
|
1639
|
+
else candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorFocusable.bind(null, options));
|
|
1640
|
+
return candidates;
|
|
1641
|
+
};
|
|
1642
|
+
//#endregion
|
|
1643
|
+
//#region src/tabs/tabs.ts
|
|
1644
|
+
let idCounter = 0;
|
|
1645
|
+
var TabsElement = class extends HTMLElement {
|
|
1646
|
+
idPrefix = "tabs" + ++idCounter;
|
|
736
1647
|
structureSync;
|
|
737
1648
|
managedPanelTabindex = /* @__PURE__ */ new WeakSet();
|
|
738
1649
|
connectedCallback() {
|
|
739
|
-
this.addEventListener("keydown", this.onKeyDown)
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1650
|
+
this.addEventListener("keydown", this.onKeyDown);
|
|
1651
|
+
this.addEventListener("click", this.onClick);
|
|
1652
|
+
if (!this.structureSync) this.structureSync = createQueuedElementSync(this, {
|
|
1653
|
+
subtree: true,
|
|
1654
|
+
childList: true,
|
|
1655
|
+
attributes: true,
|
|
743
1656
|
attributeFilter: ["role", "aria-selected"]
|
|
744
|
-
}, () => this.syncStructure())
|
|
1657
|
+
}, () => this.syncStructure());
|
|
1658
|
+
this.structureSync.observe();
|
|
1659
|
+
this.structureSync.queue();
|
|
745
1660
|
}
|
|
746
1661
|
disconnectedCallback() {
|
|
747
|
-
this.removeEventListener("keydown", this.onKeyDown)
|
|
1662
|
+
this.removeEventListener("keydown", this.onKeyDown);
|
|
1663
|
+
this.removeEventListener("click", this.onClick);
|
|
1664
|
+
this.structureSync?.disconnect();
|
|
748
1665
|
}
|
|
749
|
-
selectTab(
|
|
750
|
-
return this.applySelection(this.tabPairs(),
|
|
1666
|
+
selectTab(index, options = {}) {
|
|
1667
|
+
return this.applySelection(this.tabPairs(), index, options);
|
|
751
1668
|
}
|
|
752
1669
|
onKeyDown = (e) => {
|
|
753
|
-
|
|
754
|
-
if (!
|
|
755
|
-
|
|
756
|
-
currentIndex: this.getEventTabIndex(e.target,
|
|
757
|
-
length:
|
|
1670
|
+
const pairs = this.tabPairs();
|
|
1671
|
+
if (!pairs.length) return;
|
|
1672
|
+
const nextIndex = getRovingIndexForKey(e.key, {
|
|
1673
|
+
currentIndex: this.getEventTabIndex(e.target, pairs),
|
|
1674
|
+
length: pairs.length,
|
|
758
1675
|
vertical: this.vertical
|
|
759
1676
|
});
|
|
760
|
-
|
|
1677
|
+
if (nextIndex !== void 0) {
|
|
1678
|
+
this.applySelection(pairs, nextIndex, { focus: true });
|
|
1679
|
+
e.stopPropagation();
|
|
1680
|
+
e.preventDefault();
|
|
1681
|
+
}
|
|
761
1682
|
};
|
|
762
1683
|
onClick = (e) => {
|
|
763
|
-
|
|
764
|
-
this.applySelection(
|
|
1684
|
+
const pairs = this.tabPairs();
|
|
1685
|
+
this.applySelection(pairs, this.getEventTabIndex(e.target, pairs), { focus: true });
|
|
765
1686
|
};
|
|
766
1687
|
syncStructure() {
|
|
767
|
-
|
|
768
|
-
|
|
1688
|
+
const pairs = this.tabPairs();
|
|
1689
|
+
if (!pairs.length) return;
|
|
1690
|
+
this.applySelection(pairs, this.getSelectedIndex(pairs, 0), { emit: false });
|
|
769
1691
|
}
|
|
770
1692
|
get vertical() {
|
|
771
1693
|
return this.tablist?.getAttribute("aria-orientation") === "vertical";
|
|
772
1694
|
}
|
|
773
1695
|
get tablist() {
|
|
774
|
-
return Array.from(this.querySelectorAll("[role=tablist]")).find((
|
|
1696
|
+
return Array.from(this.querySelectorAll("[role=tablist]")).find((el) => el.closest("ui-tabs") === this);
|
|
775
1697
|
}
|
|
776
1698
|
tabPairs() {
|
|
777
|
-
|
|
778
|
-
if (!
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
e.id ||= this.idPrefix + "_panel_" + t;
|
|
1699
|
+
const tablist = this.tablist;
|
|
1700
|
+
if (!tablist) return [];
|
|
1701
|
+
const tabs = Array.from(tablist.querySelectorAll("[role=tab]"));
|
|
1702
|
+
const panels = Array.from(this.querySelectorAll("[role=tabpanel]")).filter((el) => el.closest("ui-tabs") === this);
|
|
1703
|
+
tabs.forEach((tab, index) => {
|
|
1704
|
+
if (!tab.id) tab.id = this.idPrefix + "_tab_" + index;
|
|
784
1705
|
});
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1706
|
+
panels.forEach((panel, index) => {
|
|
1707
|
+
if (!panel.id) panel.id = this.idPrefix + "_panel_" + index;
|
|
1708
|
+
});
|
|
1709
|
+
const pairs = tabs.slice(0, Math.min(tabs.length, panels.length)).map((tab, index) => ({
|
|
1710
|
+
tab,
|
|
1711
|
+
panel: panels[index]
|
|
788
1712
|
}));
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1713
|
+
pairs.forEach(({ tab, panel }) => {
|
|
1714
|
+
tab.setAttribute("aria-controls", panel.id);
|
|
1715
|
+
panel.setAttribute("aria-labelledby", tab.id);
|
|
1716
|
+
});
|
|
1717
|
+
return pairs;
|
|
1718
|
+
}
|
|
1719
|
+
getSelectedIndex(pairs, fallback = -1) {
|
|
1720
|
+
const index = pairs.findIndex(({ tab }) => tab.getAttribute("aria-selected") === "true");
|
|
1721
|
+
return index === -1 ? fallback : index;
|
|
1722
|
+
}
|
|
1723
|
+
getEventTabIndex(target, pairs) {
|
|
1724
|
+
if (!(target instanceof Element)) return -1;
|
|
1725
|
+
const tab = target.closest("[role=tab]");
|
|
1726
|
+
return tab ? pairs.findIndex((pair) => pair.tab === tab) : -1;
|
|
1727
|
+
}
|
|
1728
|
+
applySelection(pairs, index, options = {}) {
|
|
1729
|
+
const { focus = false, emit = true } = options;
|
|
1730
|
+
if (index < 0 || index >= pairs.length) return false;
|
|
1731
|
+
const previousIndex = this.getSelectedIndex(pairs);
|
|
1732
|
+
pairs.forEach(({ tab, panel }, i) => {
|
|
1733
|
+
const selected = i === index;
|
|
1734
|
+
if (tab.getAttribute("aria-selected") !== String(selected)) tab.setAttribute("aria-selected", String(selected));
|
|
1735
|
+
if (selected) {
|
|
1736
|
+
tab.removeAttribute("tabindex");
|
|
1737
|
+
panel.hidden = false;
|
|
1738
|
+
if (focus) tab.focus();
|
|
1739
|
+
} else {
|
|
1740
|
+
tab.setAttribute("tabindex", "-1");
|
|
1741
|
+
panel.hidden = true;
|
|
1742
|
+
}
|
|
1743
|
+
this.syncPanelTabindex(panel, selected);
|
|
1744
|
+
});
|
|
1745
|
+
if (emit && previousIndex !== index) this.dispatchEvent(new Event("change"));
|
|
1746
|
+
return previousIndex !== index;
|
|
1747
|
+
}
|
|
1748
|
+
syncPanelTabindex(panel, selected) {
|
|
1749
|
+
const tabbableDescendants = tabbable(panel).filter((el) => el !== panel);
|
|
1750
|
+
if (selected && !this.hasAuthorManagedTabindex(panel) && tabbableDescendants.length === 0) {
|
|
1751
|
+
panel.setAttribute("tabindex", "0");
|
|
1752
|
+
this.managedPanelTabindex.add(panel);
|
|
815
1753
|
return;
|
|
816
1754
|
}
|
|
817
|
-
this.managedPanelTabindex.has(
|
|
1755
|
+
if (this.managedPanelTabindex.has(panel)) {
|
|
1756
|
+
panel.removeAttribute("tabindex");
|
|
1757
|
+
this.managedPanelTabindex.delete(panel);
|
|
1758
|
+
}
|
|
818
1759
|
}
|
|
819
|
-
hasAuthorManagedTabindex(
|
|
820
|
-
return
|
|
1760
|
+
hasAuthorManagedTabindex(panel) {
|
|
1761
|
+
return panel.hasAttribute("tabindex") && !this.managedPanelTabindex.has(panel);
|
|
821
1762
|
}
|
|
822
|
-
}
|
|
1763
|
+
};
|
|
1764
|
+
//#endregion
|
|
1765
|
+
//#region src/toolbar/toolbar.ts
|
|
1766
|
+
var ToolbarElement = class extends HTMLElement {
|
|
823
1767
|
structureSync;
|
|
824
1768
|
connectedCallback() {
|
|
825
|
-
this.hasAttribute("role")
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1769
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "toolbar");
|
|
1770
|
+
if (!this.hasAttribute("tabindex")) this.setAttribute("tabindex", "0");
|
|
1771
|
+
this.addEventListener("focus", this.onFocus);
|
|
1772
|
+
this.addEventListener("keydown", this.onKeyDown);
|
|
1773
|
+
if (!this.structureSync) this.structureSync = createQueuedElementSync(this, {
|
|
1774
|
+
subtree: true,
|
|
1775
|
+
childList: true,
|
|
1776
|
+
attributes: true,
|
|
829
1777
|
attributeFilter: [
|
|
830
1778
|
"disabled",
|
|
831
1779
|
"hidden",
|
|
832
1780
|
"aria-disabled"
|
|
833
1781
|
]
|
|
834
|
-
}, () => this.syncControls())
|
|
1782
|
+
}, () => this.syncControls());
|
|
1783
|
+
this.structureSync.observe();
|
|
1784
|
+
this.structureSync.queue();
|
|
835
1785
|
}
|
|
836
1786
|
disconnectedCallback() {
|
|
837
|
-
this.removeEventListener("focus", this.onFocus)
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1787
|
+
this.removeEventListener("focus", this.onFocus);
|
|
1788
|
+
this.removeEventListener("keydown", this.onKeyDown);
|
|
1789
|
+
this.structureSync?.disconnect();
|
|
1790
|
+
}
|
|
1791
|
+
onFocus = (event) => {
|
|
1792
|
+
if (event.target !== this) return;
|
|
1793
|
+
this.removeAttribute("tabindex");
|
|
1794
|
+
this.syncControls()?.focus();
|
|
841
1795
|
};
|
|
842
1796
|
onKeyDown = (e) => {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1797
|
+
const controls = this.controls;
|
|
1798
|
+
const index = controls.indexOf(e.target);
|
|
1799
|
+
if (index === -1) return;
|
|
1800
|
+
const nextIndex = getRovingIndexForKey(e.key, {
|
|
1801
|
+
currentIndex: index,
|
|
1802
|
+
length: controls.length,
|
|
848
1803
|
vertical: this.vertical
|
|
849
1804
|
});
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
this.syncCurrentControl(
|
|
1805
|
+
if (nextIndex === void 0) return;
|
|
1806
|
+
const current = controls[nextIndex];
|
|
1807
|
+
this.syncCurrentControl(controls, current);
|
|
1808
|
+
current?.focus();
|
|
1809
|
+
e.preventDefault();
|
|
853
1810
|
};
|
|
854
1811
|
syncControls() {
|
|
855
|
-
|
|
856
|
-
if (!
|
|
1812
|
+
const controls = this.controls;
|
|
1813
|
+
if (!controls.length) {
|
|
857
1814
|
this.setAttribute("tabindex", "0");
|
|
858
1815
|
return;
|
|
859
1816
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1817
|
+
const activeElement = getLookupRoot(this).activeElement;
|
|
1818
|
+
const current = this.hasAttribute("tabindex") ? controls[0] : controls.find((control) => control === activeElement) || controls.find((control) => control.getAttribute("tabindex") === "0") || controls[0];
|
|
1819
|
+
this.syncCurrentControl(controls, current);
|
|
1820
|
+
return current;
|
|
1821
|
+
}
|
|
1822
|
+
syncCurrentControl(controls, current) {
|
|
1823
|
+
controls.forEach((control) => {
|
|
1824
|
+
control.setAttribute("tabindex", control === current ? "0" : "-1");
|
|
866
1825
|
});
|
|
867
1826
|
}
|
|
868
1827
|
get vertical() {
|
|
869
1828
|
return this.getAttribute("aria-orientation") === "vertical";
|
|
870
1829
|
}
|
|
871
1830
|
get controls() {
|
|
872
|
-
return
|
|
1831
|
+
return focusable(this);
|
|
873
1832
|
}
|
|
874
|
-
}
|
|
1833
|
+
};
|
|
1834
|
+
//#endregion
|
|
1835
|
+
//#region src/tooltip/tooltip.ts
|
|
1836
|
+
let tooltipIdCounter = 0;
|
|
1837
|
+
var TooltipElement = class TooltipElement extends HTMLElement {
|
|
875
1838
|
static delay = 100;
|
|
876
1839
|
static observedAttributes = [
|
|
877
1840
|
"disabled",
|
|
@@ -880,124 +1843,182 @@ var I = class e extends HTMLElement {
|
|
|
880
1843
|
];
|
|
881
1844
|
owner;
|
|
882
1845
|
timeout;
|
|
883
|
-
sync =
|
|
1846
|
+
sync = createQueuedSync(this, () => this.syncOwner());
|
|
884
1847
|
ownerObserver;
|
|
885
1848
|
siblingObserver;
|
|
886
1849
|
managedDescription;
|
|
887
1850
|
connectedCallback() {
|
|
888
|
-
this.hasAttribute("popover")
|
|
1851
|
+
if (!this.hasAttribute("popover")) this.setAttribute("popover", "hint");
|
|
1852
|
+
if (!this.hasAttribute("role")) this.setAttribute("role", "tooltip");
|
|
1853
|
+
this.ensureTooltipId();
|
|
1854
|
+
this.addEventListener("toggle", this.onToggle);
|
|
1855
|
+
this.addEventListener("pointerenter", this.onTooltipPointerEnter);
|
|
1856
|
+
this.addEventListener("pointerleave", this.onInteractivePointerLeave);
|
|
1857
|
+
this.syncOwner();
|
|
889
1858
|
}
|
|
890
1859
|
disconnectedCallback() {
|
|
891
|
-
this.removeEventListener("toggle", this.onToggle)
|
|
1860
|
+
this.removeEventListener("toggle", this.onToggle);
|
|
1861
|
+
this.removeEventListener("pointerenter", this.onTooltipPointerEnter);
|
|
1862
|
+
this.removeEventListener("pointerleave", this.onInteractivePointerLeave);
|
|
1863
|
+
this.ownerDocument.removeEventListener("keydown", this.onDocumentKeyDown);
|
|
1864
|
+
this.siblingObserver?.disconnect();
|
|
1865
|
+
this.sync.disconnect();
|
|
1866
|
+
this.setOwner(void 0);
|
|
1867
|
+
clearTimeout(this.timeout);
|
|
892
1868
|
}
|
|
893
|
-
attributeChangedCallback(
|
|
894
|
-
!this.isConnected ||
|
|
1869
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1870
|
+
if (!this.isConnected || oldValue === newValue) return;
|
|
1871
|
+
if (name === "id" && !newValue) this.ensureTooltipId();
|
|
1872
|
+
this.syncOwner();
|
|
895
1873
|
}
|
|
896
1874
|
get disabled() {
|
|
897
1875
|
return this.hasAttribute("disabled");
|
|
898
1876
|
}
|
|
899
|
-
set disabled(
|
|
900
|
-
|
|
1877
|
+
set disabled(val) {
|
|
1878
|
+
if (val) this.setAttribute("disabled", "");
|
|
1879
|
+
else this.removeAttribute("disabled");
|
|
901
1880
|
}
|
|
902
|
-
show(
|
|
903
|
-
clearTimeout(this.timeout)
|
|
904
|
-
|
|
905
|
-
|
|
1881
|
+
show(owner = this.resolveOwner()) {
|
|
1882
|
+
clearTimeout(this.timeout);
|
|
1883
|
+
this.syncOwner(owner);
|
|
1884
|
+
const resolvedOwner = this.owner;
|
|
1885
|
+
if (!resolvedOwner || this.matches(":popover-open")) return;
|
|
1886
|
+
this.showPopover?.({ source: resolvedOwner });
|
|
906
1887
|
}
|
|
907
1888
|
hide() {
|
|
908
|
-
clearTimeout(this.timeout)
|
|
1889
|
+
clearTimeout(this.timeout);
|
|
1890
|
+
if (this.matches(":popover-open")) this.hidePopover();
|
|
909
1891
|
}
|
|
910
1892
|
onToggle = (e) => {
|
|
911
|
-
if (e.target
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
}
|
|
916
|
-
this.ownerDocument.removeEventListener("keydown", this.onDocumentKeyDown);
|
|
1893
|
+
if (e.target !== this) return;
|
|
1894
|
+
if (e.newState === "open") {
|
|
1895
|
+
this.ownerDocument.addEventListener("keydown", this.onDocumentKeyDown);
|
|
1896
|
+
return;
|
|
917
1897
|
}
|
|
1898
|
+
this.ownerDocument.removeEventListener("keydown", this.onDocumentKeyDown);
|
|
918
1899
|
};
|
|
919
1900
|
onDocumentKeyDown = (e) => {
|
|
920
|
-
e.key === "Escape"
|
|
1901
|
+
if (e.key === "Escape") this.hide();
|
|
921
1902
|
};
|
|
922
|
-
syncOwner(
|
|
923
|
-
|
|
924
|
-
|
|
1903
|
+
syncOwner(owner = this.resolveOwner()) {
|
|
1904
|
+
this.siblingObserver ??= new MutationObserver(this.sync.queue);
|
|
1905
|
+
this.siblingObserver.disconnect();
|
|
1906
|
+
if (!this.hasAttribute("for") && this.parentElement) this.siblingObserver.observe(this.parentElement, { childList: true });
|
|
1907
|
+
if (!owner) {
|
|
1908
|
+
this.hide();
|
|
1909
|
+
this.setOwner(void 0);
|
|
925
1910
|
return;
|
|
926
1911
|
}
|
|
927
|
-
this.owner && this.owner !==
|
|
1912
|
+
if (this.owner && this.owner !== owner && this.matches(":popover-open")) this.hide();
|
|
1913
|
+
this.setOwner(owner);
|
|
928
1914
|
}
|
|
929
|
-
resolveOwner(
|
|
930
|
-
|
|
931
|
-
if (
|
|
1915
|
+
resolveOwner(owner) {
|
|
1916
|
+
const candidate = owner ?? (this.hasAttribute("for") ? this.resolveExplicitOwner() : this.previousElementSibling instanceof HTMLElement ? this.previousElementSibling : void 0);
|
|
1917
|
+
if (this.disabled || !candidate || !candidate.isConnected || candidate === this || this.isOwnerDisabled(candidate)) return;
|
|
1918
|
+
return candidate;
|
|
932
1919
|
}
|
|
933
1920
|
resolveExplicitOwner() {
|
|
934
|
-
|
|
935
|
-
if (!
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1921
|
+
const ownerId = this.getAttribute("for");
|
|
1922
|
+
if (!ownerId) return void 0;
|
|
1923
|
+
const root = this.getRootNode();
|
|
1924
|
+
const owner = root instanceof ShadowRoot && root.querySelector(`#${CSS.escape(ownerId)}`) || this.ownerDocument.getElementById(ownerId);
|
|
1925
|
+
return owner instanceof HTMLElement ? owner : void 0;
|
|
1926
|
+
}
|
|
1927
|
+
setOwner(owner) {
|
|
1928
|
+
const ownerChanged = this.owner !== owner;
|
|
1929
|
+
if (ownerChanged && this.owner) {
|
|
1930
|
+
this.owner.removeEventListener("focusin", this.onOwnerFocusIn);
|
|
1931
|
+
this.owner.removeEventListener("focusout", this.onOwnerFocusOut);
|
|
1932
|
+
this.owner.removeEventListener("pointerenter", this.onOwnerPointerEnter);
|
|
1933
|
+
this.owner.removeEventListener("pointerleave", this.onInteractivePointerLeave);
|
|
1934
|
+
this.owner.removeEventListener("click", this.onOwnerClick);
|
|
1935
|
+
}
|
|
1936
|
+
this.owner = owner;
|
|
1937
|
+
this.updateManagedDescription(owner);
|
|
1938
|
+
this.ownerObserver?.disconnect();
|
|
1939
|
+
if (!owner) return;
|
|
1940
|
+
this.ownerObserver ??= new MutationObserver(this.sync.queue);
|
|
1941
|
+
this.ownerObserver.observe(owner, {
|
|
1942
|
+
attributes: true,
|
|
943
1943
|
attributeFilter: [
|
|
944
1944
|
"aria-describedby",
|
|
945
1945
|
"aria-disabled",
|
|
946
1946
|
"disabled"
|
|
947
1947
|
]
|
|
948
|
-
})
|
|
1948
|
+
});
|
|
1949
|
+
if (ownerChanged) {
|
|
1950
|
+
owner.addEventListener("focusin", this.onOwnerFocusIn);
|
|
1951
|
+
owner.addEventListener("focusout", this.onOwnerFocusOut);
|
|
1952
|
+
owner.addEventListener("pointerenter", this.onOwnerPointerEnter);
|
|
1953
|
+
owner.addEventListener("pointerleave", this.onInteractivePointerLeave);
|
|
1954
|
+
owner.addEventListener("click", this.onOwnerClick);
|
|
1955
|
+
}
|
|
949
1956
|
}
|
|
950
1957
|
onOwnerFocusIn = () => {
|
|
951
|
-
!this.owner || !this.owner.matches(":focus-visible")
|
|
1958
|
+
if (!this.owner || !this.owner.matches(":focus-visible")) return;
|
|
1959
|
+
this.show(this.owner);
|
|
952
1960
|
};
|
|
953
1961
|
onOwnerFocusOut = (e) => {
|
|
954
|
-
this.containsInteractiveTarget(e.relatedTarget)
|
|
1962
|
+
if (this.containsInteractiveTarget(e.relatedTarget)) return;
|
|
1963
|
+
this.hide();
|
|
955
1964
|
};
|
|
956
1965
|
onOwnerPointerEnter = (e) => {
|
|
957
|
-
|
|
1966
|
+
if (!this.supportsHoverInteraction(e)) return;
|
|
1967
|
+
if (!this.owner) return;
|
|
1968
|
+
if (this.containsInteractiveTarget(e.relatedTarget)) return;
|
|
1969
|
+
this.afterDelay(() => this.show(this.owner));
|
|
958
1970
|
};
|
|
959
1971
|
onTooltipPointerEnter = (e) => {
|
|
960
|
-
|
|
1972
|
+
if (!this.supportsHoverInteraction(e)) return;
|
|
1973
|
+
if (!this.containsInteractiveTarget(e.relatedTarget)) clearTimeout(this.timeout);
|
|
961
1974
|
};
|
|
962
1975
|
onInteractivePointerLeave = (e) => {
|
|
963
|
-
e.pointerType && e.pointerType !== "mouse"
|
|
1976
|
+
if (e.pointerType && e.pointerType !== "mouse") return;
|
|
1977
|
+
if (this.containsInteractiveTarget(e.relatedTarget)) return;
|
|
1978
|
+
this.afterDelay(() => this.hide());
|
|
964
1979
|
};
|
|
965
1980
|
onOwnerClick = () => {
|
|
966
1981
|
this.hide();
|
|
967
1982
|
};
|
|
968
|
-
afterDelay(
|
|
1983
|
+
afterDelay(callback) {
|
|
969
1984
|
clearTimeout(this.timeout);
|
|
970
|
-
|
|
971
|
-
this.timeout = window.setTimeout(
|
|
972
|
-
}
|
|
973
|
-
containsInteractiveTarget(
|
|
974
|
-
return
|
|
975
|
-
}
|
|
976
|
-
updateManagedDescription(
|
|
977
|
-
|
|
978
|
-
if (this.managedDescription && (this.managedDescription.owner !==
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1985
|
+
const delay = parseInt(this.getAttribute("delay") || "");
|
|
1986
|
+
this.timeout = window.setTimeout(callback, isNaN(delay) ? TooltipElement.delay : delay);
|
|
1987
|
+
}
|
|
1988
|
+
containsInteractiveTarget(target) {
|
|
1989
|
+
return target instanceof Node && (this.contains(target) || !!this.owner && this.owner.contains(target));
|
|
1990
|
+
}
|
|
1991
|
+
updateManagedDescription(owner) {
|
|
1992
|
+
const tooltipId = owner ? this.ensureTooltipId() : void 0;
|
|
1993
|
+
if (this.managedDescription && (this.managedDescription.owner !== owner || this.managedDescription.id !== tooltipId)) {
|
|
1994
|
+
const managedDescription = this.managedDescription;
|
|
1995
|
+
const tokens = this.getDescriptionTokens(managedDescription.owner).filter((token) => token !== managedDescription.id);
|
|
1996
|
+
if (tokens.length) managedDescription.owner.setAttribute("aria-describedby", tokens.join(" "));
|
|
1997
|
+
else managedDescription.owner.removeAttribute("aria-describedby");
|
|
1998
|
+
this.managedDescription = void 0;
|
|
1999
|
+
}
|
|
2000
|
+
if (!owner || !tooltipId || this.managedDescription) return;
|
|
2001
|
+
const tokens = this.getDescriptionTokens(owner);
|
|
2002
|
+
if (tokens.includes(tooltipId)) return;
|
|
2003
|
+
owner.setAttribute("aria-describedby", [...tokens, tooltipId].join(" "));
|
|
2004
|
+
this.managedDescription = {
|
|
2005
|
+
owner,
|
|
2006
|
+
id: tooltipId
|
|
2007
|
+
};
|
|
988
2008
|
}
|
|
989
|
-
getDescriptionTokens(
|
|
990
|
-
return (
|
|
2009
|
+
getDescriptionTokens(owner) {
|
|
2010
|
+
return (owner.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
991
2011
|
}
|
|
992
2012
|
ensureTooltipId() {
|
|
993
|
-
|
|
2013
|
+
if (!this.id) this.id = `ui-tooltip-${++tooltipIdCounter}`;
|
|
2014
|
+
return this.id;
|
|
994
2015
|
}
|
|
995
|
-
supportsHoverInteraction(
|
|
996
|
-
return (!
|
|
2016
|
+
supportsHoverInteraction(event) {
|
|
2017
|
+
return (!event.pointerType || event.pointerType === "mouse") && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
|
997
2018
|
}
|
|
998
|
-
isOwnerDisabled(
|
|
999
|
-
return
|
|
2019
|
+
isOwnerDisabled(owner) {
|
|
2020
|
+
return owner.hasAttribute("disabled") || owner.getAttribute("aria-disabled") === "true";
|
|
1000
2021
|
}
|
|
1001
2022
|
};
|
|
1002
2023
|
//#endregion
|
|
1003
|
-
export {
|
|
2024
|
+
export { AlertsElement, ComboboxElement, MenuElement, TabsElement, ToolbarElement, TooltipElement };
|