UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

266 lines (265 loc) • 18.4 kB
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details. v3.2.1 */ import { C as CSS_UTILITY, c as customElement } from "../../chunks/runtime.js"; import { keyed } from "lit-html/directives/keyed.js"; import { html, nothing } from "lit"; import { createRef, ref } from "lit-html/directives/ref.js"; import { LitElement, createEvent, safeClassMap, setAttribute } from "@arcgis/lumina"; import { f as filterDirectChildren, s as slotChangeGetAssignedElements, a as slotChangeHasAssignedElement, g as getElementDir, t as toAriaBoolean } from "../../chunks/dom.js"; import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js"; import { g as getIconScale } from "../../chunks/component.js"; import { css } from "@lit/reactive-element/css-tag.js"; const CSS = { actionsEnd: "actions-end", bulletPointIcon: "bullet-point", checkbox: "checkbox", checkboxContainer: "checkbox-container", checkboxLabel: "checkbox-label", checkmarkIcon: "checkmark", chevron: "chevron", childrenContainer: "children-container", iconStart: "icon-start", itemExpanded: "item--expanded", nodeAndActionsContainer: "node-actions-container", nodeContainer: "node-container" }; const SLOTS = { actionsEnd: "actions-end", children: "children" }; const ICONS = { blank: "blank", bulletPoint: "bullet-point", checkmark: "check", checkSquareF: "check-square-f", chevronRight: "chevron-right", minusSquareF: "minus-square-f", square: "square" }; const styles = css`:host([disabled]){cursor:default;-webkit-user-select:none;user-select:none;opacity:var(--calcite-opacity-disabled)}:host([disabled]) *,:host([disabled]) ::slotted(*){pointer-events:none}:host([scale=s]){--calcite-internal-tree-item-spacing-unit: .25rem;--calcite-internal-tree-item-padding-block: .25rem;--calcite-internal-tree-item-children-container-padding: 1.25rem;--calcite-internal-tree-item-line-left-position: .75rem;font-size:var(--calcite-font-size--2);line-height:1rem}:host([scale=m]){--calcite-internal-tree-item-spacing-unit: .5rem;--calcite-internal-tree-item-padding-block: .5rem;--calcite-internal-tree-item-children-container-padding: 1.5rem;--calcite-internal-tree-item-line-left-position: 1rem;font-size:var(--calcite-font-size--1);line-height:1rem}:host([scale=l]){--calcite-internal-tree-item-spacing-unit: .75rem;--calcite-internal-tree-item-padding-block: .625rem;--calcite-internal-tree-item-children-container-padding: 2.25rem;--calcite-internal-tree-item-line-left-position: 1.5rem;font-size:var(--calcite-font-size-0);line-height:1.25rem}:host{display:block;max-inline-size:100%;cursor:pointer}:host .children-container ::slotted(*){padding-inline-start:var(--calcite-internal-tree-item-children-container-padding)}.node-actions-container{display:flex;color:var(--calcite-tree-text-color, var(--calcite-color-text-3))}.node-actions-container .node-container,.node-actions-container .checkbox-container{gap:var(--calcite-internal-tree-item-spacing-unit)}.node-actions-container .node-container{padding-inline:var(--calcite-internal-tree-item-spacing-unit);padding-block:var(--calcite-internal-tree-item-padding-block)}:host([calcite-hydrated-hidden]){visibility:hidden!important;pointer-events:none}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}:host([lines]) .children-container:after{position:absolute;inset-block-start:0px;z-index:var(--calcite-z-index);inline-size:1px;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;block-size:100%;inset-inline-start:var(--calcite-internal-tree-item-line-left-position);content:"";background-color:var(--calcite-color-border-2)}:host(:not([lines])) .node-container:after{display:none}::slotted(*){min-inline-size:0px;max-inline-size:100%;overflow-wrap:break-word;color:inherit;text-decoration:none!important}::slotted(*):hover{text-decoration:none!important}::slotted(a){inline-size:100%;text-decoration-line:none}:host{outline:2px solid transparent;outline-offset:2px}:host .node-container{outline-color:transparent}:host:focus .node-container,:host:active .node-container{outline:2px solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}:host(:focus:not([disabled])) .node-container{outline:2px solid transparent;outline-offset:2px;outline:2px solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}:host(:focus:not([disabled])) .checkbox{outline:2px solid transparent;outline-offset:2px}.actions-end{display:flex;flex-direction:row;align-items:center;align-self:stretch}.checkbox-container{display:flex;align-items:center}.checkbox{line-height:0;color:var(--calcite-tree-selected-icon-color, var(--calcite-color-border-input))}.checkbox-label{pointer-events:none;display:flex;align-items:center}.children-container{position:relative;block-size:0px;transform-origin:top;overflow:hidden;opacity:0;transform:scaleY(0);transition:var(--calcite-animation-timing) cubic-bezier(.215,.44,.42,.88),opacity var(--calcite-animation-timing) cubic-bezier(.215,.44,.42,.88),all var(--calcite-animation-timing) ease-in-out}.item--expanded>.children-container{overflow:visible;opacity:1;transform:none;block-size:auto}.node-container{position:relative;display:flex;min-inline-size:0px;flex-grow:1;align-items:center}.node-container .checkmark,.node-container .bullet-point{opacity:0;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;color:var(--calcite-color-border-1)}.node-container:hover .checkmark,.node-container:hover .bullet-point,:host([selected]) .node-container:hover .checkmark,:host([selected]) .node-container:hover .bullet-point,:host(:focus:not([disabled])) .node-container .checkmark,:host(:focus:not([disabled])) .node-container .bullet-point{opacity:1}:host([selected]) .node-container,:host([selected]) .node-container:hover{font-weight:var(--calcite-font-weight-medium);color:var(--calcite-tree-text-color-selected, var(--calcite-color-text-1))}:host([selected]) .node-container .bullet-point,:host([selected]) .node-container .checkmark,:host([selected]) .node-container:hover .bullet-point,:host([selected]) .node-container:hover .checkmark{opacity:1;color:var(--calcite-tree-selected-icon-color, var(--calcite-color-brand))}:host([has-children]) .node-container .bullet-point,:host([has-children]) .node-container .checkmark{display:none}.chevron{position:relative;align-self:center;color:var(--calcite-color-text-3);transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;flex:0 0 auto;transform:rotate(0)}.calcite--rtl .chevron{transform:rotate(180deg)}.item--expanded .node-container>.chevron{transform:rotate(90deg)}:host([selected]) .checkmark,:host([selected]) .bullet-point{color:var(--calcite-tree-selected-icon-color, var(--calcite-color-brand))}:host([selected]) .checkbox{color:var(--calcite-tree-selected-icon-color, var(--calcite-color-brand))}:host([has-children][indeterminate]) .checkbox{color:var(--calcite-tree-selected-icon-color, var(--calcite-color-brand))}:host([hidden]){display:none}[hidden]{display:none}`; class TreeItem extends LitElement { constructor() { super(); this.actionSlotWrapper = createRef(); this.userChangedValue = false; this.hasEndActions = false; this.updateAfterInitialRender = false; this.depth = -1; this.disabled = false; this.expanded = false; this.indeterminate = false; this.parentExpanded = false; this.selected = false; this.calciteInternalTreeItemSelect = createEvent({ cancelable: false }); this.listen("click", this.onClick); this.listen("keydown", this.keyDownHandler); } static { this.properties = { hasEndActions: [16, {}, { state: true }], updateAfterInitialRender: [16, {}, { state: true }], depth: [11, {}, { reflect: true, type: Number }], disabled: [7, {}, { reflect: true, type: Boolean }], expanded: [7, {}, { reflect: true, type: Boolean }], hasChildren: [39, {}, { reflect: true, readOnly: true, type: Boolean }], iconFlipRtl: [3, {}, { reflect: true }], iconStart: [3, {}, { reflect: true }], indeterminate: [7, {}, { reflect: true, type: Boolean }], label: 1, lines: [7, {}, { reflect: true, type: Boolean }], parentExpanded: [5, {}, { type: Boolean }], scale: [3, {}, { reflect: true }], selected: [7, {}, { reflect: true, type: Boolean }], selectionMode: [3, {}, { reflect: true }] }; } static { this.styles = styles; } get hasChildren() { return !!this.childTree; } connectedCallback() { super.connectedCallback(); this.parentTreeItem = this.el.parentElement?.closest("calcite-tree-item"); } load() { requestAnimationFrame(() => this.updateAfterInitialRender = true); } willUpdate(changes) { this.preWillUpdate(); if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) { this.updateChildTree(); } if (changes.has("selected") && (this.hasUpdated || this.selected !== false)) { this.handleSelectedChange(this.selected); } if (changes.has("selectionMode")) { this.getSelectionMode(); } } updated() { updateHostInteraction(this); } loaded() { this.updateAncestorTree(); } handleSelectedChange(value) { if (this.selectionMode === "ancestors" && !this.userChangedValue) { if (value) { this.indeterminate = false; } this.calciteInternalTreeItemSelect.emit({ modifyCurrentSelection: true, updateItem: false }); } } getSelectionMode() { this.isSelectionMultiLike = this.selectionMode === "multiple" || this.selectionMode === "multichildren"; } onClick(event) { if (this.disabled || this.isActionEndEvent(event)) { return; } const [link] = filterDirectChildren(this.el, "a"); if (link && event.composedPath()[0].tagName.toLowerCase() !== "a") { const target = link.target === "" ? "_self" : link.target; window.open(link.href, target); } this.calciteInternalTreeItemSelect.emit({ modifyCurrentSelection: this.selectionMode === "ancestors" || this.isSelectionMultiLike, updateItem: true }); this.userChangedValue = true; } iconClickHandler(event) { event.stopPropagation(); this.expanded = !this.expanded; } childrenClickHandler(event) { event.stopPropagation(); } keyDownHandler(event) { if (this.isActionEndEvent(event) || event.defaultPrevented) { return; } switch (event.key) { case " ": this.userChangedValue = true; this.calciteInternalTreeItemSelect.emit({ modifyCurrentSelection: this.isSelectionMultiLike, updateItem: true }); event.preventDefault(); break; case "Enter": { const link = Array.from(this.el.children).find((el) => el.matches("a")); this.userChangedValue = true; if (link) { link.click(); this.selected = true; } else { this.calciteInternalTreeItemSelect.emit({ modifyCurrentSelection: this.isSelectionMultiLike, updateItem: true }); } event.preventDefault(); } } } updateChildTree() { const { childTree } = this; if (!childTree) { return; } childTree.parentExpanded = this.expanded; } handleChildrenSlotChange(event) { const childTree = slotChangeGetAssignedElements(event).filter((el) => el.matches("calcite-tree"))[0]; this.childTree = childTree; this.requestUpdate("hasChildren"); this.updateChildTree(); } isActionEndEvent(event) { const composedPath = event.composedPath(); return composedPath.includes(this.actionSlotWrapper.value); } updateAncestorTree() { const parentItem = this.parentTreeItem; if (this.selectionMode !== "ancestors" || !parentItem) { return; } if (this.selected) { const parentTree = this.el.parentElement; const siblings = Array.from(parentTree?.children); const selectedSiblings = siblings.filter((child) => child.selected); if (siblings.length === selectedSiblings.length) { parentItem.selected = true; parentItem.indeterminate = false; } else if (selectedSiblings.length > 0) { parentItem.indeterminate = true; } const childItems = Array.from(this.el.querySelectorAll("calcite-tree-item:not([disabled])")); childItems.forEach((item) => { item.selected = true; item.indeterminate = false; }); } else if (this.indeterminate) { const parentItem2 = this.parentTreeItem; parentItem2.indeterminate = true; } } actionsEndSlotChangeHandler(event) { this.hasEndActions = slotChangeHasAssignedElement(event); } preWillUpdate() { this.depth = 0; let parentTree = this.el.closest("calcite-tree"); if (!parentTree) { return; } this.selectionMode = parentTree.selectionMode; this.scale = parentTree.scale || "m"; this.lines = parentTree.lines; let nextParentTree; while (parentTree) { nextParentTree = parentTree.parentElement?.closest("calcite-tree"); if (nextParentTree === parentTree) { break; } else { parentTree = nextParentTree; this.depth = this.depth + 1; } } } render() { const rtl = getElementDir(this.el) === "rtl"; const showBulletPoint = this.selectionMode === "single" || this.selectionMode === "children" || this.selectionMode === "single-persist"; const showCheckmark = this.selectionMode === "multiple" || this.selectionMode === "multichildren"; const showBlank = this.selectionMode === "none" && !this.hasChildren; const checkboxIsIndeterminate = this.hasChildren && this.indeterminate; const chevron = this.hasChildren || this.selectionMode === "ancestors" ? html`<calcite-icon class=${safeClassMap({ [CSS.chevron]: true, [CSS_UTILITY.rtl]: rtl })} data-test-id=icon .icon=${this.hasChildren ? ICONS.chevronRight : ICONS.blank} @click=${this.iconClickHandler} .scale=${getIconScale(this.scale)}></calcite-icon>` : null; const defaultSlotNode = keyed("default-slot", html`<slot></slot>`); const checkbox = this.selectionMode === "ancestors" ? html`<div class=${safeClassMap(CSS.checkboxContainer)}><calcite-icon class=${safeClassMap(CSS.checkbox)} .icon=${this.selected ? ICONS.checkSquareF : checkboxIsIndeterminate ? ICONS.minusSquareF : ICONS.square} .scale=${getIconScale(this.scale)}></calcite-icon></div>` : null; const selectedIcon = showBulletPoint ? ICONS.bulletPoint : showCheckmark ? ICONS.checkmark : showBlank ? ICONS.blank : null; const itemIndicator = selectedIcon ? html`<calcite-icon class=${safeClassMap({ [CSS.bulletPointIcon]: selectedIcon === ICONS.bulletPoint, [CSS.checkmarkIcon]: selectedIcon === ICONS.checkmark, [CSS_UTILITY.rtl]: rtl })} .icon=${selectedIcon} .scale=${getIconScale(this.scale)}></calcite-icon>` : null; const hidden = !(this.parentExpanded || this.depth === 1); const isExpanded = this.updateAfterInitialRender && this.expanded; const { hasEndActions } = this; const slotNode = keyed("actionsEndSlot", html`<slot name=${SLOTS.actionsEnd} @slotchange=${this.actionsEndSlotChangeHandler}></slot>`); const iconStartEl = html`<calcite-icon class=${safeClassMap(CSS.iconStart)} .flipRtl=${this.iconFlipRtl === "start" || this.iconFlipRtl === "both"} .icon=${this.iconStart} .scale=${getIconScale(this.scale)}></calcite-icon>`; this.el.ariaChecked = this.selectionMode === "multiple" || this.selectionMode === "multichildren" || this.selectionMode === "ancestors" ? toAriaBoolean(this.selected) : void 0; this.el.ariaExpanded = this.hasChildren ? toAriaBoolean(isExpanded) : void 0; this.el.inert = hidden; this.el.ariaLive = "polite"; this.el.ariaSelected = this.selectionMode === "single" || this.selectionMode === "children" || this.selectionMode === "single-persist" ? toAriaBoolean(this.selected) : void 0; this.el.toggleAttribute("calcite-hydrated-hidden", hidden); this.el.role = "treeitem"; setAttribute(this.el, "tabIndex", this.disabled ? -1 : 0); return InteractiveContainer({ disabled: this.disabled, children: html`<div class=${safeClassMap({ [CSS.itemExpanded]: isExpanded })}><div class=${safeClassMap(CSS.nodeAndActionsContainer)}><div class=${safeClassMap({ [CSS.nodeContainer]: true, [CSS_UTILITY.rtl]: rtl })} data-selection-mode=${this.selectionMode ?? nothing}>${chevron}${itemIndicator}${checkbox ? checkbox : null}${this.iconStart ? iconStartEl : null}${checkbox ? html`<label class=${safeClassMap(CSS.checkboxLabel)}>${defaultSlotNode}</label>` : defaultSlotNode}</div><div class=${safeClassMap(CSS.actionsEnd)} .hidden=${!hasEndActions} ${ref(this.actionSlotWrapper)}>${slotNode}</div></div><div class=${safeClassMap({ [CSS.childrenContainer]: true, [CSS_UTILITY.rtl]: rtl })} data-test-id=calcite-tree-children @click=${this.childrenClickHandler} .role=${this.hasChildren ? "group" : void 0}><slot name=${SLOTS.children} @slotchange=${this.handleChildrenSlotChange}></slot></div></div>` }); } } customElement("calcite-tree-item", TreeItem); export { TreeItem };