import { SharedResizeObserver } from '@angular/cdk/observers/private';
import * as i0 from '@angular/core';
import { InjectionToken, input, numberAttribute, output, viewChild, signal, inject, DestroyRef, ElementRef, Component, ViewEncapsulation, ChangeDetectionStrategy, NgModule } from '@angular/core';
import { toSignal, toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { KbqButtonModule } from '@koobiq/components/button';
import { ruRULocaleData, KBQ_LOCALE_SERVICE } from '@koobiq/components/core';
import { KbqIcon } from '@koobiq/components/icon';
import * as i1 from '@koobiq/components/link';
import { KbqLinkModule } from '@koobiq/components/link';
import { map, of, pairwise, skip, debounceTime } from 'rxjs';

/**
 * Default maximum number of visible rows for the clamped text component
 * before truncation and the "collapse/expand" toggle is shown.
 */
const kbqClampedTextDefaultMaxRows = 5;
/** Localization configuration provider. */
const KBQ_CLAMPED_TEXT_LOCALE_CONFIGURATION = new InjectionToken('KbqClampedTextLocaleConfig', {
    factory: () => ruRULocaleData.clampedText
});
/**
 * Utility provider.
 * @see KBQ_CLAMPED_TEXT_LOCALE_CONFIGURATION
 */
const kbqClampedTextLocaleConfigurationProvider = (configuration) => ({
    provide: KBQ_CLAMPED_TEXT_LOCALE_CONFIGURATION,
    useValue: configuration
});

class KbqClampedText {
    constructor() {
        /**
         * Max rows before text is clamped.
         * @default kbqClampedTextDefaultMaxRows
         */
        this.rows = input(kbqClampedTextDefaultMaxRows);
        /** Collapsed state: `true` = collapsed, `false` = expanded, `undefined` = auto. */
        this.isCollapsed = input();
        /**
         * Debounce time on resize observer when recalculating toggle and text visibility.
         * @default 0
         */
        this.debounceTime = input(0, { transform: numberAttribute });
        /** Emits when collapsed state changes. Used for two-way binding with `isCollapsed`. */
        this.isCollapsedChange = output();
        /** @docs-private */
        this.text = viewChild.required('text');
        /** @docs-private */
        this.textContainer = viewChild.required('textContainer');
        /**
         * This flag controls event emission, aria/css-class calculation
         * @docs-private
         */
        this.collapsedState = signal(undefined);
        /** @docs-private */
        this.isToggleCollapsed = signal(undefined);
        /** @docs-private */
        this.hasToggle = signal(true);
        /** @docs-private */
        this.lineClamp = signal(null);
        this.destroyRef = inject(DestroyRef);
        this.elementRef = inject(ElementRef);
        this.resizeObserver = inject(SharedResizeObserver);
        this.localeService = inject(KBQ_LOCALE_SERVICE, { optional: true });
        /**
         * Clamped text locale configuration.
         * @docs-private
         */
        this.localeConfiguration = toSignal(this.localeService
            ? this.localeService.changes.pipe(map(() => this.localeService.getParams('clampedText')))
            : of(inject(KBQ_CLAMPED_TEXT_LOCALE_CONFIGURATION)));
        /**
         * This flag is used to prevent trigger resize observer on toggle click.
         */
        this.isEventFromToggle = false;
        /**
         * Calculates next collapsed state according to previous one.
         * `undefined` is treated as collapsed and not touched stated.
         */
        this.toggleCollapseState = (state) => {
            const isCollapsed = state ?? true;
            return !isCollapsed;
        };
        toObservable(this.isCollapsed)
            .pipe(pairwise(), takeUntilDestroyed())
            .subscribe(([previous, current]) => {
            this.collapsedState.set(current);
            // store previous collapsed value to reduce unnecessary changes
            this.isToggleCollapsed.set(!this.hasToggle() ? previous : current);
        });
        toObservable(this.collapsedState)
            .pipe(skip(1), takeUntilDestroyed())
            .subscribe((collapsed) => {
            if (collapsed === undefined)
                return;
            this.isCollapsedChange.emit(collapsed);
        });
        toObservable(this.rows)
            .pipe(takeUntilDestroyed())
            .subscribe((rows) => this.lineClamp.set(rows));
    }
    ngAfterViewInit() {
        const textContainer = this.textContainer().nativeElement;
        this.resizeObserver
            .observe(textContainer)
            .pipe(debounceTime(this.debounceTime()), takeUntilDestroyed(this.destroyRef))
            .subscribe(() => {
            this.updateToggleVisibilityState();
            this.updateCollapsedState();
        });
    }
    /** @docs-private */
    toggleIsCollapsed(event) {
        event.stopPropagation();
        this.collapsedState.update((state) => this.toggleCollapseState(this.isToggleCollapsed() ?? state));
        this.isToggleCollapsed.update(this.toggleCollapseState);
        this.isEventFromToggle = true;
        if (this.collapsedState()) {
            setTimeout(() => this.elementRef.nativeElement.scrollIntoView({ block: 'center', inline: 'center' }));
        }
    }
    updateToggleVisibilityState() {
        this.hasToggle.set(this.getRowsCount() > this.rows() + 1);
    }
    updateCollapsedState() {
        if (this.isEventFromToggle) {
            this.isEventFromToggle = false;
            return;
        }
        this.collapsedState.set(this.hasToggle() && (this.isToggleCollapsed() ?? true));
    }
    getRowsCount() {
        const rects = Array.from(this.text().nativeElement.getClientRects());
        return [...new Set(rects.map(({ top }) => top))].length;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqClampedText, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: KbqClampedText, isStandalone: true, selector: "kbq-clamped-text", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, isCollapsed: { classPropertyName: "isCollapsed", publicName: "isCollapsed", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isCollapsedChange: "isCollapsedChange" }, host: { properties: { "attr.aria-expanded": "collapsedState() ? \"false\" : \"true\"" }, classAttribute: "kbq-clamped-text" }, viewQueries: [{ propertyName: "text", first: true, predicate: ["text"], descendants: true, isSignal: true }, { propertyName: "textContainer", first: true, predicate: ["textContainer"], descendants: true, isSignal: true }], exportAs: ["kbqClampedText"], ngImport: i0, template: `
        <div
            #textContainer
            class="kbq-clamped-text__content"
            [style.-webkit-line-clamp]="lineClamp()"
            [style.line-clamp]="lineClamp()"
            [class.kbq-clamped-text__content_collapsed]="collapsedState()"
        >
            <span #text>
                <ng-content />
            </span>
        </div>

        @if (hasToggle()) {
            <span
                class="kbq-clamped-text__toggle"
                kbq-link
                noUnderline
                pseudo
                role="button"
                (click)="toggleIsCollapsed($event)"
                (keydown.enter)="toggleIsCollapsed($event)"
                (keydown.space)="toggleIsCollapsed($event)"
            >
                @let config = localeConfiguration();

                @if (collapsedState()) {
                    <i kbq-icon="kbq-chevron-down_16"></i>
                    <span class="kbq-link__text">{{ config!.openText }}</span>
                } @else {
                    <i kbq-icon="kbq-chevron-up_16"></i>
                    <span class="kbq-link__text">{{ config!.closeText }}</span>
                }
            </span>
        }
    `, isInline: true, styles: [".kbq-clamped-text{display:inline-flex;flex-direction:column}.kbq-clamped-text__content.kbq-clamped-text__content_collapsed{display:-webkit-box;overflow-y:hidden;-webkit-box-orient:vertical}.kbq-clamped-text__toggle{margin-top:var(--kbq-size-xxs);align-self:flex-start}\n"], dependencies: [{ kind: "component", type: KbqIcon, selector: "[kbq-icon]", inputs: ["small", "autoColor", "kbq-icon"] }, { kind: "ngmodule", type: KbqButtonModule }, { kind: "ngmodule", type: KbqLinkModule }, { kind: "directive", type: i1.KbqLink, selector: "[kbq-link]", inputs: ["disabled", "tabIndex", "pseudo", "noUnderline", "big", "compact", "useVisited", "print"], exportAs: ["kbqLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: KbqClampedText, decorators: [{
            type: Component,
            args: [{ selector: 'kbq-clamped-text', standalone: true, exportAs: 'kbqClampedText', imports: [
                        KbqIcon,
                        KbqButtonModule,
                        KbqLinkModule
                    ], template: `
        <div
            #textContainer
            class="kbq-clamped-text__content"
            [style.-webkit-line-clamp]="lineClamp()"
            [style.line-clamp]="lineClamp()"
            [class.kbq-clamped-text__content_collapsed]="collapsedState()"
        >
            <span #text>
                <ng-content />
            </span>
        </div>

        @if (hasToggle()) {
            <span
                class="kbq-clamped-text__toggle"
                kbq-link
                noUnderline
                pseudo
                role="button"
                (click)="toggleIsCollapsed($event)"
                (keydown.enter)="toggleIsCollapsed($event)"
                (keydown.space)="toggleIsCollapsed($event)"
            >
                @let config = localeConfiguration();

                @if (collapsedState()) {
                    <i kbq-icon="kbq-chevron-down_16"></i>
                    <span class="kbq-link__text">{{ config!.openText }}</span>
                } @else {
                    <i kbq-icon="kbq-chevron-up_16"></i>
                    <span class="kbq-link__text">{{ config!.closeText }}</span>
                }
            </span>
        }
    `, host: {
                        class: 'kbq-clamped-text',
                        '[attr.aria-expanded]': 'collapsedState() ? "false" : "true"'
                    }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".kbq-clamped-text{display:inline-flex;flex-direction:column}.kbq-clamped-text__content.kbq-clamped-text__content_collapsed{display:-webkit-box;overflow-y:hidden;-webkit-box-orient:vertical}.kbq-clamped-text__toggle{margin-top:var(--kbq-size-xxs);align-self:flex-start}\n"] }]
        }], ctorParameters: () => [] });

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

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

export { KBQ_CLAMPED_TEXT_LOCALE_CONFIGURATION, KbqClampedText, KbqClampedTextModule, kbqClampedTextDefaultMaxRows, kbqClampedTextLocaleConfigurationProvider };
//# sourceMappingURL=koobiq-components-clamped-text.mjs.map
