UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

338 lines (337 loc) • 9.65 kB
/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. */ import { Component, Element, Event, Prop, h, Method, Fragment, Watch } from "@stencil/core"; import { debounce, forIn } from "lodash-es"; import { CSS, ICONS, TEXT } from "./resources"; import { focusElement } from "../../utils/dom"; import { updateHostInteraction } from "../../utils/interactive"; const filterDebounceInMs = 250; export class Filter { constructor() { // -------------------------------------------------------------------------- // // Properties // // -------------------------------------------------------------------------- /** * The items to filter through. The filter uses this as the starting point, and returns items * that contain the string entered in the input, using a partial match and recursive search. * * This property is required. */ this.items = []; /** * When true, disabled prevents interaction. This state shows items with lower opacity/grayed. */ this.disabled = false; /** * The resulting items after filtering. * * @readonly */ this.filteredItems = []; /** specify the scale of filter, defaults to m */ this.scale = "m"; /** * Filter value. */ this.value = ""; // -------------------------------------------------------------------------- // // Private Methods // // -------------------------------------------------------------------------- this.filter = debounce((value) => { const regex = new RegExp(value, "i"); if (this.items.length === 0) { this.updateFiltered([]); return; } const find = (input, RE) => { let found = false; forIn(input, (val) => { if (typeof val === "function") { return; } if (Array.isArray(val) || (typeof val === "object" && val !== null)) { if (find(val, RE)) { found = true; } } else if (RE.test(val)) { found = true; } }); return found; }; const result = this.items.filter((item) => { return find(item, regex); }); this.updateFiltered(result); }, filterDebounceInMs); this.inputHandler = (event) => { const target = event.target; this.value = target.value; }; this.keyDownHandler = ({ key }) => { if (key === "Escape") { this.clear(); } }; this.clear = () => { this.value = ""; this.setFocus(); }; } watchItemsHandler() { this.filter(this.value); } valueHandler(value) { this.filter(value); } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- componentDidRender() { updateHostInteraction(this); } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- componentWillLoad() { this.filter(this.value); } // -------------------------------------------------------------------------- // // Public Methods // // -------------------------------------------------------------------------- /** Sets focus on the component. */ async setFocus() { focusElement(this.textInput); } updateFiltered(filtered) { this.filteredItems.length = 0; this.filteredItems = this.filteredItems.concat(filtered); this.calciteFilterChange.emit(); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- render() { const { disabled, scale } = this; return (h(Fragment, null, h("div", { class: CSS.container }, h("label", null, h("calcite-input", { "aria-label": this.intlLabel || TEXT.filterLabel, disabled: disabled, icon: ICONS.search, onCalciteInputInput: this.inputHandler, onKeyDown: this.keyDownHandler, placeholder: this.placeholder, ref: (el) => { this.textInput = el; }, scale: scale, type: "text", value: this.value })), this.value ? (h("button", { "aria-label": this.intlClear || TEXT.clear, class: CSS.clearButton, disabled: disabled, onClick: this.clear }, h("calcite-icon", { icon: ICONS.close, scale: scale }))) : null))); } static get is() { return "calcite-filter"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["filter.scss"] }; } static get styleUrls() { return { "$": ["filter.css"] }; } static get properties() { return { "items": { "type": "unknown", "mutable": true, "complexType": { "original": "object[]", "resolved": "object[]", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The items to filter through. The filter uses this as the starting point, and returns items\nthat contain the string entered in the input, using a partial match and recursive search.\n\nThis property is required." }, "defaultValue": "[]" }, "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" }, "filteredItems": { "type": "unknown", "mutable": true, "complexType": { "original": "object[]", "resolved": "object[]", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "readonly", "text": undefined }], "text": "The resulting items after filtering." }, "defaultValue": "[]" }, "intlClear": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "A text label that will appear on the clear button." }, "attribute": "intl-clear", "reflect": false }, "intlLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "A text label that will appear next to the input field." }, "attribute": "intl-label", "reflect": false }, "placeholder": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Placeholder text for the input element's placeholder attribute" }, "attribute": "placeholder", "reflect": false }, "scale": { "type": "string", "mutable": false, "complexType": { "original": "Scale", "resolved": "\"l\" | \"m\" | \"s\"", "references": { "Scale": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "specify the scale of filter, defaults to m" }, "attribute": "scale", "reflect": true, "defaultValue": "\"m\"" }, "value": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Filter value." }, "attribute": "value", "reflect": false, "defaultValue": "\"\"" } }; } static get events() { return [{ "method": "calciteFilterChange", "name": "calciteFilterChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "This event fires when the filter text changes." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "setFocus": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Sets focus on the component.", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "items", "methodName": "watchItemsHandler" }, { "propName": "value", "methodName": "valueHandler" }]; } }