import { isPlatformBrowser } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, NgZone, PLATFORM_ID, Injectable, input, booleanAttribute, output, ElementRef, Directive, signal, NgModule } from '@angular/core';
import { Observable } from 'rxjs';

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * A service wrapping `ResizeObserver`. This is a service for those reasons:
 * - only one `ResizeObserver` should be used for performance reason.
 * - For Angular change detection to work, explicit `ngZone` calls are necessary
 * - Observable stream
 */
class ResizeObserverService {
    listeners = new Map();
    resizeObserver;
    timerQueue = new Map();
    zone = inject(NgZone);
    constructor() {
        const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
        if (!isBrowser || !ResizeObserver) {
            return;
        }
        this.resizeObserver = new ResizeObserver((entries) => entries.forEach(entry => this.handleElement(entry.target)));
    }
    /**
     * Observe the size of an element. Returns an observable with the changes.
     * @param element - The element to observe
     * @param throttle - Throttle time in ms. Will emit this time after the resize
     * @param emitInitial - Emit the initial size after subscribe?
     * @param emitImmediate - Emit an event immediately after the size changes. Useful e.g. for visibility checks.
     */
    observe(element, throttle, emitInitial, emitImmediate) {
        let entry = this.listeners.get(element);
        if (!entry) {
            entry = { element, subscribers: [] };
            this.listeners.set(element, entry);
        }
        return new Observable(subscriber => {
            const sub = {
                sub: subscriber,
                dim: undefined,
                throttle,
                blocked: false,
                emitImmediate
            };
            this.subscriberAdded(entry, sub, emitInitial);
            return () => this.subscriberRemoved(entry, sub);
        });
    }
    subscriberAdded(entry, subscriber, emitInitial) {
        entry.subscribers.push(subscriber);
        if (entry.subscribers.length === 1) {
            this.resizeObserver?.observe(entry.element);
        }
        if (emitInitial) {
            this.schedule(0, entry.element, subscriber, false, true);
        }
    }
    subscriberRemoved(entry, subscriber) {
        const index = entry.subscribers.indexOf(subscriber);
        if (index >= 0) {
            entry.subscribers.splice(index, 1);
        }
        if (entry.subscribers.length === 0) {
            // no more subscribers, tear down everything
            this.resizeObserver?.unobserve(entry.element);
            this.listeners.delete(entry.element);
        }
        this.unschedule(subscriber);
        // close down, no re-subscription possible
        subscriber.sub.complete();
    }
    handleElement(element) {
        const entry = this.listeners.get(element);
        if (!entry) {
            this.resizeObserver?.unobserve(element);
            return;
        }
        entry.subscribers.forEach(sub => this.handleResizeSubscriber(element, sub));
    }
    handleResizeSubscriber(element, entry) {
        if (entry.blocked) {
            return;
        }
        if (entry.emitImmediate) {
            this.schedule(0, element, entry, false);
        }
        this.schedule(entry.throttle, element, entry, true);
    }
    emitSize(element, entry, force = false) {
        const dimensions = { width: element.clientWidth, height: element.clientHeight };
        if (!force &&
            entry.dim?.width === dimensions.width &&
            entry.dim?.height === dimensions.height) {
            // Prevent spurious emissions. Subpixels and all..
            return;
        }
        entry.dim = dimensions;
        entry.sub.next(dimensions);
    }
    schedule(timeout, element, subscriber, unblock, force = false) {
        if (unblock) {
            subscriber.blocked = true;
        }
        let queue = this.timerQueue.get(timeout);
        if (!queue) {
            queue = [];
            this.timerQueue.set(timeout, queue);
            setTimeout(() => {
                this.timerQueue.delete(timeout);
                this.processQueue(queue);
            }, timeout);
        }
        queue.push({ element, subscriber, unblock, force });
    }
    unschedule(entry) {
        const queued = this.timerQueue.get(entry.throttle);
        if (queued) {
            const index = queued.findIndex(q => q.subscriber === entry);
            if (index > -1) {
                queued.splice(index, 1);
            }
        }
    }
    processQueue(queue) {
        this.zone.run(() => {
            queue?.forEach(q => {
                if (q.unblock) {
                    q.subscriber.blocked = false;
                }
                this.emitSize(q.element, q.subscriber, q.force);
            });
        });
    }
    /**
     * check size on all observed elements. Only use in testing!
     * @deprecated Will be removed in major version 50! For testing purposes use the resize observer mock:
     *
     * ```ts
     * beforeEach(() => mockResizeObserver());
     * afterEach(() => restoreResizeObserver());
     * it('should trigger resize', () => {
     *   // For all observed elements
     *   MockResizeObserver.triggerResize({});
     *   // For specific HTML element
     *   MockResizeObserver.triggerResize({ target: myElement });
     * });
     * ```
  
     */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _checkAll() {
        this.listeners.forEach(entry => this.handleElement(entry.element));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ResizeObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ResizeObserverService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ResizeObserverService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Directive to emit events on element size change. Use like this:
 *   `<div (siResizeObserver)="handleResize($event)">`
 * When the size of the element changes, an event in the format
 *   `{ width: number, height: number }`
 * will be emitted. Also an initial event will be emitted on init.
 *
 * By default, events are throttled and to an event every 100ms. To change
 * this, add `[resizeThrottle]="200"` on the same element. Input in milliseconds.
 */
class SiResizeObserverDirective {
    /** @defaultValue 100 */
    resizeThrottle = input(100, ...(ngDevMode ? [{ debugName: "resizeThrottle" }] : []));
    /** @defaultValue true */
    emitInitial = input(true, { ...(ngDevMode ? { debugName: "emitInitial" } : {}), transform: booleanAttribute });
    siResizeObserver = output();
    subs;
    element = inject(ElementRef);
    service = inject(ResizeObserverService);
    ngOnInit() {
        this.subs = this.service
            .observe(this.element.nativeElement, this.resizeThrottle(), this.emitInitial())
            .subscribe(value => this.siResizeObserver.emit(value));
    }
    ngOnDestroy() {
        this.subs?.unsubscribe();
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: SiResizeObserverDirective, isStandalone: true, selector: "[siResizeObserver]", inputs: { resizeThrottle: { classPropertyName: "resizeThrottle", publicName: "resizeThrottle", isSignal: true, isRequired: false, transformFunction: null }, emitInitial: { classPropertyName: "emitInitial", publicName: "emitInitial", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { siResizeObserver: "siResizeObserver" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[siResizeObserver]'
                }]
        }], propDecorators: { resizeThrottle: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeThrottle", required: false }] }], emitInitial: [{ type: i0.Input, args: [{ isSignal: true, alias: "emitInitial", required: false }] }], siResizeObserver: [{ type: i0.Output, args: ["siResizeObserver"] }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
// keep in sync with the Bootstrap variables
const BOOTSTRAP_BREAKPOINTS = {
    smMinimum: 576,
    mdMinimum: 768,
    lgMinimum: 992,
    xlMinimum: 1200,
    xxlMinimum: 1400
};
/**
 * Directive to automatically set `si-container-*` classes so Bootstrap column classes work
 * in the context of the container instead of viewport size.
 */
class SiResponsiveContainerDirective {
    /** @defaultValue false */
    xs = signal(false, ...(ngDevMode ? [{ debugName: "xs" }] : []));
    /** @defaultValue false */
    sm = signal(false, ...(ngDevMode ? [{ debugName: "sm" }] : []));
    /** @defaultValue false */
    md = signal(false, ...(ngDevMode ? [{ debugName: "md" }] : []));
    /** @defaultValue false */
    lg = signal(false, ...(ngDevMode ? [{ debugName: "lg" }] : []));
    /** @defaultValue false */
    xl = signal(false, ...(ngDevMode ? [{ debugName: "xl" }] : []));
    /** @defaultValue false */
    xxl = signal(false, ...(ngDevMode ? [{ debugName: "xxl" }] : []));
    /** @defaultValue 100 */
    resizeThrottle = input(100, ...(ngDevMode ? [{ debugName: "resizeThrottle" }] : []));
    breakpoints = input(...(ngDevMode ? [undefined, { debugName: "breakpoints" }] : []));
    subs;
    element = inject(ElementRef);
    service = inject(ResizeObserverService);
    ngOnInit() {
        this.subs = this.service
            .observe(this.element.nativeElement, this.resizeThrottle(), true)
            .subscribe(event => this.setResponsiveSize(event.width, event.height));
    }
    ngOnDestroy() {
        this.subs?.unsubscribe();
    }
    setResponsiveSize(width, height) {
        if (!width && !height) {
            // element is not visible, no point in changing anything
            return;
        }
        const breakpoints = this.breakpoints() ?? BOOTSTRAP_BREAKPOINTS;
        this.xs.set(width < breakpoints.smMinimum);
        this.sm.set(width >= breakpoints.smMinimum && width < breakpoints.mdMinimum);
        this.md.set(width >= breakpoints.mdMinimum && width < breakpoints.lgMinimum);
        this.lg.set(width >= breakpoints.lgMinimum && width < breakpoints.xlMinimum);
        this.xl.set(width >= breakpoints.xlMinimum && width < breakpoints.xxlMinimum);
        this.xxl.set(width >= breakpoints.xxlMinimum);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResponsiveContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: SiResponsiveContainerDirective, isStandalone: true, selector: "[siResponsiveContainer]", inputs: { resizeThrottle: { classPropertyName: "resizeThrottle", publicName: "resizeThrottle", isSignal: true, isRequired: false, transformFunction: null }, breakpoints: { classPropertyName: "breakpoints", publicName: "breakpoints", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.si-container-xs": "xs()", "class.si-container-sm": "sm()", "class.si-container-md": "md()", "class.si-container-lg": "lg()", "class.si-container-xl": "xl()", "class.si-container-xxl": "xxl()" } }, exportAs: ["siResponsiveContainer"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResponsiveContainerDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[siResponsiveContainer]',
                    host: {
                        '[class.si-container-xs]': 'xs()',
                        '[class.si-container-sm]': 'sm()',
                        '[class.si-container-md]': 'md()',
                        '[class.si-container-lg]': 'lg()',
                        '[class.si-container-xl]': 'xl()',
                        '[class.si-container-xxl]': 'xxl()'
                    },
                    exportAs: 'siResponsiveContainer'
                }]
        }], propDecorators: { resizeThrottle: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeThrottle", required: false }] }], breakpoints: [{ type: i0.Input, args: [{ isSignal: true, alias: "breakpoints", required: false }] }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiResizeObserverModule {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverModule, imports: [SiResizeObserverDirective, SiResponsiveContainerDirective], exports: [SiResizeObserverDirective, SiResponsiveContainerDirective] });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiResizeObserverModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [SiResizeObserverDirective, SiResponsiveContainerDirective],
                    exports: [SiResizeObserverDirective, SiResponsiveContainerDirective]
                }]
        }] });

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

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

export { BOOTSTRAP_BREAKPOINTS, ResizeObserverService, SiResizeObserverDirective, SiResizeObserverModule, SiResponsiveContainerDirective };
//# sourceMappingURL=siemens-element-ng-resize-observer.mjs.map
