UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

335 lines (329 loc) • 18.4 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, createEvent, h, Fragment, Host } from '@stencil/core/internal/client'; import { s as slotChangeGetAssignedElements, C as CSS_UTILITY, c as getElementDir } from './dom.js'; import { c as componentLoaded, a as setUpLoadableComponent, s as setComponentLoaded } from './loadable.js'; import { u as updateMessages, c as connectMessages, s as setUpMessages, d as disconnectMessages } from './t9n.js'; import { c as connectLocalized, d as disconnectLocalized } from './locale.js'; import { d as defineCustomElement$5 } from './action.js'; import { d as defineCustomElement$4 } from './icon.js'; import { d as defineCustomElement$3 } from './loader.js'; import { d as defineCustomElement$2 } from './menu.js'; const CSS = { container: "container", content: "content", dropdownVertical: "dropdown--vertical", dropdownMenuItems: "dropdown-menu-items", dropdownAction: "dropdown-action", layoutVertical: "layout--vertical", hoverHrefIcon: "hover-href-icon", icon: "icon", iconBreadcrumb: "icon--breadcrumb", iconDropdown: "icon--dropdown", iconEnd: "icon--end", iconStart: "icon--start", isParentVertical: "parent--vertical", itemContent: "item-content", open: "open", nested: "nested", textContainer: "text-container" }; const menuItemCss = "@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{position:relative;box-sizing:border-box;display:flex;align-items:center;flex-shrink:0}:host .container,:host .item-content,:host .content{min-block-size:3rem}:host([layout=vertical]){inline-size:100%}:host(:not([layout=vertical])){block-size:100%}.container,.item-content{display:flex;block-size:100%;inline-size:100%;flex-direction:row;align-items:stretch}.content{position:relative;box-sizing:border-box;display:flex;block-size:100%;inline-size:100%;cursor:pointer;align-items:center;justify-content:center;background-color:var(--calcite-ui-foreground-1);padding-inline:1rem;font-size:var(--calcite-font-size-0);color:var(--calcite-ui-text-2);outline:2px solid transparent;outline-offset:2px;text-decoration:none;border-block-end:0.125rem solid transparent;padding-block-start:0.125rem}.content:hover{background-color:var(--calcite-ui-foreground-2);color:var(--calcite-ui-text-2)}.content:focus{background-color:var(--calcite-ui-foreground-2);color:var(--calcite-ui-text-2);outline:2px solid var(--calcite-ui-focus-color, var(--calcite-ui-brand));outline-offset:calc(\n -2px *\n calc(\n 1 -\n 2 * clamp(\n 0,\n var(--calcite-ui-focus-offset-invert),\n 1\n )\n )\n )}.content:active{background-color:var(--calcite-ui-foreground-3);color:var(--calcite-ui-text-1)}.content span{display:inline-flex}.content.layout--vertical{display:flex;inline-size:100%;justify-content:flex-start;padding-block:1rem;border-block-end:0;border-inline-end:0.25rem solid transparent}:host([active]) .content{color:var(--calcite-ui-text-1);border-color:var(--calcite-ui-brand)}:host([active]) .content .icon{--calcite-ui-icon-color:var(--calcite-ui-brand)}.icon--start{margin-inline-end:0.75rem}.icon--end{margin-inline-start:0.75rem}.icon--dropdown{position:relative;margin-inline-start:auto;margin-inline-end:0px;padding-inline-start:0.5rem;--calcite-ui-icon-color:var(--calcite-ui-text-3)}:host([layout=vertical]) .icon--dropdown{inset-inline-start:0.25rem}.icon--breadcrumb{margin-inline-end:0px;padding-inline-start:0.5rem;--calcite-ui-icon-color:var(--calcite-ui-text-3)}:host([breadcrumb]) .content{padding-inline-end:0.75rem}calcite-action{position:relative;block-size:auto;border-inline-start:1px solid var(--calcite-ui-foreground-1)}calcite-action:after{position:absolute;inset-inline-start:-1px;display:block;inline-size:1px;content:\"\";inset-block:0.75rem;background-color:var(--calcite-ui-border-3)}calcite-action:hover:after{block-size:100%;inset-block:0}.content:focus~calcite-action,.content:hover~calcite-action{color:var(--calcite-ui-text-1);border-inline-start:1px solid var(--calcite-ui-border-3)}.container:hover .dropdown-action{background-color:var(--calcite-ui-foreground-2)}.dropdown-menu-items{position:absolute;display:none;block-size:auto;min-inline-size:100%;flex-direction:column;overflow:visible;border:1px solid var(--calcite-ui-border-3);background:var(--calcite-ui-foreground-1);inset-block-start:100%;z-index:var(--calcite-app-z-index-dropdown)}.dropdown-menu-items.open{display:block}.dropdown-menu-items.nested{position:absolute;inset-block-start:-1px;transform:translateX(calc(100% - 2px))}.parent--vertical{flex-direction:column}.dropdown--vertical.dropdown-menu-items{position:relative;border-radius:0px;box-shadow:none;inset-block-start:0;transform:none}.dropdown--vertical.dropdown-menu-items:last-of-type{border-inline:0}:host([slot=submenu-item]) .parent--vertical{padding-inline-start:1.75rem}.dropdown-menu-items.nested.calcite--rtl{transform:translateX(calc(-100% + 2px))}.dropdown--vertical.dropdown-menu-items.nested.calcite--rtl{transform:none}.hover-href-icon{position:relative;inset-inline-end:0.25rem;margin-inline-start:auto;padding-inline-start:2rem;opacity:0;transition:all var(--calcite-internal-animation-timing-medium) ease-in-out}.content:focus .hover-href-icon,.content:hover .hover-href-icon{inset-inline-end:-0.25rem;opacity:1}"; const CalciteMenuItem$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement { constructor() { super(); this.__registerHost(); this.__attachShadow(); this.calciteInternalMenuItemKeyEvent = createEvent(this, "calciteInternalMenuItemKeyEvent", 7); this.calciteMenuItemSelect = createEvent(this, "calciteMenuItemSelect", 7); this.clickHandler = (event) => { if ((this.href && event.target === this.dropdownActionEl) || (!this.href && this.hasSubmenu)) { this.open = !this.open; } this.selectMenuItem(event); }; this.handleMenuItemSlotChange = (event) => { this.submenuItems = slotChangeGetAssignedElements(event); this.submenuItems.forEach((item) => { if (!item.topLevelMenuLayout) { item.topLevelMenuLayout = this.topLevelMenuLayout; } }); this.hasSubmenu = this.submenuItems.length > 0; }; this.keyDownHandler = async (event) => { // opening and closing of submenu is handled here. Any other functionality is bubbled to parent menu. if (event.key === " " || event.key === "Enter") { this.selectMenuItem(event); if (this.hasSubmenu && (!this.href || (this.href && event.target === this.dropdownActionEl))) { this.open = !this.open; } event.preventDefault(); } else if (event.key === "Escape") { if (this.open) { this.open = false; return; } this.calciteInternalMenuItemKeyEvent.emit({ event }); event.preventDefault(); } else if (event.key === "ArrowDown" || event.key === "ArrowUp") { event.preventDefault(); if ((event.target === this.dropdownActionEl || !this.href) && this.hasSubmenu && !this.open && this.layout === "horizontal") { this.open = true; return; } this.calciteInternalMenuItemKeyEvent.emit({ event, children: this.submenuItems, isSubmenuOpen: this.open && this.hasSubmenu }); } else if (event.key === "ArrowLeft") { event.preventDefault(); this.calciteInternalMenuItemKeyEvent.emit({ event, children: this.submenuItems, isSubmenuOpen: true }); } else if (event.key === "ArrowRight") { event.preventDefault(); if ((event.target === this.dropdownActionEl || !this.href) && this.hasSubmenu && !this.open && this.layout === "vertical") { this.open = true; return; } this.calciteInternalMenuItemKeyEvent.emit({ event, children: this.submenuItems, isSubmenuOpen: this.open && this.hasSubmenu }); } }; this.active = undefined; this.breadcrumb = undefined; this.href = undefined; this.iconEnd = undefined; this.iconFlipRtl = undefined; this.iconStart = undefined; this.isTopLevelItem = false; this.label = undefined; this.layout = undefined; this.messageOverrides = undefined; this.messages = undefined; this.open = false; this.rel = undefined; this.target = undefined; this.text = undefined; this.topLevelMenuLayout = undefined; this.defaultMessages = undefined; this.effectiveLocale = ""; this.hasSubmenu = false; this.submenuItems = undefined; } onMessagesChange() { /* wired up by t9n util */ } effectiveLocaleChange() { updateMessages(this, this.effectiveLocale); } //-------------------------------------------------------------------------- // // Public Methods // //-------------------------------------------------------------------------- /** Sets focus on the component. */ async setFocus() { await componentLoaded(this); this.anchorEl.focus(); } //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- handleClickOut(event) { if (this.topLevelMenuLayout !== "vertical" && this.hasSubmenu && this.open && !event.composedPath().includes(this.el)) { this.open = false; } } handleFocusOut(event) { if (this.topLevelMenuLayout !== "vertical" && !this.el.contains(event.relatedTarget)) { this.open = false; } } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- connectedCallback() { connectLocalized(this); connectMessages(this); } async componentWillLoad() { setUpLoadableComponent(this); await setUpMessages(this); } componentDidLoad() { setComponentLoaded(this); } disconnectedCallback() { disconnectLocalized(this); disconnectMessages(this); } // -------------------------------------------------------------------------- // // Private Methods // // -------------------------------------------------------------------------- blurHandler() { this.isFocused = false; } focusHandler(event) { const target = event.target; this.isFocused = true; if (target.open && !this.open) { target.open = false; } } selectMenuItem(event) { if (event.target !== this.dropdownActionEl) { this.calciteMenuItemSelect.emit(); } } //-------------------------------------------------------------------------- // // Render Methods // //-------------------------------------------------------------------------- renderIconStart() { return (h("calcite-icon", { class: `${CSS.icon} ${CSS.iconStart}`, flipRtl: this.iconFlipRtl === "start" || this.iconFlipRtl === "both", icon: this.iconStart, key: CSS.iconStart, scale: "s" })); } renderIconEnd() { return (h("calcite-icon", { class: `${CSS.icon} ${CSS.iconEnd}`, flipRtl: this.iconFlipRtl === "end" || this.iconFlipRtl === "both", icon: this.iconEnd, key: CSS.iconEnd, scale: "s" })); } renderBreadcrumbIcon(dir) { return (h("calcite-icon", { class: `${CSS.icon} ${CSS.iconBreadcrumb}`, icon: dir === "rtl" ? "chevron-left" : "chevron-right", key: CSS.iconBreadcrumb, scale: "s" })); } renderDropdownIcon(dir) { const dirChevron = dir === "rtl" ? "chevron-left" : "chevron-right"; return (h("calcite-icon", { class: `${CSS.icon} ${CSS.iconDropdown}`, icon: this.topLevelMenuLayout === "vertical" || this.isTopLevelItem ? this.open ? "chevron-up" : "chevron-down" : dirChevron, key: CSS.iconDropdown, scale: "s" })); } renderDropdownAction(dir) { const dirChevron = dir === "rtl" ? "chevron-left" : "chevron-right"; return (h("calcite-action", { class: CSS.dropdownAction, icon: this.topLevelMenuLayout === "vertical" || this.isTopLevelItem ? this.open ? "chevron-up" : "chevron-down" : dirChevron, key: CSS.dropdownAction, onClick: this.clickHandler, onKeyDown: this.keyDownHandler, text: this.messages.open, // eslint-disable-next-line react/jsx-sort-props ref: (el) => (this.dropdownActionEl = el) })); } renderSubmenuItems(dir) { return (h("calcite-menu", { class: { [CSS.dropdownMenuItems]: true, [CSS.open]: this.open, [CSS.nested]: !this.isTopLevelItem, [CSS_UTILITY.rtl]: dir === "rtl", [CSS.dropdownVertical]: this.topLevelMenuLayout === "vertical" }, label: this.messages.submenu, layout: "vertical", role: "menu" }, h("slot", { name: "submenu-item", onSlotchange: this.handleMenuItemSlotChange }))); } renderItemContent(dir) { return (h(Fragment, null, this.iconStart && this.renderIconStart(), h("div", { class: CSS.textContainer }, h("span", null, this.text)), this.iconEnd && this.renderIconEnd(), this.breadcrumb ? this.renderBreadcrumbIcon(dir) : null, !this.href && this.hasSubmenu ? this.renderDropdownIcon(dir) : null)); } render() { const dir = getElementDir(this.el); return (h(Host, { onBlur: this.blurHandler, onFocus: this.focusHandler }, h("li", { class: { [CSS.container]: true, [CSS.isParentVertical]: this.topLevelMenuLayout === "vertical" }, role: "none" }, h("div", { class: CSS.itemContent }, h("a", { "aria-current": this.isFocused ? "page" : false, "aria-expanded": this.open, "aria-haspopup": this.hasSubmenu, "aria-label": this.label, class: { [CSS.layoutVertical]: this.layout === "vertical", [CSS.content]: true }, href: this.href, onClick: this.clickHandler, onKeyDown: this.keyDownHandler, rel: this.rel, role: "menuitem", tabIndex: this.isTopLevelItem ? 0 : -1, target: this.target, // eslint-disable-next-line react/jsx-sort-props ref: (el) => (this.anchorEl = el) }, this.renderItemContent(dir), this.href && (this.topLevelMenuLayout === "vertical" || !this.isTopLevelItem) ? (h("calcite-icon", { class: CSS.hoverHrefIcon, icon: dir === "rtl" ? "arrow-left" : "arrow-right", scale: "s" })) : null), this.href && this.hasSubmenu ? this.renderDropdownAction(dir) : null), this.renderSubmenuItems(dir)))); } static get assetsDirs() { return ["assets"]; } get el() { return this; } static get watchers() { return { "messageOverrides": ["onMessagesChange"], "effectiveLocale": ["effectiveLocaleChange"] }; } static get style() { return menuItemCss; } }, [1, "calcite-menu-item", { "active": [516], "breadcrumb": [516], "href": [1], "iconEnd": [513, "icon-end"], "iconFlipRtl": [513, "icon-flip-rtl"], "iconStart": [513, "icon-start"], "isTopLevelItem": [4, "is-top-level-item"], "label": [1], "layout": [513], "messageOverrides": [1040], "messages": [1040], "open": [1540], "rel": [513], "target": [513], "text": [1], "topLevelMenuLayout": [1, "top-level-menu-layout"], "defaultMessages": [32], "effectiveLocale": [32], "hasSubmenu": [32], "submenuItems": [32], "setFocus": [64] }, [[8, "click", "handleClickOut"], [0, "focusout", "handleFocusOut"]]]); function defineCustomElement$1() { if (typeof customElements === "undefined") { return; } const components = ["calcite-menu-item", "calcite-action", "calcite-icon", "calcite-loader", "calcite-menu"]; components.forEach(tagName => { switch (tagName) { case "calcite-menu-item": if (!customElements.get(tagName)) { customElements.define(tagName, CalciteMenuItem$1); } break; case "calcite-action": if (!customElements.get(tagName)) { defineCustomElement$5(); } break; case "calcite-icon": if (!customElements.get(tagName)) { defineCustomElement$4(); } break; case "calcite-loader": if (!customElements.get(tagName)) { defineCustomElement$3(); } break; case "calcite-menu": if (!customElements.get(tagName)) { defineCustomElement$2(); } break; } }); } defineCustomElement$1(); const CalciteMenuItem = CalciteMenuItem$1; const defineCustomElement = defineCustomElement$1; export { CalciteMenuItem, defineCustomElement };