@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/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,19 +149,24 @@ 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,
|
|
108
167
|
childList: true,
|
|
109
|
-
subtree: true
|
|
168
|
+
subtree: true,
|
|
169
|
+
attributeFilter: ["src"]
|
|
110
170
|
},
|
|
111
171
|
callback: (mutations) => {
|
|
112
172
|
const isSubmenu = mutations.every(
|
|
@@ -120,13 +180,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
120
180
|
});
|
|
121
181
|
}
|
|
122
182
|
static get styles() {
|
|
123
|
-
return [
|
|
124
|
-
menuItemStyles,
|
|
125
|
-
checkmarkStyles,
|
|
126
|
-
checkmarkSmallOverrides,
|
|
127
|
-
chevronStyles,
|
|
128
|
-
chevronIconOverrides
|
|
129
|
-
];
|
|
183
|
+
return [menuItemStyles, checkmarkStyles, chevronStyles];
|
|
130
184
|
}
|
|
131
185
|
get value() {
|
|
132
186
|
return this._value || this.itemText;
|
|
@@ -144,6 +198,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
144
198
|
}
|
|
145
199
|
/**
|
|
146
200
|
* @private
|
|
201
|
+
* text content of the menu item minus whitespace
|
|
147
202
|
*/
|
|
148
203
|
get itemText() {
|
|
149
204
|
return this.itemChildren.content.reduce(
|
|
@@ -151,6 +206,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
151
206
|
""
|
|
152
207
|
);
|
|
153
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* the focusable element of the menu item
|
|
211
|
+
*/
|
|
154
212
|
get focusElement() {
|
|
155
213
|
return this;
|
|
156
214
|
}
|
|
@@ -177,15 +235,6 @@ export class MenuItem extends LikeAnchor(
|
|
|
177
235
|
this._itemChildren = { icon, content };
|
|
178
236
|
return this._itemChildren;
|
|
179
237
|
}
|
|
180
|
-
click() {
|
|
181
|
-
if (this.disabled) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (this.shouldProxyClick()) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
super.click();
|
|
188
|
-
}
|
|
189
238
|
handleClickCapture(event) {
|
|
190
239
|
if (this.disabled) {
|
|
191
240
|
event.preventDefault();
|
|
@@ -193,6 +242,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
193
242
|
event.stopPropagation();
|
|
194
243
|
return false;
|
|
195
244
|
}
|
|
245
|
+
if (this.shouldProxyClick()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
196
248
|
}
|
|
197
249
|
shouldProxyClick() {
|
|
198
250
|
let handled = false;
|
|
@@ -280,6 +332,9 @@ export class MenuItem extends LikeAnchor(
|
|
|
280
332
|
${this.renderSubmenu()}
|
|
281
333
|
`;
|
|
282
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* determines if item has a submenu and updates the `aria-haspopup` attribute
|
|
337
|
+
*/
|
|
283
338
|
manageSubmenu(event) {
|
|
284
339
|
this.submenuElement = event.target.assignedElements({
|
|
285
340
|
flatten: true
|
|
@@ -303,6 +358,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
303
358
|
firstUpdated(changes) {
|
|
304
359
|
super.firstUpdated(changes);
|
|
305
360
|
this.setAttribute("tabindex", "-1");
|
|
361
|
+
this.addEventListener("keydown", this.handleKeydown);
|
|
306
362
|
this.addEventListener("pointerdown", this.handlePointerdown);
|
|
307
363
|
this.addEventListener("pointerenter", this.closeOverlaysForRoot);
|
|
308
364
|
if (!this.hasAttribute("id")) {
|
|
@@ -314,6 +370,18 @@ export class MenuItem extends LikeAnchor(
|
|
|
314
370
|
if (this.open) return;
|
|
315
371
|
(_a = this.menuData.parentMenu) == null ? void 0 : _a.closeDescendentOverlays();
|
|
316
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
|
+
}
|
|
317
385
|
handleSubmenuClick(event) {
|
|
318
386
|
if (event.composedPath().includes(this.overlayElement)) {
|
|
319
387
|
return;
|
|
@@ -323,6 +391,7 @@ export class MenuItem extends LikeAnchor(
|
|
|
323
391
|
handleSubmenuFocus() {
|
|
324
392
|
requestAnimationFrame(() => {
|
|
325
393
|
this.overlayElement.open = this.open;
|
|
394
|
+
this.focused = false;
|
|
326
395
|
});
|
|
327
396
|
}
|
|
328
397
|
handlePointerenter() {
|
|
@@ -362,13 +431,18 @@ export class MenuItem extends LikeAnchor(
|
|
|
362
431
|
});
|
|
363
432
|
}
|
|
364
433
|
handleSubmenuOpen(event) {
|
|
434
|
+
var _a;
|
|
435
|
+
const shouldFocus = this.matches(":focus, :focus-within") || this.focused;
|
|
365
436
|
this.focused = false;
|
|
366
437
|
const parentOverlay = event.composedPath().find((el) => {
|
|
367
438
|
return el !== this.overlayElement && el.localName === "sp-overlay";
|
|
368
439
|
});
|
|
440
|
+
if (shouldFocus)
|
|
441
|
+
(_a = this.submenuElement) == null ? void 0 : _a.focus();
|
|
369
442
|
this.overlayElement.parentOverlayToForceClose = parentOverlay;
|
|
370
443
|
}
|
|
371
444
|
cleanup() {
|
|
445
|
+
this.setAttribute("aria-expanded", "false");
|
|
372
446
|
this.open = false;
|
|
373
447
|
this.active = false;
|
|
374
448
|
}
|
|
@@ -398,6 +472,12 @@ export class MenuItem extends LikeAnchor(
|
|
|
398
472
|
this.setAttribute("role", role);
|
|
399
473
|
this.updateAriaSelected();
|
|
400
474
|
}
|
|
475
|
+
willUpdate(changes) {
|
|
476
|
+
super.updated(changes);
|
|
477
|
+
if (changes.has("open") && !this.open && this.hasSubmenu && this.hasVisibleFocusInTree()) {
|
|
478
|
+
this.focus();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
401
481
|
updated(changes) {
|
|
402
482
|
var _a, _b;
|
|
403
483
|
super.updated(changes);
|
|
@@ -467,6 +547,14 @@ export class MenuItem extends LikeAnchor(
|
|
|
467
547
|
await new Promise((ready) => requestAnimationFrame(ready));
|
|
468
548
|
this.dispatchUpdate();
|
|
469
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
|
+
}
|
|
470
558
|
dispatchUpdate() {
|
|
471
559
|
if (!this.isConnected) {
|
|
472
560
|
return;
|