import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client';
import { _ as __decorate, a as __metadata } from './tslib.es6.js';
import { i as isEmpty } from './index4.js';
import { i as isNaN } from './index6.js';
import { i as isNil } from './index2.js';
import { a as ariaBooleanToString } from './aria.js';
import { i as inheritAttributes } from './attributes.js';
import { B as BEM } from './bem.js';
import { d as defaultBalAriaForm } from './form.js';
import { f as inputHandleClick, b as inputHandleHostClick, c as inputHandleFocus, d as inputHandleBlur, e as inputHandleChange, l as getUpcomingValue, s as stopEventBubbling, j as inputListenOnClick, k as inputHandleReset, i as inputSetFocus, a as inputSetBlur } from './form-input.js';
import { k as debounceEvent } from './helpers.js';
import { L as Logger } from './log.js';
import { g as getNegativeSymbol, a as getDecimalSeparator, b as getThousandSeparator, f as formatLocaleNumber } from './number.js';
import { N as NUMBER_KEYS, A as ACTION_KEYS } from './keys.constant.js';
import { d as defaultConfig } from './config.default.js';
import { L as ListenToConfig } from './config.decorator.js';

function checkIfValueIsStringAndDoesNotHaveGermanFormat(val) {
    return typeof val === 'string' && getThousandSeparator() !== '.';
}
function isNumber(value) {
    const num = parseFloat(value);
    return typeof num === 'number' && !isNaN(num);
}
function isNotNumber(value) {
    return !isNumber(value) && value !== '' && value !== getNegativeSymbol() && value !== getDecimalSeparator();
}
function toNumber(value, decimalPoints = 0) {
    let val = value;
    if (val === '' ||
        val === undefined ||
        val === null ||
        isNaN(val) ||
        val === getNegativeSymbol() ||
        val === getDecimalSeparator() ||
        !isNumber(val)) {
        return undefined;
    }
    if (checkIfValueIsStringAndDoesNotHaveGermanFormat(val)) {
        val = val.split(getThousandSeparator()).join('').split('`').join('').split("'").join('');
    }
    if (typeof val === 'string') {
        val = val.replace(getDecimalSeparator(), '.');
    }
    return decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val);
}
function toFixedNumber(value, decimalPoints = 0) {
    let val = value;
    if (isNil(val)) {
        return '';
    }
    if (checkIfValueIsStringAndDoesNotHaveGermanFormat(val)) {
        val = val.split(getThousandSeparator()).join('').split('`').join('').split("'").join('');
    }
    if (val.charAt(0) === getDecimalSeparator()) {
        val = `0${val}`;
    }
    const num = decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val.replace(getDecimalSeparator(), '.'));
    return isNaN(num) ? '' : num.toFixed(decimalPoints);
}
function mapDecimalSeparator(value) {
    return value.replace('.', getDecimalSeparator());
}
function toUserFormattedNumber(value, decimalPoints = 0, suffix = '') {
    if (isNil(value)) {
        return '';
    }
    if (value.charAt(0) === getDecimalSeparator()) {
        value = `0${value}`;
    }
    const num = decimalPoints === 0 ? parseInt(value, 10) : parseFloat(value);
    const formattedSuffix = suffix !== '' ? ` ${suffix.trim()}` : '';
    return isNaN(num) ? '' : formatLocaleNumber(num, decimalPoints) + formattedSuffix;
}
const countDecimalSeparators = (value) => (value.split(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 === getNegativeSymbol()) {
        if (onlyPositive) {
            return false;
        }
        else {
            if (selectionStart && selectionStart > 0 && selectionEnd && selectionEnd > 0) {
                return false;
            }
        }
    }
    //
    // only allow decimal separator
    if (key === getDecimalSeparator()) {
        if (decimal === 0) {
            return false;
        }
        else if (countDecimalSeparators(newValue) > 1) {
            return false;
        }
    }
    //
    // check if it is an allowed key
    if (![...NUMBER_KEYS, ...ACTION_KEYS, getDecimalSeparator(), getNegativeSymbol()].includes(key)) {
        return false;
    }
    //
    // check if decimal points are reached
    if (decimal !== 0 && newValue.includes(getDecimalSeparator()) && [...NUMBER_KEYS].includes(key)) {
        const newValueParts = newValue.split(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 = /*@__PURE__*/ proxyCustomElement(class NumberInput extends HTMLElement {
    constructor(registerHost) {
        super();
        if (registerHost !== false) {
            this.__registerHost();
        }
        this.balInput = createEvent(this, "balInput", 7);
        this.balChange = createEvent(this, "balChange", 7);
        this.balBlur = createEvent(this, "balBlur", 7);
        this.balFocus = createEvent(this, "balFocus", 7);
        this.balKeyPress = createEvent(this, "balKeyPress", 7);
        this.inputId = `bal-number-input-${numberInputIds++}`;
        this.inheritedAttributes = {};
        this.lastValue = '';
        this.inputValue = this.value;
        this.initialValue = undefined;
        this.focused = false;
        this.language = defaultConfig.language;
        this.region = defaultConfig.region;
        this.ariaForm = 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) => inputHandleClick(this, ev);
        this.handleClick = (ev) => 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) => {
            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) => {
            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);
            inputHandleChange(this);
        };
        this.onKeydown = (ev) => {
            const newValue = 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 stopEventBubbling(ev);
            }
        };
    }
    createLogger(log) {
        this.log = log;
    }
    debounceChanged() {
        this.balChange = debounceEvent(this.balChange, this.debounce);
    }
    valueChanged(newValue, oldValue) {
        const newValueAsNumber = toNumber(newValue, this.decimal);
        if (newValueAsNumber !== oldValue) {
            const isValueNotDefined = newValueAsNumber === '' || isNil(newValueAsNumber) || 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 = inheritAttributes(this.el, ['aria-label', 'tabindex', 'title']);
    }
    /**
     * LISTENERS
     * ------------------------------------------------------
     */
    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);
        }
    }
    /**
     * 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() {
        inputSetFocus(this);
    }
    /**
     * Sets blur on the native `input`. Use this method instead of the global
     * `input.blur()`.
     * @internal
     */
    async setBlur() {
        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 = getThousandSeparator();
        let decimalSeparator = 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 && (isNil(this.lastValue) || isEmpty(this.lastValue))) {
            return '0';
        }
        return this.lastValue;
    }
    /**
     * RENDER
     * ------------------------------------------------------
     */
    render() {
        const block = BEM.block('number-input');
        return (h(Host, { key: 'dfe0471d58775e45f8f20a611b1daa12c46c1fbb', onClick: this.handleClick, "aria-disabled": ariaBooleanToString(this.disabled), class: Object.assign({}, block.class()) }, 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": 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 this; }
    static get watchers() { return {
        "debounce": ["debounceChanged"],
        "value": ["valueChanged"]
    }; }
    static get style() { return balNumberInputCss; }
}, [256, "bal-number-input", {
        "name": [1],
        "invalid": [4],
        "decimal": [2],
        "onlyPositive": [4, "only-positive"],
        "suffix": [1],
        "pattern": [1],
        "placeholder": [1],
        "required": [4],
        "disabled": [4],
        "readonly": [4],
        "exactNumber": [4, "exact-number"],
        "max": [1],
        "min": [1],
        "debounce": [2],
        "value": [1032],
        "autoInvalidOff": [516, "auto-invalid-off"],
        "focused": [32],
        "language": [32],
        "region": [32],
        "ariaForm": [32],
        "nativeInputValue": [32],
        "inputPattern": [32],
        "configChanged": [64],
        "setFocus": [64],
        "setBlur": [64],
        "getInputElement": [64],
        "setAriaForm": [64]
    }, [[6, "click", "listenOnClick"], [6, "reset", "resetHandler"]], {
        "debounce": ["debounceChanged"],
        "value": ["valueChanged"]
    }]);
__decorate([
    Logger('bal-number-input'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function]),
    __metadata("design:returntype", void 0)
], NumberInput.prototype, "createLogger", null);
__decorate([
    ListenToConfig(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], NumberInput.prototype, "configChanged", null);
let numberInputIds = 0;
function defineCustomElement$1() {
    if (typeof customElements === "undefined") {
        return;
    }
    const components = ["bal-number-input"];
    components.forEach(tagName => { switch (tagName) {
        case "bal-number-input":
            if (!customElements.get(tagName)) {
                customElements.define(tagName, NumberInput);
            }
            break;
    } });
}

const BalNumberInput = NumberInput;
const defineCustomElement = defineCustomElement$1;

export { BalNumberInput, defineCustomElement };
