@spectrum-web-components/menu 0.36.0 → 0.38.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 +459 -159
- package/package.json +9 -9
- package/src/Menu.d.ts +16 -6
- package/src/Menu.dev.js +179 -65
- package/src/Menu.dev.js.map +2 -2
- package/src/Menu.js +7 -3
- package/src/Menu.js.map +3 -3
- package/src/MenuGroup.d.ts +0 -2
- package/src/MenuGroup.dev.js +8 -12
- package/src/MenuGroup.dev.js.map +2 -2
- package/src/MenuGroup.js +3 -5
- package/src/MenuGroup.js.map +3 -3
- package/src/MenuItem.d.ts +30 -23
- package/src/MenuItem.dev.js +209 -215
- package/src/MenuItem.dev.js.map +3 -3
- package/src/MenuItem.js +36 -18
- package/src/MenuItem.js.map +3 -3
- package/src/menu-item.css.dev.js +9 -9
- package/src/menu-item.css.dev.js.map +1 -1
- package/src/menu-item.css.js +9 -9
- package/src/menu-item.css.js.map +1 -1
- package/src/menu.css.dev.js +1 -1
- package/src/menu.css.dev.js.map +1 -1
- package/src/menu.css.js +1 -1
- package/src/menu.css.js.map +1 -1
- package/src/spectrum-config.js +27 -1
- package/src/spectrum-menu-item.css.dev.js +8 -8
- package/src/spectrum-menu-item.css.dev.js.map +1 -1
- package/src/spectrum-menu-item.css.js +8 -8
- package/src/spectrum-menu-item.css.js.map +1 -1
- package/stories/index.js +4 -0
- package/stories/index.js.map +2 -2
- package/stories/menu-item.stories.js +10 -0
- package/stories/menu-item.stories.js.map +2 -2
- package/stories/menu.stories.js +47 -0
- package/stories/menu.stories.js.map +2 -2
- package/stories/submenu.stories.js +117 -104
- package/stories/submenu.stories.js.map +3 -3
- package/test/menu-group.test.js +14 -1
- package/test/menu-group.test.js.map +2 -2
- package/test/menu-item.test.js +36 -0
- package/test/menu-item.test.js.map +2 -2
- package/test/menu-selects.test.js +3 -1
- package/test/menu-selects.test.js.map +2 -2
- package/test/menu.test.js +9 -1
- package/test/menu.test.js.map +2 -2
- package/test/submenu.test.js +208 -84
- package/test/submenu.test.js.map +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spectrum-web-components/menu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -85,13 +85,13 @@
|
|
|
85
85
|
],
|
|
86
86
|
"dependencies": {
|
|
87
87
|
"@lit-labs/observers": "^2.0.0",
|
|
88
|
-
"@spectrum-web-components/action-button": "^0.
|
|
89
|
-
"@spectrum-web-components/base": "^0.
|
|
90
|
-
"@spectrum-web-components/divider": "^0.
|
|
91
|
-
"@spectrum-web-components/icon": "^0.
|
|
92
|
-
"@spectrum-web-components/icons-ui": "^0.
|
|
93
|
-
"@spectrum-web-components/overlay": "^0.
|
|
94
|
-
"@spectrum-web-components/shared": "^0.
|
|
88
|
+
"@spectrum-web-components/action-button": "^0.38.0",
|
|
89
|
+
"@spectrum-web-components/base": "^0.38.0",
|
|
90
|
+
"@spectrum-web-components/divider": "^0.38.0",
|
|
91
|
+
"@spectrum-web-components/icon": "^0.38.0",
|
|
92
|
+
"@spectrum-web-components/icons-ui": "^0.38.0",
|
|
93
|
+
"@spectrum-web-components/overlay": "^0.38.0",
|
|
94
|
+
"@spectrum-web-components/shared": "^0.38.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@spectrum-css/menu": "^5.0.2"
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"./sp-*.js",
|
|
103
103
|
"./**/*.dev.js"
|
|
104
104
|
],
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "9a099b7543672f2fd4030833ab813b16c2cad62e"
|
|
106
106
|
}
|
package/src/Menu.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ declare const Menu_base: typeof SpectrumElement & {
|
|
|
26
26
|
*/
|
|
27
27
|
export declare class Menu extends Menu_base {
|
|
28
28
|
static get styles(): CSSResultArray;
|
|
29
|
-
isSubmenu
|
|
29
|
+
private get isSubmenu();
|
|
30
30
|
label: string;
|
|
31
31
|
ignore: boolean;
|
|
32
32
|
selects: undefined | 'inherit' | 'single' | 'multiple';
|
|
@@ -55,7 +55,7 @@ export declare class Menu extends Menu_base {
|
|
|
55
55
|
private resolvedRole?;
|
|
56
56
|
/**
|
|
57
57
|
* When a descendant `<sp-menu-item>` element is added or updated it will dispatch
|
|
58
|
-
* this event to announce its presence in the DOM. During the
|
|
58
|
+
* this event to announce its presence in the DOM. During the CAPTURE phase the first
|
|
59
59
|
* Menu based element that the event encounters will manage the focus state of the
|
|
60
60
|
* dispatching `<sp-menu-item>` element.
|
|
61
61
|
* @param event
|
|
@@ -63,7 +63,7 @@ export declare class Menu extends Menu_base {
|
|
|
63
63
|
private onFocusableItemAddedOrUpdated;
|
|
64
64
|
/**
|
|
65
65
|
* When a descendant `<sp-menu-item>` element is added or updated it will dispatch
|
|
66
|
-
* this event to announce its presence in the DOM. During the
|
|
66
|
+
* this event to announce its presence in the DOM. During the BUBBLE phase the first
|
|
67
67
|
* Menu based element that the event encounters that does not inherit selection will
|
|
68
68
|
* manage the selection state of the dispatching `<sp-menu-item>` element.
|
|
69
69
|
* @param event
|
|
@@ -73,24 +73,33 @@ export declare class Menu extends Menu_base {
|
|
|
73
73
|
private removeChildItem;
|
|
74
74
|
constructor();
|
|
75
75
|
focus({ preventScroll }?: FocusOptions): void;
|
|
76
|
-
private
|
|
76
|
+
private handleClick;
|
|
77
77
|
handleFocusin(event: FocusEvent): void;
|
|
78
78
|
startListeningToKeyboard(): void;
|
|
79
79
|
handleFocusout(event: FocusEvent): void;
|
|
80
80
|
stopListeningToKeyboard(): void;
|
|
81
|
+
private descendentOverlays;
|
|
82
|
+
protected handleDescendentOverlayOpened(event: Event): void;
|
|
83
|
+
protected handleDescendentOverlayClosed(event: Event): void;
|
|
84
|
+
handleSubmenuClosed: (event: Event) => void;
|
|
85
|
+
handleSubmenuOpened: (event: Event) => void;
|
|
81
86
|
selectOrToggleItem(targetItem: MenuItem): Promise<void>;
|
|
82
87
|
protected navigateWithinMenu(event: KeyboardEvent): void;
|
|
83
|
-
protected navigateBetweenRelatedMenus(
|
|
88
|
+
protected navigateBetweenRelatedMenus(event: KeyboardEvent): void;
|
|
84
89
|
handleKeydown(event: KeyboardEvent): void;
|
|
85
90
|
focusMenuItemByOffset(offset: number): MenuItem;
|
|
86
91
|
private prepareToCleanUp;
|
|
92
|
+
private _hasUpdatedSelectedItemIndex;
|
|
87
93
|
updateSelectedItemIndex(): void;
|
|
88
94
|
private _willUpdateItems;
|
|
89
95
|
private handleItemsChanged;
|
|
96
|
+
private updateCache;
|
|
90
97
|
private updateItemFocus;
|
|
98
|
+
closeDescendentOverlays(): void;
|
|
91
99
|
private forwardFocusVisibleToItem;
|
|
100
|
+
private handleSlotchange;
|
|
101
|
+
protected renderMenuItemSlot(): TemplateResult;
|
|
92
102
|
render(): TemplateResult;
|
|
93
|
-
private _notFirstUpdated;
|
|
94
103
|
protected firstUpdated(changed: PropertyValues): void;
|
|
95
104
|
protected updated(changes: PropertyValues<this>): void;
|
|
96
105
|
protected selectsChanged(): void;
|
|
@@ -98,6 +107,7 @@ export declare class Menu extends Menu_base {
|
|
|
98
107
|
disconnectedCallback(): void;
|
|
99
108
|
protected childItemsUpdated: Promise<unknown[]>;
|
|
100
109
|
protected cacheUpdated: Promise<void>;
|
|
110
|
+
protected resolveCacheUpdated: () => void;
|
|
101
111
|
protected getUpdateComplete(): Promise<boolean>;
|
|
102
112
|
}
|
|
103
113
|
export {};
|
package/src/Menu.dev.js
CHANGED
|
@@ -24,10 +24,9 @@ import menuStyles from "./menu.css.js";
|
|
|
24
24
|
function elementIsOrContains(el, isOrContains) {
|
|
25
25
|
return !!isOrContains && (el === isOrContains || el.contains(isOrContains));
|
|
26
26
|
}
|
|
27
|
-
export class Menu extends SizedMixin(SpectrumElement) {
|
|
27
|
+
export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
|
|
28
28
|
constructor() {
|
|
29
29
|
super();
|
|
30
|
-
this.isSubmenu = false;
|
|
31
30
|
this.label = "";
|
|
32
31
|
this.ignore = false;
|
|
33
32
|
this.value = "";
|
|
@@ -38,9 +37,44 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
38
37
|
this.focusedItemIndex = 0;
|
|
39
38
|
this.focusInItemIndex = 0;
|
|
40
39
|
this.selectedItemsMap = /* @__PURE__ */ new Map();
|
|
40
|
+
this.descendentOverlays = /* @__PURE__ */ new Map();
|
|
41
|
+
this.handleSubmenuClosed = (event) => {
|
|
42
|
+
event.stopPropagation();
|
|
43
|
+
const target = event.composedPath()[0];
|
|
44
|
+
target.dispatchEvent(
|
|
45
|
+
new Event("sp-menu-submenu-closed", {
|
|
46
|
+
bubbles: true,
|
|
47
|
+
composed: true
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
this.handleSubmenuOpened = (event) => {
|
|
52
|
+
event.stopPropagation();
|
|
53
|
+
const target = event.composedPath()[0];
|
|
54
|
+
target.dispatchEvent(
|
|
55
|
+
new Event("sp-menu-submenu-opened", {
|
|
56
|
+
bubbles: true,
|
|
57
|
+
composed: true
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
const focusedItem = this.childItems[this.focusedItemIndex];
|
|
61
|
+
if (focusedItem) {
|
|
62
|
+
focusedItem.focused = false;
|
|
63
|
+
}
|
|
64
|
+
const openedItem = event.composedPath().find((el) => this.childItemSet.has(el));
|
|
65
|
+
if (!openedItem)
|
|
66
|
+
return;
|
|
67
|
+
const openedItemIndex = this.childItems.indexOf(openedItem);
|
|
68
|
+
this.focusedItemIndex = openedItemIndex;
|
|
69
|
+
this.focusInItemIndex = openedItemIndex;
|
|
70
|
+
};
|
|
71
|
+
this._hasUpdatedSelectedItemIndex = false;
|
|
41
72
|
this._willUpdateItems = false;
|
|
42
|
-
this._notFirstUpdated = false;
|
|
43
73
|
this.cacheUpdated = Promise.resolve();
|
|
74
|
+
/* c8 ignore next 3 */
|
|
75
|
+
this.resolveCacheUpdated = () => {
|
|
76
|
+
return;
|
|
77
|
+
};
|
|
44
78
|
this.addEventListener(
|
|
45
79
|
"sp-menu-item-added-or-updated",
|
|
46
80
|
this.onSelectableItemAddedOrUpdated
|
|
@@ -52,13 +86,18 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
52
86
|
capture: true
|
|
53
87
|
}
|
|
54
88
|
);
|
|
55
|
-
this.addEventListener("
|
|
56
|
-
this.addEventListener("click", this.onClick);
|
|
89
|
+
this.addEventListener("click", this.handleClick);
|
|
57
90
|
this.addEventListener("focusin", this.handleFocusin);
|
|
91
|
+
this.addEventListener("focusout", this.handleFocusout);
|
|
92
|
+
this.addEventListener("sp-opened", this.handleSubmenuOpened);
|
|
93
|
+
this.addEventListener("sp-closed", this.handleSubmenuClosed);
|
|
58
94
|
}
|
|
59
95
|
static get styles() {
|
|
60
96
|
return [menuStyles];
|
|
61
97
|
}
|
|
98
|
+
get isSubmenu() {
|
|
99
|
+
return this.slot === "submenu";
|
|
100
|
+
}
|
|
62
101
|
get childItems() {
|
|
63
102
|
if (!this.cachedChildItems) {
|
|
64
103
|
this.cachedChildItems = this.updateCachedMenuItems();
|
|
@@ -67,7 +106,7 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
67
106
|
}
|
|
68
107
|
updateCachedMenuItems() {
|
|
69
108
|
this.cachedChildItems = [];
|
|
70
|
-
const slotElements = this.menuSlot
|
|
109
|
+
const slotElements = this.menuSlot.assignedElements({ flatten: true });
|
|
71
110
|
for (const slotElement of slotElements) {
|
|
72
111
|
const childMenuItems = slotElement instanceof MenuItem ? [slotElement] : [...slotElement.querySelectorAll(`*`)];
|
|
73
112
|
for (const childMenuItem of childMenuItems) {
|
|
@@ -103,17 +142,37 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
103
142
|
}
|
|
104
143
|
/**
|
|
105
144
|
* When a descendant `<sp-menu-item>` element is added or updated it will dispatch
|
|
106
|
-
* this event to announce its presence in the DOM. During the
|
|
145
|
+
* this event to announce its presence in the DOM. During the CAPTURE phase the first
|
|
107
146
|
* Menu based element that the event encounters will manage the focus state of the
|
|
108
147
|
* dispatching `<sp-menu-item>` element.
|
|
109
148
|
* @param event
|
|
110
149
|
*/
|
|
111
150
|
onFocusableItemAddedOrUpdated(event) {
|
|
151
|
+
event.menuCascade.set(this, {
|
|
152
|
+
hadFocusRoot: !!event.item.menuData.focusRoot,
|
|
153
|
+
ancestorWithSelects: event.currentAncestorWithSelects
|
|
154
|
+
});
|
|
155
|
+
if (this.selects) {
|
|
156
|
+
event.currentAncestorWithSelects = this;
|
|
157
|
+
}
|
|
158
|
+
event.item.menuData.focusRoot = event.item.menuData.focusRoot || this;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* When a descendant `<sp-menu-item>` element is added or updated it will dispatch
|
|
162
|
+
* this event to announce its presence in the DOM. During the BUBBLE phase the first
|
|
163
|
+
* Menu based element that the event encounters that does not inherit selection will
|
|
164
|
+
* manage the selection state of the dispatching `<sp-menu-item>` element.
|
|
165
|
+
* @param event
|
|
166
|
+
*/
|
|
167
|
+
onSelectableItemAddedOrUpdated(event) {
|
|
112
168
|
var _a, _b;
|
|
113
|
-
|
|
169
|
+
const cascadeData = event.menuCascade.get(this);
|
|
170
|
+
if (!cascadeData)
|
|
171
|
+
return;
|
|
172
|
+
event.item.menuData.parentMenu = event.item.menuData.parentMenu || this;
|
|
173
|
+
if (cascadeData.hadFocusRoot && !this.ignore) {
|
|
114
174
|
this.tabIndex = -1;
|
|
115
175
|
}
|
|
116
|
-
event.focusRoot = this;
|
|
117
176
|
this.addChildItem(event.item);
|
|
118
177
|
if (this.selects === "inherit") {
|
|
119
178
|
this.resolvedSelects = "inherit";
|
|
@@ -122,34 +181,33 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
122
181
|
} else if (this.selects) {
|
|
123
182
|
this.resolvedRole = this.ignore ? "none" : this.getAttribute("role") || void 0;
|
|
124
183
|
this.resolvedSelects = this.selects;
|
|
125
|
-
event.currentAncestorWithSelects = this;
|
|
126
184
|
} else {
|
|
127
185
|
this.resolvedRole = this.ignore ? "none" : this.getAttribute("role") || void 0;
|
|
128
186
|
this.resolvedSelects = this.resolvedRole === "none" ? "ignore" : "none";
|
|
129
187
|
}
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* When a descendant `<sp-menu-item>` element is added or updated it will dispatch
|
|
133
|
-
* this event to announce its presence in the DOM. During the bubble phase the first
|
|
134
|
-
* Menu based element that the event encounters that does not inherit selection will
|
|
135
|
-
* manage the selection state of the dispatching `<sp-menu-item>` element.
|
|
136
|
-
* @param event
|
|
137
|
-
*/
|
|
138
|
-
onSelectableItemAddedOrUpdated(event) {
|
|
139
188
|
const selects = this.resolvedSelects === "single" || this.resolvedSelects === "multiple";
|
|
189
|
+
event.item.menuData.cleanupSteps.push(
|
|
190
|
+
(item) => this.removeChildItem(item)
|
|
191
|
+
);
|
|
140
192
|
if ((selects || !this.selects && this.resolvedSelects !== "ignore") && !event.item.menuData.selectionRoot) {
|
|
141
193
|
event.item.setRole(this.childRole);
|
|
142
|
-
event.selectionRoot = this;
|
|
194
|
+
event.item.menuData.selectionRoot = event.item.menuData.selectionRoot || this;
|
|
195
|
+
if (event.item.selected) {
|
|
196
|
+
this.selectedItemsMap.set(event.item, true);
|
|
197
|
+
this.selectedItems = [...this.selectedItems, event.item];
|
|
198
|
+
this.selected = [...this.selected, event.item.value];
|
|
199
|
+
this.value = this.selected.join(this.valueSeparator);
|
|
200
|
+
}
|
|
143
201
|
}
|
|
144
202
|
}
|
|
145
203
|
addChildItem(item) {
|
|
146
204
|
this.childItemSet.add(item);
|
|
147
205
|
this.handleItemsChanged();
|
|
148
206
|
}
|
|
149
|
-
async removeChildItem(
|
|
150
|
-
this.childItemSet.delete(
|
|
207
|
+
async removeChildItem(item) {
|
|
208
|
+
this.childItemSet.delete(item);
|
|
151
209
|
this.cachedChildItems = void 0;
|
|
152
|
-
if (
|
|
210
|
+
if (item.focused) {
|
|
153
211
|
this.handleItemsChanged();
|
|
154
212
|
await this.updateComplete;
|
|
155
213
|
this.focus();
|
|
@@ -167,12 +225,12 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
167
225
|
}
|
|
168
226
|
this.focusMenuItemByOffset(0);
|
|
169
227
|
super.focus({ preventScroll });
|
|
170
|
-
const selectedItem = this.
|
|
228
|
+
const selectedItem = this.selectedItems[0];
|
|
171
229
|
if (selectedItem && !preventScroll) {
|
|
172
230
|
selectedItem.scrollIntoView({ block: "nearest" });
|
|
173
231
|
}
|
|
174
232
|
}
|
|
175
|
-
|
|
233
|
+
handleClick(event) {
|
|
176
234
|
if (event.defaultPrevented) {
|
|
177
235
|
return;
|
|
178
236
|
}
|
|
@@ -204,18 +262,18 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
204
262
|
}
|
|
205
263
|
handleFocusin(event) {
|
|
206
264
|
var _a;
|
|
207
|
-
const
|
|
265
|
+
const wasOrContainedRelatedTarget = elementIsOrContains(
|
|
208
266
|
this,
|
|
209
267
|
event.relatedTarget
|
|
210
268
|
);
|
|
211
|
-
if (
|
|
269
|
+
if (this.childItems.some(
|
|
212
270
|
(childItem) => childItem.menuData.focusRoot !== this
|
|
213
271
|
)) {
|
|
214
272
|
return;
|
|
215
273
|
}
|
|
216
274
|
const activeElement = this.getRootNode().activeElement;
|
|
217
275
|
const selectionRoot = ((_a = this.childItems[this.focusedItemIndex]) == null ? void 0 : _a.menuData.selectionRoot) || this;
|
|
218
|
-
if (activeElement !== selectionRoot || !
|
|
276
|
+
if (activeElement !== selectionRoot || !wasOrContainedRelatedTarget && event.target !== this) {
|
|
219
277
|
selectionRoot.focus({ preventScroll: true });
|
|
220
278
|
if (activeElement && this.focusedItemIndex === 0) {
|
|
221
279
|
const offset = this.childItems.findIndex(
|
|
@@ -230,27 +288,32 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
230
288
|
}
|
|
231
289
|
startListeningToKeyboard() {
|
|
232
290
|
this.addEventListener("keydown", this.handleKeydown);
|
|
233
|
-
this.addEventListener("focusout", this.handleFocusout);
|
|
234
291
|
}
|
|
235
292
|
handleFocusout(event) {
|
|
236
293
|
if (elementIsOrContains(this, event.relatedTarget)) {
|
|
237
|
-
event.composedPath()[0].focused = false;
|
|
238
294
|
return;
|
|
239
295
|
}
|
|
240
296
|
this.stopListeningToKeyboard();
|
|
241
|
-
|
|
242
|
-
(childItem) => childItem.menuData.focusRoot === this
|
|
243
|
-
)) {
|
|
244
|
-
const focusedItem = this.childItems[this.focusedItemIndex];
|
|
245
|
-
if (focusedItem) {
|
|
246
|
-
focusedItem.focused = false;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
297
|
+
this.childItems.forEach((child) => child.focused = false);
|
|
249
298
|
this.removeAttribute("aria-activedescendant");
|
|
250
299
|
}
|
|
251
300
|
stopListeningToKeyboard() {
|
|
252
301
|
this.removeEventListener("keydown", this.handleKeydown);
|
|
253
|
-
|
|
302
|
+
}
|
|
303
|
+
handleDescendentOverlayOpened(event) {
|
|
304
|
+
const target = event.composedPath()[0];
|
|
305
|
+
if (!target.overlayElement)
|
|
306
|
+
return;
|
|
307
|
+
this.descendentOverlays.set(
|
|
308
|
+
target.overlayElement,
|
|
309
|
+
target.overlayElement
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
handleDescendentOverlayClosed(event) {
|
|
313
|
+
const target = event.composedPath()[0];
|
|
314
|
+
if (!target.overlayElement)
|
|
315
|
+
return;
|
|
316
|
+
this.descendentOverlays.delete(target.overlayElement);
|
|
254
317
|
}
|
|
255
318
|
async selectOrToggleItem(targetItem) {
|
|
256
319
|
const resolvedSelects = this.resolvedSelects;
|
|
@@ -258,7 +321,11 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
258
321
|
const oldSelected = this.selected.slice();
|
|
259
322
|
const oldSelectedItems = this.selectedItems.slice();
|
|
260
323
|
const oldValue = this.value;
|
|
261
|
-
this.childItems[this.focusedItemIndex]
|
|
324
|
+
const focusedChild = this.childItems[this.focusedItemIndex];
|
|
325
|
+
if (focusedChild) {
|
|
326
|
+
focusedChild.focused = false;
|
|
327
|
+
focusedChild.active = false;
|
|
328
|
+
}
|
|
262
329
|
this.focusedItemIndex = this.childItems.indexOf(targetItem);
|
|
263
330
|
this.forwardFocusVisibleToItem(targetItem);
|
|
264
331
|
if (resolvedSelects === "multiple") {
|
|
@@ -322,32 +389,42 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
322
389
|
return;
|
|
323
390
|
}
|
|
324
391
|
event.preventDefault();
|
|
392
|
+
event.stopPropagation();
|
|
325
393
|
itemToFocus.scrollIntoView({ block: "nearest" });
|
|
326
394
|
}
|
|
327
|
-
navigateBetweenRelatedMenus(
|
|
395
|
+
navigateBetweenRelatedMenus(event) {
|
|
396
|
+
const { code } = event;
|
|
328
397
|
const shouldOpenSubmenu = this.isLTR && code === "ArrowRight" || !this.isLTR && code === "ArrowLeft";
|
|
329
398
|
const shouldCloseSelfAsSubmenu = this.isLTR && code === "ArrowLeft" || !this.isLTR && code === "ArrowRight";
|
|
330
399
|
if (shouldOpenSubmenu) {
|
|
400
|
+
event.stopPropagation();
|
|
331
401
|
const lastFocusedItem = this.childItems[this.focusedItemIndex];
|
|
332
402
|
if (lastFocusedItem == null ? void 0 : lastFocusedItem.hasSubmenu) {
|
|
333
|
-
this.blur();
|
|
334
403
|
lastFocusedItem.openOverlay();
|
|
335
404
|
}
|
|
336
405
|
} else if (shouldCloseSelfAsSubmenu && this.isSubmenu) {
|
|
406
|
+
event.stopPropagation();
|
|
337
407
|
this.dispatchEvent(new Event("close", { bubbles: true }));
|
|
408
|
+
this.updateSelectedItemIndex();
|
|
338
409
|
}
|
|
339
410
|
}
|
|
340
411
|
handleKeydown(event) {
|
|
341
412
|
var _a;
|
|
413
|
+
const isNotThisOrDirectChild = event.target !== this && this !== event.target.parentElement;
|
|
414
|
+
if (isNotThisOrDirectChild || event.defaultPrevented) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const lastFocusedItem = this.childItems[this.focusedItemIndex];
|
|
418
|
+
if (lastFocusedItem) {
|
|
419
|
+
lastFocusedItem.focused = true;
|
|
420
|
+
}
|
|
342
421
|
const { code } = event;
|
|
343
422
|
if (code === "Tab") {
|
|
344
423
|
this.prepareToCleanUp();
|
|
345
424
|
return;
|
|
346
425
|
}
|
|
347
426
|
if (code === "Space") {
|
|
348
|
-
const lastFocusedItem = this.childItems[this.focusedItemIndex];
|
|
349
427
|
if (lastFocusedItem == null ? void 0 : lastFocusedItem.hasSubmenu) {
|
|
350
|
-
this.blur();
|
|
351
428
|
lastFocusedItem.openOverlay();
|
|
352
429
|
return;
|
|
353
430
|
}
|
|
@@ -360,12 +437,15 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
360
437
|
this.navigateWithinMenu(event);
|
|
361
438
|
return;
|
|
362
439
|
}
|
|
363
|
-
this.navigateBetweenRelatedMenus(
|
|
440
|
+
this.navigateBetweenRelatedMenus(event);
|
|
364
441
|
}
|
|
365
442
|
focusMenuItemByOffset(offset) {
|
|
366
443
|
const step = offset || 1;
|
|
367
444
|
const focusedItem = this.childItems[this.focusedItemIndex];
|
|
368
|
-
focusedItem
|
|
445
|
+
if (focusedItem) {
|
|
446
|
+
focusedItem.focused = false;
|
|
447
|
+
focusedItem.active = focusedItem.open;
|
|
448
|
+
}
|
|
369
449
|
this.focusedItemIndex = (this.childItems.length + this.focusedItemIndex + offset) % this.childItems.length;
|
|
370
450
|
let itemToFocus = this.childItems[this.focusedItemIndex];
|
|
371
451
|
let availableItems = this.childItems.length;
|
|
@@ -404,7 +484,7 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
404
484
|
itemIndex -= 1;
|
|
405
485
|
const childItem = this.childItems[itemIndex];
|
|
406
486
|
if (childItem.menuData.selectionRoot === this) {
|
|
407
|
-
if (childItem.selected) {
|
|
487
|
+
if (childItem.selected || !this._hasUpdatedSelectedItemIndex && this.selected.includes(childItem.value)) {
|
|
408
488
|
firstOrFirstSelectedIndex = itemIndex;
|
|
409
489
|
selectedItemsMap.set(childItem, true);
|
|
410
490
|
selected.unshift(childItem.value);
|
|
@@ -430,20 +510,24 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
430
510
|
handleItemsChanged() {
|
|
431
511
|
this.cachedChildItems = void 0;
|
|
432
512
|
if (!this._willUpdateItems) {
|
|
433
|
-
let resolve = () => {
|
|
434
|
-
return;
|
|
435
|
-
};
|
|
436
|
-
this.cacheUpdated = new Promise((res) => resolve = res);
|
|
437
513
|
this._willUpdateItems = true;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
514
|
+
this.cacheUpdated = this.updateCache();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async updateCache() {
|
|
518
|
+
if (!this.hasUpdated) {
|
|
519
|
+
await Promise.all([
|
|
520
|
+
new Promise((res) => requestAnimationFrame(() => res(true))),
|
|
521
|
+
this.updateComplete
|
|
522
|
+
]);
|
|
523
|
+
} else {
|
|
524
|
+
await new Promise((res) => requestAnimationFrame(() => res(true)));
|
|
525
|
+
}
|
|
526
|
+
if (this.cachedChildItems === void 0) {
|
|
527
|
+
this.updateSelectedItemIndex();
|
|
528
|
+
this.updateItemFocus();
|
|
446
529
|
}
|
|
530
|
+
this._willUpdateItems = false;
|
|
447
531
|
}
|
|
448
532
|
updateItemFocus() {
|
|
449
533
|
if (this.childItems.length == 0) {
|
|
@@ -454,21 +538,52 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
454
538
|
this.forwardFocusVisibleToItem(focusInItem);
|
|
455
539
|
}
|
|
456
540
|
}
|
|
541
|
+
closeDescendentOverlays() {
|
|
542
|
+
this.descendentOverlays.forEach((overlay) => {
|
|
543
|
+
overlay.open = false;
|
|
544
|
+
});
|
|
545
|
+
this.descendentOverlays = /* @__PURE__ */ new Map();
|
|
546
|
+
}
|
|
457
547
|
forwardFocusVisibleToItem(item) {
|
|
458
548
|
if (item.menuData.focusRoot !== this) {
|
|
459
549
|
return;
|
|
460
550
|
}
|
|
461
|
-
|
|
551
|
+
this.closeDescendentOverlays();
|
|
552
|
+
const focused = this.hasVisibleFocusInTree() || !!this.childItems.find((child) => {
|
|
553
|
+
return child.hasVisibleFocusInTree();
|
|
554
|
+
});
|
|
555
|
+
item.focused = focused;
|
|
462
556
|
this.setAttribute("aria-activedescendant", item.id);
|
|
463
557
|
if (item.menuData.selectionRoot && item.menuData.selectionRoot !== this) {
|
|
464
558
|
item.menuData.selectionRoot.focus();
|
|
465
559
|
}
|
|
466
560
|
}
|
|
467
|
-
|
|
561
|
+
handleSlotchange({
|
|
562
|
+
target
|
|
563
|
+
}) {
|
|
564
|
+
const assignedElement = target.assignedElements({
|
|
565
|
+
flatten: true
|
|
566
|
+
});
|
|
567
|
+
if (this.childItems.length !== assignedElement.length) {
|
|
568
|
+
assignedElement.forEach((item) => {
|
|
569
|
+
if (typeof item.triggerUpdate !== "undefined") {
|
|
570
|
+
item.triggerUpdate();
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
renderMenuItemSlot() {
|
|
468
576
|
return html`
|
|
469
|
-
<slot
|
|
577
|
+
<slot
|
|
578
|
+
@sp-menu-submenu-opened=${this.handleDescendentOverlayOpened}
|
|
579
|
+
@sp-menu-submenu-closed=${this.handleDescendentOverlayClosed}
|
|
580
|
+
@slotchange=${this.handleSlotchange}
|
|
581
|
+
></slot>
|
|
470
582
|
`;
|
|
471
583
|
}
|
|
584
|
+
render() {
|
|
585
|
+
return this.renderMenuItemSlot();
|
|
586
|
+
}
|
|
472
587
|
firstUpdated(changed) {
|
|
473
588
|
super.firstUpdated(changed);
|
|
474
589
|
if (!this.hasAttribute("tabindex") && !this.ignore) {
|
|
@@ -491,17 +606,16 @@ export class Menu extends SizedMixin(SpectrumElement) {
|
|
|
491
606
|
}
|
|
492
607
|
updated(changes) {
|
|
493
608
|
super.updated(changes);
|
|
494
|
-
if (changes.has("selects") && this.
|
|
609
|
+
if (changes.has("selects") && this.hasUpdated) {
|
|
495
610
|
this.selectsChanged();
|
|
496
611
|
}
|
|
497
|
-
if (changes.has("label")) {
|
|
612
|
+
if (changes.has("label") && (this.label || typeof changes.get("label") !== "undefined")) {
|
|
498
613
|
if (this.label) {
|
|
499
614
|
this.setAttribute("aria-label", this.label);
|
|
500
615
|
} else {
|
|
501
616
|
this.removeAttribute("aria-label");
|
|
502
617
|
}
|
|
503
618
|
}
|
|
504
|
-
this._notFirstUpdated = true;
|
|
505
619
|
}
|
|
506
620
|
selectsChanged() {
|
|
507
621
|
const updates = [
|