import * as i0 from '@angular/core';
import { InjectionToken, signal, Injectable, model, inject, ElementRef, Renderer2, effect, computed, Injector, RendererStyleFlags2, Input, Component, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent } from '@angular/core';
import { NgComponentOutlet, DOCUMENT } from '@angular/common';
import { DynamicDialogRef } from 'primeng/dynamicdialog';
import * as i1 from 'primeng/drawer';
import { DrawerModule } from 'primeng/drawer';
import { TranslocoService } from '@jsverse/transloco';
import { Button } from '@masterteam/components/button';

const DynamicDrawerConfig = new InjectionToken('DynamicDrawerConfig');
/**
 * Controller to dynamically update drawer properties from inside the child component.
 * Inject this in your drawer content component to modify drawer behavior.
 */
class DrawerController {
    _styleClass = signal('', ...(ngDevMode ? [{ debugName: "_styleClass" }] : /* istanbul ignore next */ []));
    _header = signal('', ...(ngDevMode ? [{ debugName: "_header" }] : /* istanbul ignore next */ []));
    _fullScreen = signal(undefined, ...(ngDevMode ? [{ debugName: "_fullScreen" }] : /* istanbul ignore next */ []));
    _maximized = signal(false, ...(ngDevMode ? [{ debugName: "_maximized" }] : /* istanbul ignore next */ []));
    /** Get the current additional styleClass */
    get styleClass() {
        return this._styleClass;
    }
    /** Get the current header */
    get header() {
        return this._header;
    }
    /** Get the current fullScreen state */
    get fullScreen() {
        return this._fullScreen;
    }
    /** Get the current maximized (full-width) state */
    get maximized() {
        return this._maximized;
    }
    /** Add or replace the dynamic styleClass */
    setStyleClass(styleClass) {
        this._styleClass.set(styleClass);
    }
    /** Append a class to the existing styleClass */
    addClass(className) {
        const current = this._styleClass();
        if (!current.includes(className)) {
            this._styleClass.set((current + ' ' + className).trim());
        }
    }
    /** Remove a class from the styleClass */
    removeClass(className) {
        const current = this._styleClass();
        this._styleClass.set(current
            .split(' ')
            .filter((c) => c !== className)
            .join(' '));
    }
    /** Toggle a class on/off */
    toggleClass(className) {
        if (this._styleClass().includes(className)) {
            this.removeClass(className);
        }
        else {
            this.addClass(className);
        }
    }
    /** Update the drawer header */
    setHeader(header) {
        this._header.set(header);
    }
    /** Toggle fullScreen mode */
    setFullScreen(fullScreen) {
        this._fullScreen.set(fullScreen);
    }
    /** Set maximized (full-width) state */
    setMaximized(maximized) {
        this._maximized.set(maximized);
    }
    /** Toggle maximized (full-width) state */
    toggleMaximized() {
        this._maximized.set(!this._maximized());
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerController, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerController });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerController, decorators: [{
            type: Injectable
        }] });
class DynamicDrawerHost {
    visible = model(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
    cmp = signal(null, ...(ngDevMode ? [{ debugName: "cmp" }] : /* istanbul ignore next */ []));
    cfg = signal({ position: 'right' }, ...(ngDevMode ? [{ debugName: "cfg" }] : /* istanbul ignore next */ []));
    cmpInputs = signal({}, ...(ngDevMode ? [{ debugName: "cmpInputs" }] : /* istanbul ignore next */ []));
    childInjector;
    ref;
    // Guards against emitting onClose twice: the X button closes via
    // handleHide() (programmatic visible=false, no PrimeNG onHide), while a
    // backdrop click / Escape closes via PrimeNG's own close() which DOES emit
    // onHide. Without bridging onHide -> ref.close(), consumers never learn
    // about dismiss-by-outside-click / Escape.
    closed = false;
    drawerController = new DrawerController();
    host = inject((ElementRef));
    renderer = inject(Renderer2);
    transloco = inject(TranslocoService);
    resizeAnimTimer = null;
    RESIZE_ANIM_CLASS = 'mt-drawer--anim-resize';
    RESIZE_ANIM_DURATION_MS = 700;
    constructor() {
        effect(() => {
            // React only when the user toggles maximize while the drawer is open.
            // Closing/opening goes through onDrawerShow / onDrawerHide so we never
            // race with PrimeNG's slide animation.
            const isMaximized = this.drawerController.maximized();
            if (!this.visible()) {
                return;
            }
            this.applyMaximizedInlineStyles(isMaximized, true);
        });
    }
    /** Computed styleClass combining config + dynamic controller class */
    computedStyleClass = computed(() => {
        const configClass = this.cfg()?.styleClass ?? '';
        const dynamicClass = this.drawerController.styleClass();
        const maximizedClass = this.drawerController.maximized()
            ? 'mt-drawer--maximized'
            : '';
        return `${configClass} ${dynamicClass} mt-drawer ${maximizedClass}`
            .replace(/\s+/g, ' ')
            .trim();
    }, ...(ngDevMode ? [{ debugName: "computedStyleClass" }] : /* istanbul ignore next */ []));
    /** Whether the maximize toggle button is visible in the header */
    canShowMaximizeToggle = computed(() => {
        const config = this.cfg();
        if (config?.showMaximizeToggle === false) {
            return false;
        }
        if (this.computedFullScreen()) {
            return false;
        }
        const position = config?.position ?? 'right';
        return position === 'left' || position === 'right';
    }, ...(ngDevMode ? [{ debugName: "canShowMaximizeToggle" }] : /* istanbul ignore next */ []));
    /** Tooltip for maximize/restore button (state-aware) */
    maximizeButtonTooltip = computed(() => {
        const config = this.cfg();
        return this.drawerController.maximized()
            ? (config?.restoreTooltip ??
                this.transloco.translate('components.drawer.restoreSize'))
            : (config?.maximizeTooltip ??
                this.transloco.translate('components.drawer.expandFullWidth'));
    }, ...(ngDevMode ? [{ debugName: "maximizeButtonTooltip" }] : /* istanbul ignore next */ []));
    /** Icon for maximize/restore button (state-aware) */
    maximizeButtonIcon = computed(() => this.drawerController.maximized()
        ? 'layout.minimize-01'
        : 'layout.maximize-01', ...(ngDevMode ? [{ debugName: "maximizeButtonIcon" }] : /* istanbul ignore next */ []));
    /** Toggle the drawer's full-width state */
    toggleMaximized() {
        this.drawerController.toggleMaximized();
    }
    computedDrawerStyle = computed(() => {
        const appendTo = this.cfg()?.appendTo;
        if (!appendTo || appendTo === 'body') {
            return { position: 'fixed' };
        }
        return null;
    }, ...(ngDevMode ? [{ debugName: "computedDrawerStyle" }] : /* istanbul ignore next */ []));
    /** Computed header - controller overrides config if set */
    computedHeader = computed(() => {
        const dynamicHeader = this.drawerController.header();
        return dynamicHeader || this.cfg()?.header || '';
    }, ...(ngDevMode ? [{ debugName: "computedHeader" }] : /* istanbul ignore next */ []));
    /** Computed fullScreen - controller overrides config if set */
    computedFullScreen = computed(() => {
        const dynamicFullScreen = this.drawerController.fullScreen();
        return dynamicFullScreen !== undefined
            ? dynamicFullScreen
            : this.cfg()?.fullScreen;
    }, ...(ngDevMode ? [{ debugName: "computedFullScreen" }] : /* istanbul ignore next */ []));
    connect(opts) {
        this.cmp.set(opts.component);
        this.cfg.set(opts.config ?? {});
        this.cmpInputs.set(opts.config?.inputValues ?? {});
        this.ref = opts.ref;
        this.childInjector = Injector.create({
            parent: opts.parent,
            providers: [
                { provide: DynamicDrawerConfig, useValue: opts.config ?? {} },
                { provide: DynamicDialogRef, useValue: opts.ref },
                { provide: DrawerController, useValue: this.drawerController },
            ],
        });
        opts.ref.onClose.subscribe(() => {
            this.visible.set(false);
        });
    }
    show() {
        this.closed = false;
        this.visible.set(true);
    }
    handleHide(data = null) {
        this.visible.set(false);
        this.closeOnce(data);
    }
    /** Close the drawer ref at most once, regardless of the close path. */
    closeOnce(data = null) {
        if (this.closed) {
            return;
        }
        this.closed = true;
        this.ref?.close(data);
    }
    onDrawerShow() {
        // Drawer DOM only exists after PrimeNG renders it — apply current state
        // instantly so the open animation doesn't fight the resize transition.
        this.applyMaximizedInlineStyles(this.drawerController.maximized(), false);
    }
    onDrawerHide() {
        // Instant cleanup — close animation already done; we don't want to start
        // a 650ms width transition mid-hide.
        this.applyMaximizedInlineStyles(false, false);
        // PrimeNG emits (onHide) when the drawer is dismissed via backdrop click
        // or Escape (its close() -> hide(true)). The X button instead closes
        // programmatically (visible=false -> onAfterLeave -> hide(false)), so it
        // does not reach here. Bridge the dismiss paths to ref.close() so
        // consumers' onClose runs for every close path. closeOnce() de-dupes.
        this.closeOnce();
    }
    getDrawerElement() {
        return this.host.nativeElement.querySelector('.p-drawer');
    }
    applyMaximizedInlineStyles(isMaximized, animate) {
        const drawerEl = this.getDrawerElement();
        if (!drawerEl) {
            return;
        }
        if (this.resizeAnimTimer) {
            clearTimeout(this.resizeAnimTimer);
            this.resizeAnimTimer = null;
        }
        if (animate) {
            this.renderer.addClass(drawerEl, this.RESIZE_ANIM_CLASS);
            this.resizeAnimTimer = setTimeout(() => {
                this.renderer.removeClass(drawerEl, this.RESIZE_ANIM_CLASS);
                this.resizeAnimTimer = null;
            }, this.RESIZE_ANIM_DURATION_MS);
        }
        else {
            this.renderer.removeClass(drawerEl, this.RESIZE_ANIM_CLASS);
        }
        if (isMaximized) {
            this.renderer.setStyle(drawerEl, 'width', '100%', RendererStyleFlags2.Important);
            this.renderer.setStyle(drawerEl, 'max-width', '100%', RendererStyleFlags2.Important);
            this.renderer.setStyle(drawerEl, 'height', '100%', RendererStyleFlags2.Important);
            this.renderer.setStyle(drawerEl, 'max-height', '100%', RendererStyleFlags2.Important);
        }
        else {
            this.renderer.removeStyle(drawerEl, 'width');
            this.renderer.removeStyle(drawerEl, 'max-width');
            this.renderer.removeStyle(drawerEl, 'height');
            this.renderer.removeStyle(drawerEl, 'max-height');
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DynamicDrawerHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: DynamicDrawerHost, isStandalone: true, selector: "mt-dynamic-drawer-host", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, connect: { classPropertyName: "connect", publicName: "connect", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, template: `
    <p-drawer
      [(visible)]="visible"
      [appendTo]="'self'"
      [style]="computedDrawerStyle()"
      [closable]="cfg()?.closable"
      [position]="cfg()?.position"
      [fullScreen]="computedFullScreen()"
      [modal]="cfg()?.modal"
      [closeOnEscape]="cfg()?.closeOnEscape"
      [dismissible]="cfg()?.dismissible"
      [transitionOptions]="cfg()?.transitionOptions"
      [styleClass]="computedStyleClass()"
      (onShow)="onDrawerShow()"
      (onHide)="onDrawerHide()"
    >
      <ng-template #header>
        <div
          class="flex items-center justify-between px-5 w-full py-3 border-b border-surface"
        >
          <h3 class="text-xl font-semibold">{{ computedHeader() }}</h3>
          <div class="flex items-center gap-2">
            @if (canShowMaximizeToggle()) {
              <mt-button
                (onClick)="toggleMaximized()"
                variant="outlined"
                styleClass="!bg-white"
                severity="secondary"
                [icon]="maximizeButtonIcon()"
                [tooltip]="maximizeButtonTooltip()"
              >
              </mt-button>
            }
            <mt-button
              (onClick)="handleHide()"
              variant="outlined"
              styleClass="!bg-white"
              icon="general.x-close"
              severity="secondary"
            >
            </mt-button>
          </div>
        </div>
      </ng-template>
      @if (childInjector) {
        <ng-container
          *ngComponentOutlet="
            cmp();
            injector: childInjector;
            inputs: cmpInputs()
          "
        >
        </ng-container>
      }
    </p-drawer>
  `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DynamicDrawerHost, decorators: [{
            type: Component,
            args: [{
                    selector: 'mt-dynamic-drawer-host',
                    standalone: true,
                    imports: [NgComponentOutlet, DrawerModule, Button],
                    template: `
    <p-drawer
      [(visible)]="visible"
      [appendTo]="'self'"
      [style]="computedDrawerStyle()"
      [closable]="cfg()?.closable"
      [position]="cfg()?.position"
      [fullScreen]="computedFullScreen()"
      [modal]="cfg()?.modal"
      [closeOnEscape]="cfg()?.closeOnEscape"
      [dismissible]="cfg()?.dismissible"
      [transitionOptions]="cfg()?.transitionOptions"
      [styleClass]="computedStyleClass()"
      (onShow)="onDrawerShow()"
      (onHide)="onDrawerHide()"
    >
      <ng-template #header>
        <div
          class="flex items-center justify-between px-5 w-full py-3 border-b border-surface"
        >
          <h3 class="text-xl font-semibold">{{ computedHeader() }}</h3>
          <div class="flex items-center gap-2">
            @if (canShowMaximizeToggle()) {
              <mt-button
                (onClick)="toggleMaximized()"
                variant="outlined"
                styleClass="!bg-white"
                severity="secondary"
                [icon]="maximizeButtonIcon()"
                [tooltip]="maximizeButtonTooltip()"
              >
              </mt-button>
            }
            <mt-button
              (onClick)="handleHide()"
              variant="outlined"
              styleClass="!bg-white"
              icon="general.x-close"
              severity="secondary"
            >
            </mt-button>
          </div>
        </div>
      </ng-template>
      @if (childInjector) {
        <ng-container
          *ngComponentOutlet="
            cmp();
            injector: childInjector;
            inputs: cmpInputs()
          "
        >
        </ng-container>
      }
    </p-drawer>
  `,
                }]
        }], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], connect: [{
                type: Input,
                args: [{ required: true }]
            }] } });

// dynamic-drawer.service.ts
class DrawerService {
    appRef = inject(ApplicationRef);
    env = inject(EnvironmentInjector);
    rootInjector = inject(Injector);
    destroyRef = inject(DestroyRef);
    doc = inject(DOCUMENT);
    hostRef;
    defauiltConfig = {
        position: 'right',
        modal: true,
        closable: false,
        transitionOptions: '200ms cubic-bezier(0, 0, 1, 1)',
        inputValues: {},
        showMaximizeToggle: true,
    };
    constructor() {
        this.preloadDrawerHost();
    }
    open(component, config = {}) {
        config = { ...this.defauiltConfig, ...config };
        const targetEl = this.resolveAppendTarget(config.appendTo);
        this.hostRef = createComponent(DynamicDrawerHost, {
            environmentInjector: this.env,
        });
        const ref = new DynamicDialogRef();
        this.hostRef.instance.connect({
            component,
            config,
            parent: this.rootInjector,
            ref,
        });
        this.appRef.attachView(this.hostRef.hostView);
        // 📌 append to target (NOT body when provided)
        targetEl.appendChild(this.hostRef.location.nativeElement);
        // 🧹 auto-clean if app destroyed (SSR / HMR safety)
        this.destroyRef.onDestroy(() => {
            this.appRef.detachView(this.hostRef.hostView);
            this.hostRef.destroy();
            this.hostRef = undefined;
        });
        const currentHostRef = this.hostRef;
        const openDrawer = () => {
            if (this.hostRef !== currentHostRef) {
                return;
            }
            currentHostRef.instance.show();
        };
        if (this.doc.defaultView?.requestAnimationFrame) {
            this.doc.defaultView.requestAnimationFrame(openDrawer);
        }
        else {
            setTimeout(openDrawer, 0);
        }
        return ref;
    }
    preloadDrawerHost() {
        const preloadHostRef = createComponent(DynamicDrawerHost, {
            environmentInjector: this.env,
        });
        const preloadElement = preloadHostRef.location.nativeElement;
        preloadElement.style.display = 'none';
        this.appRef.attachView(preloadHostRef.hostView);
        this.doc.body.appendChild(preloadElement);
        preloadHostRef.changeDetectorRef.detectChanges();
        this.appRef.detachView(preloadHostRef.hostView);
        preloadElement.remove();
        preloadHostRef.destroy();
    }
    resolveAppendTarget(appendTo) {
        if (!appendTo)
            return this.doc.body;
        if (typeof appendTo === 'string') {
            const selector = appendTo.startsWith('#') || appendTo.startsWith('.')
                ? appendTo
                : `#${appendTo}`;
            const el = this.doc.querySelector(selector);
            if (!el) {
                return this.doc.body;
            }
            return el;
        }
        return appendTo; // HTMLElement passed directly
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: DrawerService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }], ctorParameters: () => [] });

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

export { DrawerController, DrawerService, DynamicDrawerConfig, DynamicDrawerHost };
//# sourceMappingURL=masterteam-components-dynamic-drawer.mjs.map
