@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
256 lines (255 loc) • 9.49 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 { 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
};