@reliability-design/web 1.4.4 → 1.4.6
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/dist/components/rel-accordion/rel-accordion.ts +13 -1
- package/dist/components/rel-alert/rel-alert.ts +1 -1
- package/dist/components/rel-autocomplete/rel-autocomplete.ts +33 -2
- package/dist/components/rel-badge/rel-badge.ts +6 -1
- package/dist/components/rel-button/rel-button.ts +18 -2
- package/dist/components/rel-calendar/rel-calendar.ts +86 -18
- package/dist/components/rel-carousel/rel-carousel.ts +45 -2
- package/dist/components/rel-checkbox/rel-checkbox.ts +4 -1
- package/dist/components/rel-chip/rel-chip.ts +59 -19
- package/dist/components/rel-date-picker/date-parse.test.ts +116 -0
- package/dist/components/rel-date-picker/date-parse.ts +178 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +675 -93
- package/dist/components/rel-drawer/rel-drawer.ts +28 -61
- package/dist/components/rel-menu/rel-menu.ts +130 -15
- package/dist/components/rel-menubar/rel-menubar.ts +221 -63
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +39 -3
- package/dist/components/rel-otp/rel-otp.ts +14 -4
- package/dist/components/rel-pagination/rel-pagination.ts +13 -0
- package/dist/components/rel-popover/rel-popover.ts +64 -5
- package/dist/components/rel-progress/rel-progress.ts +13 -4
- package/dist/components/rel-radio/README.md +6 -3
- package/dist/components/rel-radio/rel-radio.ts +236 -60
- package/dist/components/rel-range-picker/rel-range-picker.ts +581 -91
- package/dist/components/rel-resizable/rel-resizable-group.ts +20 -0
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +55 -13
- package/dist/components/rel-select/rel-select.ts +203 -24
- package/dist/components/rel-sheet/rel-sheet.ts +184 -18
- package/dist/components/rel-sidebar/rel-sidebar.ts +50 -40
- package/dist/components/rel-skeleton/rel-skeleton.ts +9 -0
- package/dist/components/rel-slider/rel-slider.ts +6 -3
- package/dist/components/rel-snackbar/rel-snackbar.ts +20 -16
- package/dist/components/rel-stepper/rel-stepper.ts +47 -2
- package/dist/components/rel-switch/rel-switch.ts +3 -1
- package/dist/components/rel-tab/rel-tab.ts +32 -7
- package/dist/components/rel-textfield/rel-textfield.ts +263 -75
- package/dist/components/rel-time-picker/rel-time-picker.ts +749 -261
- package/dist/components/rel-time-picker/time-parse.test.ts +67 -0
- package/dist/components/rel-time-picker/time-parse.ts +85 -0
- package/dist/components/rel-toast/rel-toast.ts +4 -2
- package/dist/components/rel-tooltip/README.md +13 -5
- package/dist/components/rel-tooltip/rel-tooltip.ts +33 -0
- package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-brand.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-item.ts +318 -319
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar-profile.ts +224 -224
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -1
- package/dist/components/rel-topbar/rel-topbar.ts +440 -440
- package/dist/components/rel-upload/rel-upload.ts +25 -0
- package/dist/index.d.mts +461 -23
- package/dist/index.d.ts +461 -23
- package/dist/index.js +3906 -807
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4143 -1055
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { LitElement, html, css } from "lit";
|
|
1
|
+
import { LitElement, html, css, nothing } from "lit";
|
|
2
2
|
import { customElement, property, state, query } from "lit/decorators.js";
|
|
3
3
|
import "@reliability-design/icons/rel-icon";
|
|
4
4
|
import "./rel-drawer-footer";
|
|
5
|
+
import { getDeepActiveElement, getFocusableElements, trapTabKey } from "../../utils/focus.js";
|
|
5
6
|
|
|
6
7
|
export type DrawerSide = "bottom" | "left" | "right";
|
|
7
8
|
export type DrawerCloseReason = "backdrop" | "escape" | "drag" | "programmatic";
|
|
@@ -327,16 +328,26 @@ export class RelDrawer extends LitElement {
|
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
private handleOpen() {
|
|
330
|
-
|
|
331
|
+
// Deep-resolved: document.activeElement retargets to a component's host
|
|
332
|
+
// (e.g. <rel-button>), whose .focus() is a no-op — restore would drop
|
|
333
|
+
// focus to <body>.
|
|
334
|
+
this.previousFocus = getDeepActiveElement();
|
|
331
335
|
this.lockScroll();
|
|
332
336
|
|
|
333
337
|
if (this.modal) {
|
|
338
|
+
// The panel lives in the shadow root, so plain this.querySelector never
|
|
339
|
+
// found it (focus was never moved in). The shared util walks the
|
|
340
|
+
// composed tree, seeing both shadow and slotted content.
|
|
334
341
|
this.updateComplete.then(() => {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
342
|
+
const panel = this.panelElement;
|
|
343
|
+
if (!panel) return;
|
|
344
|
+
const focusTarget = getFocusableElements(panel)[0];
|
|
345
|
+
if (focusTarget) {
|
|
346
|
+
focusTarget.focus();
|
|
347
|
+
} else {
|
|
348
|
+
panel.tabIndex = -1;
|
|
349
|
+
panel.focus();
|
|
350
|
+
}
|
|
340
351
|
});
|
|
341
352
|
}
|
|
342
353
|
|
|
@@ -414,35 +425,6 @@ export class RelDrawer extends LitElement {
|
|
|
414
425
|
document.body.style.paddingRight = this.originalBodyPaddingRight;
|
|
415
426
|
}
|
|
416
427
|
|
|
417
|
-
private getFocusableElements(): HTMLElement[] {
|
|
418
|
-
if (!this.panelElement) return [];
|
|
419
|
-
|
|
420
|
-
const selectors = [
|
|
421
|
-
'a[href]',
|
|
422
|
-
'button:not([disabled])',
|
|
423
|
-
'textarea:not([disabled])',
|
|
424
|
-
'input:not([disabled])',
|
|
425
|
-
'select:not([disabled])',
|
|
426
|
-
'[tabindex]:not([tabindex="-1"])',
|
|
427
|
-
].join(",");
|
|
428
|
-
|
|
429
|
-
const elements = Array.from(
|
|
430
|
-
this.panelElement.querySelectorAll(selectors)
|
|
431
|
-
) as HTMLElement[];
|
|
432
|
-
|
|
433
|
-
// Also check slotted content
|
|
434
|
-
const slots = this.panelElement.querySelectorAll("slot");
|
|
435
|
-
slots.forEach((slot) => {
|
|
436
|
-
const assigned = slot.assignedElements({ flatten: true });
|
|
437
|
-
assigned.forEach((el) => {
|
|
438
|
-
const focusableInSlot = el.querySelectorAll(selectors);
|
|
439
|
-
elements.push(...(Array.from(focusableInSlot) as HTMLElement[]));
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
return elements;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
428
|
private handleKeydown = (e: KeyboardEvent) => {
|
|
447
429
|
if (!this.open) return;
|
|
448
430
|
|
|
@@ -452,29 +434,11 @@ export class RelDrawer extends LitElement {
|
|
|
452
434
|
return;
|
|
453
435
|
}
|
|
454
436
|
|
|
455
|
-
// Focus trap
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const firstFocusable = focusable[0];
|
|
464
|
-
const lastFocusable = focusable[focusable.length - 1];
|
|
465
|
-
const activeElement = document.activeElement;
|
|
466
|
-
|
|
467
|
-
if (e.shiftKey) {
|
|
468
|
-
if (activeElement === firstFocusable) {
|
|
469
|
-
e.preventDefault();
|
|
470
|
-
lastFocusable.focus();
|
|
471
|
-
}
|
|
472
|
-
} else {
|
|
473
|
-
if (activeElement === lastFocusable) {
|
|
474
|
-
e.preventDefault();
|
|
475
|
-
firstFocusable.focus();
|
|
476
|
-
}
|
|
477
|
-
}
|
|
437
|
+
// Focus trap — the shared helper resolves the deep active element, which
|
|
438
|
+
// the old document.activeElement comparison never could (it retargets to
|
|
439
|
+
// the <rel-drawer> host, so Tab was never wrapped).
|
|
440
|
+
if (e.key === "Tab" && this.modal && this.panelElement) {
|
|
441
|
+
trapTabKey(e, this.panelElement);
|
|
478
442
|
}
|
|
479
443
|
};
|
|
480
444
|
|
|
@@ -586,6 +550,9 @@ export class RelDrawer extends LitElement {
|
|
|
586
550
|
style="${this.width ? `--drawer-width: ${this.width};` : ''}${this.maxHeight ? `--drawer-max-height: ${this.maxHeight};` : ''}${this.getPanelStyle()}"
|
|
587
551
|
role="${this.modal ? "dialog" : "region"}"
|
|
588
552
|
aria-modal="${this.modal}"
|
|
553
|
+
aria-labelledby="${hasTitle ? "drawer-title" : nothing}"
|
|
554
|
+
aria-describedby="${hasDescription ? "drawer-description" : nothing}"
|
|
555
|
+
aria-label="${hasTitle ? nothing : "Drawer"}"
|
|
589
556
|
tabindex="-1">
|
|
590
557
|
${showHandleBar
|
|
591
558
|
? html`<div
|
|
@@ -600,10 +567,10 @@ export class RelDrawer extends LitElement {
|
|
|
600
567
|
<div class="header-top">
|
|
601
568
|
<div>
|
|
602
569
|
${hasTitle
|
|
603
|
-
? html`<h2 class="title">${this.title}</h2>`
|
|
570
|
+
? html`<h2 class="title" id="drawer-title">${this.title}</h2>`
|
|
604
571
|
: ""}
|
|
605
572
|
${hasDescription
|
|
606
|
-
? html`<p class="description">${this.description}</p>`
|
|
573
|
+
? html`<p class="description" id="drawer-description">${this.description}</p>`
|
|
607
574
|
: ""}
|
|
608
575
|
</div>
|
|
609
576
|
${showCloseButton
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, html } from "lit";
|
|
1
|
+
import { LitElement, html, nothing } from "lit";
|
|
2
2
|
import { customElement, property, state } from "lit/decorators.js";
|
|
3
3
|
import "@reliability-design/icons/rel-icon";
|
|
4
4
|
import "../rel-checkbox/rel-checkbox.js";
|
|
@@ -38,8 +38,13 @@ export class RelMenu extends LitElement {
|
|
|
38
38
|
@property({ type: Array }) items: MenuItem[] = [];
|
|
39
39
|
|
|
40
40
|
@state() private focusedIndex: number = 0;
|
|
41
|
+
/** Index inside the open submenu that virtual focus sits on; -1 = main list. */
|
|
42
|
+
@state() private subFocusedIndex: number = -1;
|
|
41
43
|
@state() private _items: MenuItem[] = [];
|
|
42
44
|
|
|
45
|
+
/** Stable id prefix for aria-activedescendant targets. */
|
|
46
|
+
private menuId = `rel-menu-${Math.random().toString(36).slice(2)}`;
|
|
47
|
+
|
|
43
48
|
public static readonly _css = `
|
|
44
49
|
:host {
|
|
45
50
|
color: var(--rel-text-default);
|
|
@@ -122,7 +127,10 @@ export class RelMenu extends LitElement {
|
|
|
122
127
|
cursor: not-allowed;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
|
|
130
|
+
/* [data-focused] (presence, not ="true"): the ?data-focused boolean
|
|
131
|
+
binding renders the attribute with an EMPTY value, so a ="true"
|
|
132
|
+
selector never matches. */
|
|
133
|
+
.rel-menu-item[data-focused] {
|
|
126
134
|
outline: 2px solid var(--rel-border-focus);
|
|
127
135
|
outline-offset: -2px;
|
|
128
136
|
}
|
|
@@ -235,6 +243,7 @@ export class RelMenu extends LitElement {
|
|
|
235
243
|
// click that opened it. composedPath pierces shadow boundaries.
|
|
236
244
|
if (!e.composedPath().includes(this)) {
|
|
237
245
|
this.lockedIndex = -1;
|
|
246
|
+
this.subFocusedIndex = -1;
|
|
238
247
|
this._items = this._items.map(i => ({ ...i, open: false }));
|
|
239
248
|
}
|
|
240
249
|
};
|
|
@@ -319,38 +328,119 @@ export class RelMenu extends LitElement {
|
|
|
319
328
|
);
|
|
320
329
|
}
|
|
321
330
|
|
|
331
|
+
/** Keyboard-open locks the submenu (no hover-out to close it) and moves
|
|
332
|
+
* virtual focus onto its first entry. */
|
|
333
|
+
private openSubmenuAt(index: number) {
|
|
334
|
+
this.lockedIndex = index;
|
|
335
|
+
this.subFocusedIndex = 0;
|
|
336
|
+
this._items = this._items.map((i, idx) => ({ ...i, open: idx === index }));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private closeSubmenus() {
|
|
340
|
+
this.lockedIndex = -1;
|
|
341
|
+
this.subFocusedIndex = -1;
|
|
342
|
+
this._items = this._items.map(i => ({ ...i, open: false }));
|
|
343
|
+
}
|
|
344
|
+
|
|
322
345
|
private onKeydown(ev: KeyboardEvent) {
|
|
323
346
|
const last = this._items.length - 1;
|
|
347
|
+
const focusedItem = this._items[this.focusedIndex];
|
|
348
|
+
|
|
349
|
+
// ── Virtual focus is inside an open submenu ──
|
|
350
|
+
if (
|
|
351
|
+
this.subFocusedIndex >= 0 &&
|
|
352
|
+
this.type === "arrow" &&
|
|
353
|
+
focusedItem?.open &&
|
|
354
|
+
focusedItem.submenu?.length
|
|
355
|
+
) {
|
|
356
|
+
const subLast = focusedItem.submenu.length - 1;
|
|
357
|
+
switch (ev.key) {
|
|
358
|
+
case "ArrowDown":
|
|
359
|
+
this.subFocusedIndex = Math.min(subLast, this.subFocusedIndex + 1);
|
|
360
|
+
ev.preventDefault();
|
|
361
|
+
return;
|
|
362
|
+
case "ArrowUp":
|
|
363
|
+
this.subFocusedIndex = Math.max(0, this.subFocusedIndex - 1);
|
|
364
|
+
ev.preventDefault();
|
|
365
|
+
return;
|
|
366
|
+
case "Home":
|
|
367
|
+
this.subFocusedIndex = 0;
|
|
368
|
+
ev.preventDefault();
|
|
369
|
+
return;
|
|
370
|
+
case "End":
|
|
371
|
+
this.subFocusedIndex = subLast;
|
|
372
|
+
ev.preventDefault();
|
|
373
|
+
return;
|
|
374
|
+
case "ArrowLeft":
|
|
375
|
+
case "Escape":
|
|
376
|
+
// Close only the submenu; don't let Escape also dismiss an
|
|
377
|
+
// enclosing popover in the same keystroke.
|
|
378
|
+
this.closeSubmenus();
|
|
379
|
+
ev.preventDefault();
|
|
380
|
+
ev.stopPropagation();
|
|
381
|
+
return;
|
|
382
|
+
case "Enter":
|
|
383
|
+
case " ":
|
|
384
|
+
this.onSubClick(this.focusedIndex, this.subFocusedIndex);
|
|
385
|
+
ev.preventDefault();
|
|
386
|
+
return;
|
|
387
|
+
default:
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
324
391
|
|
|
392
|
+
// ── Virtual focus is in the main list ──
|
|
325
393
|
if (ev.key === "ArrowDown") {
|
|
326
394
|
this.focusedIndex = Math.min(last, this.focusedIndex + 1);
|
|
327
395
|
ev.preventDefault();
|
|
328
396
|
} else if (ev.key === "ArrowUp") {
|
|
329
397
|
this.focusedIndex = Math.max(0, this.focusedIndex - 1);
|
|
330
398
|
ev.preventDefault();
|
|
399
|
+
} else if (ev.key === "Home") {
|
|
400
|
+
this.focusedIndex = 0;
|
|
401
|
+
ev.preventDefault();
|
|
402
|
+
} else if (ev.key === "End") {
|
|
403
|
+
this.focusedIndex = last;
|
|
404
|
+
ev.preventDefault();
|
|
331
405
|
} else if (ev.key === "ArrowRight") {
|
|
332
406
|
const item = this._items[this.focusedIndex];
|
|
333
|
-
if (this.type === "arrow" && item?.submenu?.length) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
this._items = this._items.map((i, idx) => ({
|
|
337
|
-
...i,
|
|
338
|
-
open: idx === this.focusedIndex ? true : i.open,
|
|
339
|
-
}));
|
|
407
|
+
if (this.type === "arrow" && item?.submenu?.length && !item.disabled) {
|
|
408
|
+
this.openSubmenuAt(this.focusedIndex);
|
|
409
|
+
ev.preventDefault();
|
|
340
410
|
}
|
|
341
411
|
} else if (ev.key === "ArrowLeft" || ev.key === "Escape") {
|
|
342
|
-
this.
|
|
343
|
-
|
|
412
|
+
const anyOpen = this._items.some(i => i.open);
|
|
413
|
+
if (anyOpen) {
|
|
414
|
+
this.closeSubmenus();
|
|
415
|
+
ev.preventDefault();
|
|
416
|
+
if (ev.key === "Escape") ev.stopPropagation();
|
|
417
|
+
}
|
|
344
418
|
} else if (ev.key === "Enter" || ev.key === " ") {
|
|
345
419
|
const item = this._items[this.focusedIndex];
|
|
346
|
-
this.
|
|
420
|
+
if (this.type === "arrow" && item?.submenu?.length && !item.disabled) {
|
|
421
|
+
this.openSubmenuAt(this.focusedIndex);
|
|
422
|
+
} else {
|
|
423
|
+
this.onItemClick(item, this.focusedIndex);
|
|
424
|
+
}
|
|
347
425
|
ev.preventDefault();
|
|
348
426
|
}
|
|
349
427
|
}
|
|
350
428
|
|
|
351
429
|
private renderMenuItem(item: MenuItem, index: number) {
|
|
352
|
-
const isFocused = this.focusedIndex === index;
|
|
430
|
+
const isFocused = this.focusedIndex === index && this.subFocusedIndex < 0;
|
|
353
431
|
const hasSubmenu = this.type === "arrow" && !!item.submenu?.length;
|
|
432
|
+
const role =
|
|
433
|
+
this.type === "checkbox"
|
|
434
|
+
? "menuitemcheckbox"
|
|
435
|
+
: this.type === "select-option"
|
|
436
|
+
? "menuitemradio"
|
|
437
|
+
: "menuitem";
|
|
438
|
+
const ariaChecked =
|
|
439
|
+
this.type === "checkbox"
|
|
440
|
+
? String(!!item.checked)
|
|
441
|
+
: this.type === "select-option"
|
|
442
|
+
? String(!!item.active)
|
|
443
|
+
: nothing;
|
|
354
444
|
|
|
355
445
|
// Checkbox / select-option rows delegate their control to the real
|
|
356
446
|
// rel-checkbox / rel-radio components (pointer-events:none so the whole ROW
|
|
@@ -360,6 +450,8 @@ export class RelMenu extends LitElement {
|
|
|
360
450
|
this.type === "checkbox"
|
|
361
451
|
? html`<rel-checkbox
|
|
362
452
|
class="menu-control"
|
|
453
|
+
inert
|
|
454
|
+
aria-hidden="true"
|
|
363
455
|
label="${item.label}"
|
|
364
456
|
status="${item.checked ? "checked" : "unchecked"}"
|
|
365
457
|
state="${item.disabled ? "disabled" : "default"}"
|
|
@@ -367,6 +459,8 @@ export class RelMenu extends LitElement {
|
|
|
367
459
|
: this.type === "select-option"
|
|
368
460
|
? html`<rel-radio
|
|
369
461
|
class="menu-control"
|
|
462
|
+
inert
|
|
463
|
+
aria-hidden="true"
|
|
370
464
|
.options="${[{ value: item.label, label: item.label, disabled: !!item.disabled }]}"
|
|
371
465
|
.selectedValue="${item.active ? item.label : null}"
|
|
372
466
|
></rel-radio>`
|
|
@@ -381,8 +475,14 @@ export class RelMenu extends LitElement {
|
|
|
381
475
|
|
|
382
476
|
return html`
|
|
383
477
|
<div
|
|
478
|
+
id="${this.menuId}-item-${index}"
|
|
384
479
|
class="rel-menu-item ${item.active ? "active" : ""} ${item.disabled ? "disabled" : ""}"
|
|
385
|
-
role="
|
|
480
|
+
role="${role}"
|
|
481
|
+
aria-checked="${ariaChecked}"
|
|
482
|
+
aria-label="${this.type === "checkbox" || this.type === "select-option" ? item.label : nothing}"
|
|
483
|
+
aria-disabled="${item.disabled ? "true" : nothing}"
|
|
484
|
+
aria-haspopup="${hasSubmenu ? "menu" : nothing}"
|
|
485
|
+
aria-expanded="${hasSubmenu ? String(!!item.open) : nothing}"
|
|
386
486
|
?data-focused="${isFocused}"
|
|
387
487
|
@mouseenter="${() => this.onItemEnter(item, index)}"
|
|
388
488
|
@mouseleave="${() => this.onItemLeave(item, index)}"
|
|
@@ -392,12 +492,16 @@ export class RelMenu extends LitElement {
|
|
|
392
492
|
? html`
|
|
393
493
|
<div
|
|
394
494
|
part="panel" class="rel-submenu ${item.open ? "visible" : ""}"
|
|
495
|
+
role="menu"
|
|
395
496
|
@click="${(e: Event) => e.stopPropagation()}">
|
|
396
497
|
${item.submenu!.map(
|
|
397
498
|
(sub, subIndex) => html`
|
|
398
499
|
<div
|
|
399
|
-
|
|
500
|
+
id="${this.menuId}-item-${index}-sub-${subIndex}"
|
|
501
|
+
class="rel-menu-item ${sub.active ? "active" : ""} ${sub.disabled ? "disabled" : ""}"
|
|
400
502
|
role="menuitem"
|
|
503
|
+
aria-disabled="${sub.disabled ? "true" : nothing}"
|
|
504
|
+
?data-focused="${this.focusedIndex === index && this.subFocusedIndex === subIndex}"
|
|
401
505
|
@click="${(e: Event) => this.onSubClick(index, subIndex, e)}">
|
|
402
506
|
<span class="menu-label">${sub.label}</span>
|
|
403
507
|
</div>
|
|
@@ -411,12 +515,23 @@ export class RelMenu extends LitElement {
|
|
|
411
515
|
}
|
|
412
516
|
|
|
413
517
|
render() {
|
|
518
|
+
// DOM focus stays on the container; this tells AT which item virtual
|
|
519
|
+
// focus is on as the user arrows around (main list or open submenu).
|
|
520
|
+
const activeDescendant =
|
|
521
|
+
this._items.length === 0
|
|
522
|
+
? nothing
|
|
523
|
+
: this.subFocusedIndex >= 0
|
|
524
|
+
? `${this.menuId}-item-${this.focusedIndex}-sub-${this.subFocusedIndex}`
|
|
525
|
+
: `${this.menuId}-item-${this.focusedIndex}`;
|
|
526
|
+
|
|
414
527
|
return html`
|
|
415
528
|
<style>${RelMenu._css}</style>
|
|
416
529
|
<div
|
|
417
530
|
part="list" class="rel-menu ${this.type === "fixed" || this.type === "fit" ? this.type : ""}"
|
|
418
531
|
role="menu"
|
|
532
|
+
aria-label="Menu"
|
|
419
533
|
tabindex="0"
|
|
534
|
+
aria-activedescendant="${activeDescendant}"
|
|
420
535
|
@keydown="${this.onKeydown}"
|
|
421
536
|
@click="${(e: Event) => e.stopPropagation()}">
|
|
422
537
|
${this._items.map((item, index) => this.renderMenuItem(item, index))}
|