import { __decorate, __metadata } from "tslib";
import { h, Host } from "@stencil/core";
import { defaultBalAriaForm } from "../../utils/form";
import { deepReady, waitAfterFramePaint } from "../../utils/helpers";
import { ListenToMutation } from "../../utils/mutation";
export class Field {
    constructor() {
        this.fieldId = `bal-field-${FieldIds++}`;
        this.formControlElement = ['bal-field-control'];
        this.inputElements = [
            'bal-input',
            'bal-number-input',
            'bal-textarea',
            'bal-select',
            'bal-checkbox',
            'bal-radio',
            'bal-input-group',
            'bal-date',
            'bal-input-date',
            'bal-input-stepper',
            'bal-input-slider',
            'bal-file-upload',
            'bal-dropdown',
            'bal-segment',
        ];
        this.formElements = [...this.formControlElement, 'bal-field-label', 'bal-field-message'];
        /**
         * If `true` the form control needs to be filled. If it is set to
         * `false` an optional label is added to the label..
         */
        this.required = undefined;
        /**
         * If true, label and input are aligned horizontally within the field component, with the message positioned in a new line below.
         */
        this.horizontal = false;
        /**
         * If `true` the component gets a invalid red style.
         */
        this.invalid = undefined;
        /**
         * If `true` the component gets a valid green style.
         */
        this.valid = false;
        /**
         * If `true`, the element is not mutable, focusable, or even submitted with the form. The user can neither edit nor focus on the control, nor its form control descendants.
         */
        this.disabled = undefined;
        /**
         * If `true` the element can not mutated, meaning the user can not edit the control.
         */
        this.readonly = undefined;
        /**
         * If `true` a loading spinner is visible at the end of the input
         */
        this.loading = undefined;
        this.isDirectChild = (el) => {
            if (!el) {
                return false;
            }
            const parent = el.parentElement;
            if (!parent) {
                return false;
            }
            if (parent.nodeName.toLowerCase() === 'bal-field' && parent !== this.el) {
                return false;
            }
            if (parent === this.el) {
                return true;
            }
            return this.isDirectChild(parent);
        };
        this.isVisible = (el) => {
            if (!el) {
                return false;
            }
            return el.ariaHidden !== 'true';
        };
        this.findDirectChild = (selectors) => {
            const element = this.el.querySelector(selectors);
            const isDirectChild = this.isDirectChild(element);
            if (isDirectChild) {
                if (this.isVisible(element)) {
                    return element;
                }
            }
            return undefined;
        };
        this.findDirectChildren = (selectors) => {
            return selectors
                .map(selector => {
                return Array.from(this.el.querySelectorAll(selector)).filter(this.isDirectChild).filter(this.isVisible);
            })
                .flat();
        };
        this.mutationObserverActive = true;
    }
    requiredHandler() {
        this.updateProps([...this.inputElements, 'bal-field-label'], 'required');
    }
    invalidHandler() {
        this.updateProps([...this.inputElements, ...this.formElements], 'invalid');
    }
    validHandler() {
        this.updateProps([...this.inputElements, ...this.formElements], 'valid');
    }
    disabledHandler() {
        this.updateProps([...this.inputElements, ...this.formElements], 'disabled');
    }
    readonlyHandler() {
        this.updateProps([...this.inputElements, ...this.formElements], 'readonly');
    }
    loadingHandler() {
        this.updateProps([...this.inputElements, ...this.formControlElement], 'loading');
    }
    connectedCallback() {
        this.triggerAllHandlers();
    }
    componentWillLoad() {
        this.triggerAllHandlers();
    }
    async componentDidLoad() {
        await this.syncAriaAttributes();
    }
    async syncAriaAttributes() {
        await deepReady(this.el);
        await waitAfterFramePaint();
        const label = this.findDirectChild('bal-field-label bal-label');
        const message = this.findDirectChild('bal-field-message');
        const controls = this.findDirectChildren([
            'bal-field-control bal-input',
            'bal-field-control bal-select',
            'bal-field-control bal-input-date',
            'bal-field-control bal-dropdown',
            'bal-field-control bal-checkbox',
            'bal-field-control bal-radio',
            'bal-field-control bal-segment',
            'bal-field-control bal-segment-item',
            'bal-field-control bal-checkbox-group',
            'bal-field-control bal-radio-group',
            'bal-field-control bal-number-input',
            'bal-field-control bal-time-input',
            'bal-field-control bal-input-slider',
            'bal-field-control bal-input-stepper',
            'bal-field-control bal-textarea',
            'bal-field-control bal-file-upload',
        ]);
        const ariaForm = Object.assign({}, defaultBalAriaForm);
        if (label) {
            ariaForm.labelId = `${this.fieldId}-lbl`;
        }
        if (message) {
            ariaForm.messageId = `${this.fieldId}-msg`;
        }
        for (let index = 0; index < controls.length; index++) {
            const control = controls[index];
            if (index === 0) {
                ariaForm.controlId = `${this.fieldId}-ctrl`;
                await (label === null || label === void 0 ? void 0 : label.setAriaForm(ariaForm));
                await (message === null || message === void 0 ? void 0 : message.setAriaForm(ariaForm));
                await (control === null || control === void 0 ? void 0 : control.setAriaForm(ariaForm));
            }
            else {
                ariaForm.controlId = `${this.fieldId}-ctrl-${index}`;
                await (control === null || control === void 0 ? void 0 : control.setAriaForm(ariaForm));
            }
        }
    }
    mutationListener() {
        this.triggerAllHandlers();
        this.syncAriaAttributes();
    }
    triggerAllHandlers() {
        this.requiredHandler();
        this.invalidHandler();
        this.validHandler();
        this.readonlyHandler();
        this.disabledHandler();
        this.loadingHandler();
    }
    updateProps(selectors, key) {
        const value = this[key];
        if (value !== undefined) {
            this.notifyComponents(selectors, input => (input[key] = value));
        }
    }
    notifyComponents(selectors, callback) {
        const components = this.el.querySelectorAll(selectors.join(', '));
        components.forEach(c => callback(c));
    }
    render() {
        return (h(Host, { key: 'ef05b294a516d40c5089d051feeb46715f581bc1', id: this.fieldId, class: {
                'bal-field': true,
                'field': true,
                'bal-field--invalid': this.invalid === true,
                'bal-field--horizontal': this.horizontal === true,
            } }, h("slot", { key: '5710d7fd141b21d699004d7982d5d5bb7238cd94' }), h("span", { key: 'e1b36343cdcb60960bcdac4d938c946f40041334', class: "bal-field-hidden" })));
    }
    static get is() { return "bal-field"; }
    static get originalStyleUrls() {
        return {
            "$": ["bal-field.sass"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["bal-field.css"]
        };
    }
    static get properties() {
        return {
            "required": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true` the form control needs to be filled. If it is set to\n`false` an optional label is added to the label.."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "required",
                "defaultValue": "undefined"
            },
            "horizontal": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If true, label and input are aligned horizontally within the field component, with the message positioned in a new line below."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "horizontal",
                "defaultValue": "false"
            },
            "invalid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true` the component gets a invalid red style."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "invalid",
                "defaultValue": "undefined"
            },
            "valid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the component gets a valid green style."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "valid",
                "defaultValue": "false"
            },
            "disabled": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the element is not mutable, focusable, or even submitted with the form. The user can neither edit nor focus on the control, nor its form control descendants."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "disabled",
                "defaultValue": "undefined"
            },
            "readonly": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true` the element can not mutated, meaning the user can not edit the control."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "readonly",
                "defaultValue": "undefined"
            },
            "loading": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true` a loading spinner is visible at the end of the input"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "loading",
                "defaultValue": "undefined"
            }
        };
    }
    static get events() {
        return [{
                "method": "balFormControlDidLoad",
                "name": "balFormControlDidLoad",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted after render when element is labelled"
                },
                "complexType": {
                    "original": "BalEvents.BalFieldAriaLabelledByDetail",
                    "resolved": "HTMLElement",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }];
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "required",
                "methodName": "requiredHandler"
            }, {
                "propName": "invalid",
                "methodName": "invalidHandler"
            }, {
                "propName": "valid",
                "methodName": "validHandler"
            }, {
                "propName": "disabled",
                "methodName": "disabledHandler"
            }, {
                "propName": "readonly",
                "methodName": "readonlyHandler"
            }, {
                "propName": "loading",
                "methodName": "loadingHandler"
            }];
    }
}
__decorate([
    ListenToMutation({ subtree: false, waitAfterFramePrint: true }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], Field.prototype, "mutationListener", null);
let FieldIds = 0;
