import { Overlay } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import * as i0 from '@angular/core';
import { inject, signal, DOCUMENT, viewChild, ChangeDetectionStrategy, Component, ApplicationRef, Injector, EnvironmentInjector, TemplateRef, createComponent, Injectable } from '@angular/core';
import { areAnimationsDisabled, isRTL } from '@siemens/element-ng/common';
import { SiTranslateService, SiNoTranslateService } from '@siemens/element-translate-ng/translate';
import { Subject, ReplaySubject } from 'rxjs';
import * as i1 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';

/**
 * Reference to a modal dialog
 * @typeParam T - the type of the content
 * @typeParam CT - the close type
 */
class ModalRef {
    /** Emits the close value when the modal is hidden. */
    hidden = new Subject();
    /** Allows messaging state to consumer w/o closing the dialog. */
    message = new Subject();
    /** Emits the modal element reference when it is shown. */
    shown = new ReplaySubject(1);
    /**
     * The modal options passed during creation.
     *
     * @defaultValue `{}`
     * @see {@link SiModalService}
     */
    data = {};
    /** @defaultValue true */
    ignoreBackdropClick = true;
    /**
     * Custom class for modal-dialog
     *
     * @defaultValue ''
     */
    dialogClass = '';
    /**
     * The layer of the modal. The modal with the highest layer will be shown on top.
     *
     * @defaultValue 0
     */
    layer = 0;
    /** The default close value of the modal. */
    closeValue;
    get content() {
        return undefined;
    }
    /** Set the input of a component shown in the modal. */
    setInput(input, value) { }
    /**
     * @defaultValue `() => false`
     */
    isCurrent = () => false;
    /**
     * @defaultValue `() => {}`
     */
    detach = () => { };
    /**
     * Close the modal with a custom close value.
     *
     * @defaultValue `() => {}`
     */
    hide = () => { };
    /**
     * When `data.disableAutoHide` is set, messages the `reason`, otherwise calls {@link hide}.
     */
    messageOrHide(reason) {
        if (this.data.messageInsteadOfAutoHide) {
            this.message.next(reason);
        }
        else {
            this.hide(reason);
        }
    }
}

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiModalComponent {
    modalRef = inject((ModalRef));
    isAnimated = !areAnimationsDisabled() && this.modalRef.data.animated !== false;
    dialogClass = this.modalRef.dialogClass ?? '';
    titleId = this.modalRef.data?.ariaLabelledBy ?? '';
    init = false;
    show = signal(false, ...(ngDevMode ? [{ debugName: "show" }] : []));
    showBackdropClass = signal(undefined, ...(ngDevMode ? [{ debugName: "showBackdropClass" }] : []));
    clickStartInDialog = false;
    origBodyOverflow;
    showTimer;
    backdropTimer;
    backdropGhostClickPrevention = true;
    document = inject(DOCUMENT);
    modalContainerRef = viewChild.required('modalContainer');
    ngOnInit() {
        setTimeout(() => (this.backdropGhostClickPrevention = false), this.animationTime(300));
        this.init = true;
        this.showTimer = setTimeout(() => {
            this.show.set(true);
        }, this.animationTime(150));
    }
    ngAfterViewInit() {
        queueMicrotask(() => this.modalRef?.shown.next(this.modalContainerRef()));
    }
    ngOnDestroy() {
        this.hideBackdrop();
    }
    /** @internal */
    hideDialog(param) {
        clearTimeout(this.showTimer);
        this.show.set(false);
        // set `detach()` in modal ref to no-op so that the animation is unaffected if called
        const detach = this.modalRef.detach;
        this.modalRef.detach = () => { };
        setTimeout(() => {
            this.hideBackdrop();
            setTimeout(() => detach(), this.animationTime(150));
        }, this.animationTime(300));
        this.modalRef?.hidden.next(param);
        this.modalRef?.hidden.complete();
        this.modalRef?.message.complete();
    }
    /** @internal */
    showBackdrop() {
        if (this.modalRef?.data.animated !== false) {
            this.showBackdropClass.set(false);
            this.backdropTimer = setTimeout(() => {
                this.showBackdropClass.set(true);
            }, 16);
        }
        else {
            this.showBackdropClass.set(true);
        }
        this.origBodyOverflow = this.document.body.style.overflow;
        this.document.body.style.overflow = 'hidden';
    }
    hideBackdrop() {
        clearTimeout(this.backdropTimer);
        if (this.showBackdropClass() !== undefined) {
            this.showBackdropClass.set(false);
        }
        if (this.origBodyOverflow !== undefined) {
            this.document.body.style.overflow = this.origBodyOverflow;
            this.origBodyOverflow = undefined;
        }
    }
    clickStarted(event) {
        this.clickStartInDialog = event.target !== this.modalContainerRef().nativeElement;
    }
    onClickStop(event) {
        const clickedInBackdrop = event.target === this.modalContainerRef().nativeElement && !this.clickStartInDialog;
        if (this.modalRef?.ignoreBackdropClick || !clickedInBackdrop) {
            this.clickStartInDialog = false;
            return;
        }
        if (!this.backdropGhostClickPrevention) {
            // Called when backdrop close is allowed and user clicks on the backdrop
            this.modalRef.messageOrHide(this.modalRef.closeValue);
        }
        else {
            // When in ghost click prevention mode, avoid text selection
            this.document.getSelection()?.removeAllRanges();
        }
    }
    onEsc(event) {
        if (this.modalRef?.data.keyboard && this.modalRef?.isCurrent()) {
            event.preventDefault();
            this.modalRef.messageOrHide(this.modalRef.closeValue);
        }
    }
    animationTime(millis) {
        return this.isAnimated ? millis : 0;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiModalComponent, isStandalone: true, selector: "si-modal", host: { listeners: { "mousedown": "clickStarted($event)", "mouseup": "onClickStop($event)", "window:keydown.esc": "onEsc($event)" } }, viewQueries: [{ propertyName: "modalContainerRef", first: true, predicate: ["modalContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (showBackdropClass() !== undefined) {\n  <div\n    class=\"modal-backdrop\"\n    [class.fade]=\"modalRef.data.animated !== false\"\n    [class.show]=\"showBackdropClass()\"\n  ></div>\n}\n@if (init) {\n  <div\n    #modalContainer\n    role=\"dialog\"\n    class=\"modal d-block\"\n    aria-modal=\"true\"\n    [class.fade]=\"modalRef.data.animated !== false\"\n    [class.show]=\"show()\"\n    [attr.aria-labelledby]=\"titleId\"\n  >\n    <div cdkTrapFocus cdkTrapFocusAutoCapture [class]=\"`modal-dialog ${dialogClass}`\">\n      <div class=\"modal-content\">\n        <ng-content />\n      </div>\n    </div>\n  </div>\n}\n", dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiModalComponent, decorators: [{
            type: Component,
            args: [{ selector: 'si-modal', imports: [A11yModule], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '(mousedown)': 'clickStarted($event)',
                        '(mouseup)': 'onClickStop($event)',
                        '(window:keydown.esc)': 'onEsc($event)'
                    }, template: "@if (showBackdropClass() !== undefined) {\n  <div\n    class=\"modal-backdrop\"\n    [class.fade]=\"modalRef.data.animated !== false\"\n    [class.show]=\"showBackdropClass()\"\n  ></div>\n}\n@if (init) {\n  <div\n    #modalContainer\n    role=\"dialog\"\n    class=\"modal d-block\"\n    aria-modal=\"true\"\n    [class.fade]=\"modalRef.data.animated !== false\"\n    [class.show]=\"show()\"\n    [attr.aria-labelledby]=\"titleId\"\n  >\n    <div cdkTrapFocus cdkTrapFocusAutoCapture [class]=\"`modal-dialog ${dialogClass}`\">\n      <div class=\"modal-content\">\n        <ng-content />\n      </div>\n    </div>\n  </div>\n}\n" }]
        }], propDecorators: { modalContainerRef: [{ type: i0.ViewChild, args: ['modalContainer', { isSignal: true }] }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class ModalRefImpl extends ModalRef {
    contentRef;
    viewRef;
    modalCompRef;
    componentRef;
    overlayRef;
    get content() {
        return this.componentRef?.instance;
    }
    setInput(input, value) {
        this.componentRef?.setInput(input, value);
    }
    constructor(contentRef, layer, data, closeValue) {
        super();
        this.contentRef = contentRef;
        this.layer = layer;
        this.data = data;
        this.ignoreBackdropClick = data.ignoreBackdropClick !== false;
        this.dialogClass = data?.class ?? '';
        this.closeValue = closeValue;
    }
}
class SiModalService {
    modalsCount = 0;
    overlay = inject(Overlay);
    appRef = inject(ApplicationRef);
    injector = inject(Injector);
    environmentInjector = inject(EnvironmentInjector);
    /**
     * Shows a modal with the given content and configuration.
     * @param content - Content to be displayed in the modal can  be a template reference or a component.
     * @param config - Configuration for the modal.
     * @param closeValue - Default closing value which can be overridden by calling `modalRef.hide(value)`.
     * @returns A reference to the modal.
     */
    show(content, config, closeValue) {
        const modalRef = new ModalRefImpl(content, ++this.modalsCount, config, closeValue);
        const siModalRef = this.attachComponent(modalRef);
        modalRef.modalCompRef = siModalRef;
        modalRef.detach = () => this.detach(modalRef);
        modalRef.hide = (param) => siModalRef.instance.hideDialog(param);
        modalRef.isCurrent = () => modalRef.layer === this.modalsCount;
        if (this.modalsCount === 1) {
            siModalRef.instance.showBackdrop();
        }
        return modalRef;
    }
    attachComponent(modalRef) {
        const providers = [{ provide: ModalRef, useValue: modalRef }];
        modalRef.data.providers?.forEach(p => providers.push(p));
        const injector = Injector.create({
            providers,
            parent: modalRef.data.injector ?? this.buildInjector()
        });
        modalRef.overlayRef = this.overlay.create({
            positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
            direction: isRTL() ? 'rtl' : 'ltr'
        });
        const compPortal = new ComponentPortal(SiModalComponent, null, injector);
        // Ensure compatibility between Angular CDK 19 and 20 since constructor arguments have changed
        compPortal.projectableNodes = this.getContentProjectableNodes(injector, modalRef);
        return modalRef.overlayRef.attach(compPortal);
    }
    getContentProjectableNodes(injector, modalRef) {
        if (modalRef.contentRef instanceof TemplateRef) {
            modalRef.viewRef = modalRef.contentRef.createEmbeddedView({
                modalRef,
                ...modalRef.data.inputValues
            });
            this.appRef.attachView(modalRef.viewRef);
            return [modalRef.viewRef.rootNodes];
        }
        modalRef.componentRef = createComponent(modalRef.contentRef, {
            environmentInjector: modalRef.data.environmentInjector ?? this.environmentInjector,
            elementInjector: injector
        });
        // set initial @Input() / input()
        for (const [key, value] of Object.entries(modalRef.data?.inputValues ?? {})) {
            modalRef.componentRef?.setInput(key, value);
        }
        modalRef.componentRef.changeDetectorRef.detectChanges();
        this.appRef.attachView(modalRef.componentRef.hostView);
        return [[modalRef.componentRef.location.nativeElement]];
    }
    detach(modalRef) {
        const ref = modalRef;
        if (ref.modalCompRef) {
            ref.modalCompRef?.destroy();
            ref.modalCompRef = undefined;
            ref.componentRef?.destroy();
            ref.componentRef = undefined;
            ref.viewRef?.destroy();
            ref.viewRef = undefined;
            ref.overlayRef?.dispose();
            ref.shown.complete();
            this.modalsCount--;
        }
    }
    // TODO remove once translation must be defined at application start
    // Notification service is provided in 'root'. If no translation is defined, SiNoTranslateService is not provided
    buildInjector() {
        let injector = this.injector;
        if (!injector.get(SiTranslateService, null)) {
            injector = Injector.create({
                providers: [{ provide: SiTranslateService, useClass: SiNoTranslateService, deps: [] }],
                parent: this.injector
            });
        }
        return injector;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiModalService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiModalService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });

let idCounter = 1;
const createModalConfig = (inputs) => {
    if (inputs) {
        Object.keys(inputs).forEach(k => inputs[k] === undefined && delete inputs[k]);
    }
    else {
        inputs = {};
    }
    const config = {
        inputValues: inputs,
        ignoreBackdropClick: true,
        keyboard: false,
        animated: true,
        class: 'modal-dialog-centered'
    };
    const id = `__si-modal-id-${idCounter++}`;
    config.inputValues.titleId = id;
    config.ariaLabelledBy = id;
    return config;
};

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */

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

export { ModalRef, SiModalService, createModalConfig };
//# sourceMappingURL=siemens-element-ng-modal.mjs.map
