var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
        r = Reflect.decorate(decorators, target, key, desc);
    else
        for (var i = decorators.length - 1; i >= 0; i--)
            if (d = decorators[i])
                r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { h, Host, } from "@stencil/core";
import { ListenToConfig, defaultConfig, } from "../../utils/config";
import { getNativeInputValue, getUpcomingValue, inputHandleBlur, inputHandleChange, inputHandleClick, inputHandleFocus, inputHandleHostClick, inputHandleReset, inputListenOnClick, inputSetBlur, inputSetFocus, stopEventBubbling, } from "../../utils/form-input";
import { debounceEvent } from "../../utils/helpers";
import { inheritAttributes } from "../../utils/attributes";
import { getDecimalSeparator, getThousandSeparator } from "../../utils/number";
import { BEM } from "../../utils/bem";
import { defaultBalAriaForm } from "../../utils/form";
import { Logger } from "../../utils/log";
import { toUserFormattedNumber, isNotNumber, toNumber, toFixedNumber, validateKeyDown, mapDecimalSeparator, } from "./bal-number-input.utils";
import isNil from "lodash.isnil";
import isEmpty from "lodash.isempty";
import isNaN from "lodash.isnan";
export class NumberInput {
    constructor() {
        this.inputId = `bal-number-input-${numberInputIds++}`;
        this.inheritedAttributes = {};
        this.lastValue = '';
        this.inputValue = this.value;
        this.initialValue = undefined;
        this.onClick = (ev) => inputHandleClick(this, ev);
        this.handleClick = (ev) => inputHandleHostClick(this, ev);
        this.onInput = (_ev) => {
            var _a;
            if (this.nativeInput) {
                if (isNotNumber(this.nativeInput.value)) {
                    this.nativeInputValue = this.lastValue || '';
                    return;
                }
            }
            this.lastValue = ((_a = this.nativeInput) === null || _a === void 0 ? void 0 : _a.value) || '';
            this.balInput.emit(toNumber(this.lastValue, this.decimal));
        };
        this.onFocus = (ev) => {
            inputHandleFocus(this, ev);
            if (this.nativeInput) {
                this.nativeInputValue = mapDecimalSeparator(this.lastValue || '');
            }
        };
        this.onBlur = (ev) => {
            inputHandleBlur(this, ev);
            if (this.nativeInput) {
                this.lastValue = toFixedNumber(this.lastValueGetter, this.decimal);
                this.nativeInputValue = toUserFormattedNumber(this.lastValueGetter, this.decimal, this.suffix);
            }
            this.inputValue = toNumber(this.lastValueGetter, this.decimal);
            inputHandleChange(this);
        };
        this.onKeydown = (ev) => {
            const oldValue = getNativeInputValue(this);
            const newValue = getUpcomingValue(this, ev);
            const input = ev === null || ev === void 0 ? void 0 : ev.target;
            if (input &&
                !validateKeyDown({
                    key: ev.key,
                    decimal: this.decimal,
                    newValue,
                    oldValue,
                    selectionStart: input.selectionStart,
                    selectionEnd: input.selectionEnd,
                })) {
                return stopEventBubbling(ev);
            }
        };
        this.focused = false;
        this.language = defaultConfig.language;
        this.region = defaultConfig.region;
        this.ariaForm = defaultBalAriaForm;
        this.nativeInputValue = '';
        this.inputPattern = this.createPattern();
        this.name = this.inputId;
        this.invalid = false;
        this.decimal = 0;
        this.suffix = undefined;
        this.pattern = undefined;
        this.placeholder = undefined;
        this.required = false;
        this.disabled = false;
        this.readonly = false;
        this.exactNumber = false;
        this.max = undefined;
        this.min = undefined;
        this.debounce = 0;
        this.value = undefined;
        this.autoInvalidOff = false;
    }
    createLogger(log) {
        this.log = log;
    }
    debounceChanged() {
        this.balChange = debounceEvent(this.balChange, this.debounce);
    }
    valueChanged(newValue, oldValue) {
        if (newValue !== oldValue) {
            const isValueNotDefined = newValue === '' || isNil(newValue) || isNaN(newValue);
            const emptyValue = this.exactNumber ? '0' : '';
            const value = isValueNotDefined ? emptyValue : newValue.toString();
            this.inputValue = toNumber(toFixedNumber(value, this.decimal), this.decimal);
            this.lastValue = toFixedNumber(value, this.decimal);
            if (this.focused) {
                this.nativeInputValue = mapDecimalSeparator(this.lastValue);
            }
            else {
                this.nativeInputValue = toUserFormattedNumber(this.lastValue, this.decimal, this.suffix);
            }
        }
    }
    connectedCallback() {
        this.debounceChanged();
        this.initialValue = this.value || 0;
        if (this.value !== undefined) {
            this.valueChanged(this.value, undefined);
        }
    }
    componentDidLoad() {
        this.inputValue = this.value;
    }
    componentWillLoad() {
        this.inheritedAttributes = inheritAttributes(this.el, ['aria-label', 'tabindex', 'title']);
    }
    listenOnClick(ev) {
        inputListenOnClick(this, ev);
    }
    resetHandler(ev) {
        const formElement = ev.target;
        if (formElement === null || formElement === void 0 ? void 0 : formElement.contains(this.el)) {
            inputHandleReset(this, this.initialValue, this.resetHandlerTimer);
        }
    }
    async configChanged(state) {
        this.language = state.language;
        this.region = state.region;
        this.inputPattern = this.createPattern();
        if (this.nativeInput) {
            if (this.focused) {
                this.nativeInputValue = mapDecimalSeparator(this.lastValue);
            }
            else {
                this.nativeInputValue = toUserFormattedNumber(this.lastValue, this.decimal, this.suffix);
            }
        }
    }
    async setFocus() {
        inputSetFocus(this);
    }
    async setBlur() {
        inputSetBlur(this);
    }
    getInputElement() {
        return Promise.resolve(this.nativeInput);
    }
    async setAriaForm(ariaForm) {
        this.ariaForm = Object.assign({}, ariaForm);
    }
    createPattern() {
        if (this.pattern) {
            return this.pattern;
        }
        let suffix = this.suffix || '';
        if (suffix !== '') {
            suffix = ` ${suffix}`;
        }
        const thousandSeparator = getThousandSeparator();
        let decimalSeparator = getDecimalSeparator();
        if (decimalSeparator === ',') {
            decimalSeparator = '\\,';
        }
        return `-?\\d{1,3}(?:${thousandSeparator}\\d{3})*(?:\\${decimalSeparator}\\d{1,2})?(?:${suffix})?`;
    }
    get lastValueGetter() {
        if (this.exactNumber && (isNil(this.lastValue) || isEmpty(this.lastValue))) {
            return '0';
        }
        return this.lastValue;
    }
    render() {
        const block = BEM.block('number-input');
        return (h(Host, { onClick: this.handleClick, "aria-disabled": this.disabled ? 'true' : null, class: Object.assign({}, block.class()) }, h("input", Object.assign({ class: {
                'input': true,
                'is-disabled': this.disabled || this.readonly,
                'is-danger': this.invalid,
            }, "data-testid": "bal-number-input", ref: input => (this.nativeInput = input), id: this.ariaForm.controlId || this.inputId, "aria-labelledby": this.ariaForm.labelId, "aria-describedby": this.ariaForm.messageId, "aria-invalid": this.invalid === true ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : null, name: this.name, disabled: this.disabled, placeholder: this.placeholder || '', readonly: this.readonly, required: this.required, pattern: this.inputPattern, min: this.min, max: this.max, value: this.nativeInputValue, onInput: e => this.onInput(e), onFocus: e => this.onFocus(e), onBlur: e => this.onBlur(e), onClick: this.onClick, onKeyDown: e => this.onKeydown(e), onKeyPress: e => this.balKeyPress.emit(e) }, this.inheritedAttributes))));
    }
    static get is() { return "bal-number-input"; }
    static get originalStyleUrls() {
        return {
            "$": ["bal-number-input.sass"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["bal-number-input.css"]
        };
    }
    static get properties() {
        return {
            "name": {
                "type": "string",
                "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."
                },
                "attribute": "name",
                "reflect": false,
                "defaultValue": "this.inputId"
            },
            "invalid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the component gets a invalid style."
                },
                "attribute": "invalid",
                "reflect": false,
                "defaultValue": "false"
            },
            "decimal": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Defines the allowed decimal points for the `number-input`."
                },
                "attribute": "decimal",
                "reflect": false,
                "defaultValue": "0"
            },
            "suffix": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Adds a suffix the the input-value after blur."
                },
                "attribute": "suffix",
                "reflect": false
            },
            "pattern": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "A regular expression that the value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is `\"text\"`, `\"search\"`, `\"tel\"`, `\"url\"`, `\"email\"`, `\"date\"`, or `\"password\"`, otherwise it is ignored. When the type attribute is `\"date\"`, `pattern` will only be used in browsers that do not support the `\"date\"` input type natively. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date for more information."
                },
                "attribute": "pattern",
                "reflect": false
            },
            "placeholder": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Instructional text that shows before the input has a value."
                },
                "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"
            },
            "disabled": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "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."
                },
                "attribute": "disabled",
                "reflect": false,
                "defaultValue": "false"
            },
            "readonly": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the element can not mutated, meaning the user can not edit the control."
                },
                "attribute": "readonly",
                "reflect": false,
                "defaultValue": "false"
            },
            "exactNumber": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the input value has 0 as default value"
                },
                "attribute": "exact-number",
                "reflect": false,
                "defaultValue": "false"
            },
            "max": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The maximum value, which must not be less than its minimum (min attribute) value."
                },
                "attribute": "max",
                "reflect": false
            },
            "min": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The minimum value, which must not be greater than its maximum (max attribute) value."
                },
                "attribute": "min",
                "reflect": false
            },
            "debounce": {
                "type": "number",
                "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 `balChange` event after each keystroke. This also impacts form bindings such as `ngModel` or `v-model`."
                },
                "attribute": "debounce",
                "reflect": false,
                "defaultValue": "0"
            },
            "value": {
                "type": "number",
                "mutable": true,
                "complexType": {
                    "original": "number",
                    "resolved": "number | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The value of the input."
                },
                "attribute": "value",
                "reflect": false,
                "defaultValue": "undefined"
            },
            "autoInvalidOff": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, in Angular reactive forms the control will not be set invalid"
                },
                "attribute": "auto-invalid-off",
                "reflect": true,
                "defaultValue": "false"
            }
        };
    }
    static get states() {
        return {
            "focused": {},
            "language": {},
            "region": {},
            "ariaForm": {},
            "nativeInputValue": {},
            "inputPattern": {}
        };
    }
    static get events() {
        return [{
                "method": "balInput",
                "name": "balInput",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a keyboard input occurred."
                },
                "complexType": {
                    "original": "BalEvents.BalNumberInputInputDetail",
                    "resolved": "number | undefined",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balChange",
                "name": "balChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the value has changed."
                },
                "complexType": {
                    "original": "BalEvents.BalNumberInputChangeDetail",
                    "resolved": "number | undefined",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balBlur",
                "name": "balBlur",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the input loses focus."
                },
                "complexType": {
                    "original": "BalEvents.BalNumberInputBlurDetail",
                    "resolved": "FocusEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balFocus",
                "name": "balFocus",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the input has focus."
                },
                "complexType": {
                    "original": "BalEvents.BalNumberInputFocusDetail",
                    "resolved": "FocusEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balKeyPress",
                "name": "balKeyPress",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a keyboard key has pressed."
                },
                "complexType": {
                    "original": "BalEvents.BalNumberInputKeyPressDetail",
                    "resolved": "KeyboardEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "configChanged": {
                "complexType": {
                    "signature": "(state: BalConfigState) => Promise<void>",
                    "parameters": [{
                            "name": "state",
                            "type": "BalConfigState",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "BalConfigState": {
                            "location": "import",
                            "path": "../../utils/config",
                            "id": "src/utils/config/index.ts::BalConfigState"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": "define config for the component"
                        }]
                }
            },
            "setFocus": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets focus on the native `input`. Use this method instead of the global\n`input.focus()`.",
                    "tags": []
                }
            },
            "setBlur": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets blur on the native `input`. Use this method instead of the global\n`input.blur()`.",
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }]
                }
            },
            "getInputElement": {
                "complexType": {
                    "signature": "() => Promise<HTMLInputElement>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "HTMLInputElement": {
                            "location": "global",
                            "id": "global::HTMLInputElement"
                        }
                    },
                    "return": "Promise<HTMLInputElement>"
                },
                "docs": {
                    "text": "Returns the native `<input>` element used under the hood.",
                    "tags": []
                }
            },
            "setAriaForm": {
                "complexType": {
                    "signature": "(ariaForm: BalAriaForm) => Promise<void>",
                    "parameters": [{
                            "name": "ariaForm",
                            "type": "BalAriaForm",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "BalAriaForm": {
                            "location": "import",
                            "path": "../../utils/form",
                            "id": "src/utils/form.ts::BalAriaForm"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }]
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "debounce",
                "methodName": "debounceChanged"
            }, {
                "propName": "value",
                "methodName": "valueChanged"
            }];
    }
    static get listeners() {
        return [{
                "name": "click",
                "method": "listenOnClick",
                "target": "document",
                "capture": true,
                "passive": false
            }, {
                "name": "reset",
                "method": "resetHandler",
                "target": "document",
                "capture": true,
                "passive": false
            }];
    }
}
__decorate([
    Logger('bal-number-input')
], NumberInput.prototype, "createLogger", null);
__decorate([
    ListenToConfig()
], NumberInput.prototype, "configChanged", null);
let numberInputIds = 0;
