import { h } from "@stencil/core";
import { derivedId, hasValue, onBoolValueChanged } from '../../utils/utils';
import { NativeValidationStrategy } from '../../utils/native-validation-strategy';
let selectId = 0;
export class Select {
    constructor() {
        this.inited = false;
        this.optionListListener = (e) => this.onSelect(e);
        this.expanded = false;
        this.labels = null;
        this.options = [];
        /**
         * Is the select disabled
         */
        this.disabled = false;
        /**
         * The select id
         */
        this.inputId = `anj-select-${selectId++}`;
        /**
         * Id for the label
         */
        this.labelId = derivedId(this.inputId, 'label');
        /**
         * Does the select support multiple options
         */
        this.multiple = false;
        /**
         * The name of the select
         */
        this.name = this.inputId;
        /**
         * If true, the user must fill in a value before submitting a form.
         */
        this.required = false;
        /**
         * @internal
         * The validation strategy to use
         */
        this.validationStrategy = new NativeValidationStrategy();
    }
    invalidChanged(newVal, oldVal) {
        onBoolValueChanged(newVal, oldVal, this.anjInvalid.emit);
    }
    valueChanged(value) {
        if (this.inited) {
            this.updateOptions();
            if (this.nativeInput && this.nativeInput.value !== value) {
                this.nativeInput.value = value;
            }
            this.anjChange.emit({ value: this.value });
        }
    }
    async componentDidLoad() {
        this.anjControlDidLoad.emit();
        this.inited = true;
    }
    disconnectedCallback() {
        this.unbindOptionList();
        this.overlay = null;
    }
    async onOptionsChanged({ detail }) {
        if (!hasValue(this.value)) {
            this.value = this.valueFromSelected();
        }
        this.options = detail;
        this.updateOptions();
        this.updateValue();
    }
    /**
     * Checks the native element validity use native validators
     */
    async checkValidity() {
        if (!this.nativeInput)
            return;
        this.nativeInput.checkValidity();
        return this.validationStrategy.onValidChange(this.element, this.nativeInput);
    }
    async onClick() {
        if (this.expanded) {
            this.toggle.blur();
            return;
        }
        this.overlay = this.bindOverlay();
        this.optionList = this.bindOptionList();
        await this.optionList.reset(this.options.find(o => o.selected));
        this.anjFocus.emit();
        await this.overlay.open(this.element);
        this.expanded = true;
        this.optionList.focus();
    }
    async onBlur(event, refocusToggle = false) {
        if (event && event.relatedTarget === this.optionList) {
            return;
        }
        if (refocusToggle) {
            this.toggle.focus();
        }
        else {
            this.anjBlur.emit();
        }
        if (this.overlay) {
            await this.overlay.close();
            this.overlay = null;
        }
        this.expanded = false;
        this.unbindOptionList();
        return this.validationStrategy.onTouched(this.element);
    }
    async onSelect(event) {
        if (this.multiple) {
            this.value = this.updateMultiValue(event.detail);
        }
        else {
            this.value = event.detail;
            return this.onBlur(undefined, true);
        }
    }
    getLabels() {
        const labels = this.options.filter(opt => opt.selected).map(opt => opt.textContent);
        return labels.length > 0 ? labels : undefined;
    }
    updateValue() {
        const values = this.options.map(opt => opt.value);
        if (this.multiple) {
            const validVals = this.value.filter(val => values.includes(val));
            if (validVals.length !== this.value.length) {
                this.value = validVals;
            }
        }
        else {
            if (!values.includes(this.value)) {
                this.value = null;
            }
        }
    }
    updateMultiValue(value) {
        if (this.value.includes(value)) {
            return this.value.filter(val => val !== value);
        }
        else {
            this.value.push(value);
            return this.value.slice(0);
        }
    }
    updateOptions() {
        this.options.forEach(opt => opt.selected = this.valueSelected(opt.value));
        this.labels = this.getLabels();
    }
    valueFromSelected() {
        if (this.multiple) {
            return this.options.filter(opt => opt.selected).map(opt => opt.value);
        }
        else {
            const selected = this.options.find(opt => opt.selected);
            return selected ? selected.value : null;
        }
    }
    valueSelected(value) {
        if (Array.isArray(this.value)) {
            return this.value.includes(value);
        }
        return value === this.value;
    }
    toggleClasses() {
        const classList = ['anj-select-toggle'];
        if (!this.labels) {
            classList.push('anj-placeholder');
        }
        if (this.expanded) {
            classList.push('anj-active');
        }
        return classList.join(' ');
    }
    bindOptionList() {
        const el = this.element.querySelector('anj-option-list');
        if (el) {
            el.addEventListener('anjSelect', this.optionListListener);
        }
        return el;
    }
    unbindOptionList() {
        if (!this.optionList)
            return;
        this.optionList.removeEventListener('anjSelect', this.optionListListener);
        this.optionList = null;
    }
    bindOverlay() {
        return this.element.querySelector('anj-overlay');
    }
    render() {
        const ariaLabelBy = this.labels ? this.inputId : this.labelId;
        const overlayWidth = this.element.offsetWidth;
        return [
            h("button", { type: "button", "aria-haspopup": "listbox", "aria-label": this.labels ? this.labels.join(' ') : null, "aria-labelledby": ariaLabelBy, "aria-required": this.required, disabled: this.disabled, id: this.inputId, class: this.toggleClasses(), onClick: () => this.onClick(), onBlur: e => this.onBlur(e), ref: ref => this.toggle = ref },
                h("div", { class: "values-wrapper" }, this.labels ? this.labels.map(label => h("span", { class: "anj-selected-label" }, label)) : this.placeholder),
                h("anj-icon-opener", { open: this.expanded })),
            h("anj-overlay", { class: "anj-dropdown-menu anj-select-menu", placement: "bottom-start", style: { 'min-width': `${overlayWidth}px` } },
                h("anj-option-list", { "aria-labelledby": ariaLabelBy, "aria-multiselectable": this.multiple, expanded: this.expanded, onBlur: e => this.onBlur(e, true) },
                    h("slot", null))),
            h("input", { class: "anj-hidden-control", "aria-hidden": "true", tabindex: "-1", name: this.name, disabled: this.disabled, required: this.required, value: this.value, onInvalid: () => this.validationStrategy.onValidChange(this.element, this.nativeInput), ref: ref => this.nativeInput = ref })
        ];
    }
    static get is() { return "anj-select"; }
    static get encapsulation() { return "scoped"; }
    static get originalStyleUrls() { return {
        "$": ["select.scss"]
    }; }
    static get styleUrls() { return {
        "$": ["select.css"]
    }; }
    static get properties() { return {
        "disabled": {
            "type": "boolean",
            "mutable": false,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Is the select disabled"
            },
            "attribute": "disabled",
            "reflect": false,
            "defaultValue": "false"
        },
        "inputId": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "The select id"
            },
            "attribute": "input-id",
            "reflect": false,
            "defaultValue": "`anj-select-${selectId++}`"
        },
        "invalid": {
            "type": "boolean",
            "mutable": true,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Whether the control is valid"
            },
            "attribute": "invalid",
            "reflect": true
        },
        "labelId": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Id for the label"
            },
            "attribute": "label-id",
            "reflect": false,
            "defaultValue": "derivedId(this.inputId, 'label')"
        },
        "multiple": {
            "type": "boolean",
            "mutable": false,
            "complexType": {
                "original": "boolean",
                "resolved": "boolean",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "Does the select support multiple options"
            },
            "attribute": "multiple",
            "reflect": true,
            "defaultValue": "false"
        },
        "name": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": "The name of the select"
            },
            "attribute": "name",
            "reflect": false,
            "defaultValue": "this.inputId"
        },
        "placeholder": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": true,
            "docs": {
                "tags": [],
                "text": "Placeholder text for the select"
            },
            "attribute": "placeholder",
            "reflect": false
        },
        "required": {
            "type": "boolean",
            "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."
            },
            "attribute": "required",
            "reflect": false,
            "defaultValue": "false"
        },
        "validationStrategy": {
            "type": "unknown",
            "mutable": false,
            "complexType": {
                "original": "NativeValidationStrategy",
                "resolved": "NativeValidationStrategy",
                "references": {
                    "NativeValidationStrategy": {
                        "location": "import",
                        "path": "../../utils/native-validation-strategy"
                    }
                }
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [{
                        "text": "The validation strategy to use",
                        "name": "internal"
                    }],
                "text": ""
            },
            "defaultValue": "new NativeValidationStrategy()"
        },
        "value": {
            "type": "any",
            "mutable": true,
            "complexType": {
                "original": "any | null",
                "resolved": "any",
                "references": {}
            },
            "required": false,
            "optional": true,
            "docs": {
                "tags": [],
                "text": "the value of the select"
            },
            "attribute": "value",
            "reflect": false
        }
    }; }
    static get states() { return {
        "expanded": {},
        "hData": {},
        "labels": {},
        "options": {},
        "optionList": {}
    }; }
    static get events() { return [{
            "method": "anjControlDidLoad",
            "name": "anjControlDidLoad",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [{
                        "text": undefined,
                        "name": "internal"
                    }],
                "text": "Emitted when the control has been created."
            },
            "complexType": {
                "original": "void",
                "resolved": "void",
                "references": {}
            }
        }, {
            "method": "anjBlur",
            "name": "anjBlur",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Fires when the button is blurred"
            },
            "complexType": {
                "original": "void",
                "resolved": "void",
                "references": {}
            }
        }, {
            "method": "anjChange",
            "name": "anjChange",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Emitted when the value has changed."
            },
            "complexType": {
                "original": "SelectChangeEvent",
                "resolved": "SelectChangeEvent",
                "references": {
                    "SelectChangeEvent": {
                        "location": "import",
                        "path": "../../types/types"
                    }
                }
            }
        }, {
            "method": "anjFocus",
            "name": "anjFocus",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Fires when the button is focused"
            },
            "complexType": {
                "original": "void",
                "resolved": "void",
                "references": {}
            }
        }, {
            "method": "anjInvalid",
            "name": "anjInvalid",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": "Emitted when invalid status changes."
            },
            "complexType": {
                "original": "any",
                "resolved": "any",
                "references": {}
            }
        }]; }
    static get methods() { return {
        "checkValidity": {
            "complexType": {
                "signature": "() => Promise<boolean>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    },
                    "AnjunaFormControl": {
                        "location": "import",
                        "path": "../../utils/native-validation-strategy"
                    }
                },
                "return": "Promise<boolean>"
            },
            "docs": {
                "text": "Checks the native element validity use native validators",
                "tags": []
            }
        }
    }; }
    static get elementRef() { return "element"; }
    static get watchers() { return [{
            "propName": "invalid",
            "methodName": "invalidChanged"
        }, {
            "propName": "value",
            "methodName": "valueChanged"
        }]; }
    static get listeners() { return [{
            "name": "anjOptionsChange",
            "method": "onOptionsChanged",
            "target": undefined,
            "capture": false,
            "passive": false
        }]; }
}
