import { NgTemplateOutlet, NgClass } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, Injector, Injectable, input, model, ViewContainerRef, viewChild, contentChildren, computed, Component } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';
import { TemplateDirective } from '@seniorsistemas/angular-components/template';
import { Subject, take, takeUntil } from 'rxjs';
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { Overlay } from '@angular/cdk/overlay';

class ActiveDialog {
    dialogSize = 'md';
    escapeOnEsc = true;
    _close = new Subject();
    _dismiss = new Subject();
    close(result) {
        this._close.next(result);
        this._close.complete();
    }
    dismiss(reason) {
        this._dismiss.next(reason);
        this._dismiss.complete();
    }
    onClose$ = this._close.asObservable();
    onDismiss$ = this._dismiss.asObservable();
}

class DialogService {
    activeDialogInstances = [];
    overlay = inject(Overlay);
    injector = inject(Injector);
    constructor() {
        this.checkEscapeEvent();
    }
    open(componentRef, options) {
        const createdComponent = this.createComponent(componentRef, options);
        return {
            componentInstance: createdComponent.componentInstance,
            closed: createdComponent.activeDialog.onClose$,
            dismissed: createdComponent.activeDialog.onDismiss$,
        };
    }
    createComponent(componentRef, dialogOptions) {
        const injector = this.dialogInjector;
        const overlayRef = this.getOverlayRef();
        const portal = new ComponentPortal(componentRef, null, injector);
        const _componentRef = overlayRef.attach(portal);
        const activeDialog = this.getActiveDialog(injector);
        this.activeDialogInstances.push(activeDialog);
        this.applyDialogOptionsIntoActiveDialog(activeDialog, dialogOptions);
        this.handleDialogDestroyEvents(injector, overlayRef, dialogOptions);
        return {
            activeDialog,
            componentInstance: _componentRef.instance,
        };
    }
    handleDialogDestroyEvents(injector, overlayRef, dialogOptions) {
        const destroyClickOutside = dialogOptions?.destroyClickOutside ?? true;
        const activeDialog = this.getActiveDialog(injector);
        const destroyDialog = () => {
            overlayRef.dispose();
            const index = this.activeDialogInstances.indexOf(activeDialog);
            this.activeDialogInstances.splice(index, 1);
        };
        if (destroyClickOutside) {
            overlayRef
                .backdropClick()
                .pipe(take(1))
                .subscribe(() => {
                activeDialog.dismiss();
            });
        }
        activeDialog.onClose$.pipe(take(1)).subscribe(destroyDialog);
        activeDialog.onDismiss$.pipe(take(1)).subscribe(destroyDialog);
    }
    applyDialogOptionsIntoActiveDialog(activeDialog, dialogOptions) {
        activeDialog.dialogSize = dialogOptions?.size ?? 'md';
        activeDialog.escapeOnEsc = dialogOptions?.escapeOnEsc ?? true;
    }
    getOverlayRef() {
        const panelClass = ['h-min'];
        const overlayRef = this.overlay.create({
            hasBackdrop: true,
            positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
            panelClass,
        });
        return overlayRef;
    }
    getActiveDialog(injector) {
        return injector.get(ActiveDialog);
    }
    get dialogInjector() {
        const injector = Injector.create({
            providers: [{ provide: ActiveDialog, useClass: ActiveDialog }],
            parent: this.injector,
        });
        return injector;
    }
    checkEscapeEvent() {
        document.body.addEventListener('keydown', (keyboardEvent) => {
            const isEscapePressed = keyboardEvent.key === 'Escape';
            if (!isEscapePressed) {
                return;
            }
            const lastActiveModal = this.activeDialogInstances.at(this.activeDialogInstances.length - 1);
            if (lastActiveModal && lastActiveModal.escapeOnEsc) {
                lastActiveModal.dismiss();
            }
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DialogService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DialogService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

class InternalDialogService extends DialogService {
    constructor() {
        super();
    }
    openByTemplate(templateRef, viewContainerRef, dialogOptions) {
        const injector = this.dialogInjector;
        const overlayRef = this.getOverlayRef();
        const portal = new TemplatePortal(templateRef, viewContainerRef);
        overlayRef.attach(portal);
        const activeDialog = this.getActiveDialog(injector);
        this.applyDialogOptionsIntoActiveDialog(activeDialog, dialogOptions);
        this.activeDialogInstances.push(activeDialog);
        this.handleDialogDestroyEvents(injector, overlayRef, dialogOptions);
        return activeDialog;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InternalDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InternalDialogService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InternalDialogService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

/**
 * @description Componente de diálogo modal que exibe conteúdo em uma janela sobreposta.
 * Suporta templates customizados para cabeçalho, corpo e rodapé via `sTemplate`.
 * Pode ser utilizado de forma declarativa (via `visible`) ou programática (via `DialogService`).
 *
 * @example
 * ```html
 * <!-- Modo declarativo -->
 * <s-dialog header="Título" [(visible)]="showDialog">
 *   <ng-template sTemplate="body">Conteúdo</ng-template>
 *   <ng-template sTemplate="footer"><button (click)="showDialog = false">Fechar</button></ng-template>
 * </s-dialog>
 * ```
 *
 * @category Overlay
 */
class DialogComponent {
    /** @description Texto exibido no cabeçalho do diálogo. Pode ser substituído por um template com `sTemplate="header"`. */
    header = input();
    /**
     * @description Controla a visibilidade do diálogo. Suporta two-way binding via `[(visible)]`.
     * Ao definir `true`, o diálogo é aberto; ao definir `false`, é fechado.
     * Não deve ser usado em conjunto com `DialogService` (modo programático).
     */
    visible = model(undefined);
    /**
     * @description Instância do diálogo ativo.
     * Em modo programático (aberto via `DialogService`): injetado do pai via `skipSelf`.
     * Em modo declarativo (via `visible`): atribuído internamente ao abrir.
     *
     * O `providers` do componente sobrescreve o token com `null` para que descendentes
     * (ex: `s-dialog` dentro de um lookup dentro deste diálogo) não herdem esta instância.
     */
    activeDialog = inject(ActiveDialog, { optional: true, skipSelf: true });
    /**
     * @description Tamanho do diálogo.
     * Valores aceitos: `'sm'`, `'md'` (padrão), `'lg'`, `'xl'`, `'fullscreen'`.
     *
     * @default 'md'
     */
    size = input('md');
    /**
     * @description Classes CSS adicionais aplicadas ao container de conteúdo do diálogo.
     *
     * @default ''
     */
    contentClassName = input('');
    /**
     * @description Quando `true`, pressionar a tecla Escape fecha o diálogo.
     *
     * @default true
     */
    escapeOnEsc = input(true);
    /**
     * @description Quando `true`, clicar fora do diálogo (no backdrop) fecha o diálogo.
     *
     * @default true
     */
    destroyClickOutside = input(true);
    /**
     * @description Valor do aria-label do botão de fechar a dialog.
     *
     * @default 'Fechar'
     */
    closeAriaLabel = input('Fechar');
    viewContainerRef = inject(ViewContainerRef);
    dialogService = inject(InternalDialogService);
    template = viewChild('dialogTemplate');
    templateDiretives = contentChildren(TemplateDirective);
    onDestroy$ = new Subject();
    headerTemplate = computed(() => {
        return this.getTemplate('header');
    });
    bodyTemplate = computed(() => {
        return this.getTemplate('body');
    });
    footerTemplate = computed(() => {
        return this.getTemplate('footer');
    });
    constructor() {
        toObservable(this.visible)
            .pipe(takeUntil(this.onDestroy$))
            .subscribe(() => {
            const visible = this.visible();
            if (visible === true) {
                this.openDialog();
            }
            else if (visible === false) {
                this.activeDialog?.dismiss();
            }
        });
    }
    ngAfterViewInit() {
        if (!this.header() && !this.headerTemplate()) {
            console.error('Header is required. provide header via input or sTemplate="header"');
        }
        if (this.activeDialog && this.visible() !== undefined) {
            throw new Error('[s-dialog] O input "visible" não pode ser usado em um componente aberto via DialogService. ' +
                'Remova o binding "[visible]" — o ciclo de vida deste diálogo já é controlado pelo ActiveDialog injetado.');
        }
    }
    ngOnDestroy() {
        this.onDestroy$.next();
        this.onDestroy$.complete();
    }
    onDismiss() {
        this.activeDialog?.dismiss();
    }
    getTemplate(templateType) {
        return this.templateDiretives().find((template) => template.type === templateType)?.template;
    }
    openDialog() {
        this.activeDialog = this.dialogService.openByTemplate(this.template(), this.viewContainerRef, {
            size: this.size(),
            destroyClickOutside: this.destroyClickOutside(),
            escapeOnEsc: this.escapeOnEsc(),
        });
        this.activeDialog.onClose$.pipe(take(1)).subscribe(() => {
            this.activeDialog = null;
            this.visible.set(false);
        });
        this.activeDialog.onDismiss$.pipe(take(1)).subscribe(() => {
            this.activeDialog = null;
            this.visible.set(false);
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DialogComponent, isStandalone: true, selector: "s-dialog", inputs: { header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, contentClassName: { classPropertyName: "contentClassName", publicName: "contentClassName", isSignal: true, isRequired: false, transformFunction: null }, escapeOnEsc: { classPropertyName: "escapeOnEsc", publicName: "escapeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, destroyClickOutside: { classPropertyName: "destroyClickOutside", publicName: "destroyClickOutside", isSignal: true, isRequired: false, transformFunction: null }, closeAriaLabel: { classPropertyName: "closeAriaLabel", publicName: "closeAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, providers: [{ provide: ActiveDialog, useValue: null }], queries: [{ propertyName: "templateDiretives", predicate: TemplateDirective, isSignal: true }], viewQueries: [{ propertyName: "template", first: true, predicate: ["dialogTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "@let activeDialogContext = { activeDialog: activeDialog };\n<ng-template #dialogTemplate>\n    @if (activeDialog) {\n        @let isSm = activeDialog.dialogSize === 'sm';\n        @let isMd = activeDialog.dialogSize === 'md';\n        @let isLg = activeDialog.dialogSize === 'lg';\n        @let isXl = activeDialog.dialogSize === 'xl';\n        @let isFullscreen = activeDialog.dialogSize === 'fullscreen';\n\n        <div\n            [class]=\"contentClassName()\"\n            class=\"max-sm:h-screen max-sm:w-screen flex h-min max-h-screen w-screen flex-col bg-grayscale-0 border border-grayscale-20 shadow-[0_8px_10px_rgba(0,0,0,0.2),0_6px_30px_rgba(0,0,0,0.12),0_16px_24px_rgba(0,0,0,0.14)]\"\n            [ngClass]=\"{\n                'max-w-[300px]': isSm,\n                'max-w-[500px]': isMd,\n                'max-w-[800px]': isLg,\n                'max-w-[1140px]': isXl,\n                'h-screen': isFullscreen,\n            }\"\n            role=\"dialog\"\n            aria-modal=\"true\"\n            aria-labelledby=\"dialog-main-title\"\n        >\n            <header class=\"flex flex-wrap justify-between gap-4 p-4 pb-3\">\n                @let _header = headerTemplate();\n                @if (_header) {\n                    <ng-container *ngTemplateOutlet=\"_header; context: activeDialogContext\"></ng-container>\n                } @else {\n                    <h3\n                        id=\"dialog-main-title\"\n                        class=\"text-base font-open-sans font-normal uppercase text-grayscale-90\"\n                    >\n                        {{ header() }}\n                    </h3>\n                }\n\n                <button\n                    type=\"button\"\n                    class=\"rounded-medium flex cursor-pointer items-center justify-center border-none bg-transparent p-2 outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n                    [attr.aria-label]=\"closeAriaLabel()\"\n                    (click)=\"onDismiss()\"\n                >\n                    <i\n                        class=\"fas fa-times text-xs\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </button>\n            </header>\n            <hr class=\"mx-4 h-[1px] border-0 bg-grayscale-20\" />\n\n            <main class=\"flex-1 p-4\">\n                @let _bodyTemplate = bodyTemplate();\n                @if (_bodyTemplate) {\n                    <ng-container *ngTemplateOutlet=\"_bodyTemplate; context: activeDialogContext\"></ng-container>\n                } @else {\n                    <ng-content></ng-content>\n                }\n            </main>\n\n            @let footer = footerTemplate();\n            @if (footer) {\n                <footer class=\"flex flex-wrap gap-3 p-4 pt-0\">\n                    <hr class=\"h-[1px] w-full border-0 bg-grayscale-20\" />\n                    <div class=\"flex w-full flex-wrap justify-end gap-5\">\n                        <ng-container *ngTemplateOutlet=\"footer; context: activeDialogContext\"></ng-container>\n                    </div>\n                </footer>\n            }\n        </div>\n    }\n</ng-template>\n\n@if (visible() === undefined) {\n    <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DialogComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-dialog', standalone: true, imports: [NgTemplateOutlet, InteractiveContentDirective, NgClass], providers: [{ provide: ActiveDialog, useValue: null }], template: "@let activeDialogContext = { activeDialog: activeDialog };\n<ng-template #dialogTemplate>\n    @if (activeDialog) {\n        @let isSm = activeDialog.dialogSize === 'sm';\n        @let isMd = activeDialog.dialogSize === 'md';\n        @let isLg = activeDialog.dialogSize === 'lg';\n        @let isXl = activeDialog.dialogSize === 'xl';\n        @let isFullscreen = activeDialog.dialogSize === 'fullscreen';\n\n        <div\n            [class]=\"contentClassName()\"\n            class=\"max-sm:h-screen max-sm:w-screen flex h-min max-h-screen w-screen flex-col bg-grayscale-0 border border-grayscale-20 shadow-[0_8px_10px_rgba(0,0,0,0.2),0_6px_30px_rgba(0,0,0,0.12),0_16px_24px_rgba(0,0,0,0.14)]\"\n            [ngClass]=\"{\n                'max-w-[300px]': isSm,\n                'max-w-[500px]': isMd,\n                'max-w-[800px]': isLg,\n                'max-w-[1140px]': isXl,\n                'h-screen': isFullscreen,\n            }\"\n            role=\"dialog\"\n            aria-modal=\"true\"\n            aria-labelledby=\"dialog-main-title\"\n        >\n            <header class=\"flex flex-wrap justify-between gap-4 p-4 pb-3\">\n                @let _header = headerTemplate();\n                @if (_header) {\n                    <ng-container *ngTemplateOutlet=\"_header; context: activeDialogContext\"></ng-container>\n                } @else {\n                    <h3\n                        id=\"dialog-main-title\"\n                        class=\"text-base font-open-sans font-normal uppercase text-grayscale-90\"\n                    >\n                        {{ header() }}\n                    </h3>\n                }\n\n                <button\n                    type=\"button\"\n                    class=\"rounded-medium flex cursor-pointer items-center justify-center border-none bg-transparent p-2 outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n                    [attr.aria-label]=\"closeAriaLabel()\"\n                    (click)=\"onDismiss()\"\n                >\n                    <i\n                        class=\"fas fa-times text-xs\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </button>\n            </header>\n            <hr class=\"mx-4 h-[1px] border-0 bg-grayscale-20\" />\n\n            <main class=\"flex-1 p-4\">\n                @let _bodyTemplate = bodyTemplate();\n                @if (_bodyTemplate) {\n                    <ng-container *ngTemplateOutlet=\"_bodyTemplate; context: activeDialogContext\"></ng-container>\n                } @else {\n                    <ng-content></ng-content>\n                }\n            </main>\n\n            @let footer = footerTemplate();\n            @if (footer) {\n                <footer class=\"flex flex-wrap gap-3 p-4 pt-0\">\n                    <hr class=\"h-[1px] w-full border-0 bg-grayscale-20\" />\n                    <div class=\"flex w-full flex-wrap justify-end gap-5\">\n                        <ng-container *ngTemplateOutlet=\"footer; context: activeDialogContext\"></ng-container>\n                    </div>\n                </footer>\n            }\n        </div>\n    }\n</ng-template>\n\n@if (visible() === undefined) {\n    <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n}\n" }]
        }], ctorParameters: () => [] });

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

export { ActiveDialog, DialogComponent, DialogService };
//# sourceMappingURL=seniorsistemas-angular-components-dialog.mjs.map
