UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

300 lines (299 loc) • 8.54 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 { h } from "@stencil/core"; import { connectInteractive, disconnectInteractive, updateHostInteraction } from "../../utils/interactive"; import { createObserver } from "../../utils/observers"; import { CSS } from "./resources"; import { connectSortableComponent, disconnectSortableComponent, onSortingStart, onSortingEnd } from "../../utils/sortableComponent"; import { focusElement } from "../../utils/dom"; /** * @slot - A slot for adding sortable items. */ export class SortableList { constructor() { this.items = []; this.mutationObserver = createObserver("mutation", () => { this.setUpSorting(); }); this.dragSelector = undefined; this.group = undefined; this.handleSelector = "calcite-handle"; this.layout = "vertical"; this.disabled = false; this.loading = false; } // -------------------------------------------------------------------------- // // Lifecycle // // -------------------------------------------------------------------------- connectedCallback() { this.setUpSorting(); this.beginObserving(); connectInteractive(this); } disconnectedCallback() { disconnectInteractive(this); disconnectSortableComponent(this); this.endObserving(); } componentDidRender() { updateHostInteraction(this); } calciteHandleNudgeNextHandler(event) { this.handleNudgeEvent(event); } // -------------------------------------------------------------------------- // // Private Methods // // -------------------------------------------------------------------------- handleNudgeEvent(event) { const { direction } = event.detail; const handle = event .composedPath() .find((el) => el.matches(this.handleSelector)); const sortItem = this.items.find((item) => { return item.contains(handle) || event.composedPath().includes(item); }); const lastIndex = this.items.length - 1; const startingIndex = this.items.indexOf(sortItem); let appendInstead = false; let buddyIndex; if (direction === "up") { if (startingIndex === 0) { appendInstead = true; } else { buddyIndex = startingIndex - 1; } } else { if (startingIndex === lastIndex) { buddyIndex = 0; } else if (startingIndex === lastIndex - 1) { appendInstead = true; } else { buddyIndex = startingIndex + 2; } } this.endObserving(); if (appendInstead) { sortItem.parentElement.appendChild(sortItem); } else { sortItem.parentElement.insertBefore(sortItem, this.items[buddyIndex]); } this.items = Array.from(this.el.children); this.beginObserving(); requestAnimationFrame(() => focusElement(handle)); if ("activated" in handle) { handle.activated = true; } } setUpSorting() { const { dragSelector, group, handleSelector } = this; this.items = Array.from(this.el.children); const sortableOptions = { dataIdAttr: "id", group, handle: handleSelector, onStart: () => { this.endObserving(); onSortingStart(this); }, onEnd: () => { onSortingEnd(this); this.beginObserving(); }, onUpdate: () => { this.items = Array.from(this.el.children); this.calciteListOrderChange.emit(); } }; if (dragSelector) { sortableOptions.draggable = dragSelector; } connectSortableComponent(this, sortableOptions); } beginObserving() { this.mutationObserver?.observe(this.el, { childList: true, subtree: true }); } endObserving() { this.mutationObserver?.disconnect(); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- render() { const { layout } = this; const horizontal = layout === "horizontal" || false; return (h("div", { class: { [CSS.container]: true, [CSS.containerVertical]: !horizontal, [CSS.containerHorizontal]: horizontal } }, h("slot", null))); } static get is() { return "calcite-sortable-list"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["sortable-list.scss"] }; } static get styleUrls() { return { "$": ["sortable-list.css"] }; } static get properties() { return { "dragSelector": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Specifies which items inside the element should be draggable." }, "attribute": "drag-selector", "reflect": true }, "group": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The list's group identifier.\n\nTo drag elements from one list into another, both lists must have the same group value." }, "attribute": "group", "reflect": true }, "handleSelector": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The selector for the handle elements." }, "attribute": "handle-selector", "reflect": true, "defaultValue": "\"calcite-handle\"" }, "layout": { "type": "string", "mutable": false, "complexType": { "original": "Layout", "resolved": "\"grid\" | \"horizontal\" | \"vertical\"", "references": { "Layout": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Indicates the horizontal or vertical orientation of the component." }, "attribute": "layout", "reflect": true, "defaultValue": "\"vertical\"" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When true, disabled prevents interaction. This state shows items with lower opacity/grayed." }, "attribute": "disabled", "reflect": true, "defaultValue": "false" }, "loading": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When true, content is waiting to be loaded. This state shows a busy indicator." }, "attribute": "loading", "reflect": true, "defaultValue": "false" } }; } static get events() { return [{ "method": "calciteListOrderChange", "name": "calciteListOrderChange", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Emitted when the order of the list has changed." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get elementRef() { return "el"; } static get listeners() { return [{ "name": "calciteHandleNudge", "method": "calciteHandleNudgeNextHandler", "target": undefined, "capture": false, "passive": false }]; } }