import * as i0 from '@angular/core';
import { InjectionToken, inject, PLATFORM_ID, ElementRef, input, Directive, booleanAttribute, output, DestroyRef, afterNextRender, numberAttribute, forwardRef, NgZone, model, effect, Pipe, Injectable, DOCUMENT, isDevMode, EventEmitter, signal } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { FocusMonitor, isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader } from '@angular/cdk/a11y';
import { fromEvent, filter, merge, debounceTime, Observable, Subscription } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { normalizePassiveListenerOptions, _getEventTarget, Platform } from '@angular/cdk/platform';
import { coerceElement } from '@angular/cdk/coercion';
import { DomSanitizer, Title, Meta } from '@angular/platform-browser';
import { Router, NavigationEnd, TitleStrategy } from '@angular/router';
import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';

const AUTOFOCUSABLE = new InjectionToken('AUTOFOCUSABLE');

class AutoFocusDirective {
    platformId = inject(PLATFORM_ID);
    elementRef = inject(ElementRef);
    autofocusable = inject(AUTOFOCUSABLE, { optional: true });
    enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), alias: 'ngsAutoFocus' });
    ngOnChanges(changes) {
        if (changes['enabled'] && !changes['enabled'].firstChange) {
            this._focus();
        }
    }
    ngAfterViewInit() {
        this._focus();
    }
    _focus() {
        if (this.enabled() === false || this.enabled() === 'false') {
            return;
        }
        if (this.autofocusable) {
            this.autofocusable.focus();
        }
        else {
            this.elementRef.nativeElement.focus();
        }
        this._moveCursorToEnd(this.elementRef.nativeElement);
    }
    _moveCursorToEnd(el) {
        if (!isPlatformBrowser(this.platformId)) {
            return;
        }
        const range = document.createRange();
        const sel = window.getSelection();
        range.selectNodeContents(el);
        range.collapse(false);
        sel?.removeAllRanges();
        sel?.addRange(range);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AutoFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: AutoFocusDirective, isStandalone: true, selector: "[ngsAutoFocus]", inputs: { enabled: { classPropertyName: "enabled", publicName: "ngsAutoFocus", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["ngsAutoFocus"], usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AutoFocusDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsAutoFocus]',
                    exportAs: 'ngsAutoFocus',
                    standalone: true
                }]
        }], propDecorators: { enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsAutoFocus", required: false }] }] } });

class FocusElementDirective {
    _elementRef = inject(ElementRef);
    _focusMonitor = inject(FocusMonitor);
    checkChildren = input(true, { ...(ngDevMode ? { debugName: "checkChildren" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    elementFocused = output();
    elementBlurred = output();
    ngAfterViewInit() {
        this._focusMonitor
            .monitor(this._elementRef.nativeElement, this.checkChildren())
            .subscribe(origin => {
            this._emitFocusEvent(origin);
        });
    }
    ngOnDestroy() {
        this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
    }
    _emitFocusEvent(origin) {
        if (origin) {
            this.elementFocused.emit();
        }
        else {
            this.elementBlurred.emit();
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FocusElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: FocusElementDirective, isStandalone: true, selector: "[ngsFocusElement]", inputs: { checkChildren: { classPropertyName: "checkChildren", publicName: "checkChildren", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { elementFocused: "elementFocused", elementBlurred: "elementBlurred" }, exportAs: ["ngsFocusElement"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FocusElementDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsFocusElement]',
                    exportAs: 'ngsFocusElement',
                    standalone: true
                }]
        }], propDecorators: { checkChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkChildren", required: false }] }], elementFocused: [{ type: i0.Output, args: ["elementFocused"] }], elementBlurred: [{ type: i0.Output, args: ["elementBlurred"] }] } });

class SoundEffectDirective {
    soundSrc = input('assets/sound-effects/mouse-click.ogg', ...(ngDevMode ? [{ debugName: "soundSrc" }] : /* istanbul ignore next */ []));
    _elementRef = inject(ElementRef);
    _destroyRef = inject(DestroyRef);
    constructor() {
        afterNextRender(() => {
            this._init();
        });
    }
    _init() {
        const audio = new Audio(this.soundSrc());
        audio.volume = 0.15;
        fromEvent(this._elementRef.nativeElement, 'click')
            .pipe(takeUntilDestroyed(this._destroyRef))
            .subscribe(async (e) => {
            await audio.play();
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SoundEffectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: SoundEffectDirective, isStandalone: true, selector: "[ngsSoundEffect]", inputs: { soundSrc: { classPropertyName: "soundSrc", publicName: "soundSrc", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SoundEffectDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsSoundEffect]',
                    standalone: true
                }]
        }], ctorParameters: () => [], propDecorators: { soundSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "soundSrc", required: false }] }] } });

class DebounceTimeDirective {
    elementRef;
    renderer;
    debounceTimer;
    debounceTime = input(300, { ...(ngDevMode ? { debugName: "debounceTime" } : /* istanbul ignore next */ {}), transform: numberAttribute });
    onChange = () => { };
    onTouched = () => { };
    constructor(elementRef, renderer) {
        this.elementRef = elementRef;
        this.renderer = renderer;
    }
    writeValue(value) {
        this.renderer.setProperty(this.elementRef.nativeElement, 'value', value ?? '');
    }
    registerOnChange(fn) {
        this.onChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', isDisabled);
    }
    handleInput(event) {
        clearTimeout(this.debounceTimer);
        const target = event.target;
        const value = target?.value ?? '';
        if (!this.debounceTime()) {
            this.onChange(value);
        }
        else {
            this.debounceTimer = setTimeout(() => {
                this.onChange(value);
            }, this.debounceTime());
        }
    }
    handleBlur() {
        this.onTouched();
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: DebounceTimeDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: DebounceTimeDirective, isStandalone: true, selector: "[ngsDebounceTime][ngModel]", inputs: { debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "input": "handleInput($event)", "blur": "handleBlur()" } }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => DebounceTimeDirective),
                multi: true,
            },
        ], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: DebounceTimeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsDebounceTime][ngModel]',
                    providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => DebounceTimeDirective),
                            multi: true,
                        },
                    ],
                    host: {
                        '(input)': 'handleInput($event)',
                        '(blur)': 'handleBlur()'
                    }
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }] } });

/** Possible states for a ripple element. */
var RippleState;
(function (RippleState) {
    RippleState[RippleState["FADING_IN"] = 0] = "FADING_IN";
    RippleState[RippleState["VISIBLE"] = 1] = "VISIBLE";
    RippleState[RippleState["FADING_OUT"] = 2] = "FADING_OUT";
    RippleState[RippleState["HIDDEN"] = 3] = "HIDDEN";
})(RippleState || (RippleState = {}));
/**
 * Reference to a previously launched ripple element.
 */
class RippleRef {
    _renderer;
    element;
    config;
    _animationForciblyDisabledThroughCss;
    /** Current state of the ripple. */
    state = RippleState.HIDDEN;
    constructor(_renderer, 
    /** Reference to the ripple HTML element. */
    element, 
    /** Ripple configuration used for the ripple. */
    config, 
    /* Whether animations are forcibly disabled for ripples through CSS. */
    _animationForciblyDisabledThroughCss = false) {
        this._renderer = _renderer;
        this.element = element;
        this.config = config;
        this._animationForciblyDisabledThroughCss = _animationForciblyDisabledThroughCss;
    }
    /** Fades out the ripple element. */
    fadeOut() {
        this._renderer.fadeOutRipple(this);
    }
}
/** Options used to bind a passive capturing event. */
const passiveCapturingEventOptions = normalizePassiveListenerOptions({
    passive: true,
    capture: true,
});
/** Manages events through delegation so that as few event handlers as possible are bound. */
class RippleEventManager {
    _events = new Map();
    /** Adds an event handler. */
    addHandler(ngZone, name, element, handler) {
        const handlersForEvent = this._events.get(name);
        if (handlersForEvent) {
            const handlersForElement = handlersForEvent.get(element);
            if (handlersForElement) {
                handlersForElement.add(handler);
            }
            else {
                handlersForEvent.set(element, new Set([handler]));
            }
        }
        else {
            this._events.set(name, new Map([[element, new Set([handler])]]));
            ngZone.runOutsideAngular(() => {
                document.addEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);
            });
        }
    }
    /** Removes an event handler. */
    removeHandler(name, element, handler) {
        const handlersForEvent = this._events.get(name);
        if (!handlersForEvent) {
            return;
        }
        const handlersForElement = handlersForEvent.get(element);
        if (!handlersForElement) {
            return;
        }
        handlersForElement.delete(handler);
        if (handlersForElement.size === 0) {
            handlersForEvent.delete(element);
        }
        if (handlersForEvent.size === 0) {
            this._events.delete(name);
            document.removeEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);
        }
    }
    /** Event handler that is bound and which dispatches the events to the different targets. */
    _delegateEventHandler = (event) => {
        const target = _getEventTarget(event);
        if (target) {
            this._events.get(event.type)?.forEach((handlers, element) => {
                if (element === target || element.contains(target)) {
                    handlers.forEach(handler => handler.handleEvent(event));
                }
            });
        }
    };
}
/**
 * Default ripple animation configuration for ripples without an explicit
 * animation config specified.
 */
const defaultRippleAnimationConfig = {
    enterDuration: 225,
    exitDuration: 150,
};
/**
 * Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch
 * events to avoid synthetic mouse events.
 */
const ignoreMouseEventsTimeout = 800;
/** Events that signal that the pointer is down. */
const pointerDownEvents = ['mousedown', 'touchstart'];
/** Events that signal that the pointer is up. */
const pointerUpEvents = ['mouseup', 'mouseleave', 'touchend', 'touchcancel'];
/**
 * Helper service that performs DOM manipulations. Not intended to be used outside this module.
 */
class RippleRenderer {
    _target;
    _ngZone;
    _platform;
    /** Element where the ripples are being added to. */
    _containerElement;
    /** Element which triggers the ripple elements on mouse events. */
    _triggerElement = null;
    /** Whether the pointer is currently down or not. */
    _isPointerDown = false;
    /** Map of currently active ripple references. */
    _activeRipples = new Map();
    /** Latest non-persistent ripple that was triggered. */
    _mostRecentTransientRipple = null;
    /** Time in milliseconds when the last touchstart event happened. */
    _lastTouchStartEvent = null;
    /** Whether pointer-up event listeners have been registered. */
    _pointerUpEventsRegistered = false;
    /** Cached dimensions of the ripple container. */
    _containerRect = null;
    static _eventManager = new RippleEventManager();
    constructor(_target, _ngZone, elementOrElementRef, _platform) {
        this._target = _target;
        this._ngZone = _ngZone;
        this._platform = _platform;
        // Only do anything if we're on the browser.
        if (_platform.isBrowser) {
            this._containerElement = coerceElement(elementOrElementRef);
        }
    }
    /** Fades in a ripple at the given coordinates. */
    fadeInRipple(x, y, config = {}) {
        const containerRect = (this._containerRect =
            this._containerRect || this._containerElement.getBoundingClientRect());
        const animationConfig = { ...defaultRippleAnimationConfig, ...config.animation };
        if (config.centered) {
            x = containerRect.left + containerRect.width / 2;
            y = containerRect.top + containerRect.height / 2;
        }
        const radius = config.radius || distanceToFurthestCorner(x, y, containerRect);
        const offsetX = x - containerRect.left;
        const offsetY = y - containerRect.top;
        const enterDuration = animationConfig.enterDuration;
        const ripple = document.createElement('div');
        ripple.classList.add('ngs-ripple-element');
        ripple.style.left = `${offsetX - radius}px`;
        ripple.style.top = `${offsetY - radius}px`;
        ripple.style.height = `${radius * 2}px`;
        ripple.style.width = `${radius * 2}px`;
        if (config.color != null) {
            ripple.style.backgroundColor = config.color;
        }
        ripple.style.transitionDuration = `${enterDuration}ms`;
        this._containerElement.appendChild(ripple);
        const computedStyles = window.getComputedStyle(ripple);
        const userTransitionProperty = computedStyles.transitionProperty;
        const userTransitionDuration = computedStyles.transitionDuration;
        const animationForciblyDisabledThroughCss = userTransitionProperty === 'none' ||
            userTransitionDuration === '0s' ||
            userTransitionDuration === '0s, 0s' ||
            (containerRect.width === 0 && containerRect.height === 0);
        const rippleRef = new RippleRef(this, ripple, config, animationForciblyDisabledThroughCss);
        ripple.style.transform = 'scale3d(1, 1, 1)';
        rippleRef.state = RippleState.FADING_IN;
        if (!config.persistent) {
            this._mostRecentTransientRipple = rippleRef;
        }
        let eventListeners = null;
        if (!animationForciblyDisabledThroughCss && (enterDuration || animationConfig.exitDuration)) {
            this._ngZone.runOutsideAngular(() => {
                const onTransitionEnd = () => {
                    if (eventListeners) {
                        eventListeners.fallbackTimer = null;
                    }
                    clearTimeout(fallbackTimer);
                    this._finishRippleTransition(rippleRef);
                };
                const onTransitionCancel = () => this._destroyRipple(rippleRef);
                const fallbackTimer = setTimeout(onTransitionCancel, (enterDuration || 0) + 100);
                ripple.addEventListener('transitionend', onTransitionEnd);
                ripple.addEventListener('transitioncancel', onTransitionCancel);
                eventListeners = { onTransitionEnd, onTransitionCancel, fallbackTimer };
            });
        }
        this._activeRipples.set(rippleRef, eventListeners);
        if (animationForciblyDisabledThroughCss || !enterDuration) {
            this._finishRippleTransition(rippleRef);
        }
        return rippleRef;
    }
    /** Fades out a ripple reference. */
    fadeOutRipple(rippleRef) {
        if (rippleRef.state === RippleState.FADING_OUT || rippleRef.state === RippleState.HIDDEN) {
            return;
        }
        const rippleEl = rippleRef.element;
        const animationConfig = { ...defaultRippleAnimationConfig, ...rippleRef.config.animation };
        rippleEl.style.transitionDuration = `${animationConfig.exitDuration}ms`;
        rippleEl.style.opacity = '0';
        rippleRef.state = RippleState.FADING_OUT;
        if (rippleRef._animationForciblyDisabledThroughCss || !animationConfig.exitDuration) {
            this._finishRippleTransition(rippleRef);
        }
    }
    /** Fades out all currently active ripples. */
    fadeOutAll() {
        this._getActiveRipples().forEach(ripple => ripple.fadeOut());
    }
    /** Fades out all currently showing non-persistent ripples. */
    fadeOutAllNonPersistent() {
        this._getActiveRipples().forEach(ripple => {
            if (!ripple.config.persistent) {
                ripple.fadeOut();
            }
        });
    }
    /** Sets up the trigger event listeners */
    setupTriggerEvents(elementOrElementRef) {
        const element = coerceElement(elementOrElementRef);
        if (!this._platform.isBrowser || !element || element === this._triggerElement) {
            return;
        }
        this._removeTriggerEvents();
        this._triggerElement = element;
        pointerDownEvents.forEach(type => {
            RippleRenderer._eventManager.addHandler(this._ngZone, type, element, this);
        });
    }
    /** Handles all registered events. */
    handleEvent(event) {
        if (event.type === 'mousedown') {
            this._onMousedown(event);
        }
        else if (event.type === 'touchstart') {
            this._onTouchStart(event);
        }
        else {
            this._onPointerUp();
        }
        if (!this._pointerUpEventsRegistered && this._triggerElement) {
            this._ngZone.runOutsideAngular(() => {
                pointerUpEvents.forEach(type => {
                    this._triggerElement.addEventListener(type, this, passiveCapturingEventOptions);
                });
            });
            this._pointerUpEventsRegistered = true;
        }
    }
    /** Method that will be called if the fade-in or fade-in transition completed. */
    _finishRippleTransition(rippleRef) {
        if (rippleRef.state === RippleState.FADING_IN) {
            this._startFadeOutTransition(rippleRef);
        }
        else if (rippleRef.state === RippleState.FADING_OUT) {
            this._destroyRipple(rippleRef);
        }
    }
    /** Starts the fade-out transition of the given ripple. */
    _startFadeOutTransition(rippleRef) {
        const isMostRecentTransientRipple = rippleRef === this._mostRecentTransientRipple;
        const { persistent } = rippleRef.config;
        rippleRef.state = RippleState.VISIBLE;
        if (!persistent && (!isMostRecentTransientRipple || !this._isPointerDown)) {
            rippleRef.fadeOut();
        }
    }
    /** Destroys the given ripple. */
    _destroyRipple(rippleRef) {
        const eventListeners = this._activeRipples.get(rippleRef) ?? null;
        this._activeRipples.delete(rippleRef);
        if (!this._activeRipples.size) {
            this._containerRect = null;
        }
        if (rippleRef === this._mostRecentTransientRipple) {
            this._mostRecentTransientRipple = null;
        }
        rippleRef.state = RippleState.HIDDEN;
        if (eventListeners !== null) {
            rippleRef.element.removeEventListener('transitionend', eventListeners.onTransitionEnd);
            rippleRef.element.removeEventListener('transitioncancel', eventListeners.onTransitionCancel);
            if (eventListeners.fallbackTimer !== null) {
                clearTimeout(eventListeners.fallbackTimer);
            }
        }
        rippleRef.element.remove();
    }
    /** Function being called whenever the trigger is being pressed using mouse. */
    _onMousedown(event) {
        const isFakeMousedown = isFakeMousedownFromScreenReader(event);
        const isSyntheticEvent = this._lastTouchStartEvent && Date.now() < this._lastTouchStartEvent + ignoreMouseEventsTimeout;
        if (!this._target.rippleDisabled && !isFakeMousedown && !isSyntheticEvent) {
            this._isPointerDown = true;
            this.fadeInRipple(event.clientX, event.clientY, this._target.rippleConfig);
        }
    }
    /** Function being called whenever the trigger is being pressed using touch. */
    _onTouchStart(event) {
        if (!this._target.rippleDisabled && !isFakeTouchstartFromScreenReader(event)) {
            this._lastTouchStartEvent = Date.now();
            this._isPointerDown = true;
            const touches = event.changedTouches;
            if (touches) {
                for (let i = 0; i < touches.length; i++) {
                    this.fadeInRipple(touches[i].clientX, touches[i].clientY, this._target.rippleConfig);
                }
            }
        }
    }
    /** Function being called whenever the trigger is being released. */
    _onPointerUp() {
        if (!this._isPointerDown) {
            return;
        }
        this._isPointerDown = false;
        this._getActiveRipples().forEach(ripple => {
            const isVisible = ripple.state === RippleState.VISIBLE ||
                (ripple.config.terminateOnPointerUp && ripple.state === RippleState.FADING_IN);
            if (!ripple.config.persistent && isVisible) {
                ripple.fadeOut();
            }
        });
    }
    _getActiveRipples() {
        return Array.from(this._activeRipples.keys());
    }
    /** Removes previously registered event listeners from the trigger element. */
    _removeTriggerEvents() {
        const trigger = this._triggerElement;
        if (trigger) {
            pointerDownEvents.forEach(type => RippleRenderer._eventManager.removeHandler(type, trigger, this));
            if (this._pointerUpEventsRegistered) {
                pointerUpEvents.forEach(type => trigger.removeEventListener(type, this, passiveCapturingEventOptions));
                this._pointerUpEventsRegistered = false;
            }
        }
    }
}
/** Returns the distance from the point (x, y) to the furthest corner of a rectangle. */
function distanceToFurthestCorner(x, y, rect) {
    const distX = Math.max(Math.abs(x - rect.left), Math.abs(x - rect.right));
    const distY = Math.max(Math.abs(y - rect.top), Math.abs(y - rect.bottom));
    return Math.sqrt(distX * distX + distY * distY);
}
const RIPPLE_GLOBAL_OPTIONS = new InjectionToken('ngs-ripple-global-options');
class Ripple {
    _elementRef = inject(ElementRef);
    _ngZone = inject(NgZone);
    _platform = inject(Platform);
    _globalOptions = inject(RIPPLE_GLOBAL_OPTIONS, { optional: true }) || {};
    color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'ngsRippleColor' });
    unbounded = input(false, { ...(ngDevMode ? { debugName: "unbounded" } : /* istanbul ignore next */ {}), alias: 'ngsRippleUnbounded', transform: booleanAttribute });
    centered = model(false, { ...(ngDevMode ? { debugName: "centered" } : /* istanbul ignore next */ {}), alias: 'ngsRippleCentered' });
    radius = input(0, { ...(ngDevMode ? { debugName: "radius" } : /* istanbul ignore next */ {}), alias: 'ngsRippleRadius', transform: numberAttribute });
    animation = input(undefined, { ...(ngDevMode ? { debugName: "animation" } : /* istanbul ignore next */ {}), alias: 'ngsRippleAnimation' });
    disabled = model(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'ngsRippleDisabled' });
    trigger = model(undefined, { ...(ngDevMode ? { debugName: "trigger" } : /* istanbul ignore next */ {}), alias: 'ngsRippleTrigger' });
    _rippleRenderer;
    _isInitialized = false;
    constructor() {
        this._rippleRenderer = new RippleRenderer(this, this._ngZone, this._elementRef, this._platform);
        effect(() => {
            if (this.disabled()) {
                this.fadeOutAllNonPersistent();
            }
            this._setupTriggerEventsIfEnabled();
        });
        effect(() => {
            this.trigger();
            this._setupTriggerEventsIfEnabled();
        });
    }
    ngOnInit() {
        this._isInitialized = true;
        this._setupTriggerEventsIfEnabled();
    }
    ngOnDestroy() {
        this._rippleRenderer._removeTriggerEvents();
    }
    /** Fades out all currently showing ripple elements. */
    fadeOutAll() {
        this._rippleRenderer.fadeOutAll();
    }
    /** Fades out all currently showing non-persistent ripple elements. */
    fadeOutAllNonPersistent() {
        this._rippleRenderer.fadeOutAllNonPersistent();
    }
    get rippleConfig() {
        return {
            centered: this.centered(),
            radius: this.radius(),
            color: this.color(),
            animation: {
                ...this._globalOptions.animation,
                ...this.animation(),
            },
            terminateOnPointerUp: this._globalOptions.terminateOnPointerUp,
        };
    }
    get rippleDisabled() {
        return this.disabled() || !!this._globalOptions.disabled;
    }
    _setupTriggerEventsIfEnabled() {
        if (!this.disabled() && this._isInitialized) {
            this._rippleRenderer.setupTriggerEvents(this.trigger() || this._elementRef.nativeElement);
        }
    }
    /** Launches a manual ripple at the specified coordinates or just by the ripple config. */
    launch(configOrX, y = 0, config) {
        if (typeof configOrX === 'number') {
            return this._rippleRenderer.fadeInRipple(configOrX, y, { ...this.rippleConfig, ...config });
        }
        else {
            return this._rippleRenderer.fadeInRipple(0, 0, { ...this.rippleConfig, ...configOrX });
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Ripple, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: Ripple, isStandalone: true, selector: "[ngsRipple]", inputs: { color: { classPropertyName: "color", publicName: "ngsRippleColor", isSignal: true, isRequired: false, transformFunction: null }, unbounded: { classPropertyName: "unbounded", publicName: "ngsRippleUnbounded", isSignal: true, isRequired: false, transformFunction: null }, centered: { classPropertyName: "centered", publicName: "ngsRippleCentered", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "ngsRippleRadius", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "ngsRippleAnimation", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngsRippleDisabled", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "ngsRippleTrigger", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { centered: "ngsRippleCenteredChange", disabled: "ngsRippleDisabledChange", trigger: "ngsRippleTriggerChange" }, host: { properties: { "class.ngs-ripple-unbounded": "unbounded()" }, classAttribute: "ngs-ripple" }, exportAs: ["ngsRipple"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Ripple, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsRipple]',
                    standalone: true,
                    exportAs: 'ngsRipple',
                    host: {
                        'class': 'ngs-ripple',
                        '[class.ngs-ripple-unbounded]': 'unbounded()',
                    },
                }]
        }], ctorParameters: () => [], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleColor", required: false }] }], unbounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleUnbounded", required: false }] }], centered: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleCentered", required: false }] }, { type: i0.Output, args: ["ngsRippleCenteredChange"] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleRadius", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleAnimation", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleDisabled", required: false }] }, { type: i0.Output, args: ["ngsRippleDisabledChange"] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsRippleTrigger", required: false }] }, { type: i0.Output, args: ["ngsRippleTriggerChange"] }] } });

function injectElement() {
    return inject(ElementRef).nativeElement;
}
function isElement(node) {
    return !!node && 'nodeType' in node && node.nodeType === Node.ELEMENT_NODE;
}
function getActualTarget(event) {
    return (event.target ?? event.currentTarget);
}

class TextareaAutoSize {
    _textarea = injectElement();
    _ngZone = inject(NgZone);
    _platform = inject(Platform);
    minRows = input(1, { ...(ngDevMode ? { debugName: "minRows" } : /* istanbul ignore next */ {}), transform: numberAttribute });
    maxRows = input(Number.MAX_SAFE_INTEGER, { ...(ngDevMode ? { debugName: "maxRows" } : /* istanbul ignore next */ {}), transform: numberAttribute });
    _resizeObserver;
    ngAfterViewInit() {
        if (this._platform.isBrowser) {
            this.resize();
            this._ngZone.runOutsideAngular(() => {
                this._resizeObserver = new ResizeObserver(() => this.resize());
                this._resizeObserver.observe(this._textarea);
            });
        }
    }
    ngOnDestroy() {
        this._resizeObserver?.disconnect();
    }
    resize() {
        this._textarea.style.height = 'auto';
        const style = window.getComputedStyle(this._textarea);
        let lineHeight = parseFloat(style.lineHeight);
        if (isNaN(lineHeight)) {
            lineHeight = parseFloat(style.fontSize) * 1.2;
        }
        const paddingTop = parseFloat(style.paddingTop) || 0;
        const paddingBottom = parseFloat(style.paddingBottom) || 0;
        const borderTop = parseFloat(style.borderTopWidth) || 0;
        const borderBottom = parseFloat(style.borderBottomWidth) || 0;
        const minHeight = this.minRows() * lineHeight + paddingTop + paddingBottom + borderTop + borderBottom;
        const maxHeight = this.maxRows() * lineHeight + paddingTop + paddingBottom + borderTop + borderBottom;
        this._textarea.style.height = 'auto';
        const currentScrollHeight = this._textarea.scrollHeight;
        let height = Math.max(minHeight, currentScrollHeight);
        height = Math.min(maxHeight, height);
        this._textarea.style.height = `${height}px`;
        if (currentScrollHeight > maxHeight) {
            this._textarea.style.overflowY = 'auto';
        }
        else {
            this._textarea.style.overflowY = 'hidden';
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TextareaAutoSize, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: TextareaAutoSize, isStandalone: true, selector: "textarea[ngsTextareaAutoSize]", inputs: { minRows: { classPropertyName: "minRows", publicName: "minRows", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "rows": "1" }, listeners: { "input": "resize()" }, styleAttribute: "display: block; overflow: hidden; resize: none; box-sizing: border-box;" }, exportAs: ["ngsTextareaAutoSize"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TextareaAutoSize, decorators: [{
            type: Directive,
            args: [{
                    selector: 'textarea[ngsTextareaAutoSize]',
                    exportAs: 'ngsTextareaAutoSize',
                    host: {
                        'rows': '1',
                        '(input)': 'resize()',
                        'style': 'display: block; overflow: hidden; resize: none; box-sizing: border-box;',
                    },
                }]
        }], propDecorators: { minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }] } });

class InitialsPipe {
    transform(value) {
        if (!value) {
            return '';
        }
        const initials = [...value.toString().matchAll(/(\p{L}{1})\p{L}+/gu)];
        const result = ((initials.shift()?.[1] || '') + (initials.pop()?.[1] || '')).toUpperCase();
        return result ? result : value[0];
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InitialsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: InitialsPipe, isStandalone: true, name: "initials" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InitialsPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'initials',
                    standalone: true
                }]
        }] });

class FormatFileSizePipe {
    transform(bytes, decimalPoint = 2) {
        if (bytes == 0) {
            return '0 Bytes';
        }
        const k = 1000;
        const dm = decimalPoint || 2;
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
        const index = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, index)).toFixed(dm)) + ' ' + sizes[index];
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormatFileSizePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: FormatFileSizePipe, isStandalone: true, name: "formatFileSize" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormatFileSizePipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'formatFileSize',
                    standalone: true
                }]
        }] });

class OrderByPipe {
    transform(value, property, direction = 'asc') {
        if (!Array.isArray(value)) {
            throw new Error('Order By value should be an array');
        }
        const sorted = value.sort((a, b) => {
            if (a[property] < b[property]) {
                return -1;
            }
            else if (a[property] > b[property]) {
                return 1;
            }
            else {
                return 0;
            }
        });
        if (direction === 'asc') {
            return sorted;
        }
        else if (direction === 'desc') {
            return sorted.reverse();
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: OrderByPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: OrderByPipe, isStandalone: true, name: "orderBy" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: OrderByPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'orderBy',
                    standalone: true
                }]
        }] });

class SafeHtmlPipe {
    _domSanitizer = inject(DomSanitizer);
    transform(value) {
        return this._domSanitizer.bypassSecurityTrustHtml(value);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SafeHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SafeHtmlPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'safeHtml',
                    standalone: true
                }]
        }] });

class SafeResourceUrlPipe {
    _domSanitizer = inject(DomSanitizer);
    transform(value) {
        return this._domSanitizer.bypassSecurityTrustResourceUrl(value);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SafeResourceUrlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: SafeResourceUrlPipe, isStandalone: true, name: "safeResourceUrl" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SafeResourceUrlPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'safeResourceUrl',
                    standalone: true
                }]
        }] });

class FilterByPropertyPipe {
    getNestedValue(obj, path) {
        return path.split('.').reduce((acc, part) => acc && acc[part], obj);
    }
    transform(items, propPath, value, strict = false) {
        if (!items) {
            return [];
        }
        if (value === undefined || typeof value === 'undefined') {
            return items;
        }
        if (!strict && (value === null || value === '')) {
            return items;
        }
        return items.filter(item => {
            const propertyValue = this.getNestedValue(item, propPath);
            return propertyValue === value;
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilterByPropertyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: FilterByPropertyPipe, isStandalone: true, name: "filterByProperty" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilterByPropertyPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'filterByProperty',
                }]
        }] });

class SearchByPropertyPipe {
    getNestedValue(obj, path) {
        return path.split('.').reduce((acc, part) => acc && acc[part], obj);
    }
    transform(items, propPath, searchValue) {
        if (!items) {
            return [];
        }
        if (!searchValue) {
            return items;
        }
        const lowercasedSearchValue = searchValue.toLowerCase();
        return items.filter(item => {
            const propertyValue = this.getNestedValue(item, propPath);
            if (typeof propertyValue === 'string') {
                return propertyValue.toLowerCase().includes(lowercasedSearchValue);
            }
            return false;
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SearchByPropertyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: SearchByPropertyPipe, isStandalone: true, name: "searchByProperty" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SearchByPropertyPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'searchByProperty',
                }]
        }] });

const ENVIRONMENT = new InjectionToken('ENVIRONMENT');
class EnvironmentService {
    _environment = inject(ENVIRONMENT, {
        optional: true
    }) || {};
    getValue(key, defaultValue) {
        return this._environment[key] || defaultValue;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EnvironmentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EnvironmentService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EnvironmentService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class AnalyticsService {
    _platformId = inject(PLATFORM_ID);
    _router = inject(Router);
    _document = inject(DOCUMENT);
    _environmentService = inject(EnvironmentService);
    trackPageViews() {
        const googleAnalyticsId = this._environmentService.getValue('googleAnalyticsId');
        if (isDevMode() || !googleAnalyticsId) {
            return;
        }
        try {
            const url = 'https://www.googletagmanager.com/gtag/js?id=';
            const gTagScript = this._document.createElement('script');
            gTagScript.async = true;
            gTagScript.src = `${url}${googleAnalyticsId}`;
            this._document.head.appendChild(gTagScript);
            const dataLayerScript = this._document.createElement('script');
            dataLayerScript.innerHTML = `
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', '${googleAnalyticsId}');`;
            this._document.head.appendChild(dataLayerScript);
            if (isPlatformServer(this._platformId)) {
                return;
            }
            this._router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => {
                gtag('event', 'page_view', {
                    page_title: this._document.head.title,
                    page_path: event.urlAfterRedirects,
                    page_location: this._document.location.href
                });
            });
        }
        catch (e) {
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AnalyticsService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AnalyticsService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class InactivityTrackerService {
    _destroyRef = inject(DestroyRef);
    _document = inject(DOCUMENT);
    _interval = 10000 * 60; // 5 minutes
    _timeout;
    _interactionEvents$ = merge(fromEvent(this._document, 'click'), fromEvent(this._document, 'keydown'), fromEvent(this._document, 'mousemove'), fromEvent(this._document, 'scroll')).pipe(debounceTime(250));
    _noInteraction = new EventEmitter();
    setupInactivityTimer(interval) {
        if (interval) {
            this._interval = interval;
        }
        this._setupTimer();
        this._interactionEvents$.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
            this._setupTimer();
        });
        return this._noInteraction.pipe(takeUntilDestroyed(this._destroyRef));
    }
    reset() {
        this._setupTimer();
    }
    _setupTimer() {
        clearTimeout(this._timeout);
        this._timeout = setTimeout(() => {
            this._noInteraction.emit();
        }, this._interval);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InactivityTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InactivityTrackerService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: InactivityTrackerService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

const initialGlobalState = {
    screenLoading: true,
    sidebarHidden: false,
    pageTitle: ''
};
const GlobalStore = signalStore({ providedIn: 'root' }, withState(initialGlobalState), withMethods((store) => ({
    setScreenLoading(isLoading) {
        patchState(store, {
            screenLoading: isLoading
        });
    },
    setPageTitle(pageTitle) {
        patchState(store, {
            pageTitle
        });
    }
})));

class PageTitleStrategyService extends TitleStrategy {
    _title = inject(Title);
    _globalStore = inject(GlobalStore);
    updateTitle(routerState) {
        const title = this.buildTitle(routerState);
        if (title !== undefined) {
            this._title.setTitle(`${title} / ${this._globalStore.pageTitle()}`);
        }
        else {
            this._title.setTitle(this._globalStore.pageTitle());
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageTitleStrategyService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageTitleStrategyService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageTitleStrategyService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class ScreenLoaderService {
    _globalStore = inject(GlobalStore);
    show() {
        this._globalStore.setScreenLoading(true);
    }
    hide() {
        this._globalStore.setScreenLoading(false);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenLoaderService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenLoaderService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class SeoService {
    _router = inject(Router);
    _destroyRef = inject(DestroyRef);
    _document = inject(DOCUMENT);
    _platformId = inject(PLATFORM_ID);
    _linkCanonical;
    _title = inject(Title);
    _meta = inject(Meta);
    _globalStore = inject(GlobalStore);
    get meta() {
        return this._meta;
    }
    updateDescription(content) {
        if (!content) {
            return;
        }
        this.meta.updateTag({
            name: 'description',
            content
        });
        this.meta.updateTag({
            name: 'og:description',
            content
        });
    }
    updateOgUrl(content) {
        if (!content) {
            return;
        }
        this.meta.updateTag({
            name: 'og:url',
            content
        });
    }
    updateOgImage(content) {
        if (!content) {
            return;
        }
        this.meta.updateTag({
            name: 'og:image',
            content
        });
    }
    trackCanonicalChanges(siteUrl) {
        this._createCanonicalTag(siteUrl);
        if (isPlatformServer(this._platformId)) {
            return;
        }
        this._router.events
            .pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this._destroyRef))
            .subscribe(() => {
            const href = this.getCanonicalUrl(siteUrl);
            this._linkCanonical?.setAttribute('href', href);
            this.updateOgUrl(href);
        });
    }
    updateTitle(title) {
        this._title.setTitle(`${title} | ${this._globalStore.pageTitle()}`);
        this.meta.updateTag({
            name: 'og:title',
            content: title
        });
    }
    getCanonicalUrl(siteUrl) {
        if (this._document.URL.startsWith('http')) {
            return this._document.URL;
        }
        return siteUrl + this._document.URL;
    }
    _createCanonicalTag(siteUrl) {
        this._linkCanonical = this._document.querySelector('link[rel="canonical"]');
        if (!this._linkCanonical) {
            this._linkCanonical = this._document.createElement('link');
            this._linkCanonical.setAttribute('rel', 'canonical');
            this._document.head.appendChild(this._linkCanonical);
        }
        this._linkCanonical.setAttribute('href', this.getCanonicalUrl(siteUrl));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SeoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SeoService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SeoService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

const NGS_THEME_OPTIONS = new InjectionToken('NGS_THEME_OPTIONS', {
    factory: () => ({
        theme: 'default',
        colorScheme: 'auto',
        density: 'comfortable',
        radius: 'medium',
        persist: true,
        storageKey: 'ngs-admin',
    }),
});
function provideNgsTheme(options = {}) {
    return {
        provide: NGS_THEME_OPTIONS,
        useValue: {
            theme: 'default',
            colorScheme: 'auto',
            density: 'comfortable',
            radius: 'medium',
            persist: true,
            storageKey: 'ngs-admin',
            ...options,
        },
    };
}

class ThemeManagerService {
    _document = inject(DOCUMENT);
    _window = this._document.defaultView;
    _options = inject(NGS_THEME_OPTIONS);
    _theme = signal('default', ...(ngDevMode ? [{ debugName: "_theme" }] : /* istanbul ignore next */ []));
    _colorScheme = signal('light', ...(ngDevMode ? [{ debugName: "_colorScheme" }] : /* istanbul ignore next */ []));
    _density = signal('comfortable', ...(ngDevMode ? [{ debugName: "_density" }] : /* istanbul ignore next */ []));
    _radius = signal('medium', ...(ngDevMode ? [{ debugName: "_radius" }] : /* istanbul ignore next */ []));
    _primaryColor = signal(null, ...(ngDevMode ? [{ debugName: "_primaryColor" }] : /* istanbul ignore next */ []));
    theme = this._theme.asReadonly();
    colorScheme = this._colorScheme.asReadonly();
    density = this._density.asReadonly();
    radius = this._radius.asReadonly();
    primaryColor = this._primaryColor.asReadonly();
    constructor() {
        this.applyTheme(this._getInitialState());
        effect(() => {
            this._syncDocumentAttributes();
        });
        afterNextRender(() => {
            if (this._window !== null && this._window.matchMedia) {
                this._window
                    .matchMedia('(prefers-color-scheme: dark)')
                    .addEventListener('change', () => {
                    const storedColorScheme = this._getStoredColorScheme();
                    if (storedColorScheme !== 'light' && storedColorScheme !== 'dark') {
                        this.setColorScheme(this.getPreferredColorScheme(), false);
                    }
                });
            }
        });
    }
    getColorScheme() {
        return this._colorScheme();
    }
    toggleColorScheme() {
        if (this._getStoredColorScheme() === 'dark') {
            this.changeColorScheme('light');
        }
        else {
            this.changeColorScheme('dark');
        }
    }
    changeColorScheme(colorScheme) {
        this.setColorScheme(colorScheme);
    }
    setTheme(theme, persist = true) {
        this._theme.set(theme);
        this._persist({ theme }, persist);
    }
    setDensity(density, persist = true) {
        this._density.set(density);
        this._persist({ density }, persist);
    }
    setRadius(radius, persist = true) {
        this._radius.set(radius);
        this._persist({ radius }, persist);
    }
    setPrimaryColor(primaryColor, persist = true) {
        this._primaryColor.set(primaryColor);
        this._persist({ primaryColor: primaryColor || undefined }, persist);
    }
    applyTheme(options, persist = false) {
        if (options.theme) {
            this._theme.set(options.theme);
        }
        if (options.density) {
            this._density.set(options.density);
        }
        if (options.radius) {
            this._radius.set(options.radius);
        }
        if (options.primaryColor !== undefined) {
            this._primaryColor.set(options.primaryColor || null);
        }
        this.setColorScheme(options.colorScheme || 'auto', persist);
        this._persist(options, persist);
    }
    _getStoredColorScheme() {
        return this._getStoredState().colorScheme;
    }
    ;
    _getStoredState() {
        if (!this._options.persist || typeof localStorage === 'undefined') {
            return {};
        }
        const rawValue = localStorage.getItem(this._storageKey);
        if (!rawValue) {
            return {};
        }
        try {
            return JSON.parse(rawValue);
        }
        catch {
            return {};
        }
    }
    ;
    _persist(state, persist = true) {
        if (!persist || !this._options.persist || typeof localStorage === 'undefined') {
            return;
        }
        const meta = this._getStoredState();
        localStorage.setItem(this._storageKey, JSON.stringify({ ...meta, ...state }));
    }
    ;
    _setStoredColorScheme(colorScheme) {
        this._persist({ colorScheme: colorScheme });
    }
    ;
    getPreferredColorScheme() {
        const storedTheme = this._getStoredColorScheme();
        if (storedTheme === 'light' || storedTheme === 'dark') {
            return storedTheme;
        }
        if (this._window !== null && this._window.matchMedia) {
            return this._window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
        }
        return 'light';
    }
    ;
    setColorScheme(colorScheme, persist = true) {
        if (colorScheme === 'auto') {
            this._colorScheme.set(this.getPreferredColorScheme());
        }
        else {
            this._colorScheme.set(colorScheme);
        }
        if (persist) {
            this._setStoredColorScheme(colorScheme);
        }
    }
    ;
    _getInitialState() {
        return {
            ...this._options,
            ...this._getStoredState(),
        };
    }
    _syncDocumentAttributes() {
        const root = this._document.documentElement;
        const primaryColor = this._primaryColor();
        if (!root) {
            return;
        }
        root.classList?.toggle('dark', this._colorScheme() === 'dark');
        root.setAttribute('data-ngs-theme', this._theme());
        root.setAttribute('data-ngs-color-scheme', this._colorScheme());
        root.setAttribute('data-ngs-density', this._density());
        root.setAttribute('data-ngs-radius', this._radius());
        if (primaryColor && root.style) {
            root.style.setProperty('--ngs-color-primary', primaryColor);
        }
        else if (root.style) {
            root.style.removeProperty('--ngs-color-primary');
        }
    }
    get _storageKey() {
        return this._options.storageKey || 'ngs-admin';
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ThemeManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ThemeManagerService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ThemeManagerService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [] });

class ResizeObserverService {
    ngZone;
    constructor(ngZone) {
        this.ngZone = ngZone;
    }
    observe(element) {
        return new Observable(observer => {
            const resizeObserver = new ResizeObserver(entries => {
                this.ngZone.run(() => {
                    observer.next(entries);
                });
            });
            resizeObserver.observe(element);
            return () => {
                resizeObserver.disconnect();
            };
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: i0.NgZone }] });

class MutationObserverService {
    ngZone;
    constructor(ngZone) {
        this.ngZone = ngZone;
    }
    observe(element, options) {
        return new Observable(observer => {
            const mutationObserver = new MutationObserver(mutations => {
                this.ngZone.run(() => {
                    observer.next(mutations);
                });
            });
            mutationObserver.observe(element, options);
            return () => {
                mutationObserver.disconnect();
            };
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: i0.NgZone }] });

/** Provider for managing the error state of a form control. */
class ErrorStateMatcher {
    /**
     * Whether a control should be in an error state.
     * @param control The form control to check.
     * @param form The parent form that contains the control.
     * @returns Whether the control should be in an error state.
     */
    isErrorState(control, form) {
        return !!(control && control.invalid && (control.touched || (form && form.submitted)));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ErrorStateMatcher, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ErrorStateMatcher, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });
/** Error state matcher that displays an error when a control is dirty and invalid. */
class ShowOnDirtyErrorStateMatcher {
    isErrorState(control, form) {
        return !!(control && control.invalid && (control.dirty || (form && form.submitted)));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ShowOnDirtyErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ShowOnDirtyErrorStateMatcher });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ShowOnDirtyErrorStateMatcher, decorators: [{
            type: Injectable
        }] });

function px(value) {
    return `${value}px`;
}
function arrayShallowEquals(a, b) {
    return a.length === b.length && a.every((item, index) => item === b[index]);
}

function zonefreeScheduler() {
    const zone = inject(NgZone);
    return {
        now: () => Date.now(),
        schedule: (work, delay, state) => {
            return zone.runOutsideAngular(() => {
                const id = setTimeout(() => work(state), delay);
                return new Subscription(() => clearTimeout(id));
            });
        },
    };
}

function typedFromEvent(target, event, options) {
    return fromEvent(target, event, options);
}

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

export { AUTOFOCUSABLE, AnalyticsService, AutoFocusDirective, DebounceTimeDirective, ENVIRONMENT, EnvironmentService, ErrorStateMatcher, FilterByPropertyPipe, FocusElementDirective, FormatFileSizePipe, GlobalStore, InactivityTrackerService, InitialsPipe, MutationObserverService, NGS_THEME_OPTIONS, OrderByPipe, PageTitleStrategyService, RIPPLE_GLOBAL_OPTIONS, ResizeObserverService, Ripple, RippleRef, RippleRenderer, RippleState, SafeHtmlPipe, SafeResourceUrlPipe, ScreenLoaderService, SearchByPropertyPipe, SeoService, ShowOnDirtyErrorStateMatcher, SoundEffectDirective, TextareaAutoSize, ThemeManagerService, arrayShallowEquals, defaultRippleAnimationConfig, getActualTarget, injectElement, isElement, provideNgsTheme, px, typedFromEvent, zonefreeScheduler };
//# sourceMappingURL=ngstarter-ui-components-core.mjs.map
