import * as i0 from '@angular/core';
import { InjectionToken, input, booleanAttribute, computed, isDevMode, inject, ElementRef, Renderer2, signal, effect, forwardRef, Directive, Injectable, DestroyRef, Injector, DOCUMENT, NgModule, linkedSignal, afterNextRender } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { LocalStorageService, consumerProviderFactory } from '@fundamental-ngx/cdk/utils';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';

const ContentDensityGlobalKeyword = 'global';
const ContentDensityDefaultKeyword = 'default';

var ContentDensityMode;
(function (ContentDensityMode) {
    ContentDensityMode["COZY"] = "cozy";
    ContentDensityMode["CONDENSED"] = "condensed";
    ContentDensityMode["COMPACT"] = "compact";
})(ContentDensityMode || (ContentDensityMode = {}));

const isCompact = (density) => density === ContentDensityMode.COMPACT;
const isCondensed = (density) => density === ContentDensityMode.CONDENSED;
const isCozy = (density) => density === ContentDensityMode.COZY;
const isContentDensityMode = (density) => isCompact(density) || isCondensed(density) || isCozy(density) || density === ContentDensityGlobalKeyword;

const CONTENT_DENSITY_DIRECTIVE = new InjectionToken('ContentDensityDirective');

/** UI5 attribute name for compact mode */
const UI5_COMPACT_ATTRIBUTE = 'data-ui5-compact-size';
/**
 * Directive to control the content density of elements.
 * Used by density controllers and consumers.
 *
 * Provides signal-based state management for reactive content density tracking.
 * Also applies UI5 `data-ui5-compact-size` attribute for UI5 Web Components compatibility.
 *
 * @example
 * // Using fdContentDensity input
 * <fd-button [fdContentDensity]="'compact'">Button</fd-button>
 *
 * // Using shorthand inputs
 * <fd-button fdCompact>Compact Button</fd-button>
 * <fd-button fdCozy>Cozy Button</fd-button>
 * <fd-button fdCondensed>Condensed Button</fd-button>
 */
class ContentDensityDirective {
    /**
     * Current density mode value.
     *
     * @deprecated Use densityMode() signal instead
     * @returns The current content density mode
     */
    get value() {
        return this.densityMode();
    }
    /** @hidden */
    constructor() {
        /**
         * Sets the content density of the element dynamically.
         * Accepts 'compact', 'cozy', 'condensed', or 'global'.
         *
         * @example
         * <fd-button [fdContentDensity]="'compact'">Button</fd-button>
         */
        this.fdContentDensity = input('', ...(ngDevMode ? [{ debugName: "fdContentDensity" }] : []));
        /**
         * Shorthand for setting compact density.
         * Equivalent to `[fdContentDensity]="'compact'"`.
         *
         * @example
         * <fd-button fdCompact>Button</fd-button>
         * <fd-button [fdCompact]="isCompact">Button</fd-button>
         */
        this.fdCompact = input(false, { ...(ngDevMode ? { debugName: "fdCompact" } : {}), transform: booleanAttribute });
        /**
         * Shorthand for setting condensed density.
         * Equivalent to `[fdContentDensity]="'condensed'"`.
         *
         * @example
         * <fd-button fdCondensed>Button</fd-button>
         * <fd-button [fdCondensed]="isCondensed">Button</fd-button>
         */
        this.fdCondensed = input(false, { ...(ngDevMode ? { debugName: "fdCondensed" } : {}), transform: booleanAttribute });
        /**
         * Shorthand for setting cozy density.
         * Equivalent to `[fdContentDensity]="'cozy'"`.
         *
         * @example
         * <fd-button fdCozy>Button</fd-button>
         * <fd-button [fdCozy]="isCozy">Button</fd-button>
         */
        this.fdCozy = input(false, { ...(ngDevMode ? { debugName: "fdCozy" } : {}), transform: booleanAttribute });
        /**
         * Current content density mode as a computed signal.
         * Resolves the density based on shorthand inputs or fdContentDensity value.
         *
         * @returns The resolved content density mode
         */
        this.densityMode = computed(() => {
            // Check programmatic override first
            const programmaticDensity = this._programmaticDensity();
            if (programmaticDensity !== null) {
                return programmaticDensity;
            }
            // Check shorthand inputs
            if (this.fdCompact()) {
                return ContentDensityMode.COMPACT;
            }
            if (this.fdCondensed()) {
                return ContentDensityMode.CONDENSED;
            }
            if (this.fdCozy()) {
                return ContentDensityMode.COZY;
            }
            // Then check fdContentDensity input
            const val = this.fdContentDensity();
            if (val === '') {
                return ContentDensityGlobalKeyword;
            }
            if (!isContentDensityMode(val)) {
                if (isDevMode()) {
                    console.warn(`The value "${val}" is not a valid content density mode. Using "${ContentDensityGlobalKeyword}" instead.`);
                }
                return ContentDensityGlobalKeyword;
            }
            return val;
        }, ...(ngDevMode ? [{ debugName: "densityMode" }] : []));
        /** @hidden */
        this._elementRef = inject(ElementRef);
        /** @hidden */
        this._renderer = inject(Renderer2);
        /**
         * Internal signal for programmatic density updates.
         * Takes precedence over input bindings when set.
         */
        this._programmaticDensity = signal(null, ...(ngDevMode ? [{ debugName: "_programmaticDensity" }] : []));
        // Effect to apply UI5 attribute based on density mode
        effect(() => {
            this._applyUi5Attribute(this.densityMode());
        });
    }
    /**
     * Sets the content density programmatically.
     * Use this method when you need to update the density from code
     * (e.g., in host directives or dynamic scenarios).
     *
     * @param density The content density mode to set
     */
    setDensity(density) {
        this._programmaticDensity.set(density);
    }
    /**
     * Clears the programmatic density override.
     * After calling this, the density will be resolved from input bindings.
     */
    clearDensity() {
        this._programmaticDensity.set(null);
    }
    /**
     * Applies or removes the UI5 compact attribute based on density mode.
     * COMPACT and CONDENSED map to UI5 compact mode.
     * COZY and 'global' do not apply the attribute (UI5 defaults to cozy).
     * @hidden
     */
    _applyUi5Attribute(density) {
        const nativeElement = this._elementRef?.nativeElement;
        // Only apply to actual HTML elements, not comment/text nodes
        if (!nativeElement || !this._renderer || !(nativeElement instanceof HTMLElement)) {
            return;
        }
        const isUi5Compact = density === ContentDensityMode.COMPACT || density === ContentDensityMode.CONDENSED;
        if (isUi5Compact) {
            this._renderer.setAttribute(nativeElement, UI5_COMPACT_ATTRIBUTE, '');
        }
        else {
            this._renderer.removeAttribute(nativeElement, UI5_COMPACT_ATTRIBUTE);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: ContentDensityDirective, isStandalone: true, selector: "[fdContentDensity]:not([fdCompact]):not([fdCondensed]):not([fdCozy]),\n                [fdCompact]:not([fdContentDensity]):not([fdCondensed]):not([fdCozy]),\n                [fdCondensed]:not([fdContentDensity]):not([fdCompact]):not([fdCozy]),\n                [fdCozy]:not([fdContentDensity]):not([fdCompact]):not([fdCondensed])", inputs: { fdContentDensity: { classPropertyName: "fdContentDensity", publicName: "fdContentDensity", isSignal: true, isRequired: false, transformFunction: null }, fdCompact: { classPropertyName: "fdCompact", publicName: "fdCompact", isSignal: true, isRequired: false, transformFunction: null }, fdCondensed: { classPropertyName: "fdCondensed", publicName: "fdCondensed", isSignal: true, isRequired: false, transformFunction: null }, fdCozy: { classPropertyName: "fdCozy", publicName: "fdCozy", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
            {
                provide: CONTENT_DENSITY_DIRECTIVE,
                useExisting: forwardRef(() => ContentDensityDirective)
            }
        ], exportAs: ["fdContentDensity"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: `[fdContentDensity]:not([fdCompact]):not([fdCondensed]):not([fdCozy]),
                [fdCompact]:not([fdContentDensity]):not([fdCondensed]):not([fdCozy]),
                [fdCondensed]:not([fdContentDensity]):not([fdCompact]):not([fdCozy]),
                [fdCozy]:not([fdContentDensity]):not([fdCompact]):not([fdCondensed])`,
                    exportAs: 'fdContentDensity',
                    providers: [
                        {
                            provide: CONTENT_DENSITY_DIRECTIVE,
                            useExisting: forwardRef(() => ContentDensityDirective)
                        }
                    ]
                }]
        }], ctorParameters: () => [], propDecorators: { fdContentDensity: [{ type: i0.Input, args: [{ isSignal: true, alias: "fdContentDensity", required: false }] }], fdCompact: [{ type: i0.Input, args: [{ isSignal: true, alias: "fdCompact", required: false }] }], fdCondensed: [{ type: i0.Input, args: [{ isSignal: true, alias: "fdCondensed", required: false }] }], fdCozy: [{ type: i0.Input, args: [{ isSignal: true, alias: "fdCozy", required: false }] }] } });

/**
 * Abstract class for content density storage implementations.
 * The default implementation is MemoryContentDensityStorage.
 *
 * Provides a signal-based API for reactive content density tracking.
 *
 * @example
 * // Provide a custom storage implementation
 * providers: [
 *   { provide: ContentDensityStorage, useClass: LocalContentDensityStorage }
 * ]
 */
class ContentDensityStorage {
}

const CONTENT_DENSITY_STORAGE_KEY = new InjectionToken('Content density storage key for local storage or for url param', {
    factory: () => '__ContentDensity__'
});

const DEFAULT_CONTENT_DENSITY = new InjectionToken('Default global content density', {
    factory: () => ContentDensityMode.COZY
});

/**
 * Content density storage implementation using browser localStorage.
 * Persists the content density setting across browser sessions.
 */
class LocalContentDensityStorage {
    constructor() {
        this._defaultContentDensity = inject(DEFAULT_CONTENT_DENSITY);
        this._storageKey = inject(CONTENT_DENSITY_STORAGE_KEY);
        this._storage = inject(LocalStorageService);
        this._initialize();
        const storedDensity = this._storage.get(this._storageKey) || this._defaultContentDensity;
        this._contentDensity = signal(storedDensity, ...(ngDevMode ? [{ debugName: "_contentDensity" }] : []));
        this.contentDensity = this._contentDensity.asReadonly();
    }
    /**
     * Updates the content density and persists it to localStorage.
     * @param density The new content density mode
     */
    setContentDensity(density) {
        this._storage.set(this._storageKey, density);
        this._contentDensity.set(density);
    }
    /** @hidden */
    _initialize() {
        if (!this._storage.get(this._storageKey)) {
            this._storage.set(this._storageKey, this._defaultContentDensity);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LocalContentDensityStorage, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LocalContentDensityStorage }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LocalContentDensityStorage, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

/**
 * Content density storage implementation using in-memory state.
 * The setting is lost when the application is refreshed.
 * This is the default storage implementation.
 */
class MemoryContentDensityStorage {
    constructor() {
        const defaultDensity = inject(DEFAULT_CONTENT_DENSITY);
        this._contentDensity = signal(defaultDensity, ...(ngDevMode ? [{ debugName: "_contentDensity" }] : []));
        this.contentDensity = this._contentDensity.asReadonly();
    }
    /**
     * Updates the content density in memory.
     * @param density The new content density mode
     */
    setContentDensity(density) {
        this._contentDensity.set(density);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MemoryContentDensityStorage, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MemoryContentDensityStorage }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MemoryContentDensityStorage, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

/**
 * Content density storage implementation using URL query parameters.
 * Persists the content density setting in the URL, allowing it to be
 * shared via links and bookmarked.
 */
class UrlContentDensityStorage {
    constructor() {
        this._router = inject(Router);
        this._activatedRoute = inject(ActivatedRoute);
        this._defaultContentDensity = inject(DEFAULT_CONTENT_DENSITY);
        this._storageKey = inject(CONTENT_DENSITY_STORAGE_KEY);
        this._destroyRef = inject(DestroyRef);
        this._contentDensity = signal(this._defaultContentDensity, ...(ngDevMode ? [{ debugName: "_contentDensity" }] : []));
        this.contentDensity = this._contentDensity.asReadonly();
        this._initialize();
    }
    /**
     * Updates the content density and reflects it in the URL query parameters.
     * @param density The new content density mode
     */
    setContentDensity(density) {
        this._contentDensity.set(density);
        this._setUrlQueryParam(density);
    }
    /** @hidden */
    _initialize() {
        // Subscribe to query param changes and update signal.
        // Automatically unsubscribes when the service is destroyed.
        this._activatedRoute.queryParams.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((queryParams) => {
            const density = queryParams[this._storageKey];
            if (density && density !== this._contentDensity()) {
                this._contentDensity.set(density);
            }
        });
    }
    /** @hidden */
    _setUrlQueryParam(density) {
        const currentUrl = this._router.url;
        const [pathname, search] = currentUrl.split('?');
        const params = new URLSearchParams(search || '');
        params.delete(this._storageKey);
        params.set(this._storageKey, density);
        this._router.navigateByUrl(`${pathname}?${params.toString()}`);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UrlContentDensityStorage, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UrlContentDensityStorage }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UrlContentDensityStorage, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

/**
 * Service for managing global content density state.
 * Provides a signal-based API for reactive content density tracking.
 */
class GlobalContentDensityService {
    /**
     * Current content density value.
     *
     * @deprecated Use currentDensitySignal() instead
     * @returns The current content density mode
     */
    get currentContentDensity() {
        return this.currentDensitySignal();
    }
    constructor() {
        this._storage = inject(ContentDensityStorage);
        this._injector = inject(Injector);
        this.currentDensitySignal = this._storage.contentDensity;
    }
    /**
     * Returns an observable that emits content density changes.
     *
     * @deprecated Use currentDensitySignal signal instead
     * @returns Observable of content density changes
     */
    contentDensityListener() {
        return toObservable(this._storage.contentDensity, { injector: this._injector });
    }
    /**
     * Updates the global content density.
     * @param density The new content density mode
     */
    updateContentDensity(density) {
        this._storage.setContentDensity(density);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: GlobalContentDensityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: GlobalContentDensityService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: GlobalContentDensityService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

function generateContentDensityStorage(config) {
    if (config.storage === 'localStorage') {
        return {
            provide: ContentDensityStorage,
            useClass: LocalContentDensityStorage
        };
    }
    if (config.storage === 'memory') {
        return {
            provide: ContentDensityStorage,
            useClass: MemoryContentDensityStorage
        };
    }
    if (config.storage === 'url') {
        return {
            provide: ContentDensityStorage,
            useClass: UrlContentDensityStorage,
            deps: [Router, ActivatedRoute, DEFAULT_CONTENT_DENSITY, CONTENT_DENSITY_STORAGE_KEY, DOCUMENT]
        };
    }
    return [];
}
/**
 * Provides content density services and configurations
 * @param config
 */
function provideContentDensity(config) {
    let storage;
    const conf = config || { storage: 'memory' };
    if (typeof conf.storage === 'string') {
        storage = generateContentDensityStorage(conf);
    }
    else if (typeof conf.storage === 'object') {
        storage = conf.storage;
    }
    else {
        storage = {
            provide: ContentDensityStorage,
            useClass: MemoryContentDensityStorage
        };
    }
    return [
        {
            provide: DEFAULT_CONTENT_DENSITY,
            useValue: conf.defaultGlobalContentDensity || ContentDensityMode.COZY
        },
        {
            provide: CONTENT_DENSITY_STORAGE_KEY,
            useValue: conf.storageKey || '__ContentDensity__'
        },
        GlobalContentDensityService,
        storage
    ];
}

/**
 * @deprecated
 * Use direct imports of components and directives.
 */
class ContentDensityModule {
    /** Module with providers */
    static forRoot(config) {
        return {
            ngModule: ContentDensityModule,
            providers: [provideContentDensity(config)]
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityModule, imports: [ContentDensityDirective], exports: [ContentDensityDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [ContentDensityDirective],
                    exports: [ContentDensityDirective]
                }]
        }] });

class ContentDensityObserverSettings {
}

/**
 * Creates a computed signal that resolves the content density from multiple sources.
 *
 * Priority order:
 * 1. Parent content density observer (if restrictChildContentDensity is enabled)
 * 2. Content density directive
 * 3. Global content density service
 * 4. Default content density
 *
 * Special keywords are resolved:
 * - 'default' -> uses defaultContentDensity
 * - 'global' -> uses global service value
 */
const getChangesSource = (params) => computed(() => {
    // Get the raw mode from the appropriate source (priority order)
    const rawMode = params.parentContentDensityObserver
        ? params.parentContentDensityObserver()
        : params.contentDensityDirective
            ? params.contentDensityDirective()
            : (params.contentDensityService?.currentDensitySignal() ?? params.defaultContentDensity);
    // Resolve special keywords
    if (rawMode === ContentDensityDefaultKeyword) {
        return params.defaultContentDensity;
    }
    if (rawMode === ContentDensityGlobalKeyword) {
        return params.contentDensityService?.currentDensitySignal() ?? params.defaultContentDensity;
    }
    return rawMode;
});

const defaultContentDensityObserverConfigs = {
    modifiers: {
        [ContentDensityMode.COMPACT]: 'is-compact',
        [ContentDensityMode.COZY]: 'is-cozy',
        [ContentDensityMode.CONDENSED]: 'is-condensed'
    },
    supportedContentDensity: [ContentDensityMode.COMPACT, ContentDensityMode.COZY],
    defaultContentDensity: ContentDensityMode.COZY,
    debug: false,
    alwaysAddModifiers: false,
    restrictChildContentDensity: false,
    ui5Markers: {
        enabled: true
    }
};

const isFactoryProvider = (obj) => !!(obj && obj.useFactory);
const getDeps = (injector, defaultContentDensity) => (defaultContentDensity.deps || []).map((dep) => {
    if (Array.isArray(dep)) {
        let type;
        let flags = {};
        for (let index = 0; index < dep.length; index++) {
            const flag = dep[index]['__NG_DI_FLAG__'];
            if (typeof flag === 'number') {
                flags = { ...flags, flag };
            }
            else {
                type = dep[index];
            }
        }
        return injector.get(type, undefined, flags);
    }
    return injector.get(dep, undefined, {});
});
const getDefaultContentDensity = (injector, configuration) => {
    if (typeof configuration.defaultContentDensity === 'string') {
        return configuration.defaultContentDensity;
    }
    if (isFactoryProvider(configuration.defaultContentDensity)) {
        const deps = getDeps(injector, configuration.defaultContentDensity);
        return configuration.defaultContentDensity.useFactory(...deps);
    }
    return injector.get(configuration.defaultContentDensity, undefined, undefined);
};
const initialContentDensity = (injector, configuration) => {
    const serviceValue = injector.get(GlobalContentDensityService, null, { optional: true })?.currentContentDensity;
    if (serviceValue) {
        return serviceValue;
    }
    return getDefaultContentDensity(injector, {
        ...defaultContentDensityObserverConfigs,
        ...(configuration || {})
    });
};
/**
 * Service for observing and managing content density in components.
 *
 * Provides signal-based API for reactive content density tracking.
 * Uses linkedSignal (Angular 21+) for derived state with validation.
 */
class ContentDensityObserver {
    /**
     * Current content density value
     * @deprecated Use contentDensity() signal instead
     */
    get value() {
        return this._contentDensity();
    }
    /**
     * Whether content density is compact
     * @deprecated Use isCompactSignal() signal instead
     */
    get isCompact() {
        return this.isCompactSignal();
    }
    /**
     * Whether content density is cozy
     * @deprecated Use isCozySignal() signal instead
     */
    get isCozy() {
        return this.isCozySignal();
    }
    /**
     * Whether content density is condensed
     * @deprecated Use isCondensedSignal() signal instead
     */
    get isCondensed() {
        return this.isCondensedSignal();
    }
    constructor(_injector, _providedConfig) {
        this._destroyRef = inject(DestroyRef);
        this._alternativeTo = {
            [ContentDensityMode.COMPACT]: () => this._isSupported(ContentDensityMode.CONDENSED) ? ContentDensityMode.CONDENSED : ContentDensityMode.COZY,
            [ContentDensityMode.CONDENSED]: () => this._isSupported(ContentDensityMode.COMPACT) ? ContentDensityMode.COMPACT : ContentDensityMode.COZY,
            [ContentDensityMode.COZY]: () => ContentDensityMode.COZY // No alternative here, everyone should support it
        };
        this._globalContentDensityService = inject(GlobalContentDensityService, {
            optional: true
        });
        this._contentDensityDirective = inject(CONTENT_DENSITY_DIRECTIVE, {
            optional: true
        });
        this._parentContentDensityObserver = inject(ContentDensityObserver, {
            optional: true,
            skipSelf: true
        });
        this._renderer = inject(Renderer2);
        this._elementRef = inject(ElementRef);
        this._elements = [this._elementRef];
        // Set up config first (needed for validation)
        this.config = {
            ...defaultContentDensityObserverConfigs,
            ...(this._parentContentDensityObserver?.config ?? {}),
            ...(_providedConfig || {})
        };
        // Resolve initial density
        const resolvedInitialDensity = initialContentDensity(_injector, _providedConfig);
        // Get the changes source as a computed signal
        this._changesSource = getChangesSource({
            defaultContentDensity: resolvedInitialDensity,
            contentDensityDirective: this._contentDensityDirective?.densityMode,
            contentDensityService: this._globalContentDensityService ?? undefined,
            parentContentDensityObserver: this.config.restrictChildContentDensity
                ? this._parentContentDensityObserver?.contentDensity
                : undefined
        });
        // Use linkedSignal for derived state with validation (Angular 21+ pattern)
        // linkedSignal automatically updates when source changes, applying the computation
        this._contentDensity = linkedSignal({ ...(ngDevMode ? { debugName: "_contentDensity" } : {}), source: this._changesSource,
            computation: (source) => {
                // Guard against undefined/null source values
                if (!source || typeof source !== 'string') {
                    return ContentDensityMode.COZY; // Safe fallback to default
                }
                return this._validateAndFallback(source);
            } });
        // Public readonly signal
        this.contentDensity = this._contentDensity.asReadonly();
        // Computed boolean signals
        this.isCompactSignal = computed(() => this._contentDensity() === ContentDensityMode.COMPACT, ...(ngDevMode ? [{ debugName: "isCompactSignal" }] : []));
        this.isCozySignal = computed(() => this._contentDensity() === ContentDensityMode.COZY, ...(ngDevMode ? [{ debugName: "isCozySignal" }] : []));
        this.isCondensedSignal = computed(() => this._contentDensity() === ContentDensityMode.CONDENSED, ...(ngDevMode ? [{ debugName: "isCondensedSignal" }] : []));
        // Backward compatible signal aliases
        this.contentDensity$ = this.contentDensity;
        // Backward compatible observables
        this.isCompact$ = toObservable(this.isCompactSignal, { injector: _injector });
        this.isCozy$ = toObservable(this.isCozySignal, { injector: _injector });
        this.isCondensed$ = toObservable(this.isCondensedSignal, { injector: _injector });
        this.contentDensity$$ = toObservable(this._contentDensity, { injector: _injector });
        // Effect purely for DOM side effects (CSS classes and UI5 attribute)
        // This follows Angular 21 best practices: effects should only do side effects, not update signals
        effect(() => {
            // Read the signal to track changes
            const density = this._contentDensity();
            // Apply DOM changes (side effect)
            this._applyClass(density);
        });
        // Apply initial CSS class after first render (zoneless-compatible)
        afterNextRender(() => {
            this._applyClass(this._contentDensity());
        });
        // Register cleanup on destroy
        this._destroyRef.onDestroy(() => {
            this._cleanup();
            if (this.config.debug) {
                console.warn('ContentDensityObserver: destroyed');
            }
        });
    }
    /**
     * Add consumers to observe content density changes
     * @deprecated Use signal bindings instead
     */
    consume(...consumers) {
        this._elements.concat(...consumers.map((c) => c.elementRef));
    }
    /**
     * Completes the observer and cleans up resources
     * @deprecated Cleanup is automatic via DestroyRef
     */
    complete() {
        this._cleanup();
    }
    /**
     * Returns an observable of content density changes
     * @deprecated Use contentDensity signal instead
     */
    asObservable() {
        return this.contentDensity$$;
    }
    /**
     * Subscribe to content density changes.
     * @deprecated Use contentDensity signal with effect() instead.
     * This method exists for backward compatibility.
     */
    subscribe(observer) {
        return this.contentDensity$$.subscribe(observer);
    }
    /**
     * Remove a consumer from the observer
     * @deprecated Use signal bindings instead
     */
    removeConsumer(consumer) {
        this._elements.splice(this._elements.indexOf(consumer.elementRef), 1);
    }
    _validateAndFallback(density) {
        if (this.config.debug) {
            console.warn(`ContentDensityObserver: density changed to ${density}`);
        }
        if (!this._isSupported(density)) {
            try {
                if (this.config.debug) {
                    console.warn(`ContentDensityObserver: ${density} is not supported. Failing back to alternative one.`);
                }
                return this._alternativeTo[density]();
            }
            catch {
                throw new Error(`ContentDensityObserver: density ${density} is not supported`);
            }
        }
        return density;
    }
    _cleanup() {
        this._parentContentDensityObserver = null;
        this._contentDensityDirective = null;
        this._globalContentDensityService = null;
        this._elementRef = null;
        this._renderer = null;
        this._elements = [];
    }
    _applyClass(currentDensity) {
        if (!this.config?.modifiers) {
            return;
        }
        const modifiers = this.config.modifiers;
        const density = currentDensity ?? this._contentDensity();
        const parentContentDensityEqual = this._parentContentDensityObserver?.value === density;
        this._elements.forEach((element) => {
            Object.values(modifiers).forEach((className) => {
                this._renderer?.removeClass(element?.nativeElement, className);
            });
            // Apply/remove UI5 compact marker attribute
            this._applyUi5Marker(element?.nativeElement, density, parentContentDensityEqual);
            // Simply remove all modifiers from current element. Content density state is covered by parent element.
            if (parentContentDensityEqual && !this.config.alwaysAddModifiers) {
                return;
            }
            const modifierClass = modifiers[density];
            if (modifierClass) {
                this._renderer?.addClass(element?.nativeElement, modifierClass);
            }
        });
    }
    /**
     * Apply or remove UI5 Web Components compact marker.
     * UI5 Web Components only support cozy (default) and compact modes.
     * Both COMPACT and CONDENSED fundamental-ngx modes map to UI5 compact.
     */
    _applyUi5Marker(nativeElement, currentDensity, parentContentDensityEqual) {
        // Only apply to actual HTML elements, not comment/text nodes
        if (!this.config.ui5Markers?.enabled ||
            !nativeElement ||
            !this._renderer ||
            !(nativeElement instanceof HTMLElement)) {
            return;
        }
        const ui5CompactAttribute = 'data-ui5-compact-size';
        // Remove attribute if parent already has the same density (unless alwaysAddModifiers)
        if (parentContentDensityEqual && !this.config.alwaysAddModifiers) {
            this._renderer.removeAttribute(nativeElement, ui5CompactAttribute);
            return;
        }
        // UI5 only has cozy (default) and compact modes
        // Map: COMPACT -> compact, CONDENSED -> compact, COZY -> remove attribute
        const isUi5Compact = currentDensity === ContentDensityMode.COMPACT || currentDensity === ContentDensityMode.CONDENSED;
        if (isUi5Compact) {
            this._renderer.setAttribute(nativeElement, ui5CompactAttribute, '');
        }
        else {
            this._renderer.removeAttribute(nativeElement, ui5CompactAttribute);
        }
    }
    _isSupported(density) {
        return this.config.supportedContentDensity?.includes(density) ?? false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityObserver, deps: [{ token: i0.Injector }, { token: ContentDensityObserverSettings }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityObserver }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ContentDensityObserver, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i0.Injector }, { type: ContentDensityObserverSettings }] });

/**
 * Creates provider for ContentDensityObserver
 */
function contentDensityObserverProviders(params) {
    return [consumerProviderFactory(ContentDensityObserver, params)];
}

/** @hidden */
function mockedLocalContentDensityDirective(defaultValue = ContentDensityGlobalKeyword) {
    const densitySignal = signal(defaultValue, ...(ngDevMode ? [{ debugName: "densitySignal" }] : []));
    const mockDirective = {
        densityMode: densitySignal.asReadonly(),
        get value() {
            return densitySignal();
        }
    };
    return {
        contentDensityDirectiveProvider: {
            provide: CONTENT_DENSITY_DIRECTIVE,
            useValue: mockDirective
        },
        setContentDensity: (cd) => densitySignal.set(cd)
    };
}

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

export { CONTENT_DENSITY_DIRECTIVE, ContentDensityDefaultKeyword, ContentDensityDirective, ContentDensityGlobalKeyword, ContentDensityMode, ContentDensityModule, ContentDensityObserver, ContentDensityObserverSettings, ContentDensityStorage, GlobalContentDensityService, contentDensityObserverProviders, defaultContentDensityObserverConfigs, isCompact, isCondensed, isContentDensityMode, isCozy, mockedLocalContentDensityDirective, provideContentDensity };
//# sourceMappingURL=fundamental-ngx-core-content-density.mjs.map
