/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Build, Host, } from "@stencil/core";
import { h } from "../../utils/renderer";
import { getActiveElement } from "../../utils/active-element";
import { closestElement } from "../../utils/dom";
import { debounceEvent } from "../../utils/throttle";
import { FormControl, FormControlWrap } from "../form-control/form-control";
let selectIds = 0;
/**
 * Selects allow users to choose items from a menu of options.
 *
 * @status stable
 * @version 1.0.0
 *
 * @slot start - suitable for inline action buttons or icons that may add extra contextual information
 * @slot end - suitable for inline action buttons or icons that may add extra contextual information
 * @slot value-start - similar to 'start', however clicking elements using this slot will activate the input control. This would be suitable to show a secondary 'value' for context or to show a dropdown arrow if using a datalist
 * @slot value-end - similar to 'end', however clicking elements using this slot will activate the input control. This would be suitable to show a secondary 'value' for context or to show a dropdown arrow if using a datalist
 * @slot label - if you do not set a label attribute, you can use this slot for more complex markup
 * @slot helper - helper text to accompany the form field underneath.
 * @slot helper-end - helper text to accompany the form field on the side.
 * @slot down-arrow - use this to replace the default down arrow
 * @slot - default slot; nest `nano-option` elements
 */
export class Select {
    nativeSelect;
    valueItems = [];
    dragVal;
    mo;
    selectId = `nano-select-${selectIds++}`;
    rtl = false;
    onInit = true;
    customError = '';
    get inputCtrl() {
        return this._inputCtrl;
    }
    set inputCtrl(input) {
        if (input === this._inputCtrl)
            return;
        this._inputCtrl = input;
        this.setDataListOpts();
    }
    _inputCtrl;
    // we don't want these rendered eles decorated with @State
    // because that will cause re-renders. Use get/set to set datalist options
    _selectWrap;
    get selectWrap() {
        return this._selectWrap;
    }
    set selectWrap(ele) {
        if (this._selectWrap === ele)
            return;
        this._selectWrap = ele;
        this.setDataListOpts();
    }
    _datalist;
    get datalist() {
        return this._datalist;
    }
    set datalist(ele) {
        if (this._datalist === ele)
            return;
        this._datalist = ele;
        this.setDataListOpts();
    }
    currInsertIndex = -1;
    showErrorMsg = false;
    errorMessage = '';
    hasFocus = false;
    hasOpened = false;
    hasLabelSlot = false;
    hasHelperSlot = false;
    hasHelperEndSlot = false;
    inputSearchVal = '';
    el;
    /**
     * This will be true when the control is in an invalid state.
     * Validity is determined by the `required` prop. Or if custom validity message is set.
     */
    get invalid() {
        return this._invalid;
    }
    _invalid = null;
    /**
     * Current validation message - if any. @readonly
     */
    get validityMessage() {
        if (!this.nativeSelect)
            return '';
        return this.nativeSelect.validationMessage;
    }
    /**
     * This Boolean attribute lets you specify that a form control should have select focus when the page loads.
     */
    autofocus = false;
    /**
     * If `true`, the user cannot interact with the select.
     */
    disabled = false;
    /**
     * When should the field perform validation
     * @default 'submitThenDirty'
     */
    validateOn = 'submitThenDirty';
    /**
     * Whether to show validation errors underneath input
     */
    showInlineError = true;
    _label = '';
    /**
     * String to place within a label element. Alternatively you may use a label slot.
     */
    get label() {
        return this.required && !this._label.endsWith('*')
            ? this._label + ' *'
            : this._label;
    }
    set label(value) {
        this._label = value;
    }
    /**
     * Visually hide the label - but make it accessible.
     */
    hideLabel = false;
    /**
     * Enable floating label behaviour. Will disable placeholder if set.
     */
    floatLabel = false;
    /**
     * If `true`, the user can enter more than one value. This attribute applies when the type attribute is set to `"email"` or `"file"`, otherwise it is ignored.
     */
    multiple = false;
    /** When `multiple="true"`, control how many values to show within the control at once.
     * After the maximum, "+n" will be shown to indicate the number of additional items that are selected. */
    maxValuesVisible = 3;
    _maxValuesVisible;
    watchMaxValuesVisible() {
        if (!this.multiple)
            return;
        this._maxValuesVisible =
            this.maxValuesVisible === false ? NaN : Number(this.maxValuesVisible);
        // make sure maxValuesVisible is a number
        if (!Number.isNaN(this._maxValuesVisible)) {
            if (this.currInsertIndex > this._maxValuesVisible) {
                // make the input position is no more than the max
                this.currInsertIndex = this._maxValuesVisible;
            }
        }
        else {
            // if maxValuesVisible is undefined, it will be `NaN`
            this.currInsertIndex = this.value.length - 1;
        }
    }
    /**
     * The name of the control, which is submitted with the form data.
     */
    name = this.selectId + '-name';
    /**
     * Instructional text that shows before the select has a value. Text set here will be placed inside the label when a value is set.
     * If you don't want this behaviour just set the first option to have no value.
     */
    placeholder;
    /**
     * If `true`, the user cannot modify the value.
     */
    readonly = false;
    /**
     * If `true`, the user must fill in a value before submitting a form.
     * By default, setting this will also add an asterisk to the label.
     * You can disable this by using the `label` slot instead.
     */
    required = false;
    /**
     * The initial size of the control.
     */
    size;
    /**
     * Maximum number of options that can be selected when multiple is true
     */
    max;
    /**
     * Minimum number of options that can be selected when multiple is true
     */
    min;
    /**
     * The form element to associate the select with (its form owner). Must be the id of a form.
     */
    form;
    /**
     * in multiple mode, allow users to enter their own values
     */
    allowCustomValues = false;
    /**
     * The value of the select.
     */
    get value() {
        return this._value || (this.multiple ? [] : '');
    }
    set value(val) {
        if (!!val && !!this._value) {
            if (JSON.stringify(val) === JSON.stringify(this._value))
                return;
        }
        if (typeof val === 'string') {
            if (this.multiple)
                this._value = val.split(',').map((v) => v.trim());
            else
                this._value = val;
        }
        else if (Array.isArray(val)) {
            if (this.multiple)
                this._value = val;
            else
                this._value = val[0];
        }
    }
    _value;
    /**
     * Update the native select element when the value changes
     */
    valueChanged() {
        this.customValidate();
        if (this.onInit) {
            if (this.multiple) {
                this.currInsertIndex = this.value.length - 1;
            }
            return;
        }
        requestAnimationFrame(() => {
            this.shouldValidate();
            this.nanoChange.emit({ value: this.value });
        });
    }
    /**
     * You can set options via js as an array of strings: `domElement.options = ['option 1', 'option 2']` or
     * objects `domElement.options = [{label: 'option 1', value: 'opt-1'}]`. See `nano-option` docs for all available properties.
     * Alternatively, you can nest `<nano-option>` elements within a `<nano-select>`
     * Regardless - reading `options` will return the current component options - slotted or otherwise
     */
    get options() {
        if (this._options.length)
            return this._options;
        if (!this.datalist || !this.datalist.activeOptions)
            return [];
        return this._elementOptions.map((ao) => {
            const { value, selected, label, filterMeta } = ao;
            return { value, selected, label, filterMeta };
        });
    }
    set options(opts) {
        this._options = opts.map((opt) => {
            if (typeof opt === 'string')
                return { value: opt, label: opt };
            else
                return opt;
        });
    }
    _options = [];
    _elementOptions = [];
    /**
     * If `true`, a clear icon will appear in the select when there is a value. Clicking it clears the select.
     */
    clearable = false;
    /**
     * Hide the native select element and cover with a value 'mask'.
     * Enables a fluid nano-select width upon value change (if required). Doesn't work with multiple="true".
     */
    mask = false;
    /**
     * Set the amount of time, in milliseconds, to wait to trigger the `nanoSearchChange` event after each keystroke.
     */
    debounce = 0;
    debounceChanged() {
        this.nanoSearchChange = debounceEvent(this.nanoSearchChange, this.debounce);
    }
    shouldValidate() {
        if (this.onInit)
            return;
        if (this.invalid)
            this.showErrorMsg = this._invalid = false;
        this.customValidate();
        if (this.validateOn !== 'dirty')
            return;
        this.showInlineValidation();
    }
    /** nano-dropdown config options you can pass to the nested dropdown component */
    dropDownConfig = {};
    setDataListOpts() {
        if (!this.datalist || !this.selectWrap)
            return;
        this.datalist.input = this.inputCtrl;
        const currDWConfig = this.datalist.dropDownConfig || {};
        this.datalist.dropDownConfig = {
            distance: 10,
            ...currDWConfig,
            ...this.dropDownConfig,
            tetherTo: this.selectWrap,
        };
    }
    /**
     * A function that customizes the tag value to be rendered when `multiple="true"`.
     * @param option - the incoming option that was selected
     * @param index - the incoming option index
     * @returns Should return either a string / templateResult or a string containing trusted HTML or an HTMLElement
     */
    valueTagTemplate;
    // Custom Events
    /**
     * Emitted when the value has changed.
     */
    nanoChange;
    /**
     * Emitted when the select loses focus.
     */
    nanoBlur;
    /**
     * Emitted when the select has focus.
     */
    nanoFocus;
    /**
     * Emitted when the select has been created.
     * @internal
     */
    nanoDidLoad;
    /**
     * Emitted when the select has been removed.
     * @internal
     */
    nanoDidUnload;
    /**
     * Emitted when a keyboard input occurred on a multiple="true" input
     */
    nanoSearchChange;
    /**
     * Called when validation is performed (which depends on `validateOn`).
     * @returns event.detail: `{ isValid: boolean, errorMessage: string, originalEvent: Event }`.
     */
    nanoValidate;
    // Public Methods
    /**
     * Get the current state of the control.
     * @param validateFirst - perform validation first before reporting
     * @returns `{ isValid: boolean, errorMessage: string }`
     */
    async reportValidity(validateFirst) {
        return new Promise((resolve) => {
            if (validateFirst) {
                this.customValidate();
                this.showInlineValidation();
            }
            setTimeout(() => {
                resolve({
                    isValid: !this._invalid,
                    errorMessage: this.nativeSelect.validationMessage,
                });
            }, 50);
        });
    }
    /**
     * Sets focus on the specified `nano-select`. Use this method instead of the global
     * `select.focus()`.
     */
    async setFocus() {
        if (!this.inputCtrl)
            return;
        this.inputCtrl.focus();
        setTimeout(() => this.inputCtrl.click(), 50);
    }
    /**
     * @returns the native `<select>` element used under the hood.
     */
    getSelectElement() {
        return Promise.resolve(this.nativeSelect);
    }
    /**
     * Invalidate the field and show a custom error message.
     * To clear the error you will need to re-call this method with an empty string.
     */
    async showError(message) {
        if (this.nativeSelect) {
            this.customError = message;
            this.nativeSelect.setCustomValidity(message);
            this.showInlineValidation();
        }
    }
    /* Logic */
    get valArray() {
        return typeof this.value === 'string'
            ? this.value.length
                ? [this.value]
                : []
            : this.value;
    }
    isValidValues() {
        return this.valArray.find((val) => this.options.find((opt) => opt.value === val));
    }
    customValidate = () => {
        if (!this.nativeSelect ||
            !!this.customError ||
            !this.nativeSelect['setCustomValidity'])
            return;
        this.nativeSelect.setCustomValidity('');
        // add custom validations 'cos html5 validations are a bit rubbish on selects
        if (this.required && !this.valArray.length) {
            this.nativeSelect.setCustomValidity('Please fill in this field.');
            return true;
        }
        if (this.valArray.length &&
            !this.allowCustomValues &&
            !this.isValidValues()) {
            this.nativeSelect.setCustomValidity('Please choose an item from this field.');
            return true;
        }
        if (this.max && this.valArray.length > this.max) {
            this.nativeSelect.setCustomValidity(`Only up to ${this.max} values are allowed.`);
            return true;
        }
        if (this.min && this.valArray.length < this.min) {
            this.nativeSelect.setCustomValidity(`You must select a minimum of ${this.min} values.`);
            return true;
        }
        return false;
    };
    showInlineValidation = (ev) => {
        if (this.validateOn === 'submitThenDirty')
            this.validateOn = 'dirty';
        this._invalid = false;
        this.showErrorMsg = false;
        this.errorMessage = '';
        if (!this.nativeSelect.validity.valid) {
            this.errorMessage = this.nativeSelect.validationMessage;
            this._invalid = true;
            this.showErrorMsg = true;
        }
        this.nanoValidate.emit({
            isValid: !this.invalid,
            errorMessage: this.errorMessage,
            originalEvent: ev,
        });
    };
    handleInvalid = (ev) => {
        this._invalid = true;
        if (this.validateOn === 'submit') {
            this._invalid = this.showErrorMsg = this.customValidate();
        }
        if (this.showInlineError)
            ev.preventDefault();
        requestAnimationFrame(() => this.showInlineValidation(ev));
    };
    setValue = (e) => {
        e.preventDefault();
        if (!this.multiple) {
            this.value = e.detail.value;
            return;
        }
        if (this.value && this.value.length && this.value.includes(e.detail.value))
            return;
        if (this.max && this.value.length === this.max)
            return;
        if (Number.isNaN(this._maxValuesVisible) ||
            this.value.length <= this._maxValuesVisible) {
            this.currInsertIndex++;
        }
        this.value = [
            ...this.value.slice(0, this.currInsertIndex),
            e.detail.value,
            ...this.value.slice(this.currInsertIndex),
        ];
        this.inputSearchVal = '';
    };
    removeValue = (toFind) => {
        if (this.readonly || this.disabled)
            return;
        if (!this.multiple || !this.value.length)
            return;
        if (!toFind)
            toFind = this.value[this.value.length - 1];
        this.value = this.value.filter((val) => val !== toFind);
        if (Number.isNaN(this._maxValuesVisible) ||
            this.value.length <= this._maxValuesVisible) {
            this.currInsertIndex--;
        }
        this.setFocus();
    };
    slotChangeObserver() {
        if (Build.isServer)
            return;
        const mo = (this.mo = new MutationObserver(() => this.processSlottedContent()));
        mo.observe(this.el, { childList: true, subtree: true });
    }
    processSlottedContent() {
        // see if we have label / helper content
        this.hasLabelSlot = !!this.el.querySelectorAll('[slot="label"]').length;
        this.hasHelperSlot = !!this.el.querySelector('[slot="helper"]');
        this.hasHelperEndSlot = !!this.el.querySelector('[slot="helper-end"]');
    }
    getOptionWithValue(value) {
        let index;
        const options = this.datalist?.activeOptions?.length
            ? this.datalist.activeOptions
            : this.options;
        const option = options.find((opt, i) => {
            index = i;
            return !opt.disabled && opt.value?.length && opt.value === value;
        });
        return [option, index];
    }
    getLabel(toFind) {
        const option = this.getOptionWithValue(toFind)[0];
        return option && option.label ? option.label : toFind;
    }
    /* Event handling */
    onReset(e) {
        const form = this.form
            ? document.querySelector('#' + this.form)
            : this.el.closest('form');
        if (!form || e.target !== this.el.closest('form'))
            return;
        this.clearSelectValue();
    }
    handleBlur() {
        if (!this.hasFocus)
            return;
        requestAnimationFrame(() => {
            const target = getActiveElement();
            if (closestElement(this.el.tagName.toLowerCase(), target) !== this.el) {
                this.onBlur();
            }
        });
    }
    setOptions = () => {
        if (!this.datalist ||
            !this.datalist.activeOptions ||
            !this.datalist.activeOptions.length)
            return;
        this._elementOptions = this.datalist.activeOptions.map((ao) => {
            const { value, selected, label, filterMeta } = ao;
            return { value, selected, label, filterMeta };
        });
        this.customValidate();
    };
    handleDocumentKeyDown = (ev) => {
        if (!this.multiple || !this.hasFocus)
            return;
        if (!this.inputSearchVal) {
            let rm;
            switch (ev.key) {
                case 'Backspace':
                    if (this.inputCtrl.previousElementSibling)
                        rm = this.inputCtrl.previousElementSibling;
                    else
                        break;
                    this.removeValue(rm.dataset.value || null);
                    break;
                case 'Delete':
                    if (this.inputCtrl.nextElementSibling)
                        rm = this.inputCtrl.nextElementSibling;
                    else
                        break;
                    this.removeValue(rm.dataset.value || null);
                    break;
                case 'ArrowLeft':
                case 'ArrowRight':
                    if (!this.value.length)
                        break;
                    if (ev.key === 'ArrowLeft' && this.inputCtrl.previousSibling)
                        this.currInsertIndex--;
                    if (ev.key === 'ArrowRight' && this.inputCtrl.nextSibling)
                        this.currInsertIndex++;
                    setTimeout(() => {
                        this.inputCtrl.focus();
                    }, 20);
                    ev.preventDefault();
                    break;
            }
        }
    };
    onBlur = () => {
        this.hasFocus = false;
        requestAnimationFrame(() => {
            if (this.validateOn === 'dirty' && !this.hasFocus)
                this.showInlineValidation();
        });
        this.nanoBlur.emit();
        if (!this.allowCustomValues && this.multiple) {
            this.inputSearchVal = '';
            const event = new window.Event('change');
            this.inputCtrl.dispatchEvent(event);
        }
    };
    onFocus = () => {
        this.hasFocus = true;
        this.nanoFocus.emit();
    };
    clearSelectValue = (ev) => {
        if (this.clearable && !this.readonly && !this.disabled && ev) {
            ev.preventDefault();
            ev.stopPropagation();
        }
        if (this.multiple) {
            this.value = [];
            this.currInsertIndex = -1;
        }
        else
            this.value = '';
        this.inputCtrl.value = '';
        const event = new window.Event('change');
        this.inputCtrl.dispatchEvent(event);
    };
    onClick = () => {
        this.setFocus();
    };
    /* Multi event handlers */
    onDragStart = (ev) => {
        const ele = ev.target;
        this.dragVal = ele.dataset.value;
    };
    onDragEnd = (ev) => {
        this.dragVal = null;
        ev.preventDefault();
    };
    onDragLeave = (ev) => {
        const ele = ev.target;
        const spanVal = ele.dataset?.value || null;
        if (!spanVal ||
            this.dragVal === spanVal ||
            !this.value.includes(this.dragVal)) {
            ev.preventDefault();
            return;
        }
        const to = this.value.indexOf(spanVal);
        const from = this.value.indexOf(this.dragVal);
        const tmpArr = [...this.value];
        const tmpVal = tmpArr[to];
        tmpArr[to] = tmpArr[from];
        tmpArr[from] = tmpVal;
        this.value = [...tmpArr];
    };
    onMultiInput = (ev) => {
        this.inputSearchVal = ev.target.value.trim();
        this.nanoSearchChange.emit({ value: ev.target.value.trim() });
    };
    /* Stencil Component lifecycle hooks */
    connectedCallback() {
        this.debounceChanged();
        this.watchMaxValuesVisible();
        if (!Build.isBrowser)
            return;
        this.el.dispatchEvent(new CustomEvent('nanoDidLoad', {
            detail: this.el,
        }));
    }
    disconnectedCallback() {
        document.dispatchEvent(new CustomEvent('nanoDidUnload', {
            detail: this.el,
        }));
        if (!Build.isBrowser)
            return;
        if (this.mo)
            this.mo.disconnect();
    }
    componentDidLoad() {
        this.slotChangeObserver();
        this.customValidate();
        this.setDataListOpts();
        requestAnimationFrame(() => (this.onInit = false));
    }
    componentWillLoad() {
        this.processSlottedContent();
    }
    render() {
        const labelId = this.selectId + '-lbl';
        const moreId = this.showInlineError || this.hasHelperSlot
            ? this.selectId + '-moreId'
            : '';
        const helperEndId = this.hasHelperEndSlot ? this.selectId + '-helper' : '';
        this.rtl = this.el.ownerDocument.dir === 'rtl';
        this.valueItems = [];
        const compWrapOptions = (({ el, floatLabel, label, errorMessage, showInlineError, hasHelperSlot, hasHelperEndSlot, hasLabelSlot, hideLabel, placeholder, rtl, }) => ({
            el,
            floatLabel,
            label,
            errorMessage,
            showInlineError,
            hasHelperSlot,
            hasHelperEndSlot,
            hasLabelSlot,
            hideLabel,
            placeholder,
            rtl,
        }))(this);
        const wrapOptions = {
            ...compWrapOptions,
            labelId,
            moreId,
            helperEndId,
            hasValue: !!this.value.length || !!this.inputSearchVal.length,
            controlId: this.selectId,
            alwaysShowPlaceholder: false,
        };
        const controlOptions = (({ readonly, disabled }) => ({
            readonly,
            disabled,
            clearControl: this.clearable,
        }))(this);
        return (h(Host, { key: 'a2c56f43fef750f8e336a1612644761293b19bd4', type: this.multiple ? 'select-multiple' : 'select-one', "aria-disabled": this.disabled ? 'true' : null, class: {
                'has-value': !!this.value.length || !!this.inputSearchVal.length,
                'has-focus': this.hasFocus,
                'is-invalid': this._invalid === true,
                'is-valid': this._invalid === false,
                'nano-select': true,
            } }, h(FormControlWrap, { key: '632d901cdff60d36ae1a410f6c252c671eac8e63', ...wrapOptions, class: {
                'has-error': !!this.errorMessage &&
                    this.showInlineError &&
                    this._invalid === true,
                'has-helper': this.hasHelperSlot,
                'is-open': this.hasOpened,
                masked: this.mask,
            } }, h(FormControl, { key: '387a55518ed1c74ea944008c750a0ca42a6bbbe8', ...controlOptions, ref: (el) => (this.selectWrap = el), onClearText: this.clearSelectValue, control: this.el, endValueSlot: h("slot", { name: "down-arrow" }, h("nano-icon", { class: "select__down-arrow", name: "light/chevron-down" })), showInlineError: this.showInlineError }, this.multiple && (h("div", { key: '660b70b098520471d4f7653af5cc50388625cd1c', class: "select__multi-wrap select" }, this.multipleValues(labelId, moreId, helperEndId))), !this.multiple && [
            this.mask && (h("div", { key: '44f53317d26f2f8b85b5c8b397ccfd68a26eebb5', class: "select__mask" }, this.getLabel(this.value))),
            h("input", { key: '04f3935c7f759e3767f32faecb9e6ef81a36c777', ref: (input) => (this.inputCtrl = input), id: this.selectId, class: "select__native-input", "aria-labelledby": labelId + ' ' + moreId + ' ' + helperEndId, disabled: this.disabled, form: this.form, placeholder: this.placeholder, readonly: true, required: this.required, value: this.getLabel(this.value), onFocus: this.onFocus }),
        ])), !this.readonly && !this.disabled && (h("nano-datalist", { key: 'aae86b85c3bd2c2c562468f2a33e52c7036ad69b', ref: (el) => (this.datalist = el), selected: this.valArray, type: this.multiple ? 'selectMulti' : 'select', options: this._options.length ? this._options : undefined, onNanoDeselect: (e) => {
                e.preventDefault();
                this.removeValue(e.detail.value);
            }, onNanoOptionsUpdated: this.setOptions, onNanoSelect: this.setValue,
            // @ts-expect-error - this bubbles from the nano-dropdown
            onNanoShow: () => (this.hasOpened = true), onNanoHide: () => (this.hasOpened = false) }, this.allowCustomValues &&
            this.multiple &&
            !!this.inputSearchVal && (h("nano-option", { key: 'cf744eb6bbad3ce2d8d05354d5edaf3eab07a207', slot: "list-top", value: this.inputSearchVal, selected: false, label: this.inputSearchVal, onNanoSelect: () => this.inputCtrl.focus() }, h("span", { key: '990e00208c403d92d1586fb0b2c4618768f5fda5', slot: "check-icon" }), "Add '", this.inputSearchVal, "'")), h("slot", { key: '54fce41f95c01e0b2ff1619209ebb15a28383677' }))), h("select", { key: '1c5ff40ac84be25c6b7075ef031b29f2b6f9b007', id: this.selectId + '-hidden', class: "select__native-ctrl", ref: (select) => (this.nativeSelect = select), "aria-labelledby": labelId + ' ' + moreId + ' ' + helperEndId, disabled: this.disabled, form: this.form, multiple: this.multiple, name: this.name, required: this.required, onInvalid: this.handleInvalid }, this.allowCustomValues &&
            this.valArray.map((val) => {
                return (h("option", { value: val, selected: true }, val));
            }), !this.allowCustomValues &&
            this.options.map((opt) => {
                return (h("option", { value: opt.value, selected: this.valArray.includes(opt.value), disabled: opt.disabled, label: opt.label }, this.valArray.includes(opt.value)));
            }))));
    }
    multipleValues(labelId, moreId, helperEndId) {
        const input = (h("input", { class: "select__multi-input", id: this.selectId, ref: (input) => (this.inputCtrl = input), readonly: this.readonly, disabled: this.disabled, autoFocus: this.autofocus, autocomplete: "off", onKeyDown: this.handleDocumentKeyDown, onInput: this.onMultiInput, value: this.inputSearchVal, onTouchStart: this.onClick, onMouseDown: this.onClick, onFocus: this.onFocus, placeholder: this.placeholder && !this.value.length ? this.placeholder : '', "aria-labelledby": labelId + ' ' + moreId + ' ' + helperEndId }));
        if (!this.value.length)
            return input;
        const toReturn = [];
        this.value.forEach((val, i) => {
            if (!Number.isNaN(this._maxValuesVisible) && i > this._maxValuesVisible)
                return;
            let valueTag;
            if (i === this._maxValuesVisible) {
                valueTag = (h("nano-tag", { "data-value": val, key: val }, "+", this.value.length - this._maxValuesVisible));
            }
            else {
                valueTag = (h("nano-tag", { key: val, ref: (span) => this.valueItems.push(span), onDragStart: this.onDragStart, onDragLeave: this.onDragLeave, onDragEnd: this.onDragEnd, onDragOver: (e) => e.preventDefault(), onNanoClose: () => this.removeValue(val), draggable: true, tabIndex: -1, "data-value": val, closable: true }, this.valueTagTemplate ? (h("span", { innerHTML: this.valueTagTemplate
                        ? this.valueTagTemplate(...this.getOptionWithValue(val))
                        : undefined })) : (h("span", null, this.getLabel(val)))));
            }
            if (i === 0 && this.currInsertIndex < 0) {
                toReturn.push(input, valueTag);
            }
            else if (i === this.currInsertIndex) {
                toReturn.push(valueTag, input);
            }
            else {
                toReturn.push(valueTag);
            }
        });
        return toReturn;
    }
    static get is() { return "nano-select"; }
    static get encapsulation() { return "scoped"; }
    static get originalStyleUrls() {
        return {
            "$": ["select.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["select.css"]
        };
    }
    static get properties() {
        return {
            "invalid": {
                "type": "any",
                "attribute": "invalid",
                "mutable": false,
                "complexType": {
                    "original": "any",
                    "resolved": "any",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "This will be true when the control is in an invalid state.\nValidity is determined by the `required` prop. Or if custom validity message is set."
                },
                "getter": true,
                "setter": false,
                "reflect": true,
                "defaultValue": "null"
            },
            "validityMessage": {
                "type": "string",
                "attribute": "validity-message",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": undefined
                        }],
                    "text": "Current validation message - if any."
                },
                "getter": true,
                "setter": false,
                "reflect": false
            },
            "autofocus": {
                "type": "boolean",
                "attribute": "autofocus",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "This Boolean attribute lets you specify that a form control should have select focus when the page loads."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "disabled": {
                "type": "boolean",
                "attribute": "disabled",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the user cannot interact with the select."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "defaultValue": "false"
            },
            "validateOn": {
                "type": "string",
                "attribute": "validate-on",
                "mutable": true,
                "complexType": {
                    "original": "'dirty' | 'submit' | 'submitThenDirty'",
                    "resolved": "\"dirty\" | \"submit\" | \"submitThenDirty\"",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "'submitThenDirty'"
                        }],
                    "text": "When should the field perform validation"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'submitThenDirty'"
            },
            "showInlineError": {
                "type": "boolean",
                "attribute": "show-inline-error",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Whether to show validation errors underneath input"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "defaultValue": "true"
            },
            "label": {
                "type": "string",
                "attribute": "label",
                "mutable": true,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "String to place within a label element. Alternatively you may use a label slot."
                },
                "getter": true,
                "setter": true,
                "reflect": false
            },
            "hideLabel": {
                "type": "boolean",
                "attribute": "hide-label",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Visually hide the label - but make it accessible."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "floatLabel": {
                "type": "boolean",
                "attribute": "float-label",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Enable floating label behaviour. Will disable placeholder if set."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "multiple": {
                "type": "boolean",
                "attribute": "multiple",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the user can enter more than one value. This attribute applies when the type attribute is set to `\"email\"` or `\"file\"`, otherwise it is ignored."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "maxValuesVisible": {
                "type": "any",
                "attribute": "max-values-visible",
                "mutable": false,
                "complexType": {
                    "original": "boolean | number",
                    "resolved": "boolean | number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "When `multiple=\"true\"`, control how many values to show within the control at once.\nAfter the maximum, \"+n\" will be shown to indicate the number of additional items that are selected."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "3"
            },
            "name": {
                "type": "string",
                "attribute": "name",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The name of the control, which is submitted with the form data."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "this.selectId + '-name'"
            },
            "placeholder": {
                "type": "string",
                "attribute": "placeholder",
                "mutable": false,
                "complexType": {
                    "original": "string | null",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Instructional text that shows before the select has a value. Text set here will be placed inside the label when a value is set.\nIf you don't want this behaviour just set the first option to have no value."
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "readonly": {
                "type": "boolean",
                "attribute": "readonly",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the user cannot modify the value."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "defaultValue": "false"
            },
            "required": {
                "type": "boolean",
                "attribute": "required",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the user must fill in a value before submitting a form.\nBy default, setting this will also add an asterisk to the label.\nYou can disable this by using the `label` slot instead."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "size": {
                "type": "number",
                "attribute": "size",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The initial size of the control."
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "max": {
                "type": "number",
                "attribute": "max",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Maximum number of options that can be selected when multiple is true"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "min": {
                "type": "number",
                "attribute": "min",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Minimum number of options that can be selected when multiple is true"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "form": {
                "type": "string",
                "attribute": "form",
                "mutable": false,
                "complexType": {
                    "original": "string | null",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The form element to associate the select with (its form owner). Must be the id of a form."
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "allowCustomValues": {
                "type": "boolean",
                "attribute": "allow-custom-values",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "in multiple mode, allow users to enter their own values"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "value": {
                "type": "string",
                "attribute": "value",
                "mutable": false,
                "complexType": {
                    "original": "string | string[]",
                    "resolved": "string | string[]",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The value of the select."
                },
                "getter": true,
                "setter": true,
                "reflect": false
            },
            "options": {
                "type": "unknown",
                "attribute": "options",
                "mutable": false,
                "complexType": {
                    "original": "Array<OptionInterface | string>",
                    "resolved": "(string | OptionInterface)[]",
                    "references": {
                        "Array": {
                            "location": "global",
                            "id": "global::Array"
                        },
                        "OptionInterface": {
                            "location": "import",
                            "path": "../option/option-interface",
                            "id": "src/components/option/option-interface.d.ts::OptionInterface"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "You can set options via js as an array of strings: `domElement.options = ['option 1', 'option 2']` or\nobjects `domElement.options = [{label: 'option 1', value: 'opt-1'}]`. See `nano-option` docs for all available properties.\nAlternatively, you can nest `<nano-option>` elements within a `<nano-select>`\nRegardless - reading `options` will return the current component options - slotted or otherwise"
                },
                "getter": true,
                "setter": true
            },
            "clearable": {
                "type": "boolean",
                "attribute": "clearable",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, a clear icon will appear in the select when there is a value. Clicking it clears the select."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "mask": {
                "type": "boolean",
                "attribute": "mask",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Hide the native select element and cover with a value 'mask'.\nEnables a fluid nano-select width upon value change (if required). Doesn't work with multiple=\"true\"."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "debounce": {
                "type": "number",
                "attribute": "debounce",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set the amount of time, in milliseconds, to wait to trigger the `nanoSearchChange` event after each keystroke."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "0"
            },
            "dropDownConfig": {
                "type": "unknown",
                "attribute": "drop-down-config",
                "mutable": false,
                "complexType": {
                    "original": "Partial<DropdownInterface>",
                    "resolved": "{ host?: HTMLNanoDropdownElement; autoOpen?: boolean; open?: boolean; handleOpenChange?: () => void; closeOnSelect?: boolean; containingElement?: HTMLElement; tetherTo?: HTMLElement; handleTetherToChange?: () => void; placement?: PopoverPlacement; distance?: number; skidding?: number; hoist?: boolean; handlePopoverOptionsChange?: () => void; dialogTitle?: string; nanoShow?: EventEmitter<any>; nanoAfterShow?: EventEmitter<any>; nanoHide?: EventEmitter<any>; nanoAfterHide?: EventEmitter<any>; eventType?: EventEmitter<any>; secondaryOpen?: (ev: CustomEvent<any>) => void; secondaryClose?: (ev: CustomEvent<any>) => void; handlePanelSelect?: (event: CustomEvent<any>) => void; show?: () => Promise<void>; hide?: () => Promise<void>; connectedCallback?: () => void; componentDidLoad?: () => void; disconnectedCallback?: () => void; render?: () => any; }",
                    "references": {
                        "Partial": {
                            "location": "global",
                            "id": "global::Partial"
                        },
                        "DropdownInterface": {
                            "location": "import",
                            "path": "../dropdown/dropdown",
                            "id": "src/components/dropdown/dropdown.tsx::Dropdown"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "nano-dropdown config options you can pass to the nested dropdown component"
                },
                "getter": false,
                "setter": false,
                "defaultValue": "{}"
            },
            "valueTagTemplate": {
                "type": "unknown",
                "attribute": "value-tag-template",
                "mutable": false,
                "complexType": {
                    "original": "(option: OptionInterface, index: number) => string",
                    "resolved": "(option: OptionInterface, index: number) => string",
                    "references": {
                        "OptionInterface": {
                            "location": "import",
                            "path": "../option/option-interface",
                            "id": "src/components/option/option-interface.d.ts::OptionInterface"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "param",
                            "text": "option - the incoming option that was selected"
                        }, {
                            "name": "param",
                            "text": "index - the incoming option index"
                        }, {
                            "name": "returns",
                            "text": "Should return either a string / templateResult or a string containing trusted HTML or an HTMLElement"
                        }],
                    "text": "A function that customizes the tag value to be rendered when `multiple=\"true\"`."
                },
                "getter": false,
                "setter": false
            }
        };
    }
    static get states() {
        return {
            "currInsertIndex": {},
            "showErrorMsg": {},
            "errorMessage": {},
            "hasFocus": {},
            "hasOpened": {},
            "hasLabelSlot": {},
            "hasHelperSlot": {},
            "hasHelperEndSlot": {},
            "inputSearchVal": {},
            "_invalid": {},
            "_elementOptions": {}
        };
    }
    static get events() {
        return [{
                "method": "nanoChange",
                "name": "nanoChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the value has changed."
                },
                "complexType": {
                    "original": "SelectChangeEventDetail",
                    "resolved": "SelectChangeEventDetail",
                    "references": {
                        "SelectChangeEventDetail": {
                            "location": "import",
                            "path": "./select-interface",
                            "id": "src/components/select/select-interface.d.ts::SelectChangeEventDetail"
                        }
                    }
                }
            }, {
                "method": "nanoBlur",
                "name": "nanoBlur",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the select loses focus."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "nanoFocus",
                "name": "nanoFocus",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the select has focus."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "nanoDidLoad",
                "name": "nanoDidLoad",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }],
                    "text": "Emitted when the select has been created."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "nanoDidUnload",
                "name": "nanoDidUnload",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }],
                    "text": "Emitted when the select has been removed."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "nanoSearchChange",
                "name": "nanoSearchChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a keyboard input occurred on a multiple=\"true\" input"
                },
                "complexType": {
                    "original": "InputChangeEventDetail",
                    "resolved": "InputChangeEventDetail",
                    "references": {
                        "InputChangeEventDetail": {
                            "location": "import",
                            "path": "../input/input-interface",
                            "id": "src/components/input/input-interface.d.ts::InputChangeEventDetail"
                        }
                    }
                }
            }, {
                "method": "nanoValidate",
                "name": "nanoValidate",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [{
                            "name": "returns",
                            "text": "event.detail: `{ isValid: boolean, errorMessage: string, originalEvent: Event }`."
                        }],
                    "text": "Called when validation is performed (which depends on `validateOn`)."
                },
                "complexType": {
                    "original": "ControlValidityEventDetail",
                    "resolved": "ControlValidityEventDetail",
                    "references": {
                        "ControlValidityEventDetail": {
                            "location": "import",
                            "path": "../../types/shared",
                            "id": "src/types/shared.ts::ControlValidityEventDetail"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "reportValidity": {
                "complexType": {
                    "signature": "(validateFirst: boolean) => Promise<ControlValidity>",
                    "parameters": [{
                            "name": "validateFirst",
                            "type": "boolean",
                            "docs": "- perform validation first before reporting"
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "ControlValidity": {
                            "location": "import",
                            "path": "../../types/shared",
                            "id": "src/types/shared.ts::ControlValidity"
                        }
                    },
                    "return": "Promise<ControlValidity>"
                },
                "docs": {
                    "text": "Get the current state of the control.",
                    "tags": [{
                            "name": "param",
                            "text": "validateFirst - perform validation first before reporting"
                        }, {
                            "name": "returns",
                            "text": "`{ isValid: boolean, errorMessage: string }`"
                        }]
                }
            },
            "setFocus": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets focus on the specified `nano-select`. Use this method instead of the global\n`select.focus()`.",
                    "tags": []
                }
            },
            "getSelectElement": {
                "complexType": {
                    "signature": "() => Promise<HTMLSelectElement>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "HTMLSelectElement": {
                            "location": "global",
                            "id": "global::HTMLSelectElement"
                        }
                    },
                    "return": "Promise<HTMLSelectElement>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "returns",
                            "text": "the native `<select>` element used under the hood."
                        }]
                }
            },
            "showError": {
                "complexType": {
                    "signature": "(message: string) => Promise<void>",
                    "parameters": [{
                            "name": "message",
                            "type": "string",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Invalidate the field and show a custom error message.\nTo clear the error you will need to re-call this method with an empty string.",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "maxValuesVisible",
                "methodName": "watchMaxValuesVisible"
            }, {
                "propName": "value",
                "methodName": "valueChanged"
            }, {
                "propName": "debounce",
                "methodName": "debounceChanged"
            }, {
                "propName": "required",
                "methodName": "shouldValidate"
            }, {
                "propName": "disabled",
                "methodName": "shouldValidate"
            }, {
                "propName": "min",
                "methodName": "shouldValidate"
            }, {
                "propName": "max",
                "methodName": "shouldValidate"
            }, {
                "propName": "readonly",
                "methodName": "shouldValidate"
            }, {
                "propName": "dropDownConfig",
                "methodName": "setDataListOpts"
            }];
    }
    static get listeners() {
        return [{
                "name": "reset",
                "method": "onReset",
                "target": "body",
                "capture": false,
                "passive": false
            }, {
                "name": "mousedown",
                "method": "handleBlur",
                "target": "body",
                "capture": false,
                "passive": true
            }, {
                "name": "keydown",
                "method": "handleBlur",
                "target": undefined,
                "capture": false,
                "passive": false
            }];
    }
}
