@playcanvas/pcui
Version:
User interface component library for the web
979 lines (976 loc) • 35.5 kB
JavaScript
import { CLASS_FOCUS, CLASS_MULTIPLE_VALUES } from '../../class.mjs';
import { searchItems } from '../../helpers/search.mjs';
import { Button } from '../Button/index.mjs';
import { Container } from '../Container/index.mjs';
import { Element } from '../Element/index.mjs';
import { Label } from '../Label/index.mjs';
import { TextInput } from '../TextInput/index.mjs';
const CLASS_SELECT_INPUT = 'pcui-select-input';
const CLASS_SELECT_INPUT_CONTAINER_VALUE = `${CLASS_SELECT_INPUT}-container-value`;
const CLASS_MULTI_SELECT = `${CLASS_SELECT_INPUT}-multi`;
const CLASS_DISABLED_VALUE = `${CLASS_SELECT_INPUT}-disabled-value`;
const CLASS_HAS_DISABLED_VALUE = `${CLASS_SELECT_INPUT}-has-disabled-value`;
const CLASS_ALLOW_INPUT = 'pcui-select-input-allow-input';
const CLASS_VALUE = `${CLASS_SELECT_INPUT}-value`;
const CLASS_ICON = `${CLASS_SELECT_INPUT}-icon`;
const CLASS_INPUT = `${CLASS_SELECT_INPUT}-textinput`;
const CLASS_LIST = `${CLASS_SELECT_INPUT}-list`;
const CLASS_TAGS = `${CLASS_SELECT_INPUT}-tags`;
const CLASS_TAGS_EMPTY = `${CLASS_SELECT_INPUT}-tags-empty`;
const CLASS_TAG = `${CLASS_SELECT_INPUT}-tag`;
const CLASS_TAG_NOT_EVERYWHERE = `${CLASS_SELECT_INPUT}-tag-not-everywhere`;
const CLASS_SHADOW = `${CLASS_SELECT_INPUT}-shadow`;
const CLASS_FIT_HEIGHT = `${CLASS_SELECT_INPUT}-fit-height`;
const CLASS_SELECTED = 'pcui-selected';
const CLASS_HIGHLIGHTED = `${CLASS_SELECT_INPUT}-label-highlighted`;
const CLASS_CREATE_NEW = `${CLASS_SELECT_INPUT}-create-new`;
const CLASS_OPEN = 'pcui-open';
const DEFAULT_BOTTOM_OFFSET = 25;
/**
* An input that allows selecting from a dropdown or entering tags.
*/
class SelectInput extends Element {
/**
* Creates a new SelectInput.
*
* @param args - The arguments.
*/
constructor(args = {}) {
var _a, _b, _c, _d, _e;
// main container
const container = new Container({
dom: args.dom
});
const elementArgs = Object.assign(Object.assign({}, args), { dom: container.dom });
super(elementArgs);
this._timeoutLabelValueTabIndex = null;
this._valueToText = {};
this._valueToLabel = {};
this._labelToValue = new Map();
this._labelHighlighted = null;
this._disabledOptions = {};
this._prefix = '';
this._onInputChange = (value) => {
if (this._suspendInputChange)
return;
if (this._lastInputValue === value)
return;
this.open();
this._lastInputValue = value;
this._filterOptions(value);
};
this._onInputKeyDown = (evt) => {
if (evt.key === 'Enter' && this.enabled && !this.readOnly) {
evt.stopPropagation();
evt.preventDefault();
// on enter
let value;
if (this._labelHighlighted && this._labelToValue.has(this._labelHighlighted)) {
value = this._labelToValue.get(this._labelHighlighted);
}
else {
value = this._input.value;
}
if (value !== undefined) {
this.focus();
this.close();
if (this._valueToText[value]) {
this._onSelectValue(value);
}
else if (this._allowCreate) {
if (this._createFn) {
this._createFn(value);
}
else {
this._onSelectValue(value);
}
}
return;
}
}
this._onKeyDown(evt);
};
/**
* Handles pointer down events on the document. It has to be the document instead of the window
* because otherwise `pointerdown` is not fired when the PCUI app is running in an iframe.
* @param evt - Pointer event.
*/
this._onDocumentPointerDown = (evt) => {
// Use composedPath to handle clicks in both shadow DOM and regular DOM contexts
if (!this.dom.contains(evt.composedPath()[0])) {
this.close();
}
};
this._onKeyDown = (evt) => {
// close options on ESC and blur
if (evt.key === 'Escape') {
this.close();
return;
}
if (evt.key === 'Tab') {
this.close();
return;
}
if (!this.enabled || this.readOnly)
return;
if (evt.key === 'Enter' && !this._allowInput) {
if (this._labelHighlighted && this._labelToValue.has(this._labelHighlighted)) {
this._onSelectValue(this._labelToValue.get(this._labelHighlighted));
this.close();
}
return;
}
if (evt.key !== 'ArrowUp' && evt.key !== 'ArrowDown') {
return;
}
evt.stopPropagation();
evt.preventDefault();
if ((this._allowInput || this.multiSelect) && this._containerOptions.hidden) {
this.open();
return;
}
if (this._containerOptions.hidden) {
if (!this._options.length)
return;
let index = -1;
for (let i = 0; i < this._options.length; i++) {
if (this._options[i].v === this.value) {
index = i;
break;
}
}
if (evt.key === 'ArrowUp') {
index--;
}
else if (evt.key === 'ArrowDown') {
index++;
}
if (index >= 0 && index < this._options.length) {
this._onSelectValue(this._options[index].v);
}
}
else {
if (!this._containerOptions.dom.childNodes.length)
return;
if (!this._labelHighlighted) {
this._highlightLabel(this._containerOptions.dom.childNodes[0].ui);
}
else {
let highlightedLabelDom = this._labelHighlighted.dom;
do {
if (evt.key === 'ArrowUp') {
highlightedLabelDom = highlightedLabelDom.previousSibling;
}
else if (evt.key === 'ArrowDown') {
highlightedLabelDom = highlightedLabelDom.nextSibling;
}
} while (highlightedLabelDom && highlightedLabelDom.ui.hidden);
if (highlightedLabelDom) {
this._highlightLabel(highlightedLabelDom.ui);
}
}
}
};
this._onPointerDown = () => {
if (!this._allowInput) {
this.focus();
}
};
this._onFocus = () => {
this.class.add(CLASS_FOCUS);
this.emit('focus');
if (!this._input.hidden) {
this.open();
}
};
this._onBlur = () => {
this.class.remove(CLASS_FOCUS);
this.emit('blur');
};
this._onWheel = (evt) => {
// prevent scrolling on other stuff like the viewport
// when we are scrolling on the select input
evt.stopPropagation();
};
this._container = container;
this._container.parent = this;
this.class.add(CLASS_SELECT_INPUT);
this._containerValue = new Container({
class: CLASS_SELECT_INPUT_CONTAINER_VALUE
});
this._container.append(this._containerValue);
// focus / hover shadow element
this._domShadow = document.createElement('div');
this._domShadow.classList.add(CLASS_SHADOW);
this._containerValue.append(this._domShadow);
this._allowInput = args.allowInput;
if (this._allowInput) {
this.class.add(CLASS_ALLOW_INPUT);
}
this._allowCreate = args.allowCreate;
this._createFn = args.createFn;
this._createLabelText = args.createLabelText;
// displays current value
this._labelValue = new Label({
class: CLASS_VALUE,
tabIndex: 0
});
this._labelValue.on('click', () => {
if (this.enabled && !this.readOnly) {
// toggle dropdown list
this.toggle();
}
});
this._containerValue.append(this._labelValue);
// dropdown icon
this._labelIcon = new Label({
class: CLASS_ICON,
hidden: args.allowInput && args.multiSelect
});
this._containerValue.append(this._labelIcon);
// input for searching or adding new entries
this._input = new TextInput({
class: CLASS_INPUT,
blurOnEnter: false,
keyChange: true
});
this._containerValue.append(this._input);
this._lastInputValue = '';
this._suspendInputChange = false;
this._input.on('change', this._onInputChange);
this._input.on('keydown', this._onInputKeyDown);
this._input.on('focus', this._onFocus);
this._input.on('blur', this._onBlur);
if (args.placeholder) {
this.placeholder = args.placeholder;
}
// dropdown list
this._containerOptions = new Container({
class: CLASS_LIST,
hidden: true
});
this._containerValue.append(this._containerOptions);
// tags container
this._containerTags = new Container({
class: CLASS_TAGS,
flex: true,
flexDirection: 'row',
hidden: true
});
this._container.append(this._containerTags);
if (args.multiSelect) {
this.class.add(CLASS_MULTI_SELECT);
this._containerTags.hidden = false;
}
// events
this._labelValue.dom.addEventListener('keydown', this._onKeyDown);
this._labelValue.dom.addEventListener('focus', this._onFocus);
this._labelValue.dom.addEventListener('blur', this._onBlur);
this._labelValue.dom.addEventListener('pointerdown', this._onPointerDown);
this._containerOptions.dom.addEventListener('wheel', this._onWheel, { passive: true });
this.on('hide', () => {
this.close();
});
this._type = (_a = args.type) !== null && _a !== void 0 ? _a : 'string';
this.invalidOptions = (_b = args.invalidOptions) !== null && _b !== void 0 ? _b : [];
this.options = (_c = args.options) !== null && _c !== void 0 ? _c : [];
this._optionsFn = args.optionsFn;
this._allowNull = args.allowNull;
this._values = null;
if (args.value !== undefined) {
this.value = args.value;
}
else if (args.defaultValue) {
this.value = args.defaultValue;
}
else {
this.value = null;
}
this._renderChanges = (_d = args.renderChanges) !== null && _d !== void 0 ? _d : false;
this.on('change', () => {
this._updateInputFieldsVisibility();
if (this.renderChanges && !this.multiSelect) {
this._labelValue.flash();
}
});
this._updateInputFieldsVisibility(false);
this._onSelect = args.onSelect;
this.fallbackOrder = args.fallbackOrder;
this.disabledOptions = args.disabledOptions;
this._prefix = (_e = args.prefix) !== null && _e !== void 0 ? _e : '';
}
destroy() {
if (this._destroyed)
return;
this._labelValue.dom.removeEventListener('keydown', this._onKeyDown);
this._labelValue.dom.removeEventListener('pointerdown', this._onPointerDown);
this._labelValue.dom.removeEventListener('focus', this._onFocus);
this._labelValue.dom.removeEventListener('blur', this._onBlur);
this._containerOptions.dom.removeEventListener('wheel', this._onWheel);
window.removeEventListener('keydown', this._onKeyDown);
document.removeEventListener('pointerdown', this._onDocumentPointerDown);
if (this._timeoutLabelValueTabIndex) {
cancelAnimationFrame(this._timeoutLabelValueTabIndex);
this._timeoutLabelValueTabIndex = null;
}
super.destroy();
}
_initializeCreateLabel() {
const container = new Container({
class: CLASS_CREATE_NEW,
flex: true,
flexDirection: 'row'
});
const label = new Label({
text: this._input.value,
tabIndex: -1
});
container.append(label);
let evtChange = this._input.on('change', (value) => {
// check if label is destroyed
// during change event
if (label.destroyed)
return;
label.text = value;
if (this.invalidOptions && this.invalidOptions.indexOf(value) !== -1) {
if (!container.hidden) {
container.hidden = true;
this._resizeShadow();
}
}
else {
if (container.hidden) {
container.hidden = false;
this._resizeShadow();
}
}
});
container.on('click', (e) => {
e.stopPropagation();
const text = label.text;
this.focus();
this.close();
if (this._createFn) {
this._createFn(text);
}
else if (text) {
this._onSelectValue(text);
}
});
label.on('destroy', () => {
evtChange.unbind();
evtChange = null;
});
const labelCreateText = new Label({
text: this._createLabelText
});
container.append(labelCreateText);
this._containerOptions.append(container);
return container;
}
_convertSingleValue(value) {
if (value === null && this._allowNull)
return value;
if (this._type === 'string') {
if (!value) {
value = '';
}
else {
value = value.toString();
}
}
else if (this._type === 'number') {
if (!value) {
value = 0;
}
else {
value = parseInt(value, 10);
}
}
else if (this._type === 'boolean') {
return !!value;
}
return value;
}
_convertValue(value) {
if (value === null && this._allowNull)
return value;
if (this.multiSelect) {
if (!Array.isArray(value))
return value;
return value.map(val => this._convertSingleValue(val));
}
return this._convertSingleValue(value);
}
// Update our value with the specified selected option
_onSelectValue(value) {
value = this._convertSingleValue(value);
if (!this.multiSelect) {
this.value = value;
return;
}
if (this._values) {
let dirty = false;
this._values.forEach((arr) => {
if (!arr) {
arr = [value];
dirty = true;
}
else {
if (arr.indexOf(value) === -1) {
arr.push(value);
dirty = true;
}
}
});
if (dirty) {
this._onMultipleValuesChange(this._values);
this.emit('change', this.value);
if (this._binding) {
this._binding.addValues([value]);
}
}
}
else {
if (!this._value || !Array.isArray(this._value)) {
this.value = [value];
}
else {
if (this._value.indexOf(value) === -1) {
this._value.push(value);
this._addTag(value);
this.emit('change', this.value);
if (this._binding) {
this._binding.addValues([value]);
}
}
}
}
}
_highlightLabel(label) {
if (this._labelHighlighted === label)
return;
if (this._labelHighlighted) {
this._labelHighlighted.class.remove(CLASS_HIGHLIGHTED);
}
this._labelHighlighted = label;
if (this._labelHighlighted) {
this._labelHighlighted.class.add(CLASS_HIGHLIGHTED);
// scroll into view if necessary
const labelTop = this._labelHighlighted.dom.offsetTop;
const scrollTop = this._containerOptions.dom.scrollTop;
if (labelTop < scrollTop) {
this._containerOptions.dom.scrollTop = labelTop;
}
else if (labelTop + this._labelHighlighted.height > this._containerOptions.height + scrollTop) {
this._containerOptions.dom.scrollTop = labelTop + this._labelHighlighted.height - this._containerOptions.height;
}
}
}
// when the value is changed show the correct title
_onValueChange(value) {
if (!this.multiSelect) {
this._labelValue.value = this._prefix + (this._valueToText[String(value)] || '');
value = String(value);
for (const key in this._valueToLabel) {
const label = this._valueToLabel[key];
if (key === value) {
label.class.add(CLASS_SELECTED);
}
else {
label.class.remove(CLASS_SELECTED);
}
}
}
else {
this._labelValue.value = '';
this._containerTags.clear();
this._containerTags.class.add(CLASS_TAGS_EMPTY);
if (value && Array.isArray(value)) {
for (const val of value) {
this._addTag(val);
const label = this._valueToLabel[String(val)];
if (label) {
label.class.add(CLASS_SELECTED);
}
}
for (const key in this._valueToLabel) {
const label = this._valueToLabel[key];
if (value.indexOf(this._convertSingleValue(key)) !== -1) {
label.class.add(CLASS_SELECTED);
}
else {
label.class.remove(CLASS_SELECTED);
}
}
}
}
}
_onMultipleValuesChange(values) {
this._labelValue.value = '';
this._containerTags.clear();
this._containerTags.class.add(CLASS_TAGS_EMPTY);
const tags = {};
const valueCounts = {};
values.forEach((arr) => {
if (!arr)
return;
arr.forEach((val) => {
if (!tags[val]) {
tags[val] = this._addTag(val);
valueCounts[val] = 1;
}
else {
valueCounts[val]++;
}
});
});
// add special class to tags that do not exist everywhere
for (const val in valueCounts) {
if (valueCounts[val] !== values.length) {
tags[val].class.add(CLASS_TAG_NOT_EVERYWHERE);
const label = this._valueToLabel[String(val)];
if (label) {
label.class.remove(CLASS_SELECTED);
}
}
}
}
_addTag(value) {
const container = new Container({
flex: true,
flexDirection: 'row',
class: CLASS_TAG
});
container.append(new Label({
text: this._valueToText[String(value)] || String(value)
}));
const btnRemove = new Button({
size: 'small',
icon: 'E132',
tabIndex: -1
});
container.append(btnRemove);
btnRemove.on('click', () => this._removeTag(container, value));
this._containerTags.append(container);
this._containerTags.class.remove(CLASS_TAGS_EMPTY);
const label = this._valueToLabel[String(value)];
if (label) {
label.class.add(CLASS_SELECTED);
}
// @ts-ignore
container.value = value;
return container;
}
_removeTag(tagElement, value) {
tagElement.destroy();
const label = this._valueToLabel[String(value)];
if (label) {
label.class.remove(CLASS_SELECTED);
}
if (this._values) {
this._values.forEach((arr) => {
if (!arr)
return;
const idx = arr.indexOf(value);
if (idx !== -1) {
arr.splice(idx, 1);
}
});
}
else if (this._value && Array.isArray(this._value)) {
const idx = this._value.indexOf(value);
if (idx !== -1) {
this._value.splice(idx, 1);
}
}
this.emit('change', this.value);
if (this._binding) {
this._binding.removeValues([value]);
}
}
_filterOptions(filter) {
// first remove all options
// then search the options for best matches
// and add them back in best match order
const containerDom = this._containerOptions.dom;
while (containerDom.firstChild) {
containerDom.removeChild(containerDom.lastChild);
}
if (filter) {
const searchResults = searchItems(this._options, 't', filter);
searchResults.forEach((result) => {
const label = this._valueToLabel[String(result.v)];
containerDom.appendChild(label.dom);
});
}
else {
for (const option of this._options) {
const label = this._valueToLabel[String(option.v)];
containerDom.appendChild(label.dom);
}
}
// append create label in the end
if (this._createLabelContainer) {
containerDom.appendChild(this._createLabelContainer.dom);
}
if (containerDom.firstChild) {
this._highlightLabel(containerDom.firstChild.ui);
}
this._resizeShadow();
}
_resizeShadow() {
this._domShadow.style.height = `${this._containerValue.height + this._containerOptions.height}px`;
}
_updateInputFieldsVisibility(focused) {
let showInput = false;
let focusInput = false;
if (this._allowInput) {
if (focused) {
showInput = true;
focusInput = true;
}
else {
showInput = this.multiSelect || !this._valueToLabel[this.value];
}
}
this._labelValue.hidden = showInput;
this._labelIcon.hidden = showInput;
this._input.hidden = !showInput;
if (focusInput) {
this._input.focus();
}
if (!this._labelValue.hidden) {
// prevent label from being focused
// right after input gets unfocused
this._labelValue.tabIndex = -1;
if (!this._timeoutLabelValueTabIndex) {
this._timeoutLabelValueTabIndex = requestAnimationFrame(() => {
this._timeoutLabelValueTabIndex = null;
this._labelValue.tabIndex = 0;
});
}
}
}
focus() {
if (this._input.hidden) {
this._labelValue.dom.focus();
}
else {
this._input.focus();
}
}
blur() {
if (this._allowInput) {
this._input.blur();
}
else {
this._labelValue.dom.blur();
}
}
/**
* Opens the dropdown menu.
*/
open() {
if (!this._containerOptions.hidden || !this.enabled || this.readOnly)
return;
this._updateInputFieldsVisibility(true);
// auto-update options if necessary
if (this._optionsFn) {
this.options = this._optionsFn();
}
if (this._containerOptions.dom.childNodes.length === 0)
return;
// highlight label that displays current value
this._containerOptions.forEachChild((label) => {
label.hidden = false;
if (this._labelToValue.get(label) === this.value) {
this._highlightLabel(label);
}
});
if (!this._labelHighlighted) {
this._highlightLabel(this._containerOptions.dom.childNodes[0].ui);
}
// show options
this._containerOptions.hidden = false;
this.class.add(CLASS_OPEN);
// register keydown on entire window
window.addEventListener('keydown', this._onKeyDown);
document.addEventListener('pointerdown', this._onDocumentPointerDown);
// if the dropdown list goes below the window show it above the field
const startField = this._allowInput ? this._input.dom : this._labelValue.dom;
const rect = startField.getBoundingClientRect();
let fitHeight = (rect.bottom + this._containerOptions.height + DEFAULT_BOTTOM_OFFSET >= window.innerHeight);
if (fitHeight && rect.top - this._containerOptions.height < 0) {
// if showing it above the field means that some of it will not be visible
// then show it below instead and adjust the max height to the maximum available space
fitHeight = false;
this._containerOptions.style.maxHeight = `${window.innerHeight - rect.bottom - DEFAULT_BOTTOM_OFFSET}px`;
}
if (fitHeight) {
this.class.add(CLASS_FIT_HEIGHT);
}
else {
this.class.remove(CLASS_FIT_HEIGHT);
}
// resize the outer shadow to fit the element and the dropdown list
// we need this because the dropdown list is position: absolute
this._resizeShadow();
}
/**
* Closes the dropdown menu.
*/
close() {
// there is a potential bug here if the user has set a max height
// themselves then this will be overridden
this._containerOptions.style.maxHeight = '';
this._highlightLabel(null);
this._updateInputFieldsVisibility(false);
this._suspendInputChange = true;
this._input.value = '';
if (this._lastInputValue) {
this._lastInputValue = '';
this._filterOptions(null);
}
this._suspendInputChange = false;
if (this._containerOptions.hidden)
return;
this._containerOptions.hidden = true;
this._domShadow.style.height = '';
this.class.remove(CLASS_OPEN);
window.removeEventListener('keydown', this._onKeyDown);
document.removeEventListener('pointerdown', this._onDocumentPointerDown);
}
/**
* Toggles the dropdown menu.
*/
toggle() {
if (this._containerOptions.hidden) {
this.open();
}
else {
this.close();
}
}
unlink() {
super.unlink();
if (!this._containerOptions.hidden) {
this.close();
}
}
_updateValue(value) {
if (value === this._value)
return;
this._value = value;
this._onValueChange(value);
if (!this._suppressChange) {
this.emit('change', value);
}
if (this._binding) {
this._binding.setValue(value);
}
}
_updateDisabledValue(value) {
const labels = {};
this._containerOptions.forEachChild((label) => {
labels[label.dom.id] = label;
if (this._disabledOptions[label.dom.id]) {
label.enabled = false;
label.text = this._disabledOptions[label.dom.id];
}
else {
label.enabled = true;
label.text = this._valueToText[label.dom.id];
}
label.class.remove(CLASS_DISABLED_VALUE);
});
const disabledValue = this._disabledOptions[value] ? value : null;
let newValue = null;
if (disabledValue) {
if (this._fallbackOrder) {
for (let i = 0; i < this._fallbackOrder.length; i++) {
if (this._fallbackOrder[i] === value)
continue;
newValue = this._fallbackOrder[i];
break;
}
}
this.disabledValue = disabledValue;
labels[disabledValue].class.add(CLASS_DISABLED_VALUE);
}
else if (this._disabledValue) {
newValue = this._disabledValue;
this.disabledValue = null;
}
else {
newValue = value;
this.disabledValue = null;
}
return newValue;
}
set options(value) {
if (this._options && JSON.stringify(this._options) === JSON.stringify(value))
return;
this._containerOptions.clear();
this._labelHighlighted = null;
this._valueToText = {};
this._valueToLabel = {};
this._options = value;
// store each option value -> title pair in the optionsIndex
for (const option of this._options) {
this._valueToText[String(option.v)] = option.t;
if (option.v === '')
return;
const label = new Label({
text: option.t,
tabIndex: -1,
id: String(option.v)
});
this._labelToValue.set(label, option.v);
// store labels in an index too
this._valueToLabel[String(option.v)] = label;
// on clicking an option set it as the value and close the dropdown list
label.on('click', (e) => {
e.stopPropagation();
this._onSelectValue(option.v);
this.close();
if (this._onSelect) {
this._onSelect(this.value);
}
});
this._containerOptions.append(label);
}
this._createLabelContainer = null;
if (this._createLabelText) {
this._createLabelContainer = this._initializeCreateLabel();
}
if (this.multiSelect && this._values) {
this._onMultipleValuesChange(this._values);
}
else {
this._onValueChange(this.value);
}
if (this._lastInputValue) {
this._filterOptions(this._lastInputValue);
}
}
get options() {
return this._options.slice();
}
set invalidOptions(value) {
this._invalidOptions = value || null;
}
get invalidOptions() {
return this._invalidOptions;
}
set disabledValue(value) {
this._disabledValue = value;
if (this._disabledValue !== null) {
this.class.add(CLASS_HAS_DISABLED_VALUE);
}
else {
this.class.remove(CLASS_HAS_DISABLED_VALUE);
}
}
set disabledOptions(value) {
if (JSON.stringify(this._disabledOptions) === JSON.stringify(value))
return;
this._disabledOptions = value || {};
const newValue = this._updateDisabledValue(this._value);
this._updateValue(newValue);
}
set fallbackOrder(value) {
this._fallbackOrder = value || null;
}
get multiSelect() {
return this.class.contains(CLASS_MULTI_SELECT);
}
set value(value) {
this._values = null;
this._suspendInputChange = true;
this._input.value = '';
if (this._lastInputValue) {
this._lastInputValue = '';
this._filterOptions(null);
}
this._suspendInputChange = false;
this.class.remove(CLASS_MULTIPLE_VALUES);
value = this._convertValue(value);
if (this._value === value || this.multiSelect && this._value && this._value.equals(value)) {
// if the value is null because we are showing multiple values
// but someone wants to actually set the value of all observers to null
// then make sure we do not return early
if (value !== null || !this._allowNull || !this.class.contains(CLASS_MULTIPLE_VALUES)) {
return;
}
}
this.disabledValue = null;
this._updateValue(value);
}
get value() {
if (!this.multiSelect) {
return this._value;
}
// if multi-select then construct an array
// value from the tags that are currently visible
const result = [];
this._containerTags.dom.childNodes.forEach((dom) => {
// @ts-ignore
result.push(dom.ui.value);
});
return result;
}
/* eslint accessor-pairs: 0 */
set values(values) {
values = values.map((value) => {
return this._convertValue(value);
});
let different = false;
const value = values[0];
const multiSelect = this.multiSelect;
this._values = null;
for (let i = 1; i < values.length; i++) {
if (values[i] !== value && (!multiSelect || !values[i] || !values[i].equals(value))) {
different = true;
break;
}
}
if (different) {
this._labelValue.values = values;
// show all different tags
if (multiSelect) {
this._values = values;
this._value = null;
this._onMultipleValuesChange(this._values);
this.emit('change', this.value);
}
else {
if (this._value !== null) {
this._value = null;
this.emit('change', null);
}
}
this.class.add(CLASS_MULTIPLE_VALUES);
}
else {
this.value = values[0];
}
}
set placeholder(value) {
this._input.placeholder = value;
}
get placeholder() {
return this._input.placeholder;
}
set renderChanges(value) {
this._renderChanges = value;
}
get renderChanges() {
return this._renderChanges;
}
}
Element.register('select', SelectInput, { renderChanges: true });
Element.register('multiselect', SelectInput, { multiSelect: true, renderChanges: true });
Element.register('tags', SelectInput, { allowInput: true, allowCreate: true, multiSelect: true, renderChanges: true });
export { SelectInput };
//# sourceMappingURL=index.mjs.map