UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

256 lines (255 loc) • 9.49 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 { D as DEBOUNCE, c as customElement } from "../../chunks/runtime.js"; import { debounce } from "lodash-es"; import { html } from "lit"; import { LitElement, createEvent, safeClassMap } from "@arcgis/lumina"; import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js"; import { c as createObserver } from "../../chunks/observers.js"; import { d as disconnectSortableComponent, c as connectSortableComponent } from "../../chunks/sortableComponent.js"; import { c as componentFocusable } from "../../chunks/component.js"; import { h as focusFirstTabbable, y as getRootNode } from "../../chunks/dom.js"; import { g as guid } from "../../chunks/guid.js"; import { css } from "@lit/reactive-element/css-tag.js"; function isBlock(element) { return element.tagName === "CALCITE-BLOCK"; } const CSS = { container: "container", groupContainer: "group-container", scrim: "scrim", assistiveText: "assistive-text" }; const blockGroupSelector = "calcite-block-group"; const blockSelector = "calcite-block"; 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{display:block}.container{position:relative}.assistive-text{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host([hidden]){display:none}[hidden]{display:none}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}`; function updateBlockChildren(slotEl) { const blockChildren = slotEl.assignedElements({ flatten: true }).filter((el) => el.matches(blockSelector)); blockChildren.forEach((block) => { block.setPosition = blockChildren.indexOf(block) + 1; block.setSize = blockChildren.length; }); } class BlockGroup extends LitElement { constructor() { super(); this.dragSelector = blockSelector; this.handleSelector = "calcite-sort-handle"; this.mutationObserver = createObserver("mutation", () => { this.updateBlockItems(); }); this.updateBlockItems = debounce(() => { this.updateGroupItems(); const { dragEnabled, el, moveToItems } = this; const items = Array.from(this.el.querySelectorAll(blockSelector)); items.forEach((item) => { if (item.closest(blockGroupSelector) === el) { item.moveToItems = moveToItems.filter((moveToItem) => moveToItem.element !== el && !item.contains(moveToItem.element)); item.dragHandle = dragEnabled; } }); this.setUpSorting(); }, DEBOUNCE.nextTick); this.moveToItems = []; this.disabled = false; this.dragEnabled = false; this.loading = false; this.calciteBlockGroupDragEnd = createEvent({ cancelable: false }); this.calciteBlockGroupDragStart = createEvent({ cancelable: false }); this.calciteBlockGroupOrderChange = createEvent({ cancelable: false }); this.calciteBlockGroupMoveHalt = createEvent({ cancelable: false }); this.listen("calciteInternalAssistiveTextChange", this.handleCalciteInternalAssistiveTextChange); this.listen("calciteSortHandleReorder", this.handleSortReorder); this.listen("calciteSortHandleMove", this.handleSortMove); } static { this.properties = { assistiveText: [16, {}, { state: true }], moveToItems: [16, {}, { state: true }], canPull: [0, {}, { attribute: false }], canPut: [0, {}, { attribute: false }], disabled: [7, {}, { reflect: true, type: Boolean }], dragEnabled: [7, {}, { reflect: true, type: Boolean }], group: [3, {}, { reflect: true }], label: 1, loading: [7, {}, { reflect: true, type: Boolean }] }; } static { this.styles = styles; } async setFocus() { await componentFocusable(this); focusFirstTabbable(this.el); } putFailed(dragDetail) { this.calciteBlockGroupMoveHalt.emit(dragDetail); } connectedCallback() { super.connectedCallback(); this.connectObserver(); this.updateBlockItems(); this.setUpSorting(); this.setParentBlockGroup(); } willUpdate(changes) { if (changes.has("group") || changes.has("dragEnabled") && (this.hasUpdated || this.dragEnabled !== false)) { this.updateBlockItems(); } } updated() { updateHostInteraction(this); } disconnectedCallback() { super.disconnectedCallback(); this.disconnectObserver(); disconnectSortableComponent(this); } updateGroupItems() { const { el, group } = this; const rootNode = getRootNode(el); const blockGroups = group ? Array.from(rootNode.querySelectorAll(`${blockGroupSelector}[group="${group}"]`)).filter((blockGroup) => !blockGroup.disabled && blockGroup.dragEnabled) : []; this.moveToItems = blockGroups.map((element) => ({ element, label: element.label ?? element.id, id: guid() })); } handleCalciteInternalAssistiveTextChange(event) { this.assistiveText = event.detail.message; event.stopPropagation(); } handleSortReorder(event) { if (this.parentBlockGroupEl || event.defaultPrevented) { return; } event.preventDefault(); this.handleReorder(event); } handleSortMove(event) { if (this.parentBlockGroupEl || event.defaultPrevented) { return; } event.preventDefault(); this.handleMove(event); } connectObserver() { this.mutationObserver?.observe(this.el, { childList: true, subtree: true }); } disconnectObserver() { this.mutationObserver?.disconnect(); } setUpSorting() { const { dragEnabled } = this; if (!dragEnabled) { return; } connectSortableComponent(this); } onGlobalDragStart() { this.disconnectObserver(); } onGlobalDragEnd() { this.connectObserver(); } onDragEnd(detail) { this.calciteBlockGroupDragEnd.emit(detail); } onDragStart(detail) { detail.dragEl.sortHandleOpen = false; this.calciteBlockGroupDragStart.emit(detail); } onDragSort(detail) { this.setParentBlockGroup(); this.updateBlockItems(); this.calciteBlockGroupOrderChange.emit(detail); } setParentBlockGroup() { this.parentBlockGroupEl = this.el.parentElement?.closest(blockGroupSelector); } handleDefaultSlotChange(event) { updateBlockChildren(event.target); } handleMove(event) { const { moveTo } = event.detail; const dragEl = event.target; const fromEl = dragEl?.parentElement; const toEl = moveTo.element; const fromElItems = Array.from(fromEl.children).filter(isBlock); const oldIndex = fromElItems.indexOf(dragEl); const newIndex = 0; if (!fromEl) { return; } if (fromEl.canPull?.({ toEl, fromEl, dragEl, newIndex, oldIndex }) === false) { this.calciteBlockGroupMoveHalt.emit({ toEl, fromEl, dragEl, oldIndex, newIndex }); return; } if (toEl.canPut?.({ toEl, fromEl, dragEl, newIndex, oldIndex }) === false) { toEl.putFailed({ toEl, fromEl, dragEl, oldIndex, newIndex }); return; } dragEl.sortHandleOpen = false; this.disconnectObserver(); toEl.prepend(dragEl); this.updateBlockItems(); this.connectObserver(); this.calciteBlockGroupOrderChange.emit({ dragEl, fromEl, toEl, newIndex, oldIndex }); } handleReorder(event) { const { reorder } = event.detail; const dragEl = event.target; const parentEl = dragEl?.parentElement; if (!parentEl) { return; } dragEl.sortHandleOpen = false; const sameParentItems = Array.from(parentEl.children).filter(isBlock); const lastIndex = sameParentItems.length - 1; const oldIndex = sameParentItems.indexOf(dragEl); let newIndex = oldIndex; switch (reorder) { case "top": newIndex = 0; break; case "bottom": newIndex = lastIndex; break; case "up": newIndex = oldIndex === 0 ? 0 : oldIndex - 1; break; case "down": newIndex = oldIndex === lastIndex ? lastIndex : oldIndex + 1; break; } this.disconnectObserver(); const referenceEl = reorder === "up" || reorder === "top" ? sameParentItems[newIndex] : sameParentItems[newIndex].nextSibling; parentEl.insertBefore(dragEl, referenceEl); this.updateBlockItems(); this.connectObserver(); this.calciteBlockGroupOrderChange.emit({ dragEl, fromEl: parentEl, toEl: parentEl, newIndex, oldIndex }); } render() { const { loading, label } = this; return InteractiveContainer({ disabled: this.disabled, children: html`<div class=${safeClassMap(CSS.container)}>${this.dragEnabled ? html`<span aria-live=assertive class=${safeClassMap(CSS.assistiveText)}>${this.assistiveText}</span>` : null}${loading ? html`<calcite-scrim class=${safeClassMap(CSS.scrim)} .loading=${loading}></calcite-scrim>` : null}<div .ariaBusy=${loading} .ariaLabel=${label || ""} class=${safeClassMap(CSS.groupContainer)} role=group><slot @slotchange=${this.handleDefaultSlotChange}></slot></div></div>` }); } } customElement("calcite-block-group", BlockGroup); export { BlockGroup };