@spectrum-web-components/menu 0.34.0 → 0.34.1-rc.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 +478 -225
- package/package.json +9 -9
- package/src/Menu.d.ts +16 -6
- package/src/Menu.dev.js +160 -61
- 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 +6 -10
- 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 +24 -22
- package/src/MenuItem.dev.js +131 -170
- package/src/MenuItem.dev.js.map +3 -3
- package/src/MenuItem.js +35 -15
- package/src/MenuItem.js.map +3 -3
- package/src/menu-item.css.dev.js +1 -1
- package/src/menu-item.css.dev.js.map +1 -1
- package/src/menu-item.css.js +1 -1
- 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/stories/submenu.stories.js +28 -21
- 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-selects.test.js +3 -1
- package/test/menu-selects.test.js.map +2 -2
- package/test/menu.test.js +7 -0
- package/test/menu.test.js.map +2 -2
- package/test/submenu.test.js +198 -83
- package/test/submenu.test.js.map +2 -2
package/src/MenuGroup.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["MenuGroup.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport {\n CSSResultArray,\n html,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n queryAssignedNodes,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\n\nimport { Menu } from './Menu.js';\n// Leveraged in build systems that use aliasing to prevent multiple registrations: https://github.com/adobe/spectrum-web-components/pull/3225\nimport '@spectrum-web-components/menu/sp-menu.js';\nimport menuGroupStyles from './menu-group.css.js';\n\n/**\n * @element sp-menu-group\n *\n * @slot header - headline of the menu group\n * @slot - menu items to be listed in the group\n */\nexport class MenuGroup extends Menu {\n public static override get styles(): CSSResultArray {\n return [...super.styles, menuGroupStyles];\n }\n\n private
|
|
5
|
-
"mappings": "qNAYA,OAEI,QAAAA,MAEG,gCACP,OACI,sBAAAC,EACA,SAAAC,MACG,kDAEP,OAAS,QAAAC,MAAY,YAErB,MAAO,2CACP,OAAOC,MAAqB,sBAQrB,
|
|
6
|
-
"names": ["html", "queryAssignedNodes", "state", "Menu", "menuGroupStyles", "
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport {\n CSSResultArray,\n html,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n queryAssignedNodes,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\n\nimport { Menu } from './Menu.js';\n// Leveraged in build systems that use aliasing to prevent multiple registrations: https://github.com/adobe/spectrum-web-components/pull/3225\nimport '@spectrum-web-components/menu/sp-menu.js';\nimport menuGroupStyles from './menu-group.css.js';\n\n/**\n * @element sp-menu-group\n *\n * @slot header - headline of the menu group\n * @slot - menu items to be listed in the group\n */\nexport class MenuGroup extends Menu {\n public static override get styles(): CSSResultArray {\n return [...super.styles, menuGroupStyles];\n }\n\n private headerId = '';\n\n @queryAssignedNodes({\n slot: 'header',\n flatten: true,\n })\n private headerElements!: NodeListOf<HTMLElement>;\n\n @state()\n private headerElement?: HTMLElement;\n\n protected override get ownRole(): string {\n switch (this.selects) {\n case 'multiple':\n case 'single':\n case 'inherit':\n return 'group';\n default:\n return 'menu';\n }\n }\n\n protected updateLabel(): void {\n const headerElement = this.headerElements.length\n ? this.headerElements[0]\n : undefined;\n if (headerElement !== this.headerElement) {\n if (this.headerElement && this.headerElement.id === this.headerId) {\n this.headerElement.removeAttribute('id');\n }\n if (headerElement) {\n this.headerId =\n this.headerId ||\n `sp-menu-group-label-${crypto.randomUUID().slice(0, 8)}`;\n const headerId = headerElement.id || this.headerId;\n if (!headerElement.id) {\n headerElement.id = headerId;\n }\n this.setAttribute('aria-labelledby', headerId);\n } else {\n this.removeAttribute('aria-labelledby');\n }\n }\n this.headerElement = headerElement;\n }\n\n public override render(): TemplateResult {\n return html`\n <span class=\"header\" ?hidden=${!this.headerElement}>\n <slot name=\"header\" @slotchange=${this.updateLabel}></slot>\n </span>\n <sp-menu ignore>${this.renderMenuItemSlot()}</sp-menu>\n `;\n }\n}\n"],
|
|
5
|
+
"mappings": "qNAYA,OAEI,QAAAA,MAEG,gCACP,OACI,sBAAAC,EACA,SAAAC,MACG,kDAEP,OAAS,QAAAC,MAAY,YAErB,MAAO,2CACP,OAAOC,MAAqB,sBAQrB,aAAM,kBAAkBD,CAAK,CAA7B,kCAKH,KAAQ,SAAW,GAJnB,WAA2B,QAAyB,CAChD,MAAO,CAAC,GAAG,MAAM,OAAQC,CAAe,CAC5C,CAaA,IAAuB,SAAkB,CACrC,OAAQ,KAAK,QAAS,CAClB,IAAK,WACL,IAAK,SACL,IAAK,UACD,MAAO,QACX,QACI,MAAO,MACf,CACJ,CAEU,aAAoB,CAC1B,MAAMC,EAAgB,KAAK,eAAe,OACpC,KAAK,eAAe,CAAC,EACrB,OACN,GAAIA,IAAkB,KAAK,cAIvB,GAHI,KAAK,eAAiB,KAAK,cAAc,KAAO,KAAK,UACrD,KAAK,cAAc,gBAAgB,IAAI,EAEvCA,EAAe,CACf,KAAK,SACD,KAAK,UACL,uBAAuB,OAAO,WAAW,EAAE,MAAM,EAAG,CAAC,IACzD,MAAMC,EAAWD,EAAc,IAAM,KAAK,SACrCA,EAAc,KACfA,EAAc,GAAKC,GAEvB,KAAK,aAAa,kBAAmBA,CAAQ,OAE7C,KAAK,gBAAgB,iBAAiB,EAG9C,KAAK,cAAgBD,CACzB,CAEgB,QAAyB,CACrC,OAAOL;AAAA,2CAC4B,CAAC,KAAK;AAAA,kDACC,KAAK;AAAA;AAAA,8BAEzB,KAAK,mBAAmB;AAAA,SAElD,CACJ,CAhDYO,EAAA,CAJPN,EAAmB,CAChB,KAAM,SACN,QAAS,EACb,CAAC,GAVQ,UAWD,8BAGAM,EAAA,CADPL,EAAM,GAbE,UAcD",
|
|
6
|
+
"names": ["html", "queryAssignedNodes", "state", "Menu", "menuGroupStyles", "headerElement", "headerId", "__decorateClass"]
|
|
7
7
|
}
|
package/src/MenuItem.d.ts
CHANGED
|
@@ -3,23 +3,19 @@ import '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark100.js';
|
|
|
3
3
|
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
|
|
4
4
|
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js';
|
|
5
5
|
import type { Menu } from './Menu.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
6
|
+
import '@spectrum-web-components/overlay/sp-overlay.js';
|
|
7
|
+
import { OverlayBase } from 'overlay/src/OverlayBase.js';
|
|
8
|
+
declare type MenuCascadeItem = {
|
|
9
|
+
hadFocusRoot: boolean;
|
|
10
|
+
ancestorWithSelects?: HTMLElement;
|
|
11
|
+
};
|
|
13
12
|
export declare class MenuItemAddedOrUpdatedEvent extends Event {
|
|
14
|
-
constructor();
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
constructor(item: MenuItem);
|
|
14
|
+
clear(item: MenuItem): void;
|
|
15
|
+
menuCascade: WeakMap<HTMLElement, MenuCascadeItem>;
|
|
17
16
|
get item(): MenuItem;
|
|
18
|
-
_item
|
|
19
|
-
|
|
20
|
-
get currentAncestorWithSelects(): Menu | undefined;
|
|
21
|
-
_currentAncestorWithSelects?: Menu;
|
|
22
|
-
reset(item: MenuItem): void;
|
|
17
|
+
private _item;
|
|
18
|
+
currentAncestorWithSelects?: Menu;
|
|
23
19
|
}
|
|
24
20
|
export declare type MenuItemChildren = {
|
|
25
21
|
icon: Element[];
|
|
@@ -37,12 +33,9 @@ declare const MenuItem_base: typeof Focusable & {
|
|
|
37
33
|
* @slot value - content placed at the end of the Menu Item like values, keyboard shortcuts, etc.
|
|
38
34
|
* @slot submenu - content placed in a submenu
|
|
39
35
|
* @fires sp-menu-item-added - announces the item has been added so a parent menu can take ownerships
|
|
40
|
-
* @fires sp-menu-item-removed - announces when removed from the DOM so the parent menu can remove ownership and update selected state
|
|
41
36
|
*/
|
|
42
37
|
export declare class MenuItem extends MenuItem_base {
|
|
43
38
|
static get styles(): CSSResultArray;
|
|
44
|
-
static instanceCount: number;
|
|
45
|
-
private isInSubmenu;
|
|
46
39
|
active: boolean;
|
|
47
40
|
focused: boolean;
|
|
48
41
|
selected: boolean;
|
|
@@ -56,6 +49,7 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
56
49
|
hasSubmenu: boolean;
|
|
57
50
|
noWrap: boolean;
|
|
58
51
|
private anchorElement;
|
|
52
|
+
overlayElement: OverlayBase;
|
|
59
53
|
get focusElement(): HTMLElement;
|
|
60
54
|
get itemChildren(): MenuItemChildren;
|
|
61
55
|
private _itemChildren?;
|
|
@@ -66,6 +60,7 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
66
60
|
private proxyFocus;
|
|
67
61
|
private shouldProxyClick;
|
|
68
62
|
protected breakItemChildrenCache(): void;
|
|
63
|
+
protected renderSubmenu(): TemplateResult;
|
|
69
64
|
protected render(): TemplateResult;
|
|
70
65
|
protected manageSubmenu(event: Event & {
|
|
71
66
|
target: HTMLSlotElement;
|
|
@@ -75,9 +70,10 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
75
70
|
protected firstUpdated(changes: PropertyValues): void;
|
|
76
71
|
protected closeOverlaysForRoot(): void;
|
|
77
72
|
closeOverlay?: () => Promise<void>;
|
|
78
|
-
protected handleSubmenuClick(): void;
|
|
73
|
+
protected handleSubmenuClick(event: Event): void;
|
|
79
74
|
protected handlePointerenter(): void;
|
|
80
75
|
protected leaveTimeout?: ReturnType<typeof setTimeout>;
|
|
76
|
+
protected recentlyLeftChild: boolean;
|
|
81
77
|
protected handlePointerleave(): void;
|
|
82
78
|
/**
|
|
83
79
|
* When there is a `change` event in the submenu for this item
|
|
@@ -86,8 +82,11 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
86
82
|
* and the root of the tree to have their selection changes and
|
|
87
83
|
* be closed.
|
|
88
84
|
*/
|
|
89
|
-
protected handleSubmenuChange:
|
|
90
|
-
protected handleSubmenuPointerenter
|
|
85
|
+
protected handleSubmenuChange(event: Event): void;
|
|
86
|
+
protected handleSubmenuPointerenter(): void;
|
|
87
|
+
protected handleSubmenuPointerleave(): Promise<void>;
|
|
88
|
+
protected handleSubmenuOpen(event: Event): void;
|
|
89
|
+
protected cleanup(): void;
|
|
91
90
|
openOverlay(): Promise<void>;
|
|
92
91
|
updateAriaSelected(): void;
|
|
93
92
|
setRole(role: string): void;
|
|
@@ -95,16 +94,19 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
95
94
|
connectedCallback(): void;
|
|
96
95
|
_parentElement: HTMLElement;
|
|
97
96
|
disconnectedCallback(): void;
|
|
97
|
+
private willDispatchUpdate;
|
|
98
98
|
triggerUpdate(): Promise<void>;
|
|
99
|
+
dispatchUpdate(): void;
|
|
99
100
|
menuData: {
|
|
100
101
|
focusRoot?: Menu;
|
|
102
|
+
parentMenu?: Menu;
|
|
101
103
|
selectionRoot?: Menu;
|
|
104
|
+
cleanupSteps: ((item: MenuItem) => void)[];
|
|
102
105
|
};
|
|
103
106
|
}
|
|
104
107
|
declare global {
|
|
105
108
|
interface GlobalEventHandlersEventMap {
|
|
106
109
|
'sp-menu-item-added-or-updated': MenuItemAddedOrUpdatedEvent;
|
|
107
|
-
'sp-menu-item-removed': MenuItemRemovedEvent;
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
export {};
|
package/src/MenuItem.dev.js
CHANGED
|
@@ -22,83 +22,38 @@ import { LikeAnchor } from "@spectrum-web-components/shared/src/like-anchor.js";
|
|
|
22
22
|
import { Focusable } from "@spectrum-web-components/shared/src/focusable.js";
|
|
23
23
|
import "@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js";
|
|
24
24
|
import chevronStyles from "@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js";
|
|
25
|
-
import { openOverlay } from "@spectrum-web-components/overlay/src/loader.js";
|
|
26
|
-
import { OverlayCloseEvent } from "@spectrum-web-components/overlay/src/overlay-events.js";
|
|
27
25
|
import menuItemStyles from "./menu-item.css.js";
|
|
28
26
|
import checkmarkStyles from "@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js";
|
|
29
|
-
import { reparentChildren } from "@spectrum-web-components/shared/src/reparent-children.js";
|
|
30
27
|
import { MutationController } from "@lit-labs/observers/mutation-controller.js";
|
|
28
|
+
import "@spectrum-web-components/overlay/sp-overlay.js";
|
|
31
29
|
const POINTERLEAVE_TIMEOUT = 100;
|
|
32
|
-
export class MenuItemRemovedEvent extends Event {
|
|
33
|
-
constructor() {
|
|
34
|
-
super("sp-menu-item-removed", {
|
|
35
|
-
bubbles: true,
|
|
36
|
-
composed: true
|
|
37
|
-
});
|
|
38
|
-
this.focused = false;
|
|
39
|
-
}
|
|
40
|
-
get item() {
|
|
41
|
-
return this._item;
|
|
42
|
-
}
|
|
43
|
-
reset(item) {
|
|
44
|
-
this._item = item;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
30
|
export class MenuItemAddedOrUpdatedEvent extends Event {
|
|
48
|
-
constructor() {
|
|
31
|
+
constructor(item) {
|
|
49
32
|
super("sp-menu-item-added-or-updated", {
|
|
50
33
|
bubbles: true,
|
|
51
34
|
composed: true
|
|
52
35
|
});
|
|
36
|
+
this.menuCascade = /* @__PURE__ */ new WeakMap();
|
|
37
|
+
this.clear(item);
|
|
53
38
|
}
|
|
54
|
-
|
|
55
|
-
this.item.menuData.focusRoot = this.item.menuData.focusRoot || root;
|
|
56
|
-
}
|
|
57
|
-
set selectionRoot(root) {
|
|
58
|
-
this.item.menuData.selectionRoot = this.item.menuData.selectionRoot || root;
|
|
59
|
-
}
|
|
60
|
-
get item() {
|
|
61
|
-
return this._item;
|
|
62
|
-
}
|
|
63
|
-
set currentAncestorWithSelects(ancestor) {
|
|
64
|
-
this._currentAncestorWithSelects = ancestor;
|
|
65
|
-
}
|
|
66
|
-
get currentAncestorWithSelects() {
|
|
67
|
-
return this._currentAncestorWithSelects;
|
|
68
|
-
}
|
|
69
|
-
reset(item) {
|
|
39
|
+
clear(item) {
|
|
70
40
|
this._item = item;
|
|
71
|
-
this.
|
|
41
|
+
this.currentAncestorWithSelects = void 0;
|
|
72
42
|
item.menuData = {
|
|
43
|
+
cleanupSteps: [],
|
|
73
44
|
focusRoot: void 0,
|
|
74
|
-
selectionRoot: void 0
|
|
45
|
+
selectionRoot: void 0,
|
|
46
|
+
parentMenu: void 0
|
|
75
47
|
};
|
|
48
|
+
this.menuCascade = /* @__PURE__ */ new WeakMap();
|
|
76
49
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let removeEvent = new MenuItemRemovedEvent();
|
|
80
|
-
let addOrUpdateEventRafId = 0;
|
|
81
|
-
function resetAddOrUpdateEvent() {
|
|
82
|
-
if (addOrUpdateEventRafId === 0) {
|
|
83
|
-
addOrUpdateEventRafId = requestAnimationFrame(() => {
|
|
84
|
-
addOrUpdateEvent = new MenuItemAddedOrUpdatedEvent();
|
|
85
|
-
addOrUpdateEventRafId = 0;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
let removeEventEventtRafId = 0;
|
|
90
|
-
function resetRemoveEvent() {
|
|
91
|
-
if (removeEventEventtRafId === 0) {
|
|
92
|
-
removeEventEventtRafId = requestAnimationFrame(() => {
|
|
93
|
-
removeEvent = new MenuItemRemovedEvent();
|
|
94
|
-
removeEventEventtRafId = 0;
|
|
95
|
-
});
|
|
50
|
+
get item() {
|
|
51
|
+
return this._item;
|
|
96
52
|
}
|
|
97
53
|
}
|
|
98
|
-
|
|
54
|
+
export class MenuItem extends LikeAnchor(Focusable) {
|
|
99
55
|
constructor() {
|
|
100
56
|
super();
|
|
101
|
-
this.isInSubmenu = false;
|
|
102
57
|
this.active = false;
|
|
103
58
|
this.focused = false;
|
|
104
59
|
this.selected = false;
|
|
@@ -106,28 +61,17 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
106
61
|
this.hasSubmenu = false;
|
|
107
62
|
this.noWrap = false;
|
|
108
63
|
this.open = false;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
* then we "click" this item to cascade the selection up the
|
|
112
|
-
* menu tree allowing all submenus between the initial selection
|
|
113
|
-
* and the root of the tree to have their selection changes and
|
|
114
|
-
* be closed.
|
|
115
|
-
*/
|
|
116
|
-
this.handleSubmenuChange = () => {
|
|
117
|
-
var _a;
|
|
118
|
-
(_a = this.menuData.selectionRoot) == null ? void 0 : _a.selectOrToggleItem(this);
|
|
119
|
-
};
|
|
120
|
-
this.handleSubmenuPointerenter = () => {
|
|
121
|
-
if (this.leaveTimeout) {
|
|
122
|
-
clearTimeout(this.leaveTimeout);
|
|
123
|
-
delete this.leaveTimeout;
|
|
124
|
-
}
|
|
64
|
+
this.proxyFocus = () => {
|
|
65
|
+
this.focus();
|
|
125
66
|
};
|
|
67
|
+
this.recentlyLeftChild = false;
|
|
68
|
+
this.willDispatchUpdate = false;
|
|
126
69
|
this.menuData = {
|
|
127
70
|
focusRoot: void 0,
|
|
128
|
-
|
|
71
|
+
parentMenu: void 0,
|
|
72
|
+
selectionRoot: void 0,
|
|
73
|
+
cleanupSteps: []
|
|
129
74
|
};
|
|
130
|
-
this.proxyFocus = this.proxyFocus.bind(this);
|
|
131
75
|
this.addEventListener("click", this.handleClickCapture, {
|
|
132
76
|
capture: true
|
|
133
77
|
});
|
|
@@ -209,9 +153,6 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
209
153
|
return false;
|
|
210
154
|
}
|
|
211
155
|
}
|
|
212
|
-
proxyFocus() {
|
|
213
|
-
this.focus();
|
|
214
|
-
}
|
|
215
156
|
shouldProxyClick() {
|
|
216
157
|
let handled = false;
|
|
217
158
|
if (this.anchorElement) {
|
|
@@ -224,6 +165,50 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
224
165
|
this._itemChildren = void 0;
|
|
225
166
|
this.triggerUpdate();
|
|
226
167
|
}
|
|
168
|
+
renderSubmenu() {
|
|
169
|
+
const slot = html`
|
|
170
|
+
<slot
|
|
171
|
+
name="submenu"
|
|
172
|
+
@slotchange=${this.manageSubmenu}
|
|
173
|
+
@sp-menu-item-added-or-updated=${{
|
|
174
|
+
handleEvent: (event) => {
|
|
175
|
+
event.clear(event.item);
|
|
176
|
+
},
|
|
177
|
+
capture: true
|
|
178
|
+
}}
|
|
179
|
+
@focusin=${(event) => event.stopPropagation()}
|
|
180
|
+
></slot>
|
|
181
|
+
`;
|
|
182
|
+
if (!this.hasSubmenu) {
|
|
183
|
+
return slot;
|
|
184
|
+
}
|
|
185
|
+
return html`
|
|
186
|
+
<sp-overlay
|
|
187
|
+
.triggerElement=${this}
|
|
188
|
+
?disabled=${!this.hasSubmenu}
|
|
189
|
+
?open=${this.hasSubmenu && this.open}
|
|
190
|
+
.placement=${this.isLTR ? "right-start" : "left-start"}
|
|
191
|
+
.offset=${[-10, -5]}
|
|
192
|
+
.type=${"auto"}
|
|
193
|
+
@close=${(event) => event.stopPropagation()}
|
|
194
|
+
>
|
|
195
|
+
<sp-popover
|
|
196
|
+
@change=${(event) => {
|
|
197
|
+
this.handleSubmenuChange(event);
|
|
198
|
+
this.open = false;
|
|
199
|
+
}}
|
|
200
|
+
@pointerenter=${this.handleSubmenuPointerenter}
|
|
201
|
+
@pointerleave=${this.handleSubmenuPointerleave}
|
|
202
|
+
@sp-menu-item-added-or-updated=${(event) => event.stopPropagation()}
|
|
203
|
+
>
|
|
204
|
+
${slot}
|
|
205
|
+
</sp-popover>
|
|
206
|
+
</sp-overlay>
|
|
207
|
+
<sp-icon-chevron100
|
|
208
|
+
class="spectrum-UIIcon-ChevronRight100 chevron icon"
|
|
209
|
+
></sp-icon-chevron100>
|
|
210
|
+
`;
|
|
211
|
+
}
|
|
227
212
|
render() {
|
|
228
213
|
return html`
|
|
229
214
|
<slot name="icon"></slot>
|
|
@@ -242,26 +227,17 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
242
227
|
ariaHidden: true,
|
|
243
228
|
className: "button anchor hidden"
|
|
244
229
|
}) : html``}
|
|
245
|
-
|
|
246
|
-
hidden
|
|
247
|
-
name="submenu"
|
|
248
|
-
@slotchange=${this.manageSubmenu}
|
|
249
|
-
></slot>
|
|
250
|
-
${this.hasSubmenu ? html`
|
|
251
|
-
<sp-icon-chevron100
|
|
252
|
-
class="spectrum-UIIcon-ChevronRight100 chevron icon"
|
|
253
|
-
></sp-icon-chevron100>
|
|
254
|
-
` : html``}
|
|
230
|
+
${this.renderSubmenu()}
|
|
255
231
|
`;
|
|
256
232
|
}
|
|
257
233
|
manageSubmenu(event) {
|
|
258
234
|
const assignedElements = event.target.assignedElements({
|
|
259
235
|
flatten: true
|
|
260
236
|
});
|
|
261
|
-
this.hasSubmenu =
|
|
237
|
+
this.hasSubmenu = !!assignedElements.length;
|
|
262
238
|
}
|
|
263
|
-
handleRemoveActive(
|
|
264
|
-
if (
|
|
239
|
+
handleRemoveActive() {
|
|
240
|
+
if (this.open) {
|
|
265
241
|
return;
|
|
266
242
|
}
|
|
267
243
|
this.active = false;
|
|
@@ -273,20 +249,21 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
273
249
|
super.firstUpdated(changes);
|
|
274
250
|
this.setAttribute("tabindex", "-1");
|
|
275
251
|
this.addEventListener("pointerdown", this.handlePointerdown);
|
|
252
|
+
this.addEventListener("pointerenter", this.closeOverlaysForRoot);
|
|
276
253
|
if (!this.hasAttribute("id")) {
|
|
277
|
-
this.id = `sp-menu-item-${
|
|
254
|
+
this.id = `sp-menu-item-${crypto.randomUUID().slice(0, 8)}`;
|
|
278
255
|
}
|
|
279
|
-
this.addEventListener("pointerenter", this.closeOverlaysForRoot);
|
|
280
256
|
}
|
|
281
257
|
closeOverlaysForRoot() {
|
|
258
|
+
var _a;
|
|
282
259
|
if (this.open)
|
|
283
260
|
return;
|
|
284
|
-
|
|
285
|
-
root: this.menuData.focusRoot
|
|
286
|
-
});
|
|
287
|
-
this.dispatchEvent(overalyCloseEvent);
|
|
261
|
+
(_a = this.menuData.parentMenu) == null ? void 0 : _a.closeDescendentOverlays();
|
|
288
262
|
}
|
|
289
|
-
handleSubmenuClick() {
|
|
263
|
+
handleSubmenuClick(event) {
|
|
264
|
+
if (event.composedPath().includes(this.overlayElement)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
290
267
|
this.openOverlay();
|
|
291
268
|
}
|
|
292
269
|
handlePointerenter() {
|
|
@@ -298,64 +275,53 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
298
275
|
this.openOverlay();
|
|
299
276
|
}
|
|
300
277
|
handlePointerleave() {
|
|
301
|
-
if (this.
|
|
278
|
+
if (this.open && !this.recentlyLeftChild) {
|
|
302
279
|
this.leaveTimeout = setTimeout(() => {
|
|
303
280
|
delete this.leaveTimeout;
|
|
304
|
-
|
|
305
|
-
this.closeOverlay();
|
|
281
|
+
this.open = false;
|
|
306
282
|
}, POINTERLEAVE_TIMEOUT);
|
|
307
283
|
}
|
|
308
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* When there is a `change` event in the submenu for this item
|
|
287
|
+
* then we "click" this item to cascade the selection up the
|
|
288
|
+
* menu tree allowing all submenus between the initial selection
|
|
289
|
+
* and the root of the tree to have their selection changes and
|
|
290
|
+
* be closed.
|
|
291
|
+
*/
|
|
292
|
+
handleSubmenuChange(event) {
|
|
293
|
+
var _a;
|
|
294
|
+
event.stopPropagation();
|
|
295
|
+
(_a = this.menuData.selectionRoot) == null ? void 0 : _a.selectOrToggleItem(this);
|
|
296
|
+
}
|
|
297
|
+
handleSubmenuPointerenter() {
|
|
298
|
+
this.recentlyLeftChild = true;
|
|
299
|
+
}
|
|
300
|
+
async handleSubmenuPointerleave() {
|
|
301
|
+
requestAnimationFrame(() => {
|
|
302
|
+
this.recentlyLeftChild = false;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
handleSubmenuOpen(event) {
|
|
306
|
+
this.focused = false;
|
|
307
|
+
const parentOverlay = event.composedPath().find((el) => {
|
|
308
|
+
return el !== this.overlayElement && el.localName === "sp-overlay";
|
|
309
|
+
});
|
|
310
|
+
this.overlayElement.parentOverlayToForceClose = parentOverlay;
|
|
311
|
+
}
|
|
312
|
+
cleanup() {
|
|
313
|
+
this.open = false;
|
|
314
|
+
this.active = false;
|
|
315
|
+
}
|
|
309
316
|
async openOverlay() {
|
|
310
317
|
if (!this.hasSubmenu || this.open || this.disabled) {
|
|
311
318
|
return;
|
|
312
319
|
}
|
|
313
320
|
this.open = true;
|
|
314
321
|
this.active = true;
|
|
315
|
-
|
|
316
|
-
'slot[name="submenu"]'
|
|
317
|
-
).assignedElements()[0];
|
|
318
|
-
submenu.addEventListener(
|
|
319
|
-
"pointerenter",
|
|
320
|
-
this.handleSubmenuPointerenter
|
|
321
|
-
);
|
|
322
|
-
submenu.addEventListener("change", this.handleSubmenuChange);
|
|
323
|
-
const popover = document.createElement("sp-popover");
|
|
324
|
-
const returnSubmenu = reparentChildren([submenu], popover, {
|
|
325
|
-
position: "beforeend",
|
|
326
|
-
prepareCallback: (el) => {
|
|
327
|
-
const slotName = el.slot;
|
|
328
|
-
el.tabIndex = 0;
|
|
329
|
-
el.removeAttribute("slot");
|
|
330
|
-
el.isSubmenu = true;
|
|
331
|
-
return (el2) => {
|
|
332
|
-
el2.tabIndex = -1;
|
|
333
|
-
el2.slot = slotName;
|
|
334
|
-
el2.isSubmenu = false;
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
const closeOverlay = openOverlay(this, "click", popover, {
|
|
339
|
-
placement: this.isLTR ? "right-start" : "left-start",
|
|
340
|
-
receivesFocus: "auto",
|
|
341
|
-
root: this.menuData.focusRoot
|
|
342
|
-
});
|
|
343
|
-
const closeSubmenu = async () => {
|
|
344
|
-
delete this.closeOverlay;
|
|
345
|
-
(await closeOverlay)();
|
|
346
|
-
};
|
|
347
|
-
this.closeOverlay = closeSubmenu;
|
|
348
|
-
const cleanup = (event) => {
|
|
349
|
-
event.stopPropagation();
|
|
350
|
-
delete this.closeOverlay;
|
|
351
|
-
returnSubmenu();
|
|
352
|
-
this.open = false;
|
|
353
|
-
this.active = false;
|
|
354
|
-
};
|
|
355
|
-
this.addEventListener("sp-closed", cleanup, {
|
|
322
|
+
this.addEventListener("sp-closed", this.cleanup, {
|
|
356
323
|
once: true
|
|
357
324
|
});
|
|
358
|
-
popover.addEventListener("change", closeSubmenu);
|
|
359
325
|
}
|
|
360
326
|
updateAriaSelected() {
|
|
361
327
|
const role = this.getAttribute("role");
|
|
@@ -373,12 +339,14 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
373
339
|
this.updateAriaSelected();
|
|
374
340
|
}
|
|
375
341
|
updated(changes) {
|
|
342
|
+
var _a;
|
|
376
343
|
super.updated(changes);
|
|
377
|
-
if (changes.has("label")) {
|
|
344
|
+
if (changes.has("label") && (this.label || typeof changes.get("label") !== "undefined")) {
|
|
378
345
|
this.setAttribute("aria-label", this.label || "");
|
|
379
346
|
}
|
|
380
|
-
if (changes.has("active")) {
|
|
347
|
+
if (changes.has("active") && (this.active || typeof changes.get("active") !== "undefined")) {
|
|
381
348
|
if (this.active) {
|
|
349
|
+
(_a = this.menuData.selectionRoot) == null ? void 0 : _a.closeDescendentOverlays();
|
|
382
350
|
this.addEventListener("pointerup", this.handleRemoveActive);
|
|
383
351
|
this.addEventListener("pointerleave", this.handleRemoveActive);
|
|
384
352
|
this.addEventListener("pointercancel", this.handleRemoveActive);
|
|
@@ -401,11 +369,12 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
401
369
|
if (changes.has("selected")) {
|
|
402
370
|
this.updateAriaSelected();
|
|
403
371
|
}
|
|
404
|
-
if (changes.has("hasSubmenu")) {
|
|
372
|
+
if (changes.has("hasSubmenu") && (this.hasSubmenu || typeof changes.get("hasSubmenu") !== "undefined")) {
|
|
405
373
|
if (this.hasSubmenu) {
|
|
406
374
|
this.addEventListener("click", this.handleSubmenuClick);
|
|
407
375
|
this.addEventListener("pointerenter", this.handlePointerenter);
|
|
408
376
|
this.addEventListener("pointerleave", this.handlePointerleave);
|
|
377
|
+
this.addEventListener("sp-opened", this.handleSubmenuOpen);
|
|
409
378
|
} else if (!this.closeOverlay) {
|
|
410
379
|
this.removeEventListener("click", this.handleSubmenuClick);
|
|
411
380
|
this.removeEventListener(
|
|
@@ -416,42 +385,31 @@ const _MenuItem = class extends LikeAnchor(Focusable) {
|
|
|
416
385
|
"pointerleave",
|
|
417
386
|
this.handlePointerleave
|
|
418
387
|
);
|
|
388
|
+
this.removeEventListener("sp-opened", this.handleSubmenuOpen);
|
|
419
389
|
}
|
|
420
390
|
}
|
|
421
391
|
}
|
|
422
392
|
connectedCallback() {
|
|
423
393
|
super.connectedCallback();
|
|
424
|
-
this.
|
|
425
|
-
if (this.isInSubmenu) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
addOrUpdateEvent.reset(this);
|
|
429
|
-
this.dispatchEvent(addOrUpdateEvent);
|
|
430
|
-
resetAddOrUpdateEvent();
|
|
431
|
-
this._parentElement = this.parentElement;
|
|
394
|
+
this.triggerUpdate();
|
|
432
395
|
}
|
|
433
396
|
disconnectedCallback() {
|
|
434
|
-
|
|
435
|
-
removeEvent.reset(this);
|
|
436
|
-
this._parentElement.dispatchEvent(removeEvent);
|
|
437
|
-
resetRemoveEvent();
|
|
438
|
-
}
|
|
439
|
-
this.isInSubmenu = false;
|
|
440
|
-
this._itemChildren = void 0;
|
|
397
|
+
this.menuData.cleanupSteps.forEach((removal) => removal(this));
|
|
441
398
|
super.disconnectedCallback();
|
|
442
399
|
}
|
|
443
400
|
async triggerUpdate() {
|
|
444
|
-
if (this.
|
|
401
|
+
if (this.willDispatchUpdate) {
|
|
445
402
|
return;
|
|
446
403
|
}
|
|
404
|
+
this.willDispatchUpdate = true;
|
|
447
405
|
await new Promise((ready) => requestAnimationFrame(ready));
|
|
448
|
-
|
|
449
|
-
this.dispatchEvent(addOrUpdateEvent);
|
|
450
|
-
resetAddOrUpdateEvent();
|
|
406
|
+
this.dispatchUpdate();
|
|
451
407
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
408
|
+
dispatchUpdate() {
|
|
409
|
+
this.dispatchEvent(new MenuItemAddedOrUpdatedEvent(this));
|
|
410
|
+
this.willDispatchUpdate = false;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
455
413
|
__decorateClass([
|
|
456
414
|
property({ type: Boolean, reflect: true })
|
|
457
415
|
], MenuItem.prototype, "active", 2);
|
|
@@ -481,6 +439,9 @@ __decorateClass([
|
|
|
481
439
|
query(".anchor")
|
|
482
440
|
], MenuItem.prototype, "anchorElement", 2);
|
|
483
441
|
__decorateClass([
|
|
484
|
-
|
|
442
|
+
query("sp-overlay")
|
|
443
|
+
], MenuItem.prototype, "overlayElement", 2);
|
|
444
|
+
__decorateClass([
|
|
445
|
+
property({ type: Boolean, reflect: true })
|
|
485
446
|
], MenuItem.prototype, "open", 2);
|
|
486
447
|
//# sourceMappingURL=MenuItem.dev.js.map
|