UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

307 lines (306 loc) • 8.79 kB
/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. * v1.5.0-next.4 */ import { h, Host } from "@stencil/core"; import { focusElement, focusElementInGroup, slotChangeGetAssignedElements } from "../../utils/dom"; import { componentLoaded, setComponentLoaded, setUpLoadableComponent } from "../../utils/loadable"; import { connectLocalized, disconnectLocalized } from "../../utils/locale"; import { connectMessages, disconnectMessages, setUpMessages, updateMessages } from "../../utils/t9n"; import { unwatchGlobalAttributes, watchGlobalAttributes } from "../../utils/globalAttributes"; export class CalciteMenu { constructor() { this.menuItems = []; //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- this.handleMenuSlotChange = (event) => { this.menuItems = slotChangeGetAssignedElements(event); this.setMenuItemLayout(this.menuItems, this.layout); }; this.label = undefined; this.layout = "horizontal"; this.messageOverrides = undefined; this.messages = undefined; this.defaultMessages = undefined; this.effectiveLocale = ""; this.globalAttributes = { role: "menubar" }; } handleLayoutChange(value) { this.setMenuItemLayout(this.menuItems, value); } onMessagesChange() { /* wired up by t9n util */ } effectiveLocaleChange() { updateMessages(this, this.effectiveLocale); } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- connectedCallback() { connectLocalized(this); connectMessages(this); watchGlobalAttributes(this, ["role"]); } async componentWillLoad() { setUpLoadableComponent(this); await setUpMessages(this); } componentDidLoad() { setComponentLoaded(this); } disconnectedCallback() { disconnectLocalized(this); disconnectMessages(this); unwatchGlobalAttributes(this); } //-------------------------------------------------------------------------- // // Public Methods // //-------------------------------------------------------------------------- /** Sets focus on the component's first focusable element. */ async setFocus() { await componentLoaded(this); this.el.focus(); } //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- calciteInternalNavMenuItemKeyEvent(event) { const target = event.target; const submenuItems = event.detail.children; const key = event.detail.event.key; event.stopPropagation(); if (key === "ArrowDown") { if (target.layout === "vertical") { focusElementInGroup(this.menuItems, target, "next", false); } else { if (event.detail.isSubmenuOpen) { submenuItems[0].setFocus(); } } } else if (key === "ArrowUp") { if (this.layout === "vertical") { focusElementInGroup(this.menuItems, target, "previous", false); } else { if (event.detail.isSubmenuOpen) { submenuItems[submenuItems.length - 1].setFocus(); } } } else if (key === "ArrowRight") { if (this.layout === "horizontal") { focusElementInGroup(this.menuItems, target, "next", false); } else { if (event.detail.isSubmenuOpen) { submenuItems[0].setFocus(); } } } else if (key === "ArrowLeft") { if (this.layout === "horizontal") { focusElementInGroup(this.menuItems, target, "previous", false); } else { if (event.detail.isSubmenuOpen) { this.focusParentElement(event.target); } } } else if (key === "Escape") { this.focusParentElement(event.target); } event.preventDefault(); } focusParentElement(el) { const parentEl = el.parentElement; if (parentEl) { focusElement(parentEl); parentEl.open = false; } } setMenuItemLayout(items, layout) { items.forEach((item) => { item.layout = layout; if (this.globalAttributes.role === "menubar") { item.isTopLevelItem = true; item.topLevelMenuLayout = this.layout; } }); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- render() { return (h(Host, null, h("ul", { "aria-label": this.label, ...this.globalAttributes }, h("slot", { onSlotchange: this.handleMenuSlotChange })))); } static get is() { return "calcite-menu"; } static get encapsulation() { return "shadow"; } static get delegatesFocus() { return true; } static get originalStyleUrls() { return { "$": ["menu.scss"] }; } static get styleUrls() { return { "$": ["menu.css"] }; } static get assetsDirs() { return ["assets"]; } static get properties() { return { "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "Accessible name for the component." }, "attribute": "label", "reflect": false }, "layout": { "type": "string", "mutable": false, "complexType": { "original": "Layout", "resolved": "\"horizontal\" | \"vertical\"", "references": { "Layout": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the layout of the component." }, "attribute": "layout", "reflect": true, "defaultValue": "\"horizontal\"" }, "messageOverrides": { "type": "unknown", "mutable": true, "complexType": { "original": "Partial<MenuMessages>", "resolved": "{ more?: string; }", "references": { "Partial": { "location": "global" }, "MenuMessages": { "location": "import", "path": "./assets/menu/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Use this property to override individual strings used by the component." } }, "messages": { "type": "unknown", "mutable": true, "complexType": { "original": "MenuMessages", "resolved": "{ more: string; }", "references": { "MenuMessages": { "location": "import", "path": "./assets/menu/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "Made into a prop for testing purposes only." } } }; } static get states() { return { "defaultMessages": {}, "effectiveLocale": {}, "globalAttributes": {} }; } static get methods() { return { "setFocus": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Sets focus on the component's first focusable element.", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "layout", "methodName": "handleLayoutChange" }, { "propName": "messageOverrides", "methodName": "onMessagesChange" }, { "propName": "effectiveLocale", "methodName": "effectiveLocaleChange" }]; } static get listeners() { return [{ "name": "calciteInternalMenuItemKeyEvent", "method": "calciteInternalNavMenuItemKeyEvent", "target": undefined, "capture": false, "passive": false }]; } }