@spectrum-web-components/menu 1.2.0-beta.9 → 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 +105 -18
- 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/MenuGroup.dev.js
CHANGED
|
@@ -28,15 +28,19 @@ export class MenuGroup extends Menu {
|
|
|
28
28
|
static get styles() {
|
|
29
29
|
return [...super.styles, menuGroupStyles];
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* a menu group must have the role `group`
|
|
33
|
+
* and should never function as a menu
|
|
34
|
+
*/
|
|
31
35
|
get ownRole() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
return "group";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* only a menu controls roving tabindex;
|
|
40
|
+
* groups should defer navigation to parent menu
|
|
41
|
+
*/
|
|
42
|
+
get controlsRovingTabindex() {
|
|
43
|
+
return false;
|
|
40
44
|
}
|
|
41
45
|
updateLabel() {
|
|
42
46
|
const headerElement = this.headerElements.length ? this.headerElements[0] : void 0;
|
|
@@ -62,7 +66,7 @@ export class MenuGroup extends Menu {
|
|
|
62
66
|
<span class="header" ?hidden=${!this.headerElement}>
|
|
63
67
|
<slot name="header" @slotchange=${this.updateLabel}></slot>
|
|
64
68
|
</span>
|
|
65
|
-
|
|
69
|
+
${this.renderMenuItemSlot()}
|
|
66
70
|
`;
|
|
67
71
|
}
|
|
68
72
|
}
|
package/src/MenuGroup.dev.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';\nimport { randomID } from '@spectrum-web-components/shared/src/random-id.js';\n\nimport { Menu } from './Menu.dev.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
|
|
5
|
-
"mappings": ";;;;;;;;;;;AAYA;AAAA,EAEI;AAAA,OAEG;AACP;AAAA,EACI;AAAA,EACA;AAAA,OACG;AACP,SAAS,gBAAgB;AAEzB,SAAS,YAAY;AAErB,OAAO;AACP,OAAO,qBAAqB;AAQrB,aAAM,kBAAkB,KAAK;AAAA,EAA7B;AAAA;AAKH,SAAQ,WAAW;AAAA;AAAA,EAJnB,WAA2B,SAAyB;AAChD,WAAO,CAAC,GAAG,MAAM,QAAQ,eAAe;AAAA,EAC5C;AAAA,
|
|
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';\nimport { randomID } from '@spectrum-web-components/shared/src/random-id.js';\n\nimport { Menu } from './Menu.dev.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 /**\n * a menu group must have the role `group`\n * and should never function as a menu\n */\n protected override get ownRole(): string {\n return 'group';\n }\n\n /**\n * only a menu controls roving tabindex;\n * groups should defer navigation to parent menu\n */\n protected override get controlsRovingTabindex(): boolean {\n return false;\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 || `sp-menu-group-label-${randomID()}`;\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 ${this.renderMenuItemSlot()}\n `;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;AAYA;AAAA,EAEI;AAAA,OAEG;AACP;AAAA,EACI;AAAA,EACA;AAAA,OACG;AACP,SAAS,gBAAgB;AAEzB,SAAS,YAAY;AAErB,OAAO;AACP,OAAO,qBAAqB;AAQrB,aAAM,kBAAkB,KAAK;AAAA,EAA7B;AAAA;AAKH,SAAQ,WAAW;AAAA;AAAA,EAJnB,WAA2B,SAAyB;AAChD,WAAO,CAAC,GAAG,MAAM,QAAQ,eAAe;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,IAAuB,UAAkB;AACrC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAuB,yBAAkC;AACrD,WAAO;AAAA,EACX;AAAA,EAEU,cAAoB;AAC1B,UAAM,gBAAgB,KAAK,eAAe,SACpC,KAAK,eAAe,CAAC,IACrB;AACN,QAAI,kBAAkB,KAAK,eAAe;AACtC,UAAI,KAAK,iBAAiB,KAAK,cAAc,OAAO,KAAK,UAAU;AAC/D,aAAK,cAAc,gBAAgB,IAAI;AAAA,MAC3C;AACA,UAAI,eAAe;AACf,aAAK,WACD,KAAK,YAAY,uBAAuB,SAAS,CAAC;AACtD,cAAM,WAAW,cAAc,MAAM,KAAK;AAC1C,YAAI,CAAC,cAAc,IAAI;AACnB,wBAAc,KAAK;AAAA,QACvB;AACA,aAAK,aAAa,mBAAmB,QAAQ;AAAA,MACjD,OAAO;AACH,aAAK,gBAAgB,iBAAiB;AAAA,MAC1C;AAAA,IACJ;AACA,SAAK,gBAAgB;AAAA,EACzB;AAAA,EAEgB,SAAyB;AACrC,WAAO;AAAA,2CAC4B,CAAC,KAAK,aAAa;AAAA,kDACZ,KAAK,WAAW;AAAA;AAAA,cAEpD,KAAK,mBAAmB,CAAC;AAAA;AAAA,EAEnC;AACJ;AApDY;AAAA,EAJP,mBAAmB;AAAA,IAChB,MAAM;AAAA,IACN,SAAS;AAAA,EACb,CAAC;AAAA,GAVQ,UAWD;AAGA;AAAA,EADP,MAAM;AAAA,GAbE,UAcD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/MenuGroup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";var n=Object.defineProperty;var
|
|
1
|
+
"use strict";var n=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var l=(i,d,e,t)=>{for(var r=t>1?void 0:t?o(d,e):d,s=i.length-1,a;s>=0;s--)(a=i[s])&&(r=(t?a(d,e,r):a(r))||r);return t&&r&&n(d,e,r),r};import{html as h}from"@spectrum-web-components/base";import{queryAssignedNodes as m,state as p}from"@spectrum-web-components/base/src/decorators.js";import{randomID as u}from"@spectrum-web-components/shared/src/random-id.js";import{Menu as b}from"./Menu.js";import"@spectrum-web-components/menu/sp-menu.js";import f from"./menu-group.css.js";export class MenuGroup extends b{constructor(){super(...arguments);this.headerId=""}static get styles(){return[...super.styles,f]}get ownRole(){return"group"}get controlsRovingTabindex(){return!1}updateLabel(){const e=this.headerElements.length?this.headerElements[0]:void 0;if(e!==this.headerElement)if(this.headerElement&&this.headerElement.id===this.headerId&&this.headerElement.removeAttribute("id"),e){this.headerId=this.headerId||`sp-menu-group-label-${u()}`;const t=e.id||this.headerId;e.id||(e.id=t),this.setAttribute("aria-labelledby",t)}else this.removeAttribute("aria-labelledby");this.headerElement=e}render(){return h`
|
|
2
2
|
<span class="header" ?hidden=${!this.headerElement}>
|
|
3
3
|
<slot name="header" @slotchange=${this.updateLabel}></slot>
|
|
4
4
|
</span>
|
|
5
|
-
|
|
6
|
-
`}}l([
|
|
5
|
+
${this.renderMenuItemSlot()}
|
|
6
|
+
`}}l([m({slot:"header",flatten:!0})],MenuGroup.prototype,"headerElements",2),l([p()],MenuGroup.prototype,"headerElement",2);
|
|
7
7
|
//# sourceMappingURL=MenuGroup.js.map
|
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';\nimport { randomID } from '@spectrum-web-components/shared/src/random-id.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
|
|
5
|
-
"mappings": "qNAYA,OAEI,QAAAA,MAEG,gCACP,OACI,sBAAAC,EACA,SAAAC,MACG,kDACP,OAAS,YAAAC,MAAgB,mDAEzB,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,
|
|
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';\nimport { randomID } from '@spectrum-web-components/shared/src/random-id.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 /**\n * a menu group must have the role `group`\n * and should never function as a menu\n */\n protected override get ownRole(): string {\n return 'group';\n }\n\n /**\n * only a menu controls roving tabindex;\n * groups should defer navigation to parent menu\n */\n protected override get controlsRovingTabindex(): boolean {\n return false;\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 || `sp-menu-group-label-${randomID()}`;\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 ${this.renderMenuItemSlot()}\n `;\n }\n}\n"],
|
|
5
|
+
"mappings": "qNAYA,OAEI,QAAAA,MAEG,gCACP,OACI,sBAAAC,EACA,SAAAC,MACG,kDACP,OAAS,YAAAC,MAAgB,mDAEzB,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,CAiBA,IAAuB,SAAkB,CACrC,MAAO,OACX,CAMA,IAAuB,wBAAkC,CACrD,MAAO,EACX,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,UAAY,uBAAuBH,EAAS,CAAC,GACtD,MAAMI,EAAWD,EAAc,IAAM,KAAK,SACrCA,EAAc,KACfA,EAAc,GAAKC,GAEvB,KAAK,aAAa,kBAAmBA,CAAQ,CACjD,MACI,KAAK,gBAAgB,iBAAiB,EAG9C,KAAK,cAAgBD,CACzB,CAEgB,QAAyB,CACrC,OAAON;AAAA,2CAC4B,CAAC,KAAK,aAAa;AAAA,kDACZ,KAAK,WAAW;AAAA;AAAA,cAEpD,KAAK,mBAAmB,CAAC;AAAA,SAEnC,CACJ,CApDYQ,EAAA,CAJPP,EAAmB,CAChB,KAAM,SACN,QAAS,EACb,CAAC,GAVQ,UAWD,8BAGAO,EAAA,CADPN,EAAM,GAbE,UAcD",
|
|
6
6
|
"names": ["html", "queryAssignedNodes", "state", "randomID", "Menu", "menuGroupStyles", "headerElement", "headerId", "__decorateClass"]
|
|
7
7
|
}
|
package/src/MenuItem.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ type MenuCascadeItem = {
|
|
|
8
8
|
hadFocusRoot: boolean;
|
|
9
9
|
ancestorWithSelects?: HTMLElement;
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Fires when a menu item is added or updated so that a parent menu can track it.
|
|
13
|
+
*/
|
|
11
14
|
export declare class MenuItemAddedOrUpdatedEvent extends Event {
|
|
12
15
|
constructor(item: MenuItem);
|
|
13
16
|
clear(item: MenuItem): void;
|
|
@@ -16,6 +19,26 @@ export declare class MenuItemAddedOrUpdatedEvent extends Event {
|
|
|
16
19
|
private _item;
|
|
17
20
|
currentAncestorWithSelects?: Menu;
|
|
18
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Fires to forward keyboard event information to parent menu.
|
|
24
|
+
*/
|
|
25
|
+
export declare class MenuItemKeydownEvent extends KeyboardEvent {
|
|
26
|
+
root?: MenuItem;
|
|
27
|
+
private _event?;
|
|
28
|
+
constructor({ root, event }: {
|
|
29
|
+
root?: MenuItem;
|
|
30
|
+
event?: KeyboardEvent;
|
|
31
|
+
});
|
|
32
|
+
get altKey(): boolean;
|
|
33
|
+
get code(): string;
|
|
34
|
+
get ctrlKey(): boolean;
|
|
35
|
+
get isComposing(): boolean;
|
|
36
|
+
get key(): string;
|
|
37
|
+
get location(): number;
|
|
38
|
+
get metaKey(): boolean;
|
|
39
|
+
get repeat(): boolean;
|
|
40
|
+
get shiftKey(): boolean;
|
|
41
|
+
}
|
|
19
42
|
export type MenuItemChildren = {
|
|
20
43
|
icon: Element[];
|
|
21
44
|
content: Node[];
|
|
@@ -43,31 +66,55 @@ declare const MenuItem_base: typeof Focusable & {
|
|
|
43
66
|
export declare class MenuItem extends MenuItem_base {
|
|
44
67
|
static get styles(): CSSResultArray;
|
|
45
68
|
abortControllerSubmenu: AbortController;
|
|
69
|
+
/**
|
|
70
|
+
* whether the menu item is active or has an active descendant
|
|
71
|
+
*/
|
|
46
72
|
active: boolean;
|
|
47
73
|
private dependencyManager;
|
|
74
|
+
/**
|
|
75
|
+
* whether the menu item has keyboard focus
|
|
76
|
+
*/
|
|
48
77
|
focused: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* whether the menu item is selected
|
|
80
|
+
*/
|
|
49
81
|
selected: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* value of the menu item which is used for selection
|
|
84
|
+
*/
|
|
50
85
|
get value(): string;
|
|
51
86
|
set value(value: string);
|
|
52
87
|
private _value;
|
|
53
88
|
/**
|
|
54
89
|
* @private
|
|
90
|
+
* text content of the menu item minus whitespace
|
|
55
91
|
*/
|
|
56
92
|
get itemText(): string;
|
|
93
|
+
/**
|
|
94
|
+
* whether the menu item has a submenu
|
|
95
|
+
*/
|
|
57
96
|
hasSubmenu: boolean;
|
|
58
97
|
contentSlot: HTMLSlotElement;
|
|
59
98
|
iconSlot: HTMLSlotElement;
|
|
99
|
+
/**
|
|
100
|
+
* whether menu item text content should not wrap
|
|
101
|
+
*/
|
|
60
102
|
noWrap: boolean;
|
|
61
103
|
private anchorElement;
|
|
62
104
|
overlayElement: Overlay;
|
|
63
105
|
private submenuElement?;
|
|
106
|
+
/**
|
|
107
|
+
* the focusable element of the menu item
|
|
108
|
+
*/
|
|
64
109
|
get focusElement(): HTMLElement;
|
|
65
110
|
protected get hasIcon(): boolean;
|
|
66
111
|
get itemChildren(): MenuItemChildren;
|
|
67
112
|
private _itemChildren?;
|
|
68
113
|
constructor();
|
|
114
|
+
/**
|
|
115
|
+
* whether submenu is open
|
|
116
|
+
*/
|
|
69
117
|
open: boolean;
|
|
70
|
-
click(): void;
|
|
71
118
|
private handleClickCapture;
|
|
72
119
|
private handleSlottableRequest;
|
|
73
120
|
private proxyFocus;
|
|
@@ -75,12 +122,21 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
75
122
|
protected breakItemChildrenCache(): void;
|
|
76
123
|
protected renderSubmenu(): TemplateResult;
|
|
77
124
|
protected render(): TemplateResult;
|
|
125
|
+
/**
|
|
126
|
+
* determines if item has a submenu and updates the `aria-haspopup` attribute
|
|
127
|
+
*/
|
|
78
128
|
protected manageSubmenu(event: Event & {
|
|
79
129
|
target: HTMLSlotElement;
|
|
80
130
|
}): void;
|
|
81
131
|
private handlePointerdown;
|
|
82
132
|
protected firstUpdated(changes: PropertyValues): void;
|
|
133
|
+
/**
|
|
134
|
+
* forward key info from keydown event to parent menu
|
|
135
|
+
*/
|
|
136
|
+
handleKeydown: (event: KeyboardEvent) => void;
|
|
83
137
|
protected closeOverlaysForRoot(): void;
|
|
138
|
+
protected handleFocus(event: FocusEvent): void;
|
|
139
|
+
protected handleBlur(event: FocusEvent): void;
|
|
84
140
|
protected handleSubmenuClick(event: Event): void;
|
|
85
141
|
protected handleSubmenuFocus(): void;
|
|
86
142
|
protected handleBeforetoggle: (event: Event) => void;
|
|
@@ -103,12 +159,15 @@ export declare class MenuItem extends MenuItem_base {
|
|
|
103
159
|
openOverlay(): Promise<void>;
|
|
104
160
|
updateAriaSelected(): void;
|
|
105
161
|
setRole(role: string): void;
|
|
162
|
+
protected willUpdate(changes: PropertyValues<this>): void;
|
|
106
163
|
protected updated(changes: PropertyValues<this>): void;
|
|
107
164
|
connectedCallback(): void;
|
|
108
165
|
_parentElement: HTMLElement;
|
|
109
166
|
disconnectedCallback(): void;
|
|
110
167
|
private willDispatchUpdate;
|
|
111
168
|
triggerUpdate(): Promise<void>;
|
|
169
|
+
focus(): void;
|
|
170
|
+
blur(): void;
|
|
112
171
|
dispatchUpdate(): void;
|
|
113
172
|
menuData: {
|
|
114
173
|
focusRoot?: Menu;
|
package/src/MenuItem.dev.js
CHANGED
|
@@ -27,11 +27,9 @@ import { LikeAnchor } from "@spectrum-web-components/shared/src/like-anchor.js";
|
|
|
27
27
|
import { Focusable } from "@spectrum-web-components/shared/src/focusable.js";
|
|
28
28
|
import "@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js";
|
|
29
29
|
import chevronStyles from "@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js";
|
|
30
|
-
import chevronIconOverrides from "@spectrum-web-components/icon/src/icon-chevron-overrides.css.js";
|
|
31
30
|
import { DependencyManagerController } from "@spectrum-web-components/reactive-controllers/src/DependencyManger.js";
|
|
32
31
|
import menuItemStyles from "./menu-item.css.js";
|
|
33
32
|
import checkmarkStyles from "@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js";
|
|
34
|
-
import checkmarkSmallOverrides from "@spectrum-web-components/icon/src/icon-checkmark-overrides.css.js";
|
|
35
33
|
import { MutationController } from "@lit-labs/observers/mutation-controller.js";
|
|
36
34
|
import { SlottableRequestEvent } from "@spectrum-web-components/overlay/src/slottable-request-event.js";
|
|
37
35
|
const POINTERLEAVE_TIMEOUT = 100;
|
|
@@ -59,6 +57,49 @@ export class MenuItemAddedOrUpdatedEvent extends Event {
|
|
|
59
57
|
return this._item;
|
|
60
58
|
}
|
|
61
59
|
}
|
|
60
|
+
export class MenuItemKeydownEvent extends KeyboardEvent {
|
|
61
|
+
constructor({ root, event }) {
|
|
62
|
+
super("sp-menu-item-keydown", { bubbles: true, composed: true });
|
|
63
|
+
this.root = root;
|
|
64
|
+
this._event = event;
|
|
65
|
+
}
|
|
66
|
+
get altKey() {
|
|
67
|
+
var _a;
|
|
68
|
+
return ((_a = this._event) == null ? void 0 : _a.altKey) || false;
|
|
69
|
+
}
|
|
70
|
+
get code() {
|
|
71
|
+
var _a;
|
|
72
|
+
return ((_a = this._event) == null ? void 0 : _a.code) || "";
|
|
73
|
+
}
|
|
74
|
+
get ctrlKey() {
|
|
75
|
+
var _a;
|
|
76
|
+
return ((_a = this._event) == null ? void 0 : _a.ctrlKey) || false;
|
|
77
|
+
}
|
|
78
|
+
get isComposing() {
|
|
79
|
+
var _a;
|
|
80
|
+
return ((_a = this._event) == null ? void 0 : _a.isComposing) || false;
|
|
81
|
+
}
|
|
82
|
+
get key() {
|
|
83
|
+
var _a;
|
|
84
|
+
return ((_a = this._event) == null ? void 0 : _a.key) || "";
|
|
85
|
+
}
|
|
86
|
+
get location() {
|
|
87
|
+
var _a;
|
|
88
|
+
return ((_a = this._event) == null ? void 0 : _a.location) || 0;
|
|
89
|
+
}
|
|
90
|
+
get metaKey() {
|
|
91
|
+
var _a;
|
|
92
|
+
return ((_a = this._event) == null ? void 0 : _a.metaKey) || false;
|
|
93
|
+
}
|
|
94
|
+
get repeat() {
|
|
95
|
+
var _a;
|
|
96
|
+
return ((_a = this._event) == null ? void 0 : _a.repeat) || false;
|
|
97
|
+
}
|
|
98
|
+
get shiftKey() {
|
|
99
|
+
var _a;
|
|
100
|
+
return ((_a = this._event) == null ? void 0 : _a.shiftKey) || false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
62
103
|
export class MenuItem extends LikeAnchor(
|
|
63
104
|
ObserveSlotText(ObserveSlotPresence(Focusable, '[slot="icon"]'))
|
|
64
105
|
) {
|
|
@@ -81,6 +122,20 @@ export class MenuItem extends LikeAnchor(
|
|
|
81
122
|
this.proxyFocus = () => {
|
|
82
123
|
this.focus();
|
|
83
124
|
};
|
|
125
|
+
/**
|
|
126
|
+
* forward key info from keydown event to parent menu
|
|
127
|
+
*/
|
|
128
|
+
this.handleKeydown = (event) => {
|
|
129
|
+
const { target, key } = event;
|
|
130
|
+
const openSubmenuKey = this.hasSubmenu && !this.open && [" ", "Enter"].includes(key);
|
|
131
|
+
if (target === this) {
|
|
132
|
+
if (["ArrowLeft", "ArrowRight", "Escape"].includes(key) || openSubmenuKey)
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
this.dispatchEvent(
|
|
135
|
+
new MenuItemKeydownEvent({ root: this, event })
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
84
139
|
this.handleBeforetoggle = (event) => {
|
|
85
140
|
if (event.newState === "closed") {
|
|
86
141
|
this.open = true;
|
|
@@ -94,14 +149,18 @@ export class MenuItem extends LikeAnchor(
|
|
|
94
149
|
this.recentlyLeftChild = false;
|
|
95
150
|
this.willDispatchUpdate = false;
|
|
96
151
|
this.menuData = {
|
|
152
|
+
// menu that controls ArrowUp/ArrowDown navigation
|
|
97
153
|
focusRoot: void 0,
|
|
98
154
|
parentMenu: void 0,
|
|
155
|
+
// menu or menu group that controls selection
|
|
99
156
|
selectionRoot: void 0,
|
|
100
157
|
cleanupSteps: []
|
|
101
158
|
};
|
|
102
159
|
this.addEventListener("click", this.handleClickCapture, {
|
|
103
160
|
capture: true
|
|
104
161
|
});
|
|
162
|
+
this.addEventListener("focus", this.handleFocus);
|
|
163
|
+
this.addEventListener("blur", this.handleBlur);
|
|
105
164
|
new MutationController(this, {
|
|
106
165
|
config: {
|
|
107
166
|
characterData: true,
|
|
@@ -121,13 +180,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
121
180
|
});
|
|
122
181
|
}
|
|
123
182
|
static get styles() {
|
|
124
|
-
return [
|
|
125
|
-
menuItemStyles,
|
|
126
|
-
checkmarkStyles,
|
|
127
|
-
checkmarkSmallOverrides,
|
|
128
|
-
chevronStyles,
|
|
129
|
-
chevronIconOverrides
|
|
130
|
-
];
|
|
183
|
+
return [menuItemStyles, checkmarkStyles, chevronStyles];
|
|
131
184
|
}
|
|
132
185
|
get value() {
|
|
133
186
|
return this._value || this.itemText;
|
|
@@ -145,6 +198,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
145
198
|
}
|
|
146
199
|
/**
|
|
147
200
|
* @private
|
|
201
|
+
* text content of the menu item minus whitespace
|
|
148
202
|
*/
|
|
149
203
|
get itemText() {
|
|
150
204
|
return this.itemChildren.content.reduce(
|
|
@@ -152,6 +206,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
152
206
|
""
|
|
153
207
|
);
|
|
154
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* the focusable element of the menu item
|
|
211
|
+
*/
|
|
155
212
|
get focusElement() {
|
|
156
213
|
return this;
|
|
157
214
|
}
|
|
@@ -178,15 +235,6 @@ export class MenuItem extends LikeAnchor(
|
|
|
178
235
|
this._itemChildren = { icon, content };
|
|
179
236
|
return this._itemChildren;
|
|
180
237
|
}
|
|
181
|
-
click() {
|
|
182
|
-
if (this.disabled) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (this.shouldProxyClick()) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
super.click();
|
|
189
|
-
}
|
|
190
238
|
handleClickCapture(event) {
|
|
191
239
|
if (this.disabled) {
|
|
192
240
|
event.preventDefault();
|
|
@@ -194,6 +242,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
194
242
|
event.stopPropagation();
|
|
195
243
|
return false;
|
|
196
244
|
}
|
|
245
|
+
if (this.shouldProxyClick()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
197
248
|
}
|
|
198
249
|
shouldProxyClick() {
|
|
199
250
|
let handled = false;
|
|
@@ -281,6 +332,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
281
332
|
${this.renderSubmenu()}
|
|
282
333
|
`;
|
|
283
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* determines if item has a submenu and updates the `aria-haspopup` attribute
|
|
337
|
+
*/
|
|
284
338
|
manageSubmenu(event) {
|
|
285
339
|
this.submenuElement = event.target.assignedElements({
|
|
286
340
|
flatten: true
|
|
@@ -304,6 +358,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
304
358
|
firstUpdated(changes) {
|
|
305
359
|
super.firstUpdated(changes);
|
|
306
360
|
this.setAttribute("tabindex", "-1");
|
|
361
|
+
this.addEventListener("keydown", this.handleKeydown);
|
|
307
362
|
this.addEventListener("pointerdown", this.handlePointerdown);
|
|
308
363
|
this.addEventListener("pointerenter", this.closeOverlaysForRoot);
|
|
309
364
|
if (!this.hasAttribute("id")) {
|
|
@@ -315,6 +370,18 @@ export class MenuItem extends LikeAnchor(
|
|
|
315
370
|
if (this.open) return;
|
|
316
371
|
(_a = this.menuData.parentMenu) == null ? void 0 : _a.closeDescendentOverlays();
|
|
317
372
|
}
|
|
373
|
+
handleFocus(event) {
|
|
374
|
+
const { target } = event;
|
|
375
|
+
if (target === this) {
|
|
376
|
+
this.focused = true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
handleBlur(event) {
|
|
380
|
+
const { target } = event;
|
|
381
|
+
if (target === this) {
|
|
382
|
+
this.focused = false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
318
385
|
handleSubmenuClick(event) {
|
|
319
386
|
if (event.composedPath().includes(this.overlayElement)) {
|
|
320
387
|
return;
|
|
@@ -324,6 +391,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
324
391
|
handleSubmenuFocus() {
|
|
325
392
|
requestAnimationFrame(() => {
|
|
326
393
|
this.overlayElement.open = this.open;
|
|
394
|
+
this.focused = false;
|
|
327
395
|
});
|
|
328
396
|
}
|
|
329
397
|
handlePointerenter() {
|
|
@@ -363,13 +431,18 @@ export class MenuItem extends LikeAnchor(
|
|
|
363
431
|
});
|
|
364
432
|
}
|
|
365
433
|
handleSubmenuOpen(event) {
|
|
434
|
+
var _a;
|
|
435
|
+
const shouldFocus = this.matches(":focus, :focus-within") || this.focused;
|
|
366
436
|
this.focused = false;
|
|
367
437
|
const parentOverlay = event.composedPath().find((el) => {
|
|
368
438
|
return el !== this.overlayElement && el.localName === "sp-overlay";
|
|
369
439
|
});
|
|
440
|
+
if (shouldFocus)
|
|
441
|
+
(_a = this.submenuElement) == null ? void 0 : _a.focus();
|
|
370
442
|
this.overlayElement.parentOverlayToForceClose = parentOverlay;
|
|
371
443
|
}
|
|
372
444
|
cleanup() {
|
|
445
|
+
this.setAttribute("aria-expanded", "false");
|
|
373
446
|
this.open = false;
|
|
374
447
|
this.active = false;
|
|
375
448
|
}
|
|
@@ -399,6 +472,12 @@ export class MenuItem extends LikeAnchor(
|
|
|
399
472
|
this.setAttribute("role", role);
|
|
400
473
|
this.updateAriaSelected();
|
|
401
474
|
}
|
|
475
|
+
willUpdate(changes) {
|
|
476
|
+
super.updated(changes);
|
|
477
|
+
if (changes.has("open") && !this.open && this.hasSubmenu && this.hasVisibleFocusInTree()) {
|
|
478
|
+
this.focus();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
402
481
|
updated(changes) {
|
|
403
482
|
var _a, _b;
|
|
404
483
|
super.updated(changes);
|
|
@@ -468,6 +547,14 @@ export class MenuItem extends LikeAnchor(
|
|
|
468
547
|
await new Promise((ready) => requestAnimationFrame(ready));
|
|
469
548
|
this.dispatchUpdate();
|
|
470
549
|
}
|
|
550
|
+
focus() {
|
|
551
|
+
super.focus();
|
|
552
|
+
this.dispatchEvent(new FocusEvent("focus"));
|
|
553
|
+
}
|
|
554
|
+
blur() {
|
|
555
|
+
this.dispatchEvent(new FocusEvent("blur"));
|
|
556
|
+
super.blur();
|
|
557
|
+
}
|
|
471
558
|
dispatchUpdate() {
|
|
472
559
|
if (!this.isConnected) {
|
|
473
560
|
return;
|