import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Platform } from '@angular/cdk/platform';
import * as i0 from '@angular/core';
import { InjectionToken, inject, Renderer2, booleanAttribute, numberAttribute, Directive, Optional, Self, Inject, Host, Input, NgModule } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import * as i2 from '@koobiq/components/core';
import { KBQ_WINDOW, KBQ_PARENT_ANIMATION_COMPONENT } from '@koobiq/components/core';
import { KbqFormFieldControl, KbqFormFieldModule } from '@koobiq/components/form-field';
import { Subject } from 'rxjs';
import { delay } from 'rxjs/operators';
import { A11yModule } from '@angular/cdk/a11y';

const KBQ_TEXTAREA_VALUE_ACCESSOR = new InjectionToken('KBQ_TEXTAREA_VALUE_ACCESSOR');
let nextUniqueId = 0;
class KbqTextarea {
    /** Parameter enables or disables the ability to automatically increase the height.
     * If set to false, the textarea becomes vertically resizable. */
    get canGrow() {
        return !this.maxRowLimitReached && this._canGrow;
    }
    set canGrow(value) {
        this._canGrow = value;
    }
    /**
     * 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);
        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);
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get value() {
        return this.valueAccessor.value;
    }
    set value(value) {
        if (value !== this.value) {
            this.valueAccessor.value = value;
            this.stateChanges.next();
        }
    }
    /** Flag that will be set to true when the maximum number of lines is reached.
     * Maximum number of rows can be set using the maxRows input. */
    get maxRowLimitReached() {
        return this.rowsCount > this.maxRows;
    }
    constructor(elementRef, ngControl, parentForm, parentFormGroup, defaultErrorStateMatcher, inputValueAccessor, parent, ngZone) {
        this.elementRef = elementRef;
        this.ngControl = ngControl;
        this.parentForm = parentForm;
        this.parentFormGroup = parentFormGroup;
        this.defaultErrorStateMatcher = defaultErrorStateMatcher;
        this.parent = parent;
        this.ngZone = ngZone;
        /** Whether the component is in an error state. */
        this.errorState = false;
        this.isBrowser = inject(Platform).isBrowser;
        this.renderer = inject(Renderer2);
        this.window = inject(KBQ_WINDOW);
        this._canGrow = true;
        /**
         * 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 = 'textarea';
        this.uid = `kbq-textarea-${nextUniqueId++}`;
        this._disabled = false;
        this._required = false;
        this.lineHeight = 0;
        this.minHeight = 0;
        /** Grow textarea height to avoid vertical scroll  */
        this.grow = () => {
            if (!this.isBrowser || !this._canGrow)
                return;
            this.ngZone.runOutsideAngular(() => {
                const textarea = this.elementRef.nativeElement;
                const clone = textarea.cloneNode(false);
                this.renderer.appendChild(this.renderer.parentNode(textarea), clone);
                const outerHeight = parseInt(this.window.getComputedStyle(textarea).height, 10);
                const diff = outerHeight - +textarea.clientHeight;
                clone.style.minHeight = '0'; // this line is important to height recalculation
                const height = Math.max(this.minHeight, +clone.scrollHeight + diff + this.freeRowsHeight);
                clone.remove();
                this.rowsCount = Math.floor(height / this.lineHeight);
                if (!this.maxRowLimitReached) {
                    textarea.style.minHeight = `${height}px`;
                }
                else if (!textarea.style.minHeight && this.lineHeight) {
                    // need for first initialization when value above maxRows
                    textarea.style.minHeight = `${this.maxRows * this.lineHeight}px`;
                }
            });
        };
        // If no input value accessor was explicitly specified, use the element as the textarea value
        // accessor.
        this.valueAccessor = inputValueAccessor || this.elementRef.nativeElement;
        this.previousNativeValue = this.value;
        // Force setter to be called in case id was not specified.
        this.id = this.id;
        // eslint-disable-next-line @angular-eslint/no-lifecycle-call
        this.parent?.animationDone.subscribe(() => this.ngOnInit());
        this.growSubscription = this.stateChanges.pipe(delay(0)).subscribe(this.grow);
    }
    ngOnInit() {
        if (!this.isBrowser)
            return;
        Promise.resolve().then(() => {
            this.lineHeight = parseInt(this.window.getComputedStyle(this.elementRef.nativeElement).lineHeight, 10);
            const paddingTop = parseInt(this.window.getComputedStyle(this.elementRef.nativeElement).paddingTop, 10);
            const paddingBottom = parseInt(this.window.getComputedStyle(this.elementRef.nativeElement).paddingBottom, 10);
            this.minHeight = this.lineHeight + paddingTop + paddingBottom;
            this.freeRowsHeight = this.freeRowsHeight ?? this.lineHeight;
        });
        setTimeout(this.grow, 0);
    }
    ngOnChanges() {
        this.stateChanges.next();
    }
    ngOnDestroy() {
        this.stateChanges.complete();
        this.growSubscription.unsubscribe();
    }
    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();
        }
    }
    onBlur() {
        this.focusChanged(false);
        if (this.ngControl?.control) {
            const control = this.ngControl.control;
            control.updateValueAndValidity({ emitEvent: false });
            control.statusChanges.emit(control.status);
        }
    }
    /** Focuses the textarea. */
    focus() {
        this.elementRef.nativeElement.focus();
    }
    /** Callback for the cases where the focused state of the textarea changes. */
    focusChanged(isFocused) {
        if (isFocused !== this.focused) {
            this.focused = isFocused;
            this.stateChanges.next();
        }
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    get empty() {
        return !this.elementRef.nativeElement.value && !this.isBadInput();
    }
    /**
     * Implemented as part of KbqFormFieldControl.
     * @docs-private
     */
    onContainerClick() {
        this.focus();
    }
    /** Does some manual dirty checking on the native textarea `value` property. */
    dirtyCheckNativeValue() {
        const newValue = this.value;
        if (this.previousNativeValue !== newValue) {
            this.previousNativeValue = newValue;
            this.stateChanges.next();
        }
    }
    /** Checks whether the textarea 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 && validity.badInput;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqTextarea, deps: [{ token: i0.ElementRef }, { token: i1.NgControl, optional: true, self: true }, { token: i1.NgForm, optional: true }, { token: i1.FormGroupDirective, optional: true }, { token: i2.ErrorStateMatcher }, { token: KBQ_TEXTAREA_VALUE_ACCESSOR, optional: true, self: true }, { token: KBQ_PARENT_ANIMATION_COMPONENT, host: true, optional: true }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
    /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "18.2.14", type: KbqTextarea, selector: "textarea[kbqTextarea]", inputs: { canGrow: ["canGrow", "canGrow", booleanAttribute], maxRows: "maxRows", errorStateMatcher: "errorStateMatcher", disabled: "disabled", id: "id", placeholder: "placeholder", freeRowsHeight: ["freeRowsHeight", "freeRowsHeight", numberAttribute], required: "required", value: "value" }, host: { listeners: { "blur": "onBlur()", "focus": "focusChanged(true)", "paste": "stateChanges.next()" }, properties: { "class.kbq-textarea-resizable": "!canGrow", "class.kbq-textarea_max-row-limit-reached": "maxRowLimitReached", "attr.id": "id", "attr.placeholder": "placeholder", "attr.aria-invalid": "errorState", "disabled": "disabled", "required": "required" }, classAttribute: "kbq-textarea kbq-scrollbar" }, providers: [{ provide: KbqFormFieldControl, useExisting: KbqTextarea }], exportAs: ["kbqTextarea"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqTextarea, decorators: [{
            type: Directive,
            args: [{
                    selector: 'textarea[kbqTextarea]',
                    exportAs: 'kbqTextarea',
                    host: {
                        class: 'kbq-textarea kbq-scrollbar',
                        '[class.kbq-textarea-resizable]': '!canGrow',
                        '[class.kbq-textarea_max-row-limit-reached]': 'maxRowLimitReached',
                        '[attr.id]': 'id',
                        '[attr.placeholder]': 'placeholder',
                        '[attr.aria-invalid]': 'errorState',
                        '[disabled]': 'disabled',
                        '[required]': 'required',
                        '(blur)': 'onBlur()',
                        '(focus)': 'focusChanged(true)',
                        '(paste)': 'stateChanges.next()'
                    },
                    providers: [{ provide: KbqFormFieldControl, useExisting: KbqTextarea }]
                }]
        }], 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: i2.ErrorStateMatcher }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Self
                }, {
                    type: Inject,
                    args: [KBQ_TEXTAREA_VALUE_ACCESSOR]
                }] }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Host
                }, {
                    type: Inject,
                    args: [KBQ_PARENT_ANIMATION_COMPONENT]
                }] }, { type: i0.NgZone }], propDecorators: { canGrow: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], maxRows: [{
                type: Input
            }], errorStateMatcher: [{
                type: Input
            }], disabled: [{
                type: Input
            }], id: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], freeRowsHeight: [{
                type: Input,
                args: [{ transform: numberAttribute }]
            }], required: [{
                type: Input
            }], value: [{
                type: Input
            }] } });

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

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

export { KBQ_TEXTAREA_VALUE_ACCESSOR, KbqTextarea, KbqTextareaModule };
//# sourceMappingURL=koobiq-components-textarea.mjs.map
