UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

256 lines (251 loc) • 10.2 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 { proxyCustomElement, HTMLElement, h, Host } from '@stencil/core/internal/client'; import { s as slotChangeGetAssignedElements, i as focusElementInGroup, k as focusElement } from './dom.js'; import { a as setUpLoadableComponent, s as setComponentLoaded, c as componentLoaded } from './loadable.js'; import { c as connectLocalized, d as disconnectLocalized } from './locale.js'; import { u as updateMessages, c as connectMessages, s as setUpMessages, d as disconnectMessages } from './t9n.js'; import { c as createObserver } from './observers.js'; const allowedGlobalAttributes = ["lang", "role"]; const elementToComponentAndObserverOptionsMap = new Map(); let mutationObserver; function updateGlobalAttributes(component, attributeFilter, reuseAttributes = false) { const { el } = component; const updatedAttributes = reuseAttributes ? component.globalAttributes : {}; attributeFilter .filter((attr) => !!allowedGlobalAttributes.includes(attr) && !!el.hasAttribute(attr)) .forEach((attr) => { const value = el.getAttribute(attr); if (value !== null) { updatedAttributes[attr] = value; } }); component.globalAttributes = updatedAttributes; } function processMutations(mutations) { mutations.forEach(({ target }) => { const [component, options] = elementToComponentAndObserverOptionsMap.get(target); updateGlobalAttributes(component, options.attributeFilter); }); } /** * Helper to set up listening for changes to global attributes. * * render(): VNode { * return (<Host> * <ul {...this.globalAttributes}></div> * </Host>); * } * * @param component * @param attributeFilter */ function watchGlobalAttributes(component, attributeFilter) { const { el } = component; const observerOptions = { attributeFilter }; elementToComponentAndObserverOptionsMap.set(el, [component, observerOptions]); updateGlobalAttributes(component, attributeFilter, true); if (!mutationObserver) { mutationObserver = createObserver("mutation", processMutations); } mutationObserver.observe(el, observerOptions); } /** * Helper remove listening for changes to inherited attributes. * * @param component */ function unwatchGlobalAttributes(component) { elementToComponentAndObserverOptionsMap.delete(component.el); // we explicitly process queued mutations and disconnect and reconnect // the observer until MutationObserver gets an `unobserve` method // see https://github.com/whatwg/dom/issues/126 processMutations(mutationObserver.takeRecords()); mutationObserver.disconnect(); for (const [element, [, observerOptions]] of elementToComponentAndObserverOptionsMap.entries()) { mutationObserver.observe(element, observerOptions); } } const menuCss = "@keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in-down{0%{opacity:0;transform:translate3D(0, -5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;transform:translate3D(0, 5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-right{0%{opacity:0;transform:translate3D(-5px, 0, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-left{0%{opacity:0;transform:translate3D(5px, 0, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-scale{0%{opacity:0;transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;transform:scale3D(1, 1, 1)}}:root{--calcite-animation-timing:calc(150ms * var(--calcite-internal-duration-factor));--calcite-internal-duration-factor:var(--calcite-duration-factor, 1);--calcite-internal-animation-timing-fast:calc(100ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-medium:calc(200ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-slow:calc(300ms * var(--calcite-internal-duration-factor))}.calcite-animate{opacity:0;animation-fill-mode:both;animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{animation-name:in}.calcite-animate__in-down{animation-name:in-down}.calcite-animate__in-up{animation-name:in-up}.calcite-animate__in-right{animation-name:in-right}.calcite-animate__in-left{animation-name:in-left}.calcite-animate__in-scale{animation-name:in-scale}@media (prefers-reduced-motion: reduce){:root{--calcite-internal-duration-factor:0.01}}:root{--calcite-floating-ui-transition:var(--calcite-animation-timing);--calcite-floating-ui-z-index:var(--calcite-app-z-index-dropdown)}:host([hidden]){display:none}:host{display:flex}ul{margin:0px;display:inline-flex;block-size:100%;align-items:center;padding:0px}:host([layout=vertical]) ul{display:flex;inline-size:100%;flex-direction:column}"; const CalciteMenu = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement { constructor() { super(); this.__registerHost(); this.__attachShadow(); 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 delegatesFocus() { return true; } static get assetsDirs() { return ["assets"]; } get el() { return this; } static get watchers() { return { "layout": ["handleLayoutChange"], "messageOverrides": ["onMessagesChange"], "effectiveLocale": ["effectiveLocaleChange"] }; } static get style() { return menuCss; } }, [17, "calcite-menu", { "label": [1], "layout": [513], "messageOverrides": [1040], "messages": [1040], "defaultMessages": [32], "effectiveLocale": [32], "globalAttributes": [32], "setFocus": [64] }, [[0, "calciteInternalMenuItemKeyEvent", "calciteInternalNavMenuItemKeyEvent"]]]); function defineCustomElement() { if (typeof customElements === "undefined") { return; } const components = ["calcite-menu"]; components.forEach(tagName => { switch (tagName) { case "calcite-menu": if (!customElements.get(tagName)) { customElements.define(tagName, CalciteMenu); } break; } }); } defineCustomElement(); export { CalciteMenu as C, defineCustomElement as d };