UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

312 lines (306 loc) • 13.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, Host } from '@stencil/core/internal/client'; import { k as focusElement, o as nodeListToArray, p as getRootNode } from './dom.js'; function isTreeItem(element) { return element?.matches("calcite-tree-item"); } function getEnabledSiblingItem(el, direction) { const directionProp = direction === "down" ? "nextElementSibling" : "previousElementSibling"; let currentEl = el; let enabledEl = null; while (isTreeItem(currentEl)) { if (!currentEl.disabled) { enabledEl = currentEl; break; } currentEl = currentEl[directionProp]; } return enabledEl; } const treeCss = "@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:block}:host(:focus){outline:2px solid transparent;outline-offset:2px}"; const Tree = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement { constructor() { super(); this.__registerHost(); this.__attachShadow(); this.calciteTreeSelect = createEvent(this, "calciteTreeSelect", 6); this.lines = false; this.child = undefined; this.scale = "m"; this.selectionMode = "single"; this.selectedItems = []; } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- componentWillRender() { const parent = this.el.parentElement?.closest("calcite-tree"); this.lines = parent ? parent.lines : this.lines; this.scale = parent ? parent.scale : this.scale; this.selectionMode = parent ? parent.selectionMode : this.selectionMode; this.child = !!parent; } render() { return (h(Host, { "aria-multiselectable": this.child ? undefined : (this.selectionMode === "multiple" || this.selectionMode === "multichildren").toString(), role: !this.child ? "tree" : undefined, tabIndex: this.getRootTabIndex() }, h("slot", null))); } //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- onFocus() { if (!this.child) { const focusTarget = this.el.querySelector("calcite-tree-item[selected]:not([disabled])") || this.el.querySelector("calcite-tree-item:not([disabled])"); focusElement(focusTarget); } } onFocusIn(event) { const focusedFromRootOrOutsideTree = event.relatedTarget === this.el || !this.el.contains(event.relatedTarget); if (focusedFromRootOrOutsideTree) { // gives user the ability to tab into external elements (modifying tabindex property will not work in firefox) this.el.removeAttribute("tabindex"); } } onFocusOut(event) { const willFocusOutsideTree = !this.el.contains(event.relatedTarget); if (willFocusOutsideTree) { this.el.tabIndex = this.getRootTabIndex(); } } onClick(event) { const target = event.target; const childItems = nodeListToArray(target.querySelectorAll("calcite-tree-item")); if (this.child) { return; } if (!this.child) { event.preventDefault(); event.stopPropagation(); } if (this.selectionMode === "ancestors" && !this.child) { this.updateAncestorTree(event); return; } const isNoneSelectionMode = this.selectionMode === "none"; const shouldSelect = this.selectionMode !== null && (!target.hasChildren || (target.hasChildren && (this.selectionMode === "children" || this.selectionMode === "multichildren"))); const shouldDeselectAllChildren = this.selectionMode === "multichildren" && target.hasChildren; const shouldModifyToCurrentSelection = !isNoneSelectionMode && event.detail.modifyCurrentSelection && (this.selectionMode === "multiple" || this.selectionMode === "multichildren"); const shouldClearCurrentSelection = !shouldModifyToCurrentSelection && (((this.selectionMode === "single" || this.selectionMode === "multiple") && childItems.length <= 0) || this.selectionMode === "children" || this.selectionMode === "multichildren"); const shouldUpdateExpand = ["children", "multichildren"].includes(this.selectionMode) || (["single", "multiple"].includes(this.selectionMode) && target.hasChildren && !event.detail.forceToggle); if (!this.child) { const targetItems = []; if (shouldSelect) { targetItems.push(target); } if (shouldClearCurrentSelection) { const selectedItems = nodeListToArray(this.el.querySelectorAll("calcite-tree-item[selected]")); selectedItems.forEach((treeItem) => { if (!targetItems.includes(treeItem)) { treeItem.selected = false; } }); } if (shouldUpdateExpand) { if (["single", "multiple"].includes(this.selectionMode)) { target.expanded = !target.expanded; } else if (this.selectionMode === "multichildren") { target.expanded = !target.selected; } else if (this.selectionMode === "children") { target.expanded = target.selected ? !target.expanded : true; } } if (shouldDeselectAllChildren) { childItems.forEach((item) => { item.selected = false; if (item.hasChildren) { item.expanded = false; } }); } if (shouldModifyToCurrentSelection) { window.getSelection().removeAllRanges(); } if ((shouldModifyToCurrentSelection && target.selected) || event.detail.forceToggle) { targetItems.forEach((treeItem) => { if (!treeItem.disabled) { treeItem.selected = false; } }); } else if (!isNoneSelectionMode) { targetItems.forEach((treeItem) => { if (!treeItem.disabled) { treeItem.selected = true; } }); } } this.selectedItems = isNoneSelectionMode ? [target] : nodeListToArray(this.el.querySelectorAll("calcite-tree-item")).filter((i) => i.selected); this.calciteTreeSelect.emit(); event.stopPropagation(); } keyDownHandler(event) { const root = this.el.closest("calcite-tree:not([child])"); const target = event.target; if (!(root === this.el && target.tagName === "CALCITE-TREE-ITEM" && this.el.contains(target))) { return; } if (event.key === "ArrowDown") { const next = getEnabledSiblingItem(target.nextElementSibling, "down"); if (next) { next.focus(); event.preventDefault(); } return; } if (event.key === "ArrowUp") { const previous = getEnabledSiblingItem(target.previousElementSibling, "up"); if (previous) { previous.focus(); event.preventDefault(); } } if (event.key === "ArrowLeft" && !target.disabled) { // When focus is on an open node, closes the node. if (target.hasChildren && target.expanded) { target.expanded = false; event.preventDefault(); return; } // When focus is on a child node that is also either an end node or a closed node, moves focus to its parent node. const parentItem = target.parentElement.closest("calcite-tree-item"); if (parentItem && (!target.hasChildren || target.expanded === false)) { parentItem.focus(); event.preventDefault(); return; } // When focus is on a root node that is also either an end node or a closed node, does nothing. return; } if (event.key === "ArrowRight" && !target.disabled) { if (target.hasChildren) { if (target.expanded && getRootNode(this.el).activeElement === target) { // When focus is on an open node, moves focus to the first child node. getEnabledSiblingItem(target.querySelector("calcite-tree-item"), "down")?.focus(); event.preventDefault(); } else { // When focus is on a closed node, opens the node; focus does not move. target.expanded = true; event.preventDefault(); } } return; } } updateAncestorTree(event) { const item = event.target; if (item.disabled) { return; } const ancestors = []; let parent = item.parentElement.closest("calcite-tree-item"); while (parent) { ancestors.push(parent); parent = parent.parentElement.closest("calcite-tree-item"); } const childItems = Array.from(item.querySelectorAll("calcite-tree-item:not([disabled])")); const childItemsWithNoChildren = childItems.filter((child) => !child.hasChildren); const childItemsWithChildren = childItems.filter((child) => child.hasChildren); const futureSelected = item.hasChildren ? !(item.selected || item.indeterminate) : !item.selected; childItemsWithNoChildren.forEach((el) => { el.selected = futureSelected; el.indeterminate = false; }); function updateItemState(childItems, item) { const selected = childItems.filter((child) => child.selected); const unselected = childItems.filter((child) => !child.selected); item.selected = selected.length === childItems.length; item.indeterminate = selected.length > 0 && unselected.length > 0; } childItemsWithChildren.forEach((el) => { const directChildItems = Array.from(el.querySelectorAll(":scope > calcite-tree > calcite-tree-item")); updateItemState(directChildItems, el); }); if (item.hasChildren) { updateItemState(childItems, item); } else { item.selected = futureSelected; item.indeterminate = false; } ancestors.forEach((ancestor) => { const descendants = nodeListToArray(ancestor.querySelectorAll("calcite-tree-item")); const activeDescendants = descendants.filter((el) => el.selected); if (activeDescendants.length === 0) { ancestor.selected = false; ancestor.indeterminate = false; return; } const indeterminate = activeDescendants.length < descendants.length; ancestor.indeterminate = indeterminate; ancestor.selected = !indeterminate; }); this.selectedItems = nodeListToArray(this.el.querySelectorAll("calcite-tree-item")).filter((i) => i.selected); this.calciteTreeSelect.emit(); } // -------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- getRootTabIndex() { return !this.child ? 0 : -1; } get el() { return this; } static get style() { return treeCss; } }, [1, "calcite-tree", { "lines": [1540], "child": [1540], "scale": [1537], "selectionMode": [1537, "selection-mode"], "selectedItems": [1040] }, [[0, "focus", "onFocus"], [0, "focusin", "onFocusIn"], [0, "focusout", "onFocusOut"], [0, "calciteInternalTreeItemSelect", "onClick"], [0, "keydown", "keyDownHandler"]]]); function defineCustomElement$1() { if (typeof customElements === "undefined") { return; } const components = ["calcite-tree"]; components.forEach(tagName => { switch (tagName) { case "calcite-tree": if (!customElements.get(tagName)) { customElements.define(tagName, Tree); } break; } }); } defineCustomElement$1(); const CalciteTree = Tree; const defineCustomElement = defineCustomElement$1; export { CalciteTree, defineCustomElement };