@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
266 lines (265 loc) • 18.4 kB
JavaScript
/*! 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 ;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 }::slotted(*):hover{text-decoration:none }::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
};