@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
688 lines (687 loc) • 21.5 kB
JavaScript
/*!
* 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
}];
}
}