@spectrum-web-components/menu 1.2.0-beta.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +503 -253
- package/package.json +11 -11
- package/src/Menu.d.ts +62 -10
- package/src/Menu.dev.js +137 -169
- package/src/Menu.dev.js.map +2 -2
- package/src/Menu.js +2 -2
- package/src/Menu.js.map +3 -3
- package/src/MenuGroup.d.ts +9 -0
- package/src/MenuGroup.dev.js +13 -9
- package/src/MenuGroup.dev.js.map +2 -2
- package/src/MenuGroup.js +3 -3
- package/src/MenuGroup.js.map +2 -2
- package/src/MenuItem.d.ts +60 -1
- package/src/MenuItem.dev.js +107 -19
- package/src/MenuItem.dev.js.map +2 -2
- package/src/MenuItem.js +7 -7
- package/src/MenuItem.js.map +3 -3
- package/stories/index.js +13 -5
- package/stories/index.js.map +2 -2
- package/stories/menu.stories.js +20 -0
- package/stories/menu.stories.js.map +2 -2
- package/test/menu-group.test.js +88 -92
- package/test/menu-group.test.js.map +2 -2
- package/test/menu-item.test.js +39 -1
- package/test/menu-item.test.js.map +2 -2
- package/test/menu-selects.test.js +42 -49
- package/test/menu-selects.test.js.map +2 -2
- package/test/menu.test.js +102 -98
- package/test/menu.test.js.map +2 -2
- package/test/submenu.test.js +60 -34
- package/test/submenu.test.js.map +2 -2
package/src/Menu.dev.js
CHANGED
|
@@ -19,9 +19,7 @@ import {
|
|
|
19
19
|
query
|
|
20
20
|
} from "@spectrum-web-components/base/src/decorators.js";
|
|
21
21
|
import menuStyles from "./menu.css.js";
|
|
22
|
-
|
|
23
|
-
return !!isOrContains && (el === isOrContains || el.contains(isOrContains));
|
|
24
|
-
}
|
|
22
|
+
import { RovingTabindexController } from "@spectrum-web-components/reactive-controllers/src/RovingTabindex.js";
|
|
25
23
|
export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
26
24
|
constructor() {
|
|
27
25
|
super();
|
|
@@ -58,15 +56,8 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
58
56
|
composed: true
|
|
59
57
|
})
|
|
60
58
|
);
|
|
61
|
-
const focusedItem = this.childItems[this.focusedItemIndex];
|
|
62
|
-
if (focusedItem) {
|
|
63
|
-
focusedItem.focused = false;
|
|
64
|
-
}
|
|
65
59
|
const openedItem = event.composedPath().find((el) => this.childItemSet.has(el));
|
|
66
60
|
if (!openedItem) return;
|
|
67
|
-
const openedItemIndex = this.childItems.indexOf(openedItem);
|
|
68
|
-
this.focusedItemIndex = openedItemIndex;
|
|
69
|
-
this.focusInItemIndex = openedItemIndex;
|
|
70
61
|
};
|
|
71
62
|
this._hasUpdatedSelectedItemIndex = false;
|
|
72
63
|
this._willUpdateItems = false;
|
|
@@ -75,6 +66,26 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
75
66
|
this.resolveCacheUpdated = () => {
|
|
76
67
|
return;
|
|
77
68
|
};
|
|
69
|
+
if (!this.rovingTabindexController && this.controlsRovingTabindex) {
|
|
70
|
+
this.rovingTabindexController = new RovingTabindexController(this, {
|
|
71
|
+
direction: "vertical",
|
|
72
|
+
focusInIndex: (elements) => {
|
|
73
|
+
let firstEnabledIndex = -1;
|
|
74
|
+
const firstSelectedIndex = elements == null ? void 0 : elements.findIndex(
|
|
75
|
+
(el, index) => {
|
|
76
|
+
if (!elements[firstEnabledIndex] && !el.disabled) {
|
|
77
|
+
firstEnabledIndex = index;
|
|
78
|
+
}
|
|
79
|
+
return el.selected && !el.disabled;
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
return elements && firstSelectedIndex && elements[firstSelectedIndex] ? firstSelectedIndex : firstEnabledIndex;
|
|
83
|
+
},
|
|
84
|
+
elements: () => this.childItems,
|
|
85
|
+
isFocusableElement: this.isFocusableElement.bind(this),
|
|
86
|
+
hostDelegatesFocus: true
|
|
87
|
+
});
|
|
88
|
+
}
|
|
78
89
|
this.addEventListener(
|
|
79
90
|
"sp-menu-item-added-or-updated",
|
|
80
91
|
this.onSelectableItemAddedOrUpdated
|
|
@@ -87,9 +98,9 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
87
98
|
}
|
|
88
99
|
);
|
|
89
100
|
this.addEventListener("click", this.handleClick);
|
|
101
|
+
this.addEventListener("focusout", this.handleFocusout);
|
|
102
|
+
this.addEventListener("sp-menu-item-keydown", this.handleKeydown);
|
|
90
103
|
this.addEventListener("pointerup", this.handlePointerup);
|
|
91
|
-
this.addEventListener("focusin", this.handleFocusin);
|
|
92
|
-
this.addEventListener("blur", this.handleBlur);
|
|
93
104
|
this.addEventListener("sp-opened", this.handleSubmenuOpened);
|
|
94
105
|
this.addEventListener("sp-closed", this.handleSubmenuClosed);
|
|
95
106
|
}
|
|
@@ -100,7 +111,7 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
100
111
|
return this.slot === "submenu";
|
|
101
112
|
}
|
|
102
113
|
get selected() {
|
|
103
|
-
return this._selected;
|
|
114
|
+
return !this.selects ? [] : this._selected;
|
|
104
115
|
}
|
|
105
116
|
set selected(selected) {
|
|
106
117
|
if (selected === this.selected) {
|
|
@@ -122,6 +133,16 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
122
133
|
});
|
|
123
134
|
this.requestUpdate("selected", old);
|
|
124
135
|
}
|
|
136
|
+
get focusInItem() {
|
|
137
|
+
var _a;
|
|
138
|
+
return (_a = this.rovingTabindexController) == null ? void 0 : _a.focusInElement;
|
|
139
|
+
}
|
|
140
|
+
get controlsRovingTabindex() {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* child items managed by menu
|
|
145
|
+
*/
|
|
125
146
|
get childItems() {
|
|
126
147
|
if (!this.cachedChildItems) {
|
|
127
148
|
this.cachedChildItems = this.updateCachedMenuItems();
|
|
@@ -129,16 +150,17 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
129
150
|
return this.cachedChildItems;
|
|
130
151
|
}
|
|
131
152
|
updateCachedMenuItems() {
|
|
132
|
-
|
|
153
|
+
var _a;
|
|
133
154
|
if (!this.menuSlot) {
|
|
134
155
|
return [];
|
|
135
156
|
}
|
|
157
|
+
const itemsList = [];
|
|
136
158
|
const slottedElements = this.menuSlot.assignedElements({
|
|
137
159
|
flatten: true
|
|
138
160
|
});
|
|
139
161
|
for (const [i, slottedElement] of slottedElements.entries()) {
|
|
140
162
|
if (this.childItemSet.has(slottedElement)) {
|
|
141
|
-
|
|
163
|
+
itemsList.push(slottedElement);
|
|
142
164
|
continue;
|
|
143
165
|
}
|
|
144
166
|
const isHTMLSlotElement = slottedElement.localName === "slot";
|
|
@@ -152,6 +174,8 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
152
174
|
...flattenedChildren
|
|
153
175
|
);
|
|
154
176
|
}
|
|
177
|
+
this.cachedChildItems = [...itemsList];
|
|
178
|
+
(_a = this.rovingTabindexController) == null ? void 0 : _a.clearElementCache();
|
|
155
179
|
return this.cachedChildItems;
|
|
156
180
|
}
|
|
157
181
|
/**
|
|
@@ -206,9 +230,6 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
206
230
|
const cascadeData = event.menuCascade.get(this);
|
|
207
231
|
if (!cascadeData) return;
|
|
208
232
|
event.item.menuData.parentMenu = event.item.menuData.parentMenu || this;
|
|
209
|
-
if (cascadeData.hadFocusRoot && !this.ignore) {
|
|
210
|
-
this.tabIndex = -1;
|
|
211
|
-
}
|
|
212
233
|
this.addChildItem(event.item);
|
|
213
234
|
if (this.selects === "inherit") {
|
|
214
235
|
this.resolvedSelects = "inherit";
|
|
@@ -221,6 +242,9 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
221
242
|
this.resolvedRole = this.ignore ? "none" : this.getAttribute("role") || void 0;
|
|
222
243
|
this.resolvedSelects = this.resolvedRole === "none" ? "ignore" : "none";
|
|
223
244
|
}
|
|
245
|
+
if (this.resolvedRole === "none") {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
224
248
|
const selects = this.resolvedSelects === "single" || this.resolvedSelects === "multiple";
|
|
225
249
|
event.item.menuData.cleanupSteps.push(
|
|
226
250
|
(item) => this.removeChildItem(item)
|
|
@@ -241,30 +265,50 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
241
265
|
this.handleItemsChanged();
|
|
242
266
|
}
|
|
243
267
|
async removeChildItem(item) {
|
|
268
|
+
if (item.focused || item.hasAttribute("focused") || item.active) {
|
|
269
|
+
this._updateFocus = this.getNeighboringFocusableElement(item);
|
|
270
|
+
}
|
|
244
271
|
this.childItemSet.delete(item);
|
|
245
272
|
this.cachedChildItems = void 0;
|
|
246
|
-
if (item.focused) {
|
|
247
|
-
this.handleItemsChanged();
|
|
248
|
-
await this.updateComplete;
|
|
249
|
-
this.focus();
|
|
250
|
-
}
|
|
251
273
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
274
|
+
/**
|
|
275
|
+
* for picker elements, will set focus on first selected item
|
|
276
|
+
*/
|
|
277
|
+
focusOnFirstSelectedItem({
|
|
278
|
+
preventScroll
|
|
279
|
+
} = {}) {
|
|
280
|
+
var _a;
|
|
281
|
+
if (!this.rovingTabindexController) return;
|
|
282
|
+
const selectedItem = this.selectedItems.find(
|
|
283
|
+
(el) => this.isFocusableElement(el)
|
|
284
|
+
);
|
|
285
|
+
if (!selectedItem) {
|
|
286
|
+
this.focus({ preventScroll });
|
|
260
287
|
return;
|
|
261
288
|
}
|
|
262
|
-
this.focusMenuItemByOffset(0);
|
|
263
|
-
super.focus({ preventScroll });
|
|
264
|
-
const selectedItem = this.selectedItems[0];
|
|
265
289
|
if (selectedItem && !preventScroll) {
|
|
266
290
|
selectedItem.scrollIntoView({ block: "nearest" });
|
|
267
291
|
}
|
|
292
|
+
(_a = this.rovingTabindexController) == null ? void 0 : _a.focusOnItem(selectedItem);
|
|
293
|
+
}
|
|
294
|
+
focus({ preventScroll } = {}) {
|
|
295
|
+
if (this.rovingTabindexController) {
|
|
296
|
+
if (!this.childItems.length || this.childItems.every((childItem) => childItem.disabled)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (this.childItems.some(
|
|
300
|
+
(childItem) => childItem.menuData.focusRoot !== this
|
|
301
|
+
)) {
|
|
302
|
+
super.focus({ preventScroll });
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this.rovingTabindexController.focus({ preventScroll });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
handleFocusout() {
|
|
309
|
+
var _a;
|
|
310
|
+
if (!this.matches(":focus-within"))
|
|
311
|
+
(_a = this.rovingTabindexController) == null ? void 0 : _a.reset();
|
|
268
312
|
}
|
|
269
313
|
handleClick(event) {
|
|
270
314
|
if (this.pointerUpTarget === event.target) {
|
|
@@ -315,40 +359,6 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
315
359
|
}
|
|
316
360
|
this.prepareToCleanUp();
|
|
317
361
|
}
|
|
318
|
-
handleFocusin(event) {
|
|
319
|
-
var _a;
|
|
320
|
-
if (this.childItems.some(
|
|
321
|
-
(childItem) => childItem.menuData.focusRoot !== this
|
|
322
|
-
)) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
const activeElement = this.getRootNode().activeElement;
|
|
326
|
-
const selectionRoot = ((_a = this.childItems[this.focusedItemIndex]) == null ? void 0 : _a.menuData.selectionRoot) || this;
|
|
327
|
-
if (activeElement !== selectionRoot || event.target !== this) {
|
|
328
|
-
selectionRoot.focus({ preventScroll: true });
|
|
329
|
-
if (activeElement && this.focusedItemIndex === 0) {
|
|
330
|
-
const offset = this.childItems.findIndex(
|
|
331
|
-
(childItem) => childItem === activeElement
|
|
332
|
-
);
|
|
333
|
-
this.focusMenuItemByOffset(Math.max(offset, 0));
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
this.startListeningToKeyboard();
|
|
337
|
-
}
|
|
338
|
-
startListeningToKeyboard() {
|
|
339
|
-
this.addEventListener("keydown", this.handleKeydown);
|
|
340
|
-
}
|
|
341
|
-
handleBlur(event) {
|
|
342
|
-
if (elementIsOrContains(this, event.relatedTarget)) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
this.stopListeningToKeyboard();
|
|
346
|
-
this.childItems.forEach((child) => child.focused = false);
|
|
347
|
-
this.removeAttribute("aria-activedescendant");
|
|
348
|
-
}
|
|
349
|
-
stopListeningToKeyboard() {
|
|
350
|
-
this.removeEventListener("keydown", this.handleKeydown);
|
|
351
|
-
}
|
|
352
362
|
handleDescendentOverlayOpened(event) {
|
|
353
363
|
const target = event.composedPath()[0];
|
|
354
364
|
if (!target.overlayElement) return;
|
|
@@ -362,19 +372,34 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
362
372
|
if (!target.overlayElement) return;
|
|
363
373
|
this.descendentOverlays.delete(target.overlayElement);
|
|
364
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* given a menu item, returns the next focusable menu item before or after it;
|
|
377
|
+
* if no menu item is provided, returns the first focusable menu item
|
|
378
|
+
* @param menuItem {MenuItem}
|
|
379
|
+
* @param before {boolean} return the item before; default is false
|
|
380
|
+
* @returns {MenuItem}
|
|
381
|
+
*/
|
|
382
|
+
getNeighboringFocusableElement(menuItem, before = false) {
|
|
383
|
+
var _a;
|
|
384
|
+
const diff = before ? -1 : 1;
|
|
385
|
+
const elements = ((_a = this.rovingTabindexController) == null ? void 0 : _a.elements) || [];
|
|
386
|
+
const index = !!menuItem ? elements.indexOf(menuItem) : -1;
|
|
387
|
+
let newIndex = Math.min(Math.max(0, index + diff), elements.length - 1);
|
|
388
|
+
while (!this.isFocusableElement(elements[newIndex]) && 0 < newIndex && newIndex < elements.length - 1) {
|
|
389
|
+
newIndex += diff;
|
|
390
|
+
}
|
|
391
|
+
return !!this.isFocusableElement(elements[newIndex]) ? elements[newIndex] : menuItem || elements[0];
|
|
392
|
+
}
|
|
365
393
|
async selectOrToggleItem(targetItem) {
|
|
394
|
+
var _a;
|
|
366
395
|
const resolvedSelects = this.resolvedSelects;
|
|
367
396
|
const oldSelectedItemsMap = new Map(this.selectedItemsMap);
|
|
368
397
|
const oldSelected = this.selected.slice();
|
|
369
398
|
const oldSelectedItems = this.selectedItems.slice();
|
|
370
399
|
const oldValue = this.value;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
focusedChild.focused = false;
|
|
374
|
-
focusedChild.active = false;
|
|
400
|
+
if (targetItem.menuData.selectionRoot !== this) {
|
|
401
|
+
return;
|
|
375
402
|
}
|
|
376
|
-
this.focusedItemIndex = this.childItems.indexOf(targetItem);
|
|
377
|
-
this.forwardFocusVisibleToItem(targetItem);
|
|
378
403
|
if (resolvedSelects === "multiple") {
|
|
379
404
|
if (this.selectedItemsMap.has(targetItem)) {
|
|
380
405
|
this.selectedItemsMap.delete(targetItem);
|
|
@@ -423,49 +448,37 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
423
448
|
targetItem.selected = true;
|
|
424
449
|
} else if (resolvedSelects === "multiple") {
|
|
425
450
|
targetItem.selected = !targetItem.selected;
|
|
451
|
+
} else if (!targetItem.hasSubmenu && ((_a = targetItem == null ? void 0 : targetItem.menuData) == null ? void 0 : _a.focusRoot) === this) {
|
|
452
|
+
this.dispatchEvent(new Event("close", { bubbles: true }));
|
|
426
453
|
}
|
|
427
454
|
}
|
|
428
|
-
navigateWithinMenu(event) {
|
|
429
|
-
const { key } = event;
|
|
430
|
-
const lastFocusedItem = this.childItems[this.focusedItemIndex];
|
|
431
|
-
const direction = key === "ArrowDown" ? 1 : -1;
|
|
432
|
-
const itemToFocus = this.focusMenuItemByOffset(direction);
|
|
433
|
-
if (itemToFocus === lastFocusedItem) {
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
event.preventDefault();
|
|
437
|
-
event.stopPropagation();
|
|
438
|
-
itemToFocus.scrollIntoView({ block: "nearest" });
|
|
439
|
-
}
|
|
440
455
|
navigateBetweenRelatedMenus(event) {
|
|
441
|
-
const { key } = event;
|
|
442
|
-
event.stopPropagation();
|
|
456
|
+
const { key, root } = event;
|
|
443
457
|
const shouldOpenSubmenu = this.isLTR && key === "ArrowRight" || !this.isLTR && key === "ArrowLeft";
|
|
444
|
-
const shouldCloseSelfAsSubmenu = this.isLTR && key === "ArrowLeft" || !this.isLTR && key === "ArrowRight";
|
|
458
|
+
const shouldCloseSelfAsSubmenu = this.isLTR && key === "ArrowLeft" || !this.isLTR && key === "ArrowRight" || key === "Escape";
|
|
459
|
+
const lastFocusedItem = root;
|
|
445
460
|
if (shouldOpenSubmenu) {
|
|
446
|
-
const lastFocusedItem = this.childItems[this.focusedItemIndex];
|
|
447
461
|
if (lastFocusedItem == null ? void 0 : lastFocusedItem.hasSubmenu) {
|
|
462
|
+
event.stopPropagation();
|
|
448
463
|
lastFocusedItem.openOverlay();
|
|
449
464
|
}
|
|
450
465
|
} else if (shouldCloseSelfAsSubmenu && this.isSubmenu) {
|
|
466
|
+
event.stopPropagation();
|
|
451
467
|
this.dispatchEvent(new Event("close", { bubbles: true }));
|
|
452
468
|
this.updateSelectedItemIndex();
|
|
453
469
|
}
|
|
454
470
|
}
|
|
455
471
|
handleKeydown(event) {
|
|
456
|
-
|
|
472
|
+
var _a;
|
|
473
|
+
if (event.defaultPrevented || !this.rovingTabindexController) {
|
|
457
474
|
return;
|
|
458
475
|
}
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
const { key } = event;
|
|
464
|
-
if (event.shiftKey && event.target !== this && this.hasAttribute("tabindex")) {
|
|
476
|
+
const { key, root, shiftKey, target } = event;
|
|
477
|
+
const openSubmenuKey = ["Enter", " "].includes(key);
|
|
478
|
+
if (shiftKey && target !== this && this.hasAttribute("tabindex")) {
|
|
465
479
|
this.removeAttribute("tabindex");
|
|
466
480
|
const replaceTabindex = (event2) => {
|
|
467
481
|
if (!event2.shiftKey && !this.hasAttribute("tabindex")) {
|
|
468
|
-
this.tabIndex = 0;
|
|
469
482
|
document.removeEventListener("keyup", replaceTabindex);
|
|
470
483
|
this.removeEventListener("focusout", replaceTabindex);
|
|
471
484
|
}
|
|
@@ -474,61 +487,33 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
474
487
|
this.addEventListener("focusout", replaceTabindex);
|
|
475
488
|
}
|
|
476
489
|
if (key === "Tab") {
|
|
477
|
-
this.
|
|
490
|
+
this.closeDescendentOverlays();
|
|
478
491
|
return;
|
|
479
492
|
}
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (key === " " || key === "Enter") {
|
|
487
|
-
const childItem = this.childItems[this.focusedItemIndex];
|
|
488
|
-
if (childItem && childItem.menuData.selectionRoot === event.target) {
|
|
489
|
-
event.preventDefault();
|
|
490
|
-
childItem.click();
|
|
491
|
-
}
|
|
493
|
+
if (openSubmenuKey && (root == null ? void 0 : root.hasSubmenu) && !root.open) {
|
|
494
|
+
event.preventDefault();
|
|
495
|
+
root.openOverlay();
|
|
492
496
|
return;
|
|
493
497
|
}
|
|
494
|
-
if (key === "
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
498
|
+
if (key === " " || key === "Enter") {
|
|
499
|
+
event.preventDefault();
|
|
500
|
+
(_a = root == null ? void 0 : root.focusElement) == null ? void 0 : _a.click();
|
|
501
|
+
if (root) this.selectOrToggleItem(root);
|
|
499
502
|
return;
|
|
500
503
|
}
|
|
501
504
|
this.navigateBetweenRelatedMenus(event);
|
|
502
505
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (focusedItem) {
|
|
507
|
-
focusedItem.focused = false;
|
|
508
|
-
focusedItem.active = focusedItem.open;
|
|
509
|
-
}
|
|
510
|
-
this.focusedItemIndex = (this.childItems.length + this.focusedItemIndex + offset) % this.childItems.length;
|
|
511
|
-
let itemToFocus = this.childItems[this.focusedItemIndex];
|
|
512
|
-
let availableItems = this.childItems.length;
|
|
513
|
-
while ((itemToFocus == null ? void 0 : itemToFocus.disabled) && availableItems) {
|
|
514
|
-
availableItems -= 1;
|
|
515
|
-
this.focusedItemIndex = (this.childItems.length + this.focusedItemIndex + step) % this.childItems.length;
|
|
516
|
-
itemToFocus = this.childItems[this.focusedItemIndex];
|
|
517
|
-
}
|
|
518
|
-
if (!(itemToFocus == null ? void 0 : itemToFocus.disabled)) {
|
|
519
|
-
this.forwardFocusVisibleToItem(itemToFocus);
|
|
520
|
-
}
|
|
521
|
-
return itemToFocus;
|
|
522
|
-
}
|
|
506
|
+
/**
|
|
507
|
+
* on focus, removes focus from focus styling item, and updates the selected item index
|
|
508
|
+
*/
|
|
523
509
|
prepareToCleanUp() {
|
|
524
510
|
document.addEventListener(
|
|
525
511
|
"focusout",
|
|
526
512
|
() => {
|
|
527
513
|
requestAnimationFrame(() => {
|
|
528
|
-
const focusedItem = this.
|
|
514
|
+
const focusedItem = this.focusInItem;
|
|
529
515
|
if (focusedItem) {
|
|
530
516
|
focusedItem.focused = false;
|
|
531
|
-
this.updateSelectedItemIndex();
|
|
532
517
|
}
|
|
533
518
|
});
|
|
534
519
|
},
|
|
@@ -556,11 +541,6 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
556
541
|
}
|
|
557
542
|
}
|
|
558
543
|
}
|
|
559
|
-
selectedItems.map((item, i) => {
|
|
560
|
-
if (i > 0) {
|
|
561
|
-
item.focused = false;
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
544
|
this.selectedItemsMap = selectedItemsMap;
|
|
565
545
|
this._selected = selected;
|
|
566
546
|
this.selectedItems = selectedItems;
|
|
@@ -591,13 +571,11 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
591
571
|
this._willUpdateItems = false;
|
|
592
572
|
}
|
|
593
573
|
updateItemFocus() {
|
|
574
|
+
var _a;
|
|
575
|
+
(_a = this.focusInItem) == null ? void 0 : _a.setAttribute("tabindex", "0");
|
|
594
576
|
if (this.childItems.length == 0) {
|
|
595
577
|
return;
|
|
596
578
|
}
|
|
597
|
-
const focusInItem = this.childItems[this.focusInItemIndex];
|
|
598
|
-
if (this.getRootNode().activeElement === focusInItem.menuData.focusRoot) {
|
|
599
|
-
this.forwardFocusVisibleToItem(focusInItem);
|
|
600
|
-
}
|
|
601
579
|
}
|
|
602
580
|
closeDescendentOverlays() {
|
|
603
581
|
this.descendentOverlays.forEach((overlay) => {
|
|
@@ -605,23 +583,10 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
605
583
|
});
|
|
606
584
|
this.descendentOverlays = /* @__PURE__ */ new Map();
|
|
607
585
|
}
|
|
608
|
-
forwardFocusVisibleToItem(item) {
|
|
609
|
-
if (!item || item.menuData.focusRoot !== this) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
this.closeDescendentOverlays();
|
|
613
|
-
const focused = this.hasVisibleFocusInTree() || !!this.childItems.find((child) => {
|
|
614
|
-
return child.hasVisibleFocusInTree();
|
|
615
|
-
});
|
|
616
|
-
item.focused = focused;
|
|
617
|
-
this.setAttribute("aria-activedescendant", item.id);
|
|
618
|
-
if (item.menuData.selectionRoot && item.menuData.selectionRoot !== this) {
|
|
619
|
-
item.menuData.selectionRoot.focus();
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
586
|
handleSlotchange({
|
|
623
587
|
target
|
|
624
588
|
}) {
|
|
589
|
+
var _a;
|
|
625
590
|
const assignedElements = target.assignedElements({
|
|
626
591
|
flatten: true
|
|
627
592
|
});
|
|
@@ -636,6 +601,10 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
636
601
|
}
|
|
637
602
|
});
|
|
638
603
|
}
|
|
604
|
+
if (!!this._updateFocus) {
|
|
605
|
+
(_a = this.rovingTabindexController) == null ? void 0 : _a.focusOnItem(this._updateFocus);
|
|
606
|
+
this._updateFocus = void 0;
|
|
607
|
+
}
|
|
639
608
|
}
|
|
640
609
|
renderMenuItemSlot() {
|
|
641
610
|
return html`
|
|
@@ -651,14 +620,6 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
651
620
|
}
|
|
652
621
|
firstUpdated(changed) {
|
|
653
622
|
super.firstUpdated(changed);
|
|
654
|
-
if (!this.hasAttribute("tabindex") && !this.ignore) {
|
|
655
|
-
const role = this.getAttribute("role");
|
|
656
|
-
if (role === "group") {
|
|
657
|
-
this.tabIndex = -1;
|
|
658
|
-
} else {
|
|
659
|
-
this.tabIndex = 0;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
623
|
const updates = [
|
|
663
624
|
new Promise((res) => requestAnimationFrame(() => res(true)))
|
|
664
625
|
];
|
|
@@ -698,6 +659,9 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
698
659
|
}
|
|
699
660
|
this.updateComplete.then(() => this.updateItemFocus());
|
|
700
661
|
}
|
|
662
|
+
isFocusableElement(el) {
|
|
663
|
+
return el ? !el.disabled : false;
|
|
664
|
+
}
|
|
701
665
|
disconnectedCallback() {
|
|
702
666
|
this.cachedChildItems = void 0;
|
|
703
667
|
this.selectedItems = [];
|
|
@@ -713,6 +677,10 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
|
713
677
|
return complete;
|
|
714
678
|
}
|
|
715
679
|
}
|
|
680
|
+
Menu.shadowRootOptions = {
|
|
681
|
+
...SpectrumElement.shadowRootOptions,
|
|
682
|
+
delegatesFocus: true
|
|
683
|
+
};
|
|
716
684
|
__decorateClass([
|
|
717
685
|
property({ type: String, reflect: true })
|
|
718
686
|
], Menu.prototype, "label", 2);
|