import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { getSupportedInputTypes } from '@angular/cdk/platform';
import * as i0 from '@angular/core';
import { forwardRef, EventEmitter, booleanAttribute, Directive, Attribute, Optional, Inject, Input, InjectionToken, Self, NgModule } from '@angular/core';
import * as i1 from '@angular/forms';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators, FormsModule } from '@angular/forms';
import * as i3 from '@koobiq/components/core';
import { ruRUFormattersData, KBQ_DEFAULT_PRECISION_SEPARATOR, normalizeNumber, checkAndNormalizeLocalizedNumber, formatNumberWithLocale, KBQ_LOCALE_SERVICE } from '@koobiq/components/core';
import { KbqFormFieldControl, KbqFormFieldModule } from '@koobiq/components/form-field';
import { Subject } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { V, X, Z, NUMPAD_MINUS, DASH, FF_MINUS, DELETE, BACKSPACE, TAB, ESCAPE, ENTER, LEFT_ARROW, RIGHT_ARROW, HOME, END, isSelectAll, isCopy, isFunctionKey, isNumberKey, isNumpadKey, UP_ARROW, DOWN_ARROW } from '@koobiq/cdk/keycodes';
import { A11yModule } from '@angular/cdk/a11y';
import { KbqIconModule } from '@koobiq/components/icon';

function getKbqInputUnsupportedTypeError(inputType) {
    return Error(`Input type "${inputType}" isn't supported by kbqInput.`);
}

const KBQ_INPUT_NUMBER_DEFAULT_CONFIGURATION = ruRUFormattersData.input.number;
const BIG_STEP = 10;
const SMALL_STEP = 1;
function normalizeSplitter(value) {
    return value ? value.replace(/,/g, KBQ_DEFAULT_PRECISION_SEPARATOR) : value;
}
function isFloat(value) {
    return /^-?\d+\.\d+$/.test(value);
}
function isInt(value) {
    return /^-?\d+$/.test(value);
}
function isDigit(value) {
    return isFloat(value) || isInt(value);
}
function getPrecision(value) {
    const arr = value.toString().split(KBQ_DEFAULT_PRECISION_SEPARATOR);
    return arr.length === 1 ? 1 : Math.pow(10, arr[1].length);
}
function add(value1, value2) {
    const precision = Math.max(getPrecision(value1), getPrecision(value2));
    return (value1 * precision + value2 * precision) / precision;
}
const KBQ_NUMBER_INPUT_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => KbqNumberInput),
    multi: true
};
class KbqNumberInput {
    get value() {
        return this._value;
    }
    set value(value) {
        const oldValue = this.value;
        this._value = value;
        if (oldValue !== value) {
            this.setViewValue(this.formatNumber(value));
            this.valueChange.emit(value);
        }
    }
    get disabled() {
        return this._disabled;
    }
    set disabled(value) {
        const newValue = coerceBooleanProperty(value);
        const element = this.nativeElement;
        if (this._disabled !== newValue) {
            this._disabled = newValue;
            this.disabledChange.emit(newValue);
        }
        // We need to null check the `blur` method, because it's undefined during SSR.
        if (newValue && element.blur) {
            // Normally, native input elements automatically blur if they turn disabled. This behavior
            // is problematic, because it would mean that it triggers another change detection cycle,
            // which then causes a changed after checked error if the input element was focused before.
            element.blur();
        }
    }
    get nativeElement() {
        return this.elementRef.nativeElement;
    }
    get viewValue() {
        return this.nativeElement.value;
    }
    get ngControl() {
        return this.control;
    }
    get fractionSeparator() {
        return this.config.fractionSeparator;
    }
    get groupSeparator() {
        return this.config.groupSeparator;
    }
    constructor(elementRef, renderer, step, bigStep, min, max, localeService) {
        this.elementRef = elementRef;
        this.renderer = renderer;
        this.localeService = localeService;
        /** Emits when the value changes (either due to user input or programmatic change). */
        this.valueChange = new EventEmitter();
        /** Emits when the disabled state has changed */
        this.disabledChange = new EventEmitter();
        this.stateChanges = new Subject();
        /**
         * Allows input and pasting of integers only.
         */
        this.integer = false;
        this.withThousandSeparator = true;
        this._disabled = false;
        this.focused = false;
        this.onTouched = () => { };
        this.isCtrlV = (event) => {
            return event.keyCode === V && (event.ctrlKey || event.metaKey);
        };
        this.isCtrlX = (event) => {
            return event.keyCode === X && (event.ctrlKey || event.metaKey);
        };
        this.isCtrlZ = (event) => {
            return event.keyCode === Z && (event.ctrlKey || event.metaKey);
        };
        this.isPeriod = (event) => {
            return (this.groupSeparator.includes(event.key) ||
                [this.fractionSeparator, KBQ_DEFAULT_PRECISION_SEPARATOR].includes(event.key));
        };
        this.cvaOnChange = () => { };
        this.updateLocaleParams = () => {
            this.config = this.localeService.getParams('input').number;
            this.setViewValue(this.formatNumber(this.value));
        };
        this.step = isDigit(step) ? parseFloat(step) : SMALL_STEP;
        this.bigStep = isDigit(bigStep) ? parseFloat(bigStep) : BIG_STEP;
        this.min = isDigit(min) ? parseFloat(min) : -Infinity;
        this.max = isDigit(max) ? parseFloat(max) : Infinity;
        if ('valueAsNumber' in this.nativeElement) {
            Object.defineProperty(Object.getPrototypeOf(this.nativeElement), 'valueAsNumber', {
                get() {
                    const res = parseFloat(normalizeSplitter(this.value));
                    return isNaN(res) ? null : res;
                }
            });
        }
        this.localeService?.changes.pipe(takeUntilDestroyed()).subscribe(this.updateLocaleParams);
        if (!localeService) {
            this.initDefaultParams();
        }
    }
    ngOnDestroy() {
        this.valueChange.complete();
        this.disabledChange.complete();
    }
    onContainerClick() {
        this.focus();
    }
    focus() {
        this.nativeElement.focus();
    }
    // Implemented as part of ControlValueAccessor.
    writeValue(value) {
        this.value = value;
    }
    // Implemented as part of ControlValueAccessor.
    registerOnChange(fn) {
        this.cvaOnChange = fn;
    }
    // Implemented as part of ControlValueAccessor.
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    // Implemented as part of ControlValueAccessor.
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
    }
    focusChanged(isFocused) {
        if (isFocused !== this.focused) {
            this.focused = isFocused;
            this.stateChanges.next();
        }
    }
    onKeyDown(event) {
        const keyCode = event.keyCode;
        const minuses = [NUMPAD_MINUS, DASH, FF_MINUS];
        const serviceKeys = [DELETE, BACKSPACE, TAB, ESCAPE, ENTER];
        const arrows = [LEFT_ARROW, RIGHT_ARROW];
        const allowedKeys = [HOME, END].concat(arrows).concat(serviceKeys).concat(minuses);
        // should parse normalized fractionSeparator
        const viewValueToBeChecked = normalizeNumber(this.viewValue, this.config);
        const shouldSkipForIntegerMode = this.integer && this.isPeriod(event);
        const isMinusAllowed = minuses.includes(keyCode) && (this.viewValue.includes(event.key) || this.min >= 0);
        const isSignAndFractionSepAlreadyExists = this.isPeriod(event) &&
            [this.fractionSeparator, KBQ_DEFAULT_PRECISION_SEPARATOR].includes(event.key) &&
            viewValueToBeChecked.indexOf(KBQ_DEFAULT_PRECISION_SEPARATOR) !== -1;
        if (shouldSkipForIntegerMode || isMinusAllowed || isSignAndFractionSepAlreadyExists) {
            event.preventDefault();
            return;
        }
        if (allowedKeys.indexOf(keyCode) !== -1 || [
            isSelectAll,
            isCopy,
            this.isCtrlV,
            this.isCtrlX,
            this.isCtrlZ,
            isFunctionKey,
            this.isPeriod
        ].some((fn) => fn(event))) {
            // let it happen, don't do anything
            return;
        }
        const isLetter = !isNumberKey(event) && !isNumpadKey(event);
        // Ensure that it is not a number and stop the keypress
        if (event.shiftKey || isLetter) {
            event.preventDefault();
            // process steps
            const step = event.shiftKey ? this.bigStep : this.step;
            if (keyCode === UP_ARROW) {
                this.stepUp(step);
            }
            if (keyCode === DOWN_ARROW) {
                this.stepDown(step);
            }
        }
    }
    onInput(event) {
        const currentValueLength = this.formatNumber(this.value)?.length || 0;
        const previousSelectionStart = this.nativeElement.selectionStart || 0;
        setTimeout(() => {
            const fromPaste = event.inputType === 'insertFromPaste';
            let formattedValue;
            if (fromPaste) {
                formattedValue = this.formatNumber(this.valueFromPaste);
            }
            else {
                /*this.viewValue is raw and should be reformatted to localized number */
                formattedValue = this.formatViewValue();
                if (this.withThousandSeparator) {
                    const offsetWhenSeparatorAdded = 2;
                    Promise.resolve().then(() => {
                        if (this.value &&
                            Math.abs(this.value) >= 1000 &&
                            Math.abs(this.viewValue.length - currentValueLength) === offsetWhenSeparatorAdded) {
                            // move selection to the left/right if separator was added/removed
                            const cursorPosition = Math.max(0, previousSelectionStart + Math.sign(this.viewValue.length - currentValueLength));
                            this.renderer.setProperty(this.nativeElement, 'selectionStart', cursorPosition);
                            this.renderer.setProperty(this.nativeElement, 'selectionEnd', cursorPosition);
                        }
                    });
                }
            }
            this.setViewValue(formattedValue, !fromPaste);
            if (this.viewValue !== '-') {
                this.viewToModelUpdate(formattedValue);
            }
        });
    }
    onPaste(event) {
        this.valueFromPaste = checkAndNormalizeLocalizedNumber(event.clipboardData?.getData('text'), this.localeService?.id);
        if (this.valueFromPaste === null || isNaN(this.valueFromPaste)) {
            event.preventDefault();
        }
        else if (this.integer && isFloat(this.valueFromPaste.toString())) {
            event.preventDefault();
            const parsedValue = Number.parseInt(this.valueFromPaste.toString());
            this.setViewValue(this.formatNumber(parsedValue));
            this.viewToModelUpdate(parsedValue.toString());
        }
    }
    stepUp(step) {
        this.nativeElement.focus();
        const res = Math.max(Math.min(add(this.value || 0, step), this.max), this.min);
        this.setViewValue(this.formatNumber(res));
        this._value = res;
        this.cvaOnChange(res);
        this.valueChange.emit(res);
    }
    stepDown(step) {
        this.nativeElement.focus();
        const res = Math.min(Math.max(add(this.value || 0, -step), this.min), this.max);
        this.setViewValue(this.formatNumber(res));
        this._value = res;
        this.cvaOnChange(res);
        this.valueChange.emit(res);
    }
    initDefaultParams() {
        this.config = KBQ_INPUT_NUMBER_DEFAULT_CONFIGURATION;
    }
    setViewValue(value, savePosition = false) {
        const cursorPosition = this.nativeElement.selectionStart;
        this.renderer.setProperty(this.nativeElement, 'value', value);
        if (savePosition) {
            this.renderer.setProperty(this.nativeElement, 'selectionStart', cursorPosition);
            this.renderer.setProperty(this.nativeElement, 'selectionEnd', cursorPosition);
        }
    }
    viewToModelUpdate(newValue) {
        const normalizedValue = newValue === null ? null : +normalizeNumber(newValue, this.config);
        if (normalizedValue !== this.value) {
            this._value = normalizedValue;
            this.cvaOnChange(normalizedValue);
            this.valueChange.emit(normalizedValue);
        }
        this.ngControl?.updateValueAndValidity({ emitEvent: false });
    }
    formatViewValue() {
        // we just need to skip the minus sign and not do any formatting
        if (this.viewValue === '-')
            return this.viewValue;
        if (this.viewValue === null ||
            this.viewValue === '' ||
            Number.isNaN(+normalizeNumber(this.viewValue, this.config))) {
            return null;
        }
        const separator = this.groupSeparator.includes(' ') && this.fractionSeparator === ',' ? /[,.]/ : this.fractionSeparator;
        const [intPart, fractionPart] = this.viewValue
            .split(separator)
            .map((valuePart) => normalizeNumber(valuePart, this.config));
        return this.createLocalizedNumberFromParts(+intPart, fractionPart);
    }
    formatNumber(value) {
        if (value === null || value === undefined)
            return null;
        const [intPart, fractionPart] = value.toString().split(KBQ_DEFAULT_PRECISION_SEPARATOR);
        return this.createLocalizedNumberFromParts(+intPart, fractionPart);
    }
    createLocalizedNumberFromParts(intPart, fractionPart) {
        const formatOptions = {
            useGrouping: this.withThousandSeparator,
            maximumFractionDigits: 20
        };
        if (this.withThousandSeparator && this.config.startFormattingFrom) {
            formatOptions.useGrouping = intPart >= Math.pow(10, this.config.startFormattingFrom);
        }
        const localeId = !this.localeService || this.localeService.id === 'es-LA' ? 'ru-RU' : this.localeService.id;
        const formatter = new Intl.NumberFormat(localeId, formatOptions);
        const formattedIntPart = formatNumberWithLocale(intPart, formatter, this.config);
        if (fractionPart === undefined) {
            return formattedIntPart;
        }
        let formattedFractionPart = '';
        for (const numChar of fractionPart) {
            formattedFractionPart += formatter.format(+numChar);
        }
        return `${formattedIntPart}${this.fractionSeparator}${formattedFractionPart}`;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqNumberInput, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: 'step', attribute: true }, { token: 'big-step', attribute: true }, { token: 'min', attribute: true }, { token: 'max', attribute: true }, { token: KBQ_LOCALE_SERVICE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "18.2.14", type: KbqNumberInput, selector: "input[kbqNumberInput]", inputs: { integer: ["integer", "integer", booleanAttribute], bigStep: "bigStep", step: "step", min: "min", max: "max", withThousandSeparator: ["withThousandSeparator", "withThousandSeparator", booleanAttribute], startFormattingFrom: "startFormattingFrom", value: "value", disabled: "disabled" }, host: { listeners: { "blur": "focusChanged(false)", "focus": "focusChanged(true)", "paste": "onPaste($event)", "keydown": "onKeyDown($event)", "input": "onInput($event)" } }, providers: [KBQ_NUMBER_INPUT_VALUE_ACCESSOR], exportAs: ["kbqNumericalInput"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqNumberInput, decorators: [{
            type: Directive,
            args: [{
                    selector: `input[kbqNumberInput]`,
                    exportAs: 'kbqNumericalInput',
                    providers: [KBQ_NUMBER_INPUT_VALUE_ACCESSOR],
                    host: {
                        '(blur)': 'focusChanged(false)',
                        '(focus)': 'focusChanged(true)',
                        '(paste)': 'onPaste($event)',
                        '(keydown)': 'onKeyDown($event)',
                        '(input)': 'onInput($event)'
                    }
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
                    type: Attribute,
                    args: ['step']
                }] }, { type: undefined, decorators: [{
                    type: Attribute,
                    args: ['big-step']
                }] }, { type: undefined, decorators: [{
                    type: Attribute,
                    args: ['min']
                }] }, { type: undefined, decorators: [{
                    type: Attribute,
                    args: ['max']
                }] }, { type: i3.KbqLocaleService, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [KBQ_LOCALE_SERVICE]
                }] }], propDecorators: { integer: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], bigStep: [{
                type: Input
            }], step: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }], withThousandSeparator: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], startFormattingFrom: [{
                type: Input
            }], value: [{
                type: Input
            }], disabled: [{
                type: Input
            }] } });

const KBQ_INPUT_VALUE_ACCESSOR = new InjectionToken('KBQ_INPUT_VALUE_ACCESSOR');

const KBQ_INPUT_INVALID_TYPES = [
    'button',
    'checkbox',
    'file',
    'hidden',
    'image',
    'radio',
    'range',
    'reset',
    'submit'
];
let nextUniqueId$1 = 0;
class KbqInput {
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get disabled() {
        if (this.ngControl && this.ngControl.disabled !== null) {
            return this.ngControl.disabled;
        }
        return this._disabled;
    }
    set disabled(value) {
        this._disabled = coerceBooleanProperty(value);
        // Browsers may not fire the blur event if the input is disabled too quickly.
        // Reset from here to ensure that the element doesn't become stuck.
        if (this.focused) {
            this.focused = false;
            this.stateChanges.next();
        }
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get id() {
        return this._id;
    }
    set id(value) {
        this._id = value || this.uid;
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get required() {
        return this._required;
    }
    set required(value) {
        this._required = coerceBooleanProperty(value);
    }
    /** Input type of the element. */
    get type() {
        return this._type;
    }
    set type(value) {
        this._type = value || 'text';
        this.validateType();
        // When using Angular inputs, developers are no longer able to set the properties on the native
        // input element. To ensure that bindings for `type` work, we need to sync the setter
        // with the native property. Textarea elements don't support the type property or attribute.
        if (getSupportedInputTypes().has(this._type)) {
            this.elementRef.nativeElement.type = this._type;
        }
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get value() {
        return this.inputValueAccessor.value;
    }
    set value(value) {
        if (value !== this.value) {
            this.inputValueAccessor.value = value;
            this.stateChanges.next();
        }
    }
    constructor(elementRef, ngControl, numberInput, parentForm, parentFormGroup, defaultErrorStateMatcher, inputValueAccessor) {
        this.elementRef = elementRef;
        this.ngControl = ngControl;
        this.numberInput = numberInput;
        this.parentForm = parentForm;
        this.parentFormGroup = parentFormGroup;
        this.defaultErrorStateMatcher = defaultErrorStateMatcher;
        /** Whether the component is in an error state. */
        this.errorState = false;
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.focused = false;
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.stateChanges = new Subject();
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.controlType = 'input';
        this.uid = `kbq-input-${nextUniqueId$1++}`;
        this.neverEmptyInputTypes = [
            'date',
            'datetime',
            'datetime-local',
            'month',
            'time',
            'week'
        ].filter((t) => getSupportedInputTypes().has(t));
        this._disabled = false;
        this._required = false;
        this._type = 'text';
        // If no input value accessor was explicitly specified, use the element as the input value accessor.
        this.inputValueAccessor = inputValueAccessor || this.elementRef.nativeElement;
        this.previousNativeValue = this.value;
        // Force setter to be called in case id was not specified.
        this.id = this.id;
    }
    ngOnChanges() {
        this.stateChanges.next();
    }
    ngOnDestroy() {
        this.stateChanges.complete();
    }
    ngDoCheck() {
        if (this.ngControl) {
            // We need to re-evaluate this on every change detection cycle, because there are some
            // error triggers that we can't subscribe to (e.g. parent form submissions). This means
            // that whatever logic is in here has to be super lean or we risk destroying the performance.
            this.updateErrorState();
        }
        // We need to dirty-check the native element's value, because there are some cases where
        // we won't be notified when it changes (e.g. the consumer isn't using forms or they're
        // updating the value using `emitEvent: false`).
        this.dirtyCheckNativeValue();
    }
    updateErrorState() {
        const oldState = this.errorState;
        const parent = this.parentFormGroup || this.parentForm;
        const matcher = this.errorStateMatcher || this.defaultErrorStateMatcher;
        const control = this.ngControl ? this.ngControl.control : null;
        const newState = matcher.isErrorState(control, parent);
        if (newState !== oldState) {
            this.errorState = newState;
            this.stateChanges.next();
        }
    }
    /** Focuses the input. */
    focus() {
        this.elementRef.nativeElement.focus();
    }
    onBlur() {
        this.focusChanged(false);
        if (this.ngControl?.control) {
            const control = this.ngControl.control;
            control.updateValueAndValidity({ emitEvent: false });
            control.statusChanges.emit(control.status);
        }
    }
    /** Callback for the cases where the focused state of the input changes. */
    focusChanged(isFocused) {
        if (this.focused !== isFocused) {
            this.focused = isFocused;
            this.stateChanges.next();
        }
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get empty() {
        return !this.isNeverEmpty() && !this.elementRef.nativeElement.value && !this.isBadInput();
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    onContainerClick() {
        this.focus();
    }
    /** Does some manual dirty checking on the native input `value` property. */
    dirtyCheckNativeValue() {
        const newValue = this.value;
        if (this.previousNativeValue !== newValue) {
            this.previousNativeValue = newValue;
            this.stateChanges.next();
        }
    }
    /** Make sure the input is a supported type. */
    validateType() {
        if (KBQ_INPUT_INVALID_TYPES.indexOf(this._type) > -1) {
            throw getKbqInputUnsupportedTypeError(this._type);
        }
    }
    /** Checks whether the input type is one of the types that are never empty. */
    isNeverEmpty() {
        return this.neverEmptyInputTypes.indexOf(this._type) > -1;
    }
    /** Checks whether the input is invalid based on the native validation. */
    isBadInput() {
        // The `validity` property won't be present on platform-server.
        const validity = this.elementRef.nativeElement.validity;
        return validity?.badInput;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInput, deps: [{ token: i0.ElementRef }, { token: i1.NgControl, optional: true, self: true }, { token: KbqNumberInput, optional: true, self: true }, { token: i1.NgForm, optional: true }, { token: i1.FormGroupDirective, optional: true }, { token: i3.ErrorStateMatcher }, { token: KBQ_INPUT_VALUE_ACCESSOR, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: KbqInput, selector: "input[kbqInput],input[kbqNumberInput]", inputs: { errorStateMatcher: "errorStateMatcher", placeholder: "placeholder", disabled: "disabled", id: "id", required: "required", type: "type", value: "value" }, host: { listeners: { "blur": "onBlur()", "focus": "focusChanged(true)" }, properties: { "attr.id": "id", "attr.placeholder": "placeholder", "attr.disabled": "disabled || null", "required": "required" }, classAttribute: "kbq-input" }, providers: [
            {
                provide: KbqFormFieldControl,
                useExisting: KbqInput
            }
        ], exportAs: ["kbqInput"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInput, decorators: [{
            type: Directive,
            args: [{
                    selector: `input[kbqInput],input[kbqNumberInput]`,
                    exportAs: 'kbqInput',
                    host: {
                        class: 'kbq-input',
                        // Native input properties that are overwritten by Angular inputs need to be synced with
                        // the native input element. Otherwise property bindings for those don't work.
                        '[attr.id]': 'id',
                        '[attr.placeholder]': 'placeholder',
                        '[attr.disabled]': 'disabled || null',
                        '[required]': 'required',
                        '(blur)': 'onBlur()',
                        '(focus)': 'focusChanged(true)'
                    },
                    providers: [
                        {
                            provide: KbqFormFieldControl,
                            useExisting: KbqInput
                        }
                    ]
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.NgControl, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }] }, { type: KbqNumberInput, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }] }, { type: i1.NgForm, decorators: [{
                    type: Optional
                }] }, { type: i1.FormGroupDirective, decorators: [{
                    type: Optional
                }] }, { type: i3.ErrorStateMatcher }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }, {
                    type: Inject,
                    args: [KBQ_INPUT_VALUE_ACCESSOR]
                }] }], propDecorators: { errorStateMatcher: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], disabled: [{
                type: Input
            }], id: [{
                type: Input
            }], required: [{
                type: Input
            }], type: [{
                type: Input
            }], value: [{
                type: Input
            }] } });
class KbqInputMono {
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputMono, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: KbqInputMono, selector: "input[kbqInputMonospace]", host: { classAttribute: "kbq-input_monospace" }, exportAs: ["KbqInputMonospace"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputMono, decorators: [{
            type: Directive,
            args: [{
                    selector: 'input[kbqInputMonospace]',
                    exportAs: 'KbqInputMonospace',
                    host: { class: 'kbq-input_monospace' }
                }]
        }] });

const MIN_VALIDATOR = {
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => MinValidator),
    multi: true
};
/**
 * A directive which installs the MinValidator for any `formControlName`,
 * `formControl`, or control with `ngModel` that also has a `min` attribute.
 */
class MinValidator {
    ngOnChanges(changes) {
        if ('min' in changes) {
            this.createValidator();
            if (this.onChange) {
                this.onChange();
            }
        }
    }
    validate(c) {
        return this.validator(c);
    }
    registerOnValidatorChange(fn) {
        this.onChange = fn;
    }
    createValidator() {
        this.validator = Validators.min(parseInt(this.min, 10));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MinValidator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MinValidator, selector: "[min][formControlName],[min][formControl],[min][ngModel]", inputs: { min: "min" }, host: { properties: { "attr.min": "min ? min : null" } }, providers: [MIN_VALIDATOR], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MinValidator, decorators: [{
            type: Directive,
            args: [{
                    selector: '[min][formControlName],[min][formControl],[min][ngModel]',
                    providers: [MIN_VALIDATOR],
                    host: {
                        '[attr.min]': 'min ? min : null'
                    }
                }]
        }], propDecorators: { min: [{
                type: Input
            }] } });
const MAX_VALIDATOR = {
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => MaxValidator),
    multi: true
};
/**
 * A directive which installs the MaxValidator for any `formControlName`,
 * `formControl`, or control with `ngModel` that also has a `min` attribute.
 */
class MaxValidator {
    ngOnChanges(changes) {
        if ('max' in changes) {
            this.createValidator();
            if (this.onChange) {
                this.onChange();
            }
        }
    }
    validate(c) {
        return this.validator(c);
    }
    registerOnValidatorChange(fn) {
        this.onChange = fn;
    }
    createValidator() {
        this.validator = Validators.max(parseInt(this.max, 10));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaxValidator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MaxValidator, selector: "[max][formControlName],[max][formControl],[max][ngModel]", inputs: { max: "max" }, host: { properties: { "attr.max": "max ? max : null" } }, providers: [MAX_VALIDATOR], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaxValidator, decorators: [{
            type: Directive,
            args: [{
                    selector: '[max][formControlName],[max][formControl],[max][ngModel]',
                    providers: [MAX_VALIDATOR],
                    host: {
                        '[attr.max]': 'max ? max : null'
                    }
                }]
        }], propDecorators: { max: [{
                type: Input
            }] } });

let nextUniqueId = 0;
class KbqInputPassword {
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get disabled() {
        if (this.ngControl?.disabled !== null) {
            return this.ngControl.disabled;
        }
        return this._disabled;
    }
    set disabled(value) {
        this._disabled = coerceBooleanProperty(value);
        // Browsers may not fire the blur event if the input is disabled too quickly.
        // Reset from here to ensure that the element doesn't become stuck.
        if (this.focused) {
            this.focused = false;
            this.stateChanges.next(null);
        }
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get id() {
        return this._id;
    }
    set id(value) {
        this._id = value || this.uid;
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get required() {
        return this._required;
    }
    set required(value) {
        this._required = coerceBooleanProperty(value);
    }
    // this.elementRef.nativeElement.type = this._type;
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get value() {
        return this._inputValueAccessor.value;
    }
    set value(value) {
        if (value === this.value) {
            return;
        }
        this._inputValueAccessor.value = value;
        this.stateChanges.next(null);
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get empty() {
        return !this.elementRef.nativeElement.value && !this.isBadInput();
    }
    constructor(elementRef, ngControl, parentForm, parentFormGroup, defaultErrorStateMatcher, inputValueAccessor) {
        this.elementRef = elementRef;
        this.ngControl = ngControl;
        this.parentForm = parentForm;
        this.parentFormGroup = parentFormGroup;
        this.defaultErrorStateMatcher = defaultErrorStateMatcher;
        /** Whether the component is in an error state. */
        this.errorState = false;
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.focused = false;
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.stateChanges = new Subject();
        this.checkRule = new Subject();
        /**
         * Implemented as part of KbqFormFieldControl.
         * @docs-private
         */
        this.controlType = 'input-password';
        this.elementType = 'password';
        this.uid = `kbq-input-${nextUniqueId++}`;
        this._disabled = false;
        this._required = false;
        // If no input value accessor was explicitly specified, use the element as the input value
        // accessor.
        this._inputValueAccessor = inputValueAccessor || this.elementRef.nativeElement;
        this.previousNativeValue = this.value;
        // Force setter to be called in case id was not specified.
        this.id = this.id;
    }
    ngOnChanges() {
        this.stateChanges.next(null);
    }
    ngOnDestroy() {
        this.stateChanges.complete();
    }
    ngDoCheck() {
        if (this.ngControl) {
            // We need to re-evaluate this on every change detection cycle, because there are some
            // error triggers that we can't subscribe to (e.g. parent form submissions). This means
            // that whatever logic is in here has to be super lean or we risk destroying the performance.
            this.updateErrorState();
        }
        // We need to dirty-check the native element's value, because there are some cases where
        // we won't be notified when it changes (e.g. the consumer isn't using forms or they're
        // updating the value using `emitEvent: false`).
        this.dirtyCheckNativeValue();
    }
    updateErrorState() {
        const oldState = this.errorState;
        const parent = this.parentFormGroup || this.parentForm;
        const matcher = this.errorStateMatcher || this.defaultErrorStateMatcher;
        const control = this.ngControl ? this.ngControl.control : null;
        const newState = matcher.isErrorState(control, parent);
        if (newState !== oldState) {
            this.errorState = newState;
            this.stateChanges.next(null);
        }
    }
    checkRules() {
        this.checkRule.next();
    }
    toggleType() {
        this.elementType = this.elementType === 'password' ? 'text' : 'password';
    }
    /** Focuses the input. */
    focus() {
        this.elementRef.nativeElement.focus();
    }
    onBlur() {
        this.focusChanged(false);
        if (this.ngControl?.control) {
            const control = this.ngControl.control;
            control.updateValueAndValidity({ emitEvent: false });
            control.statusChanges.emit(control.status);
        }
    }
    /** Callback for the cases where the focused state of the input changes. */
    focusChanged(isFocused) {
        if (isFocused === this.focused) {
            return;
        }
        this.focused = isFocused;
        this.stateChanges.next({ focused: this.focused });
    }
    onInput() {
        // This is a noop function and is used to let Angular know whenever the value changes.
        // Angular will run a new change detection each time the `input` event has been dispatched.
        // It's necessary that Angular recognizes the value change, because when floatingLabel
        // is set to false and Angular forms aren't used, the placeholder won't recognize the
        // value changes and will not disappear.
        // Listening to the input event wouldn't be necessary when the input is using the
        // FormsModule or ReactiveFormsModule, because Angular forms also listens to input events.
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    onContainerClick() {
        this.focus();
    }
    /** Does some manual dirty checking on the native input `value` property. */
    dirtyCheckNativeValue() {
        if (this.previousNativeValue !== this.value) {
            this.previousNativeValue = this.value;
            this.stateChanges.next(null);
        }
    }
    /** Checks whether the input is invalid based on the native validation. */
    isBadInput() {
        // The `validity` property won't be present on platform-server.
        return this.elementRef.nativeElement.validity?.badInput;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputPassword, deps: [{ token: i0.ElementRef }, { token: i1.NgControl, optional: true, self: true }, { token: i1.NgForm, optional: true }, { token: i1.FormGroupDirective, optional: true }, { token: i3.ErrorStateMatcher }, { token: KBQ_INPUT_VALUE_ACCESSOR, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: KbqInputPassword, selector: "input[kbqInputPassword]", inputs: { errorStateMatcher: "errorStateMatcher", placeholder: "placeholder", disabled: "disabled", id: "id", required: "required", value: "value" }, host: { listeners: { "blur": "onBlur()", "focus": "focusChanged(true)", "input": "onInput()" }, properties: { "attr.id": "id", "attr.type": "elementType", "attr.placeholder": "placeholder", "attr.disabled": "disabled || null", "required": "required" }, classAttribute: "kbq-input kbq-input-password" }, providers: [
            {
                provide: KbqFormFieldControl,
                useExisting: KbqInputPassword
            }
        ], exportAs: ["kbqInputPassword"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputPassword, decorators: [{
            type: Directive,
            args: [{
                    selector: `input[kbqInputPassword]`,
                    exportAs: 'kbqInputPassword',
                    host: {
                        class: 'kbq-input kbq-input-password',
                        // Native input properties that are overwritten by Angular inputs need to be synced with
                        // the native input element. Otherwise property bindings for those don't work.
                        '[attr.id]': 'id',
                        '[attr.type]': 'elementType',
                        '[attr.placeholder]': 'placeholder',
                        '[attr.disabled]': 'disabled || null',
                        '[required]': 'required',
                        '(blur)': 'onBlur()',
                        '(focus)': 'focusChanged(true)',
                        '(input)': 'onInput()'
                    },
                    providers: [
                        {
                            provide: KbqFormFieldControl,
                            useExisting: KbqInputPassword
                        }
                    ]
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.NgControl, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }] }, { type: i1.NgForm, decorators: [{
                    type: Optional
                }] }, { type: i1.FormGroupDirective, decorators: [{
                    type: Optional
                }] }, { type: i3.ErrorStateMatcher }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }, {
                    type: Inject,
                    args: [KBQ_INPUT_VALUE_ACCESSOR]
                }] }], propDecorators: { errorStateMatcher: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], disabled: [{
                type: Input
            }], id: [{
                type: Input
            }], required: [{
                type: Input
            }], value: [{
                type: Input
            }] } });

class KbqInputModule {
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: KbqInputModule, declarations: [KbqInput,
            KbqNumberInput,
            KbqInputPassword,
            KbqInputMono,
            MinValidator,
            MaxValidator], imports: [A11yModule,
            FormsModule,
            KbqIconModule], exports: [KbqInput,
            KbqNumberInput,
            KbqInputPassword,
            KbqInputMono,
            MinValidator,
            MaxValidator,
            KbqFormFieldModule] }); }
    /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputModule, imports: [A11yModule,
            FormsModule,
            KbqIconModule, KbqFormFieldModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqInputModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        FormsModule,
                        KbqIconModule
                    ],
                    declarations: [
                        KbqInput,
                        KbqNumberInput,
                        KbqInputPassword,
                        KbqInputMono,
                        MinValidator,
                        MaxValidator
                    ],
                    exports: [
                        KbqInput,
                        KbqNumberInput,
                        KbqInputPassword,
                        KbqInputMono,
                        MinValidator,
                        MaxValidator,
                        KbqFormFieldModule
                    ]
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { BIG_STEP, KBQ_INPUT_NUMBER_DEFAULT_CONFIGURATION, KBQ_INPUT_VALUE_ACCESSOR, KBQ_NUMBER_INPUT_VALUE_ACCESSOR, KbqInput, KbqInputModule, KbqInputMono, KbqInputPassword, KbqNumberInput, MAX_VALIDATOR, MIN_VALIDATOR, MaxValidator, MinValidator, SMALL_STEP, add, getPrecision, isDigit, isFloat, isInt, normalizeSplitter };
//# sourceMappingURL=koobiq-components-input.mjs.map
