'use strict';

var index$2 = require('./index-feA4skrY.js');
var tslib_es6 = require('./tslib.es6-DrfCPgT9.js');
var index$3 = require('./index-CuY5w2n4.js');
var index = require('./index-ifu9fMyU.js');
var index$1 = require('./index-BZ67Dcnb.js');
var aria = require('./aria-BzkqcV7W.js');
var attributes = require('./attributes-Cp0kpySW.js');
var bem = require('./bem-p0PWVN8n.js');
var form = require('./form-BSlIiqcR.js');
var formInput = require('./form-input-BwFB4Rej.js');
var helpers = require('./helpers-HuaiWXrB.js');
var log = require('./log-CXDMiw7o.js');
var number = require('./number-Zs70JOU-.js');
var keys_constant = require('./keys.constant-LnwxHAZo.js');
var config_default = require('./config.default-Cko-wpRU.js');
var config_decorator = require('./config.decorator-CUe4yCTd.js');
require('./_commonjsHelpers-BJu3ubxk.js');
require('./browser-BwmkTmm8.js');
require('./config.utils-Civnz2jT.js');

function checkIfValueIsStringAndDoesNotHaveGermanFormat(val) {
    return typeof val === 'string' && number.getThousandSeparator() !== '.';
}
function isNumber(value) {
    const num = parseFloat(value);
    return typeof num === 'number' && !index.isNaN(num);
}
function isNotNumber(value) {
    return !isNumber(value) && value !== '' && value !== number.getNegativeSymbol() && value !== number.getDecimalSeparator();
}
function toNumber(value, decimalPoints = 0) {
    let val = value;
    if (val === '' ||
        val === undefined ||
        val === null ||
        index.isNaN(val) ||
        val === number.getNegativeSymbol() ||
        val === number.getDecimalSeparator() ||
        !isNumber(val)) {
        return undefined;
    }
    if (checkIfValueIsStringAndDoesNotHaveGermanFormat(val)) {
        val = val.split(number.getThousandSeparator()).join('').split('`').join('').split("'").join('');
    }
    if (typeof val === 'string') {
        val = val.replace(number.getDecimalSeparator(), '.');
    }
    return decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val);
}
function toFixedNumber(value, decimalPoints = 0) {
    let val = value;
    if (index$1.isNil(val)) {
        return '';
    }
    if (checkIfValueIsStringAndDoesNotHaveGermanFormat(val)) {
        val = val.split(number.getThousandSeparator()).join('').split('`').join('').split("'").join('');
    }
    if (val.charAt(0) === number.getDecimalSeparator()) {
        val = `0${val}`;
    }
    const num = decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val.replace(number.getDecimalSeparator(), '.'));
    return index.isNaN(num) ? '' : num.toFixed(decimalPoints);
}
function mapDecimalSeparator(value) {
    return value.replace('.', number.getDecimalSeparator());
}
function toUserFormattedNumber(value, decimalPoints = 0, suffix = '') {
    if (index$1.isNil(value)) {
        return '';
    }
    if (value.charAt(0) === number.getDecimalSeparator()) {
        value = `0${value}`;
    }
    const num = decimalPoints === 0 ? parseInt(value, 10) : parseFloat(value);
    const formattedSuffix = suffix !== '' ? ` ${suffix.trim()}` : '';
    return index.isNaN(num) ? '' : number.formatLocaleNumber(num, decimalPoints) + formattedSuffix;
}
const countDecimalSeparators = (value) => (value.split(number.getDecimalSeparator()) || []).length - 1 || 0;
function validateKeyDown({ key, ctrlKey, metaKey, selectionStart, selectionEnd, newValue, decimal, onlyPositive, }) {
    //
    // allow select all, copy and paste
    if (['a', 'c', 'v'].includes(key) && (ctrlKey || metaKey)) {
        return true;
    }
    //
    // only allow negative symbols at the start of the input
    if (key === number.getNegativeSymbol()) {
        if (onlyPositive) {
            return false;
        }
        else {
            if (selectionStart && selectionStart > 0 && selectionEnd && selectionEnd > 0) {
                return false;
            }
        }
    }
    //
    // only allow decimal separator
    if (key === number.getDecimalSeparator()) {
        if (decimal === 0) {
            return false;
        }
        else if (countDecimalSeparators(newValue) > 1) {
            return false;
        }
    }
    //
    // check if it is an allowed key
    if (![...keys_constant.NUMBER_KEYS, ...keys_constant.ACTION_KEYS, number.getDecimalSeparator(), number.getNegativeSymbol()].includes(key)) {
        return false;
    }
    //
    // check if decimal points are reached
    if (decimal !== 0 && newValue.includes(number.getDecimalSeparator()) && [...keys_constant.NUMBER_KEYS].includes(key)) {
        const newValueParts = newValue.split(number.getDecimalSeparator());
        const decimalPart = newValueParts[newValueParts.length - 1];
        if (decimalPart.length > decimal) {
            return false;
        }
    }
    return true;
}

const balNumberInputCss = ".bal-number-input{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:block;position:relative;-ms-flex:1;flex:1;-ms-flex-align:center;align-items:center;width:100%;padding:0;font-family:var(--bal-font-family-text)}";

const NumberInput = class {
    constructor(hostRef) {
        index$2.registerInstance(this, hostRef);
        this.balInput = index$2.createEvent(this, "balInput");
        this.balChange = index$2.createEvent(this, "balChange");
        this.balBlur = index$2.createEvent(this, "balBlur");
        this.balFocus = index$2.createEvent(this, "balFocus");
        this.balKeyPress = index$2.createEvent(this, "balKeyPress");
        this.inputId = `bal-number-input-${numberInputIds++}`;
        this.inheritedAttributes = {};
        this.lastValue = '';
        this.inputValue = this.value;
        this.initialValue = undefined;
        this.focused = false;
        this.language = config_default.defaultConfig.language;
        this.region = config_default.defaultConfig.region;
        this.ariaForm = form.defaultBalAriaForm;
        this.nativeInputValue = '';
        this.inputPattern = this.createPattern();
        /**
         * PUBLIC PROPERTY API
         * ------------------------------------------------------
         */
        /**
         * The name of the control, which is submitted with the form data.
         */
        this.name = this.inputId;
        /**
         * If `true` the component gets a invalid style.
         */
        this.invalid = false;
        /**
         * Defines the allowed decimal points for the `number-input`.
         */
        this.decimal = 0;
        /**
         * Allows only positive number values.
         */
        this.onlyPositive = false;
        /**
         * If `true`, the user must fill in a value before submitting a form.
         */
        this.required = 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 = false;
        /**
         * If `true` the element can not mutated, meaning the user can not edit the control.
         */
        this.readonly = false;
        /**
         * If `true` the input value has 0 as default value
         */
        this.exactNumber = false;
        /**
         * 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`.
         */
        this.debounce = 0;
        /**
         * The value of the input.
         */
        this.value = undefined;
        /**
         * If `true`, in Angular reactive forms the control will not be set invalid
         */
        this.autoInvalidOff = false;
        /**
         * EVENT BINDING
         * ------------------------------------------------------
         */
        this.onClick = (ev) => formInput.inputHandleClick(this, ev);
        this.handleClick = (ev) => formInput.inputHandleHostClick(this, ev);
        this.onInput = (_ev) => {
            var _a;
            //
            // if the new value is not a number, the last value will be restored
            if (this.nativeInput) {
                if (isNotNumber(this.nativeInput.value)) {
                    this.nativeInputValue = this.lastValue || '';
                    return;
                }
            }
            //
            // if new value is accepted the last value gets updated and input event will be fired
            this.lastValue = ((_a = this.nativeInput) === null || _a === void 0 ? void 0 : _a.value) || '';
            this.balInput.emit(toNumber(this.lastValue, this.decimal));
        };
        this.onFocus = (ev) => {
            formInput.inputHandleFocus(this, ev);
            //
            // restore the input with the last user value without the formatting
            if (this.nativeInput) {
                this.nativeInputValue = mapDecimalSeparator(this.lastValue || '');
                clearTimeout(this.selectTimeout);
                this.selectTimeout = setTimeout(() => this.nativeInput.select());
            }
        };
        this.onBlur = (ev) => {
            formInput.inputHandleBlur(this, ev);
            //
            // on focus out the input value gets a pretty format
            if (this.nativeInput) {
                this.lastValue = toFixedNumber(this.lastValueGetter, this.decimal);
                this.nativeInputValue = toUserFormattedNumber(this.lastValueGetter, this.decimal, this.suffix);
                this.nativeInput.value = this.nativeInputValue;
            }
            this.inputValue = toNumber(this.lastValueGetter, this.decimal);
            formInput.inputHandleChange(this);
        };
        this.onKeydown = (ev) => {
            const newValue = formInput.getUpcomingValue(this, ev);
            const input = ev === null || ev === void 0 ? void 0 : ev.target;
            if (input &&
                !validateKeyDown({
                    key: ev.key,
                    ctrlKey: ev.ctrlKey,
                    metaKey: ev.metaKey,
                    decimal: this.decimal,
                    newValue,
                    selectionStart: input.selectionStart,
                    selectionEnd: input.selectionEnd,
                    onlyPositive: this.onlyPositive,
                })) {
                return formInput.stopEventBubbling(ev);
            }
        };
    }
    createLogger(log) {
        this.log = log;
    }
    debounceChanged() {
        this.balChange = helpers.debounceEvent(this.balChange, this.debounce);
    }
    valueChanged(newValue, oldValue) {
        const newValueAsNumber = toNumber(newValue, this.decimal);
        if (newValueAsNumber !== oldValue) {
            const isValueNotDefined = newValueAsNumber === '' || index$1.isNil(newValueAsNumber) || index.isNaN(newValueAsNumber);
            const emptyValue = this.exactNumber ? '0' : '';
            const value = isValueNotDefined ? emptyValue : newValueAsNumber.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);
            }
        }
    }
    /**
     * LIFECYCLE
     * ------------------------------------------------------
     */
    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 = attributes.inheritAttributes(this.el, ['aria-label', 'tabindex', 'title']);
    }
    /**
     * LISTENERS
     * ------------------------------------------------------
     */
    listenOnClick(ev) {
        formInput.inputListenOnClick(this, ev);
    }
    resetHandler(ev) {
        const formElement = ev.target;
        if (formElement === null || formElement === void 0 ? void 0 : formElement.contains(this.el)) {
            formInput.inputHandleReset(this, this.initialValue, this.resetHandlerTimer);
        }
    }
    /**
     * PUBLIC METHODS
     * ------------------------------------------------------
     */
    /**
     * @internal define config for the component
     */
    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);
            }
        }
    }
    /**
     * Sets focus on the native `input`. Use this method instead of the global
     * `input.focus()`.
     */
    async setFocus() {
        formInput.inputSetFocus(this);
    }
    /**
     * Sets blur on the native `input`. Use this method instead of the global
     * `input.blur()`.
     * @internal
     */
    async setBlur() {
        formInput.inputSetBlur(this);
    }
    /**
     * Returns the native `<input>` element used under the hood.
     */
    getInputElement() {
        return Promise.resolve(this.nativeInput);
    }
    /**
     * @internal
     */
    async setAriaForm(ariaForm) {
        this.ariaForm = Object.assign({}, ariaForm);
    }
    /**
     * GETTERS
     * ------------------------------------------------------
     */
    createPattern() {
        if (this.pattern) {
            return this.pattern;
        }
        let suffix = this.suffix || '';
        if (suffix !== '') {
            suffix = ` ${suffix}`;
        }
        const thousandSeparator = number.getThousandSeparator();
        let decimalSeparator = number.getDecimalSeparator();
        if (decimalSeparator === ',') {
            decimalSeparator = '\\,';
        }
        let negativeSymbol = '-';
        if (this.onlyPositive) {
            negativeSymbol = '';
        }
        return `${negativeSymbol}?\\d{1,3}(?:${thousandSeparator}\\d{3})*(?:\\${decimalSeparator}\\d{1,2})?(?:${suffix})?`;
    }
    get lastValueGetter() {
        if (this.exactNumber && (index$1.isNil(this.lastValue) || index$3.isEmpty(this.lastValue))) {
            return '0';
        }
        return this.lastValue;
    }
    /**
     * RENDER
     * ------------------------------------------------------
     */
    render() {
        const block = bem.BEM.block('number-input');
        return (index$2.h(index$2.Host, { key: 'dfe0471d58775e45f8f20a611b1daa12c46c1fbb', onClick: this.handleClick, "aria-disabled": aria.ariaBooleanToString(this.disabled), class: Object.assign({}, block.class()) }, index$2.h("input", Object.assign({ key: 'bfd1c5515ae11030d1efa3f135cc0e2ba53f3bc0', 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": aria.ariaBooleanToString(this.disabled), 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))));
    }
    get el() { return index$2.getElement(this); }
    static get watchers() { return {
        "debounce": ["debounceChanged"],
        "value": ["valueChanged"]
    }; }
};
tslib_es6.__decorate([
    log.Logger('bal-number-input'),
    tslib_es6.__metadata("design:type", Function),
    tslib_es6.__metadata("design:paramtypes", [Function]),
    tslib_es6.__metadata("design:returntype", void 0)
], NumberInput.prototype, "createLogger", null);
tslib_es6.__decorate([
    config_decorator.ListenToConfig(),
    tslib_es6.__metadata("design:type", Function),
    tslib_es6.__metadata("design:paramtypes", [Object]),
    tslib_es6.__metadata("design:returntype", Promise)
], NumberInput.prototype, "configChanged", null);
let numberInputIds = 0;
NumberInput.style = balNumberInputCss;

exports.bal_number_input = NumberInput;
