UNPKG

@limetech/lime-elements

Version:
1,014 lines (1,013 loc) • 39.3 kB
import { MDCTextField } from "@material/textfield"; import { h, Host, } from "@stencil/core"; import { handleKeyboardEvent } from "./chip-set-input-helpers"; import translate from "../../global/translations"; import { getHref, getTarget } from "../../util/link-helper"; import { isEqual } from "lodash-es"; import { createRandomString } from "../../util/random-string"; /** * :::note * **Regarding `click` and `interact` events:** * * The `interact` event is emitted when a chip is interacted with, and is * the recommended way to listen for chip interactions. * * However, if you need to handle clicks differently depending on which chip * was clicked, or whether the click was on a chip or elsewhere, you need to * listen to the native `click` event instead. * * Native `click` events are passed through, and if the click came from * a chip, the chip object is available in the event object under * `<event object>.Lime.chip`. * * Example usage: * ```ts * private handleClick(event: Event) { * if (event && 'Lime' in event && (event.Lime as any).chip) { * if ((event.Lime as { chip: Chip }).chip.href) { * // Chip has href, so let the browser open the link. * return; * } * // handle click on chip without href * } else { * // handle click elsewhere * } * } * ``` * ::: * * @exampleComponent limel-example-chip-set-basic * @exampleComponent limel-example-chip-set-choice * @exampleComponent limel-example-chip-set-filter * @exampleComponent limel-example-chip-set-filter-badge * @exampleComponent limel-example-chip-set-input * @exampleComponent limel-example-chip-set-input-type-with-menu-items * @exampleComponent limel-example-chip-set-input-type-text * @exampleComponent limel-example-chip-set-input-type-search * @exampleComponent limel-example-chip-icon-color * @exampleComponent limel-example-chip-set-image * @exampleComponent limel-example-chip-set-composite */ export class ChipSet { constructor() { /** * List of chips for the set */ this.value = []; /** * True if the chip set should be disabled */ this.disabled = false; /** * For chip-sets of type `input`, set to `true` to disable adding and * removing chips, but allow interaction with existing chips in the set. * For any other types, setting either `readonly` or `disabled` disables * the chip-set. */ this.readonly = false; /** * Set to `true` to indicate that the current value of the input field is * invalid. */ this.invalid = false; /** * For chip-sets of type `input`. Value to use for the `type` attribute on the * input field inside the chip-set. */ this.inputType = 'text'; /** * True if the control requires a value */ this.required = false; /** * Whether the input field should be emptied when the chip-set loses focus. */ this.emptyInputOnBlur = true; /** * Whether the "Clear all" buttons should be shown */ this.clearAllButton = true; /** * For chip-sets of type `input`. When the value is null, no leading icon is used. * Leading icon to show to the far left in the text field */ this.leadingIcon = null; /** * For chip-set of type `input`. Sets delimiters between chips. */ this.delimiter = null; /** * For chip-set of type `input`, defines whether the input field should have autocomplete enabled. * Read more about the `autocomplete` attribute * [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete). */ this.autocomplete = 'off'; /** * Defines the language for translations. * Will translate the translatable strings on the components. For example, the clear all chips label. */ this.language = 'en'; this.editMode = false; this.textValue = ''; this.blurred = false; this.inputChipIndexSelected = null; this.handleKeyDown = handleKeyboardEvent; this.renderContent = (value) => { if (this.type === 'input') { return this.renderInputChips(); } return value.map(this.renderChip); }; this.getValue = () => { return this.value.map((chip) => (Object.assign(Object.assign({}, chip), (this.type && { selected: this.selectedChipIds.includes(chip.id), })))); }; this.floatLabelAbove = () => { if (this.value.length > 0 || this.editMode || this.readonly || this.textValue) { return true; } }; this.hasHelperText = () => { return this.helperText !== null && this.helperText !== undefined; }; this.renderHelperLine = () => { const maxItems = this.maxItems === 1 ? undefined : this.maxItems; if (!maxItems && !this.hasHelperText()) { return; } return (h("limel-helper-line", { length: this.value.length, maxLength: maxItems, helperText: this.helperText, invalid: this.isInvalid() })); }; this.catchInputChipClicks = (chip) => (event) => { /* * We need to add the `chip` to the event object so that the consumer * can get the chip object when the chip is clicked. * This is necessary for the consumer to be able to handle the click * event itself, based on which chip was clicked, or whether the click * was on a chip or elsewhere. The reason the consumer can't just look * at the event target is that that information is hidden by the * shadow DOM. * * See documentation for the `interact` event for more information. */ event.Lime = { chip: chip }; if (this.isSelectableChip(chip)) { this.updateSelectedChipIds(chip); this.change.emit(chip); } this.emitInteraction(chip); }; this.handleRemoveChip = (event) => { this.removeChip(event.detail); }; this.removeChip = (identifier) => { const newValue = this.value.filter((chip) => { return chip.id !== identifier; }); this.change.emit(newValue); }; this.clearAllChipsLabel = () => { return translate.get('chip-set.clear-all', this.language); }; this.labelId = createRandomString(); this.renderChip = this.renderChip.bind(this); this.renderInputChip = this.renderInputChip.bind(this); this.isFull = this.isFull.bind(this); this.handleTextFieldFocus = this.handleTextFieldFocus.bind(this); this.handleInputBlur = this.handleInputBlur.bind(this); this.handleTextInput = this.handleTextInput.bind(this); this.inputFieldOnChange = this.inputFieldOnChange.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); this.inputHidden = this.inputHidden.bind(this); this.handleDeleteAllIconClick = this.handleDeleteAllIconClick.bind(this); this.renderDelimiter = this.renderDelimiter.bind(this); } connectedCallback() { this.initialize(); } initialize() { if (this.value.length > 0) { this.selectedChipIds = this.value .filter((chip) => chip.selected) .map((chip) => chip.id); } } /** * Used to find out whether the chip-set is in edit mode. * * @returns `true` if the chip-set is in edit mode, `false` otherwise. */ async getEditMode() { return this.editMode; } /** * Used to set focus to the chip-set input field. * * @param emptyInput - if `true`, any text in the input is discarded * @returns does not return anything, but methods have to be async */ async setFocus(emptyInput = false) { if (this.disabled || this.readonly) { return; } this.editMode = true; if (emptyInput) { this.textValue = ''; } this.host.shadowRoot.querySelector('input').focus(); this.startEdit.emit(); } /** * Used to empty the input field. Used in conjunction with `emptyInputOnBlur` to let the * consumer control when the input is emptied. * * @returns does not return anything, but methods have to be async */ async emptyInput() { this.syncEmptyInput(); } componentDidLoad() { this.triggerIconColorWarning(this.value); if (this.type === 'input') { this.mdcTextField = new MDCTextField(this.host.shadowRoot.querySelector('.mdc-text-field')); } } componentDidUpdate() { const input = this.host.shadowRoot.querySelector('input'); if (input && this.editMode) { input.focus(); } } disconnectedCallback() { if (this.mdcTextField) { this.mdcTextField.destroy(); } } render() { var _a; const classes = { 'mdc-chip-set': true, 'mdc-text-field--with-trailing-icon': true, disabled: this.disabled || this.readonly, }; if (this.type) { classes[`mdc-chip-set--${this.type}`] = true; } if (this.type === 'input') { Object.assign(classes, { 'mdc-text-field': true, 'mdc-text-field--outlined': true, 'mdc-chip-set--input': true, 'lime-text-field--readonly': this.readonly, 'has-chips': this.value.length > 0, 'has-leading-icon': this.leadingIcon !== null, 'has-clear-all-button': this.clearAllButton, }); } const value = this.getValue(); return (h(Host, { key: 'cc38b12787a887fa3cf6d54412d460fffdc10d37' }, h("limel-notched-outline", { key: '363ee1953a0880a987acbaeb75ad7682c03c26ec', labelId: this.labelId, label: this.label, required: this.required, invalid: this.invalid || this.isInvalid(), disabled: this.disabled, readonly: this.readonly, hasValue: !!((_a = this.value) === null || _a === void 0 ? void 0 : _a.length), hasLeadingIcon: !!this.leadingIcon, hasFloatingLabel: this.floatLabelAbove() }, h("div", Object.assign({ key: 'c05be9685e36d5283791bf30aceb937f8da0c0a0', slot: "content" }, this.getContentProps(), { class: classes }), this.renderContent(value))), this.renderHelperLine())); } getContentProps() { if (this.type === 'input') { return { onClick: this.handleTextFieldFocus, }; } return { role: 'grid', }; } handleChangeChips(newValue, oldValue) { if (isEqual(newValue, oldValue)) { return; } this.syncEmptyInput(); this.initialize(); } renderInputChips() { return [ this.value.map(this.renderInputChip), h("input", { tabIndex: this.disabled || this.readonly ? -1 : 0, type: this.inputType, id: this.labelId, disabled: this.readonly || this.disabled, class: { 'mdc-text-field__input': true, hidden: this.inputHidden(), }, value: this.textValue, onBlur: this.handleInputBlur, onFocus: this.handleTextFieldFocus, onKeyDown: this.handleKeyDown, onInput: this.handleTextInput, // Some browsers emit a change event on input elements, we need to stop // that event from propagating since we are emitting our own change event onChange: this.inputFieldOnChange, placeholder: this.isFull() ? '' : this.searchLabel, readonly: this.isFull(), autocomplete: this.autocomplete }), this.renderLeadingIcon(), this.renderClearAllChipsButton(), ]; } isFull() { return !!this.maxItems && this.value.length >= this.maxItems; } isInvalid() { var _a; if (this.readonly) { // A readonly field can never be invalid. return false; } if (this.invalid) { return true; } if (!this.required) { return false; } if (!this.blurred) { return false; } return !((_a = this.value) === null || _a === void 0 ? void 0 : _a.length); } inputFieldOnChange(event) { event.stopPropagation(); } /** * Enter edit mode when the text field receives focus. When editMode is true, the input element will be visible */ handleTextFieldFocus() { if (this.disabled || this.readonly) { return; } if (this.editMode) { return; } this.editMode = true; this.startEdit.emit(); } /** * Exit edit mode when the input element loses focus. This makes sure the input element does not take up any * additional space when the user it not typing anything */ handleInputBlur() { if (this.emptyInputOnBlur) { this.syncEmptyInput(); } this.editMode = false; this.blurred = true; this.inputChipIndexSelected = null; // This timeout is needed in order to let a new element receive focus setTimeout(() => { this.stopEdit.emit(); }, 0); } syncEmptyInput() { this.textValue = ''; } inputHidden() { var _a; if (this.editMode) { return this.isFull(); } // If there are chips in the picker, hide the input to avoid the input // being placed on a new line and adding ugly space beneath the chips. // If there are no chips, show the input, or the picker will look weird. return !!((_a = this.value) === null || _a === void 0 ? void 0 : _a.length); } handleTextInput(event) { var _a; event.stopPropagation(); this.inputChipIndexSelected = null; this.textValue = event.target.value; this.input.emit((_a = event.target.value) === null || _a === void 0 ? void 0 : _a.trim()); } emitInteraction(chip) { this.interact.emit(chip); } renderChip(chip) { const chipType = this.type === 'filter' ? 'filter' : 'default'; const chipProps = this.getChipProps(chip, chipType); return h("limel-chip", Object.assign({}, chipProps)); } renderInputChip(chip, index, chips) { const chipProps = this.getChipProps(chip, 'default'); const isLastChip = index === chips.length - 1; return [ h("limel-chip", Object.assign({ key: chip.id, class: { 'can-be-removed': this.inputChipIndexSelected === index, } }, chipProps)), !(isLastChip && this.inputHidden()) && this.renderDelimiter(), ]; } getChipProps(chip, chipType) { const removable = this.type === 'input' && chip.removable && !this.readonly; const readonly = this.readonly && this.type !== 'input'; return Object.assign({ role: 'row', identifier: chip.id, text: chip.text, icon: chip.icon, image: chip.image, badge: chip.badge, selected: chip.selected, disabled: this.disabled, loading: chip.loading, readonly: readonly, type: chipType, removable: removable, menuItems: chip.menuItems, onClick: this.catchInputChipClicks(chip), onRemove: this.handleRemoveChip }, (chip.href && { link: { href: getHref(chip.href), target: getTarget(chip.href), }, })); } isSelectableChip(chip) { return this.type !== 'input' && 'selected' in chip; } updateSelectedChipIds(chip) { chip.selected = !chip.selected; const id = chip.id; if (this.type === 'choice') { this.updateChoiceTypeSelectedIds(id); } else { this.updateFilterTypeSelectedIds(id); } } updateChoiceTypeSelectedIds(id) { this.selectedChipIds = this.isChipSelected(id) ? [] : [id]; } isChipSelected(id) { return this.selectedChipIds.includes(id); } updateFilterTypeSelectedIds(id) { if (this.isChipSelected(id)) { this.removeChipIdFromSelectedChipIds(id); } else { this.addChipIdToSelectedChipIds(id); } } removeChipIdFromSelectedChipIds(id) { this.selectedChipIds = this.selectedChipIds.filter((chipId) => chipId !== id); } addChipIdToSelectedChipIds(id) { this.selectedChipIds = [...this.selectedChipIds, id]; } renderLeadingIcon() { if (!this.leadingIcon) { return; } return (h("i", { class: "mdc-text-field__icon search-icon" }, h("limel-icon", { name: this.leadingIcon }))); } renderClearAllChipsButton() { if (this.disabled || this.readonly || !this.clearAllButton) { return; } return (h("a", { href: "", onClick: this.handleDeleteAllIconClick, class: "mdc-text-field__icon clear-all-button", tabindex: "0", role: "button", title: this.clearAllChipsLabel(), "aria-label": this.clearAllChipsLabel() })); } handleDeleteAllIconClick(event) { event.preventDefault(); this.change.emit([]); } renderDelimiter() { if (!this.delimiter) { return; } return h("div", { class: "delimiter" }, this.delimiter); } triggerIconColorWarning(value) { for (const chip of value) { if (chip.icon && (chip.iconFillColor || chip.iconBackgroundColor || chip.iconTitle)) { console.warn("The `iconFillColor`, `iconBackgroundColor`, and `iconTitle` props are deprecated now! Use the new `Icon` interface and instead of `iconColor: 'color-name', `iconBackgroundColor: 'color-name', and `iconTitle: 'title'`, write `icon { name: 'icon-name', color: 'color-name', backgroundColor: 'color-name', title: 'title' }`."); } } } static get is() { return "limel-chip-set"; } static get encapsulation() { return "shadow"; } static get delegatesFocus() { return true; } static get originalStyleUrls() { return { "$": ["chip-set.scss"] }; } static get styleUrls() { return { "$": ["chip-set.css"] }; } static get properties() { return { "value": { "type": "unknown", "mutable": false, "complexType": { "original": "Chip[]", "resolved": "Chip<any>[]", "references": { "Chip": { "location": "import", "path": "../chip-set/chip.types", "id": "src/components/chip-set/chip.types.ts::Chip", "referenceLocation": "Chip" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "List of chips for the set" }, "getter": false, "setter": false, "defaultValue": "[]" }, "type": { "type": "string", "mutable": false, "complexType": { "original": "'choice' | 'filter' | 'input'", "resolved": "\"choice\" | \"filter\" | \"input\"", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Type of chip set\n\n- `choice` renders a set of selectable chips where only one is selectable. The `removable` property is ignored\n- `filter` renders a set of selectable chips where all are selectable.\n- `input` renders a set of chips that can be used in conjunction with an input field\n\nIf no type is set, a basic set of chips without additional functionality will be rendered" }, "getter": false, "setter": false, "reflect": true, "attribute": "type" }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Label for the chip-set" }, "getter": false, "setter": false, "reflect": true, "attribute": "label" }, "helperText": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Optional helper text to display below the chipset.\nWhen type is `input`, the helper text is displayed below the\ninput field when it has focus.\nWhen type is not `input`, the helper text is always displayed\nif the device is touch screen; otherwise it is shown when chip-set\nis hovered or focused using keyboard navigation." }, "getter": false, "setter": false, "reflect": true, "attribute": "helper-text" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "True if the chip set should be disabled" }, "getter": false, "setter": false, "reflect": true, "attribute": "disabled", "defaultValue": "false" }, "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-sets of type `input`, set to `true` to disable adding and\nremoving chips, but allow interaction with existing chips in the set.\nFor any other types, setting either `readonly` or `disabled` disables\nthe chip-set." }, "getter": false, "setter": false, "reflect": true, "attribute": "readonly", "defaultValue": "false" }, "invalid": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to indicate that the current value of the input field is\ninvalid." }, "getter": false, "setter": false, "reflect": true, "attribute": "invalid", "defaultValue": "false" }, "inputType": { "type": "string", "mutable": false, "complexType": { "original": "'search' | 'text'", "resolved": "\"search\" | \"text\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-sets of type `input`. Value to use for the `type` attribute on the\ninput field inside the chip-set." }, "getter": false, "setter": false, "reflect": true, "attribute": "input-type", "defaultValue": "'text'" }, "maxItems": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-sets of type `input`. Limits the maximum number of chips.\nWhen the value is `0` or not set, no limit is applied." }, "getter": false, "setter": false, "reflect": true, "attribute": "max-items" }, "required": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "True if the control requires a value" }, "getter": false, "setter": false, "reflect": true, "attribute": "required", "defaultValue": "false" }, "searchLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Search label to display when type is `input` and component is in search mode" }, "getter": false, "setter": false, "reflect": true, "attribute": "search-label" }, "emptyInputOnBlur": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Whether the input field should be emptied when the chip-set loses focus." }, "getter": false, "setter": false, "reflect": true, "attribute": "empty-input-on-blur", "defaultValue": "true" }, "clearAllButton": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Whether the \"Clear all\" buttons should be shown" }, "getter": false, "setter": false, "reflect": false, "attribute": "clear-all-button", "defaultValue": "true" }, "leadingIcon": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-sets of type `input`. When the value is null, no leading icon is used.\nLeading icon to show to the far left in the text field" }, "getter": false, "setter": false, "reflect": true, "attribute": "leading-icon", "defaultValue": "null" }, "delimiter": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-set of type `input`. Sets delimiters between chips." }, "getter": false, "setter": false, "reflect": true, "attribute": "delimiter", "defaultValue": "null" }, "autocomplete": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "For chip-set of type `input`, defines whether the input field should have autocomplete enabled.\nRead more about the `autocomplete` attribute\n[here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)." }, "getter": false, "setter": false, "reflect": true, "attribute": "autocomplete", "defaultValue": "'off'" }, "language": { "type": "string", "mutable": false, "complexType": { "original": "Languages", "resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"", "references": { "Languages": { "location": "import", "path": "../date-picker/date.types", "id": "src/components/date-picker/date.types.ts::Languages", "referenceLocation": "Languages" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines the language for translations.\nWill translate the translatable strings on the components. For example, the clear all chips label." }, "getter": false, "setter": false, "reflect": false, "attribute": "language", "defaultValue": "'en'" } }; } static get states() { return { "editMode": {}, "textValue": {}, "blurred": {}, "inputChipIndexSelected": {}, "selectedChipIds": {} }; } static get events() { return [{ "method": "interact", "name": "interact", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Dispatched when a chip is interacted with" }, "complexType": { "original": "Chip", "resolved": "Chip<any>", "references": { "Chip": { "location": "import", "path": "../chip-set/chip.types", "id": "src/components/chip-set/chip.types.ts::Chip", "referenceLocation": "Chip" } } } }, { "method": "change", "name": "change", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Dispatched when a chip is selected/deselected" }, "complexType": { "original": "Chip | Chip[]", "resolved": "Chip<any> | Chip<any>[]", "references": { "Chip": { "location": "import", "path": "../chip-set/chip.types", "id": "src/components/chip-set/chip.types.ts::Chip", "referenceLocation": "Chip" } } } }, { "method": "startEdit", "name": "startEdit", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when an input chip set has received focus and editing in the text field has started" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "stopEdit", "name": "stopEdit", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when an input chip set has lost focus and editing in the text field has ended" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "input", "name": "input", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Dispatched when the input is changed for type `input`" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }]; } static get methods() { return { "getEditMode": { "complexType": { "signature": "() => Promise<boolean>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<boolean>" }, "docs": { "text": "Used to find out whether the chip-set is in edit mode.", "tags": [{ "name": "returns", "text": "`true` if the chip-set is in edit mode, `false` otherwise." }] } }, "setFocus": { "complexType": { "signature": "(emptyInput?: boolean) => Promise<void>", "parameters": [{ "name": "emptyInput", "type": "boolean", "docs": "- if `true`, any text in the input is discarded" }], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Used to set focus to the chip-set input field.", "tags": [{ "name": "param", "text": "emptyInput - if `true`, any text in the input is discarded" }, { "name": "returns", "text": "does not return anything, but methods have to be async" }] } }, "emptyInput": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Used to empty the input field. Used in conjunction with `emptyInputOnBlur` to let the\nconsumer control when the input is emptied.", "tags": [{ "name": "returns", "text": "does not return anything, but methods have to be async" }] } } }; } static get elementRef() { return "host"; } static get watchers() { return [{ "propName": "value", "methodName": "handleChangeChips" }]; } }