UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

688 lines (687 loc) • 21.5 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 { componentLoaded, setComponentLoaded, setUpLoadableComponent } from "../../utils/loadable"; import { connectLocalized, disconnectLocalized } from "../../utils/locale"; import { createObserver } from "../../utils/observers"; import { connectMessages, disconnectMessages, setUpMessages, updateMessages } from "../../utils/t9n"; import { calciteInternalListItemValueChangeHandler, calciteListFocusOutHandler, calciteListItemChangeHandler, cleanUpObserver, deselectRemovedItems, deselectSiblingItems, getItemData, handleFilter, handleFilterEvent, handleInitialFilter, initialize, initializeObserver, keyDownHandler, moveItemIndex, mutationObserverCallback, removeItem, selectSiblings, setFocus, setUpItems } from "../pick-list/shared-list-logic"; import List from "../pick-list/shared-list-render"; import { CSS, ICON_TYPES } from "./resources"; import { getHandleAndItemElement, getScreenReaderText } from "./utils"; import { connectSortableComponent, disconnectSortableComponent, onSortingStart, onSortingEnd } from "../../utils/sortableComponent"; import { focusElement } from "../../utils/dom"; /** * @deprecated Use the `list` component instead. * @slot - A slot for adding `calcite-value-list-item` elements. List items are displayed as a vertical list. * @slot menu-actions - A slot for adding a button and menu combination for performing actions, such as sorting. */ export class ValueList { constructor() { this.lastSelectedItem = null; this.mutationObserver = createObserver("mutation", mutationObserverCallback.bind(this)); this.setFilterEl = (el) => { this.filterEl = el; }; this.setFilteredItems = (filteredItems) => { this.filteredItems = filteredItems; }; this.deselectRemovedItems = deselectRemovedItems.bind(this); this.deselectSiblingItems = deselectSiblingItems.bind(this); this.selectSiblings = selectSiblings.bind(this); this.handleFilter = handleFilter.bind(this); this.handleFilterEvent = handleFilterEvent.bind(this); this.getItemData = getItemData.bind(this); this.keyDownHandler = (event) => { if (event.defaultPrevented) { return; } const { handle, item } = getHandleAndItemElement(event); if (handle && !item.handleActivated && event.key === " ") { this.updateScreenReaderText(getScreenReaderText(item, "commit", this)); } if (!handle || !item.handleActivated) { keyDownHandler.call(this, event); return; } event.preventDefault(); const { items } = this; if (event.key === " ") { this.updateScreenReaderText(getScreenReaderText(item, "active", this)); } if ((event.key !== "ArrowUp" && event.key !== "ArrowDown") || items.length <= 1) { return; } const { el } = this; const nextIndex = moveItemIndex(this, item, event.key === "ArrowUp" ? "up" : "down"); if (nextIndex === items.length - 1) { el.appendChild(item); } else { const itemAtNextIndex = el.children[nextIndex]; const insertionReferenceItem = itemAtNextIndex === item.nextElementSibling ? itemAtNextIndex.nextElementSibling : itemAtNextIndex; el.insertBefore(item, insertionReferenceItem); } this.items = this.getItems(); this.calciteListOrderChange.emit(this.items.map(({ value }) => value)); requestAnimationFrame(() => focusElement(handle)); item.handleActivated = true; this.updateHandleAriaLabel(handle, getScreenReaderText(item, "change", this)); }; this.storeAssistiveEl = (el) => { this.assistiveTextEl = el; }; this.handleFocusIn = (event) => { const { handle, item } = getHandleAndItemElement(event); if (!item?.handleActivated && item && handle) { this.updateHandleAriaLabel(handle, getScreenReaderText(item, "idle", this)); } }; this.disabled = false; this.dragEnabled = false; this.filteredItems = []; this.filteredData = []; this.filterEnabled = false; this.filterPlaceholder = undefined; this.filterText = undefined; this.group = undefined; this.loading = false; this.multiple = false; this.selectionFollowsFocus = false; this.messageOverrides = undefined; this.messages = undefined; this.dataForFilter = []; this.defaultMessages = undefined; this.effectiveLocale = ""; this.selectedValues = new Map(); } onMessagesChange() { /* wired up by t9n util */ } effectiveLocaleChange() { updateMessages(this, this.effectiveLocale); } // -------------------------------------------------------------------------- // // Lifecycle // // -------------------------------------------------------------------------- connectedCallback() { connectInteractive(this); connectLocalized(this); connectMessages(this); initialize.call(this); initializeObserver.call(this); this.setUpSorting(); } async componentWillLoad() { setUpLoadableComponent(this); await setUpMessages(this); } componentDidLoad() { setComponentLoaded(this); handleInitialFilter.call(this); } componentDidRender() { updateHostInteraction(this); } disconnectedCallback() { disconnectInteractive(this); disconnectSortableComponent(this); disconnectLocalized(this); disconnectMessages(this); cleanUpObserver.call(this); } calciteListFocusOutHandler(event) { calciteListFocusOutHandler.call(this, event); } calciteListItemRemoveHandler(event) { removeItem.call(this, event); } calciteListItemChangeHandler(event) { calciteListItemChangeHandler.call(this, event); } calciteInternalListItemPropsChangeHandler(event) { event.stopPropagation(); this.setUpFilter(); } calciteInternalListItemValueChangeHandler(event) { calciteInternalListItemValueChangeHandler.call(this, event); event.stopPropagation(); } // -------------------------------------------------------------------------- // // Private Methods // // -------------------------------------------------------------------------- getItems() { return Array.from(this.el.querySelectorAll("calcite-value-list-item")); } setUpItems() { setUpItems.call(this, "calcite-value-list-item"); this.setUpSorting(); } setUpFilter() { if (this.filterEnabled) { this.dataForFilter = this.getItemData(); } } setUpSorting() { const { dragEnabled, group } = this; if (!dragEnabled) { return; } connectSortableComponent(this, { dataIdAttr: "id", group, handle: `.${CSS.handle}`, draggable: "calcite-value-list-item", onStart: () => { cleanUpObserver.call(this); onSortingStart(this); }, onEnd: () => { onSortingEnd(this); initializeObserver.call(this); }, onUpdate: () => { this.items = Array.from(this.el.querySelectorAll("calcite-value-list-item")); const values = this.items.map((item) => item.value); this.calciteListOrderChange.emit(values); } }); } handleBlur() { if (this.dragEnabled) { this.updateScreenReaderText(""); } } // -------------------------------------------------------------------------- // // Public Methods // // -------------------------------------------------------------------------- /** Returns the currently selected items */ async getSelectedItems() { return this.selectedValues; } /** * Sets focus on the component's first focusable element. * * @param focusId */ async setFocus(focusId) { await componentLoaded(this); return setFocus.call(this, focusId); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- getIconType() { let type = null; if (this.dragEnabled) { type = ICON_TYPES.grip; } return type; } updateScreenReaderText(text) { this.assistiveTextEl.textContent = text; } updateHandleAriaLabel(handleElement, text) { handleElement.ariaLabel = text; } handleValueListItemBlur(event) { const { item, handle } = event.detail; if (!item?.handleActivated && item) { this.updateHandleAriaLabel(handle, getScreenReaderText(item, "idle", this)); } event.stopPropagation(); } render() { return (h(List, { onBlur: this.handleBlur, onFocusin: this.handleFocusIn, onKeyDown: this.keyDownHandler, props: this })); } static get is() { return "calcite-value-list"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["value-list.scss"] }; } static get styleUrls() { return { "$": ["value-list.css"] }; } static get assetsDirs() { return ["assets"]; } static get properties() { return { "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, interaction is prevented and the component is displayed with lower opacity." }, "attribute": "disabled", "reflect": true, "defaultValue": "false" }, "dragEnabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, `calcite-value-list-item`s are sortable via a draggable button." }, "attribute": "drag-enabled", "reflect": true, "defaultValue": "false" }, "filteredItems": { "type": "unknown", "mutable": true, "complexType": { "original": "HTMLCalciteValueListItemElement[]", "resolved": "HTMLCalciteValueListItemElement[]", "references": { "HTMLCalciteValueListItemElement": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "readonly", "text": undefined }], "text": "The currently filtered items." }, "defaultValue": "[]" }, "filteredData": { "type": "unknown", "mutable": true, "complexType": { "original": "ItemData", "resolved": "{ label: string; description: string; metadata: Record<string, unknown>; value: string; }[]", "references": { "ItemData": { "location": "import", "path": "../pick-list/shared-list-logic" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "readonly", "text": undefined }], "text": "The currently filtered data." }, "defaultValue": "[]" }, "filterEnabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, an input appears at the top of the component that can be used by end users to filter list items." }, "attribute": "filter-enabled", "reflect": true, "defaultValue": "false" }, "filterPlaceholder": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Placeholder text for the filter's input field." }, "attribute": "filter-placeholder", "reflect": true }, "filterText": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Text for the filter input field." }, "attribute": "filter-text", "reflect": true }, "group": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The component's group identifier.\n\nTo drag elements from one list into another, both lists must have the same group value." }, "attribute": "group", "reflect": true }, "loading": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, a busy indicator is displayed." }, "attribute": "loading", "reflect": true, "defaultValue": "false" }, "multiple": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Similar to standard radio buttons and checkboxes.\nWhen `true`, a user can select multiple `calcite-value-list-item`s at a time.\nWhen `false`, only a single `calcite-value-list-item` can be selected at a time,\nand a new selection will deselect previous selections." }, "attribute": "multiple", "reflect": true, "defaultValue": "false" }, "selectionFollowsFocus": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true` and single-selection is enabled, the selection changes when navigating `calcite-value-list-item`s via keyboard." }, "attribute": "selection-follows-focus", "reflect": true, "defaultValue": "false" }, "messageOverrides": { "type": "unknown", "mutable": true, "complexType": { "original": "Partial<ValueListMessages>", "resolved": "{ dragHandleActive?: string; dragHandleChange?: string; dragHandleCommit?: string; dragHandleIdle?: string; }", "references": { "Partial": { "location": "global" }, "ValueListMessages": { "location": "import", "path": "./assets/value-list/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Use this property to override individual strings used by the component." } }, "messages": { "type": "unknown", "mutable": true, "complexType": { "original": "ValueListMessages", "resolved": "{ dragHandleActive: string; dragHandleChange: string; dragHandleCommit: string; dragHandleIdle: string; }", "references": { "ValueListMessages": { "location": "import", "path": "./assets/value-list/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "Made into a prop for testing purposes only" } } }; } static get states() { return { "dataForFilter": {}, "defaultMessages": {}, "effectiveLocale": {}, "selectedValues": {} }; } static get events() { return [{ "method": "calciteListChange", "name": "calciteListChange", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Emits when any of the list item selections have changed." }, "complexType": { "original": "Map<string, HTMLCalciteValueListItemElement>", "resolved": "Map<string, HTMLCalciteValueListItemElement>", "references": { "Map": { "location": "global" }, "HTMLCalciteValueListItemElement": { "location": "global" } } } }, { "method": "calciteListOrderChange", "name": "calciteListOrderChange", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Emits when the order of the list has changed." }, "complexType": { "original": "any[]", "resolved": "any[]", "references": {} } }, { "method": "calciteListFilter", "name": "calciteListFilter", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Emits when a filter has changed." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "getSelectedItems": { "complexType": { "signature": "() => Promise<Map<string, HTMLCalciteValueListItemElement>>", "parameters": [], "references": { "Promise": { "location": "global" }, "Map": { "location": "global" }, "HTMLCalciteValueListItemElement": { "location": "global" } }, "return": "Promise<Map<string, HTMLCalciteValueListItemElement>>" }, "docs": { "text": "Returns the currently selected items", "tags": [] } }, "setFocus": { "complexType": { "signature": "(focusId?: ListFocusId) => Promise<void>", "parameters": [{ "tags": [{ "name": "param", "text": "focusId" }], "text": "" }], "references": { "Promise": { "location": "global" }, "ListFocusId": { "location": "import", "path": "../pick-list/shared-list-logic" } }, "return": "Promise<void>" }, "docs": { "text": "Sets focus on the component's first focusable element.", "tags": [{ "name": "param", "text": "focusId" }] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "messageOverrides", "methodName": "onMessagesChange" }, { "propName": "effectiveLocale", "methodName": "effectiveLocaleChange" }]; } static get listeners() { return [{ "name": "focusout", "method": "calciteListFocusOutHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteListItemRemove", "method": "calciteListItemRemoveHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteListItemChange", "method": "calciteListItemChangeHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteInternalListItemPropsChange", "method": "calciteInternalListItemPropsChangeHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteInternalListItemValueChange", "method": "calciteInternalListItemValueChangeHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteValueListItemDragHandleBlur", "method": "handleValueListItemBlur", "target": undefined, "capture": false, "passive": false }]; } }