UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

31,432 lines 1.82 MB
import { Observable, Subject, BehaviorSubject, ReplaySubject, merge, of, combineLatest, timer, fromEvent, Subscription, from, isObservable, concat } from 'rxjs';
import * as i0 from '@angular/core';
import { Directive, Injectable, InjectionToken, inject, RendererFactory2, ElementRef, NgZone, EventEmitter, Output, Input, HostBinding, Component, NgModule, Renderer2, PLATFORM_ID, HostListener, ContentChildren, ChangeDetectorRef, QueryList, ChangeDetectionStrategy, ContentChild, TemplateRef, ViewChild, ViewContainerRef, forwardRef, Pipe, ViewEncapsulation, LOCALE_ID, DestroyRef, ViewChildren, SecurityContext, Attribute, ComponentFactoryResolver, Injector, ApplicationRef, IterableDiffers } from '@angular/core';
import { takeUntil, filter, debounceTime, map, switchMap, take, pairwise, distinctUntilChanged, first, tap, delay, combineLatest as combineLatest$1, auditTime, mergeMap, skip, withLatestFrom, startWith } from 'rxjs/operators';
import { coerceBooleanProperty, coerceNumberProperty, coerceArray, coerceCssPixelValue } from '@angular/cdk/coercion';
import * as i1$1 from '@angular/cdk/a11y';
import { FocusMonitor, FocusKeyManager, A11yModule, LiveAnnouncer } from '@angular/cdk/a11y';
import * as i1 from '@angular/common';
import { isPlatformBrowser, CommonModule, WeekDay, formatDate, LocationStrategy, DOCUMENT, isPlatformServer } from '@angular/common';
import { Platform, PlatformModule } from '@angular/cdk/platform';
import * as i9 from 'angular-split';
import { SplitComponent, SplitAreaDirective, AngularSplitModule } from 'angular-split';
import { END, HOME, DOWN_ARROW, RIGHT_ARROW, UP_ARROW, LEFT_ARROW, TAB, ENTER, SPACE, ESCAPE, DELETE, BACKSPACE, PAGE_DOWN, PAGE_UP } from '@angular/cdk/keycodes';
import * as i2 from '@angular/router';
import { RouterModule, Router, NavigationEnd, ActivatedRoute, NavigationCancel, NavigationError } from '@angular/router';
import * as i2$2 from '@angular/forms';
import { NG_VALUE_ACCESSOR, FormsModule, FormGroupDirective, NG_VALIDATORS } from '@angular/forms';
import { trigger, transition, style, animate, query, stagger, state } from '@angular/animations';
import { Overlay, OverlayModule, ScrollDispatcher } from '@angular/cdk/overlay';
import { TemplatePortal, ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal';
import * as i2$1 from '@angular/cdk/observers';
import { ObserversModule as ObserversModule$1 } from '@angular/cdk/observers';
import { ScrollDispatcher as ScrollDispatcher$1, ViewportRuler } from '@angular/cdk/scrolling';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i6 from '@angular/cdk/drag-drop';
import { moveItemInArray, CdkDropList, CdkDragHandle, CDK_DRAG_HANDLE, CdkDrag, CDK_DRAG_PARENT, transferArrayItem, CDK_DROP_LIST, DragDropModule } from '@angular/cdk/drag-drop';
import { DomSanitizer } from '@angular/platform-browser';
import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { select, transition as transition$1, easeCubic, pointer, interpolate, arc, zoom, zoomTransform, hierarchy, tree, linkVertical, scaleLinear, partition, sum } from 'd3';
import { ArrayDataSource } from '@angular/cdk/collections';
import * as i5 from '@angular/cdk/tree';
import { FlatTreeControl, CdkTreeModule } from '@angular/cdk/tree';
import { AutofillMonitor } from '@angular/cdk/text-field';

var Color;
(function (Color) {
    Color["Primary"] = "primary";
    Color["Accent"] = "accent";
    Color["Secondary"] = "secondary";
    Color["Alternate1"] = "alternate1";
    Color["Alternate2"] = "alternate2";
    Color["Alternate3"] = "alternate3";
    Color["Vibrant1"] = "vibrant1";
    Color["Vibrant2"] = "vibrant2";
    Color["Grey1"] = "grey1";
    Color["Grey2"] = "grey2";
    Color["Grey3"] = "grey3";
    Color["Grey4"] = "grey4";
    Color["Grey5"] = "grey5";
    Color["Grey6"] = "grey6";
    Color["Grey7"] = "grey7";
    Color["Grey8"] = "grey8";
    Color["Chart1"] = "chart1";
    Color["Chart2"] = "chart2";
    Color["Chart3"] = "chart3";
    Color["Chart4"] = "chart4";
    Color["Chart5"] = "chart5";
    Color["Chart6"] = "chart6";
    Color["Ok"] = "ok";
    Color["Warning"] = "warning";
    Color["Critical"] = "critical";
    Color["Partition1"] = "partition1";
    Color["Partition9"] = "partition9";
    Color["Partition10"] = "partition10";
    Color["Partition11"] = "partition11";
    Color["Partition12"] = "partition12";
    Color["Partition13"] = "partition13";
    Color["Partition14"] = "partition14";
    Color["SocialChartNode"] = "social-chart-node";
    Color["SocialChartEdge"] = "social-chart-edge";
})(Color || (Color = {}));

/**
 * Determine the type of icon based upon the identifier.
 *
 * We support the following iconset:
 *
 * - `ux-icon` - UX Icon Set
 * - `component` - Component icon not tied to a specific set
 *
 * @param identifier - The name of the icon
 */
function getIconType(identifier) {
    if (identifier && identifier.trim().indexOf('ux-') === 0) {
        return IconType.UxIcon;
    }
    return IconType.Component;
}
var IconType;
(function (IconType) {
    IconType["UxIcon"] = "ux-icon";
    IconType["Component"] = "component";
})(IconType || (IconType = {}));

/**
 * This is a simple RxJS operator to allow us to avoid the
 * "expression has changed after it was checked issue"
 * by making the subscription asynchronous. We could just use a
 * delay operator but this uses a timeout which is significantly
 * slower than using requestAnimationFrame.
 */
const tick = () => (source) => new Observable(subscriber => {
    source.subscribe({
        next(value) {
            requestAnimationFrame(() => subscriber.next(value));
        },
        error(err) {
            subscriber.error(err);
        },
        complete() {
            subscriber.complete();
        },
    });
});

/**
 * A button will trigger a click event whenever the a mouse click occurs or the enter key is pressed.
 * These functions can be used to identify if a `click` event was caused by the keyboard or
 * by a mouse.
 *
 * The `event.detail` property will change based on the source of the event.
 * A mouse click will have varying values based on the browser, however
 * the enter key will always have a value of `0` so we can check against that
 */
function isKeyboardTrigger(event) {
    return event.detail === 0;
}
function isMouseTrigger(event) {
    return !isKeyboardTrigger(event);
}

class AccordionPanelHeadingDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionPanelHeadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: AccordionPanelHeadingDirective, isStandalone: false, selector: "ux-accordion-panel-header", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionPanelHeadingDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-accordion-panel-header',
                    standalone: false,
                }]
        }] });

class AccordionService {
    constructor() {
        this.collapseOthers = false;
        this.collapse = new Subject();
    }
    collapseAll() {
        this.collapse.next();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionService, decorators: [{
            type: Injectable
        }] });

const ACCESSIBILITY_OPTIONS_TOKEN = new InjectionToken('ACCESSIBILITY_OPTIONS');

class AccessibilityOptionsService {
    constructor() {
        /** Get the user specified options - but handle cases where they may not be specified */
        this._options = inject(ACCESSIBILITY_OPTIONS_TOKEN, { optional: true });
        /** Determine the default options */
        this._defaultOptions = {
            mouseFocusIndicator: false,
            touchFocusIndicator: false,
            keyboardFocusIndicator: true,
            programmaticFocusIndicator: false,
        };
    }
    /** Get the complete options populating unspecified options with the default values */
    get options() {
        return { ...this._defaultOptions, ...this._options };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityOptionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityOptionsService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityOptionsService, decorators: [{
            type: Injectable
        }] });

class LocalFocusIndicatorOptions {
}

class FocusIndicator {
    /** Apply a class when the item is focused */
    set isFocused(isFocused) {
        // update the class on the element
        isFocused
            ? this._renderer.addClass(this._element, 'ux-focus-indicator-active')
            : this._renderer.removeClass(this._element, 'ux-focus-indicator-active');
        // emit the focus state
        this.isFocused$.next(isFocused);
    }
    /** Provide a convenience getter to allow access to focus state without a subscription */
    get isFocused() {
        return this.isFocused$.value;
    }
    constructor(_element, _focusMonitor, _renderer, _options, _focusIndicatorOrigin) {
        this._element = _element;
        this._focusMonitor = _focusMonitor;
        this._renderer = _renderer;
        this._options = _options;
        this._focusIndicatorOrigin = _focusIndicatorOrigin;
        /** An observable to monitor the focus state */
        this.isFocused$ = new BehaviorSubject(false);
        /** An observable to monitor the focus origin */
        this.origin$ = new Subject();
        /** Remove all subscriptions on destroy */
        this._onDestroy = new Subject();
        // check if the element is already being monitored
        if (!_element.classList.contains('ux-focus-indicator')) {
            this.initialise();
        }
    }
    /** Setup the focus monitoring */
    initialise() {
        // add a class to the element to specify we are controlling the focus
        this._renderer.addClass(this._element, 'ux-focus-indicator');
        // watch for any changes to the focus state
        this._focusMonitor
            .monitor(this._element, this._options.checkChildren)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onFocusChange.bind(this));
    }
    /** Focus the element with a specific origin */
    focus(origin, options) {
        this._focusIndicatorOrigin.setOrigin(origin);
        this._element.focus(options);
    }
    /** Tear down the subscriptions */
    destroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this.isFocused$.complete();
        this._focusMonitor.stopMonitoring(this._element);
    }
    /** Allow the options to be updates */
    setOptions(options) {
        this._options = { ...this._options, ...options };
    }
    /** Monitor changes to an elements focus state */
    onFocusChange(origin) {
        // if the origin is null then we blurred
        if (origin === null) {
            this.isFocused = false;
            this.origin$.next(null);
            return;
        }
        // get the origin if there is one
        const syntheticOrigin = this._focusIndicatorOrigin.getOrigin();
        // emit the origin
        this.origin$.next(syntheticOrigin || origin);
        switch (syntheticOrigin || origin) {
            case 'mouse':
                this.isFocused = this._options.mouseFocusIndicator;
                break;
            case 'touch':
                this.isFocused = this._options.touchFocusIndicator;
                break;
            case 'keyboard':
                this.isFocused = this._options.keyboardFocusIndicator;
                break;
            case 'program':
                this.isFocused = this._options.programmaticFocusIndicator;
                break;
            default:
                this.isFocused = false;
        }
    }
}

class FocusIndicatorOriginService {
    /** Store the event source origin */
    setOrigin(origin) {
        this._origin = origin;
    }
    /** Get the most recent event origin */
    getOrigin() {
        // get the most recent origin if there is one
        const origin = this._origin;
        // we should clear the origin so this value doesn't cause issues with future focus events
        this._origin = null;
        return origin;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOriginService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOriginService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOriginService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class FocusIndicatorService {
    constructor() {
        this._localOptions = inject(ACCESSIBILITY_OPTIONS_TOKEN, { optional: true });
        this.rendererFactory = inject(RendererFactory2);
        this._focusMonitor = inject(FocusMonitor);
        this._globalOptions = inject(AccessibilityOptionsService);
        this._focusIndicatorOrigin = inject(FocusIndicatorOriginService);
        // programmatically create a renderer as it can't be injected into a service
        this._renderer = this.rendererFactory.createRenderer(null, null);
    }
    /** This is essentially just a factory method to prevent the user having to pass in focus monitor, renderer and global options each time */
    monitor(element, options = {
        ...this._globalOptions.options,
        ...this._localOptions,
        checkChildren: false,
    }) {
        return new FocusIndicator(element, this._focusMonitor, this._renderer, { ...this._globalOptions.options, ...this._localOptions, ...options }, this._focusIndicatorOrigin);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class FocusIndicatorDirective {
    /** Specify whether or not we should mark this element as having focus if a child is focused */
    set checkChildren(checkChildren) {
        // allow a string to be used so we can skip checking a binding for performance benefits
        checkChildren = coerceBooleanProperty(checkChildren);
        if (checkChildren !== null && checkChildren !== undefined) {
            this._checkChildren = checkChildren;
            this.setOptions();
        }
    }
    /** Indicate whether or not mouse events should cause the focus indicator to appear - will override any global setting */
    set mouseFocusIndicator(mouseFocusIndicator) {
        // allow a string to be used so we can skip checking a binding for performance benefits
        mouseFocusIndicator = coerceBooleanProperty(mouseFocusIndicator);
        if (mouseFocusIndicator !== null && mouseFocusIndicator !== undefined) {
            this._options.set('mouseFocusIndicator', mouseFocusIndicator);
            this.setOptions();
        }
    }
    /** Indicate whether or not touch events should cause the focus indicator to appear - will override any global setting */
    set touchFocusIndicator(touchFocusIndicator) {
        // allow a string to be used so we can skip checking a binding for performance benefits
        touchFocusIndicator = coerceBooleanProperty(touchFocusIndicator);
        if (touchFocusIndicator !== null && touchFocusIndicator !== undefined) {
            this._options.set('touchFocusIndicator', touchFocusIndicator);
            this.setOptions();
        }
    }
    /** Indicate whether or not keyboard events should cause the focus indicator to appear - will override any global setting */
    set keyboardFocusIndicator(keyboardFocusIndicator) {
        // allow a string to be used so we can skip checking a binding for performance benefits
        keyboardFocusIndicator = coerceBooleanProperty(keyboardFocusIndicator);
        if (keyboardFocusIndicator !== null && keyboardFocusIndicator !== undefined) {
            this._options.set('keyboardFocusIndicator', keyboardFocusIndicator);
            this.setOptions();
        }
    }
    /** Indicate whether or not programmatic events should cause the focus indicator to appear - will override any global setting */
    set programmaticFocusIndicator(programmaticFocusIndicator) {
        // allow a string to be used so we can skip checking a binding for performance benefits
        programmaticFocusIndicator = coerceBooleanProperty(programmaticFocusIndicator);
        if (programmaticFocusIndicator !== null && programmaticFocusIndicator !== undefined) {
            this._options.set('programmaticFocusIndicator', programmaticFocusIndicator);
            this.setOptions();
        }
    }
    constructor() {
        this.optionsService = inject(AccessibilityOptionsService);
        this._elementRef = inject(ElementRef);
        this._focusIndicatorService = inject(FocusIndicatorService);
        this._ngZone = inject(NgZone);
        this.localOptions = inject(LocalFocusIndicatorOptions, { optional: true });
        /** Emit the latest focus state */
        this.indicator = new EventEmitter();
        /** Store a private reference for the checkChildren option */
        this._checkChildren = false;
        /** Store all configuation options*/
        this._options = new Map();
        /** Unsubscribe on component destroy */
        this._onDestroy = new Subject();
        // set the inital option values based on global options
        for (const option in this.optionsService.options || {}) {
            this._options.set(option, this.optionsService.options[option]);
        }
        // set the inital option values based on local options (if there are any)
        for (const option in this.localOptions || {}) {
            this._options.set(option, this.localOptions[option]);
        }
    }
    /** Setup the focus monitoring */
    ngOnInit() {
        // start the focus monitoring
        this._focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement, {
            checkChildren: this._checkChildren,
            mouseFocusIndicator: this._options.get('mouseFocusIndicator'),
            touchFocusIndicator: this._options.get('touchFocusIndicator'),
            keyboardFocusIndicator: this._options.get('keyboardFocusIndicator'),
            programmaticFocusIndicator: this._options.get('programmaticFocusIndicator'),
        });
        // subscribe to the focus state to emit an event on change
        this._focusIndicator.isFocused$.pipe(takeUntil(this._onDestroy)).subscribe(isFocused => {
            // emit the latest value
            this._ngZone.run(() => this.indicator.emit(isFocused));
        });
    }
    /** Tear down the directive */
    ngOnDestroy() {
        if (this._focusIndicator) {
            this._focusIndicator.destroy();
        }
        // unsubscribe from all observables
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Focus this element with a specific origin */
    focus(origin, options) {
        this._focusIndicator.focus(origin, options);
    }
    /** Update the focus indicator with the latest options */
    setOptions() {
        if (this._focusIndicator) {
            this._focusIndicator.setOptions({
                checkChildren: this._checkChildren,
                mouseFocusIndicator: this._options.get('mouseFocusIndicator'),
                touchFocusIndicator: this._options.get('touchFocusIndicator'),
                keyboardFocusIndicator: this._options.get('keyboardFocusIndicator'),
                programmaticFocusIndicator: this._options.get('programmaticFocusIndicator'),
            });
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FocusIndicatorDirective, isStandalone: false, selector: "[uxFocusIndicator]", inputs: { checkChildren: "checkChildren", mouseFocusIndicator: "mouseFocusIndicator", touchFocusIndicator: "touchFocusIndicator", keyboardFocusIndicator: "keyboardFocusIndicator", programmaticFocusIndicator: "programmaticFocusIndicator" }, outputs: { indicator: "indicator" }, exportAs: ["ux-focus-indicator"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFocusIndicator]',
                    exportAs: 'ux-focus-indicator',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { checkChildren: [{
                type: Input
            }], mouseFocusIndicator: [{
                type: Input
            }], touchFocusIndicator: [{
                type: Input
            }], keyboardFocusIndicator: [{
                type: Input
            }], programmaticFocusIndicator: [{
                type: Input
            }], indicator: [{
                type: Output
            }] } });

let uniqueId$e = 1;
class AccordionPanelComponent {
    constructor() {
        this.accordion = inject(AccordionService);
        this.panelId = `ux-accordion-panel-${uniqueId$e++}`;
        this.headingId = `${this.panelId}-heading`;
        this.disabled = false;
        this.expanded = false;
        this.expandedChange = new EventEmitter();
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        this.accordion.collapse.pipe(takeUntil(this._onDestroy)).subscribe(() => this.collapse());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    toggle() {
        if (this.expanded) {
            this.collapse();
            return;
        }
        // check if we should collapse others
        if (this.accordion.collapseOthers) {
            this.accordion.collapseAll();
        }
        // store the new expanded state
        this.expand();
    }
    expand() {
        if (this.disabled === false && this.expanded === false) {
            this.expanded = true;
            this.expandedChange.next(true);
        }
    }
    collapse() {
        if (this.disabled === false && this.expanded === true) {
            this.expanded = false;
            this.expandedChange.next(false);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AccordionPanelComponent, isStandalone: false, selector: "ux-accordion-panel", inputs: { panelId: "panelId", headingId: "headingId", disabled: "disabled", heading: "heading", expanded: "expanded" }, outputs: { expandedChange: "expandedChange" }, host: { properties: { "class.panel-open": "this.expanded" }, classAttribute: "panel panel-default" }, ngImport: i0, template: "<div\n  class=\"panel-heading\"\n  role=\"button\"\n  uxFocusIndicator\n  [tabindex]=\"disabled ? -1 : 0\"\n  [id]=\"headingId\"\n  [attr.aria-expanded]=\"expanded\"\n  [attr.aria-controls]=\"panelId\"\n  [attr.aria-disabled]=\"disabled\"\n  (click)=\"toggle()\"\n  (keydown.enter)=\"toggle()\"\n  (keydown.space)=\"toggle(); $event.preventDefault()\"\n  (keydown.spacebar)=\"toggle(); $event.preventDefault()\"\n>\n  <div class=\"panel-title\">\n    {{ heading }}\n    <ng-content select=\"ux-accordion-panel-header\"></ng-content>\n  </div>\n</div>\n\n<div\n  [id]=\"panelId\"\n  class=\"panel-collapse collapse\"\n  [class.in]=\"expanded\"\n  role=\"tabpanel\"\n  [attr.aria-labelledby]=\"headingId\"\n>\n  <div class=\"panel-body\">\n    <ng-content></ng-content>\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionPanelComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-accordion-panel', host: {
                        class: 'panel panel-default',
                    }, standalone: false, template: "<div\n  class=\"panel-heading\"\n  role=\"button\"\n  uxFocusIndicator\n  [tabindex]=\"disabled ? -1 : 0\"\n  [id]=\"headingId\"\n  [attr.aria-expanded]=\"expanded\"\n  [attr.aria-controls]=\"panelId\"\n  [attr.aria-disabled]=\"disabled\"\n  (click)=\"toggle()\"\n  (keydown.enter)=\"toggle()\"\n  (keydown.space)=\"toggle(); $event.preventDefault()\"\n  (keydown.spacebar)=\"toggle(); $event.preventDefault()\"\n>\n  <div class=\"panel-title\">\n    {{ heading }}\n    <ng-content select=\"ux-accordion-panel-header\"></ng-content>\n  </div>\n</div>\n\n<div\n  [id]=\"panelId\"\n  class=\"panel-collapse collapse\"\n  [class.in]=\"expanded\"\n  role=\"tabpanel\"\n  [attr.aria-labelledby]=\"headingId\"\n>\n  <div class=\"panel-body\">\n    <ng-content></ng-content>\n  </div>\n</div>\n" }]
        }], propDecorators: { panelId: [{
                type: Input
            }], headingId: [{
                type: Input
            }], disabled: [{
                type: Input
            }], heading: [{
                type: Input
            }], expanded: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.panel-open']
            }], expandedChange: [{
                type: Output
            }] } });

class AccordionComponent {
    constructor() {
        this._accordion = inject(AccordionService);
    }
    set collapseOthers(collapseOthers) {
        this._accordion.collapseOthers = collapseOthers;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AccordionComponent, isStandalone: false, selector: "ux-accordion", inputs: { collapseOthers: "collapseOthers" }, host: { attributes: { "aria-multiselectable": "true" }, classAttribute: "panel-group" }, providers: [AccordionService], ngImport: i0, template: "<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-accordion', providers: [AccordionService], host: {
                        class: 'panel-group',
                        'aria-multiselectable': 'true',
                    }, standalone: false, template: "<ng-content></ng-content>\n" }]
        }], propDecorators: { collapseOthers: [{
                type: Input
            }] } });

const KEPPEL_COLOR_SET = {
    primary: '#00a7a2',
    accent: '#7b63a3',
    secondary: '#fff',
    alternate1: '#3baa43',
    alternate2: '#025662',
    alternate3: '#b08f5c',
    vibrant1: '#00cceb',
    vibrant2: '#ff9048',
    grey1: '#2a2a2a',
    grey2: '#333',
    grey3: '#666',
    grey4: '#999',
    grey5: '#ccc',
    grey6: '#eee',
    grey7: '#f5f5f5',
    grey8: '#fafafa',
    chart1: '#00a7a2',
    chart2: '#7b63a3',
    chart3: '#3baa43',
    chart4: '#025662',
    chart5: '#b08f5c',
    chart6: '#ccc',
    ok: '#3baa43',
    warning: '#ff9048',
    critical: '#ff454f',
    partition1: '#635387',
    partition9: '#4a4066',
    partition10: '#308935',
    partition11: '#023e42',
    partition12: '#91744d',
    partition13: '#999',
    partition14: '#294266',
    'social-chart-node': '#00cceb',
    'social-chart-edge': '#00cceb',
};

const MICRO_FOCUS_COLOR_SET = {
    'brand-blue': '#0073e7',
    cerulean: '#1668c1',
    aqua: '#29ceff',
    aquamarine: '#2fd6c3',
    fuchsia: '#c6179d',
    indigo: '#7425ad',
    'dark-blue': '#231ca5',
    white: '#ffffff',
    'slightly-gray': '#f5f7f8',
    'bright-gray': '#f1f2f3',
    gray: '#dcdedf',
    silver: '#bdbec0',
    'dim-gray': '#656668',
    'dark-gray': '#323435',
    black: '#000000',
    'crimson-negative': '#e5004c',
    apricot: '#f48b34',
    yellow: '#fcdb1f',
    'green-positive': '#1aac60',
    ultramarine: '#3939c6',
    skyblue: '#00abf3',
    'pale-aqua': '#43e4ff',
    'pale-green': '#1ffbba',
    lime: '#75da4d',
    orange: '#ffce00',
    magenta: '#eb23c2',
    'pale-purple': '#ba47e2',
    'dark-ultramarine': '#271782',
    steelblue: '#014272',
    'arctic-blue': '#0b8eac',
    emerald: '#00a989',
    olive: '#5bba36',
    goldenrod: '#ffb000',
    purple: '#9b1e83',
    'pale-eggplant': '#5216ac',
    red: '#ff454f',
    'pale-amber': '#ffb24d',
    'pale-lemon': '#fde159',
    'pale-emerald': '#33c180',
    plum: '#b21646',
    copper: '#e57828',
    amber: '#ffc002',
    'leaf-green': '#118c4f',
    'forest-green': '#00645a',
    primary: '#0073e7',
    accent: '#7425ad',
    secondary: '#ffffff',
    alternate1: '#29ceff',
    alternate2: '#2fd6c3',
    alternate3: '#c6179d',
    vibrant1: '#43e4ff',
    vibrant2: '#ffce00',
    grey1: '#000000',
    grey2: '#323435',
    grey3: '#656668',
    grey4: '#bdbec0',
    grey5: '#dcdedf',
    grey6: '#f1f2f3',
    grey7: '#f5f7f8',
    grey8: '#ffffff',
    chart1: '#3939c6',
    chart2: '#00abf3',
    chart3: '#75da4d',
    chart4: '#ffce00',
    chart5: '#eb23c2',
    chart6: '#ba47e2',
    info: '#00abf3',
    ok: '#1aac60',
    warning: '#fcdb1f',
    danger: '#f48b34',
    critical: '#e5004c',
    partition1: '#7425ad',
    partition9: '#5216ac',
    partition10: '#5bba36',
    partition11: '#014272',
    partition12: '#ffb000',
    partition13: '#bdbec0',
    partition14: '#271782',
    'social-chart-node': '#ff00ff',
    'social-chart-edge': '#ff00ff',
};

const colorSets = {
    keppel: {
        colorValueSet: KEPPEL_COLOR_SET,
    },
    microFocus: {
        colorValueSet: MICRO_FOCUS_COLOR_SET,
    },
};
/** Provide a default color set for an application */
const COLOR_SET_TOKEN = new InjectionToken('COLOR_SET_TOKEN');

class ThemeColor {
    constructor(_r, _g, _b, _a = '1') {
        this._r = _r;
        this._g = _g;
        this._b = _b;
        this._a = _a;
    }
    /**
     * Create a ThemeColor object from a CSS color string
     * @param value The CSS color string to derive a ThemeColor object from
     */
    static parse(value) {
        let r, g, b, a = '1';
        const rgbaPattern = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/;
        const shortHexPattern = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
        const longHexPattern = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
        const rgbaMatch = value.match(rgbaPattern);
        const shortHexMatch = value.match(shortHexPattern);
        const longHexMatch = value.match(longHexPattern);
        if (rgbaMatch) {
            r = rgbaMatch[1];
            g = rgbaMatch[2];
            b = rgbaMatch[3];
            a = rgbaMatch[4] ? rgbaMatch[4] : '1';
        }
        else if (longHexMatch) {
            r = parseInt(longHexMatch[1], 16).toString();
            g = parseInt(longHexMatch[2], 16).toString();
            b = parseInt(longHexMatch[3], 16).toString();
        }
        else if (shortHexMatch) {
            r = parseInt(shortHexMatch[1] + shortHexMatch[1], 16).toString();
            g = parseInt(shortHexMatch[2] + shortHexMatch[2], 16).toString();
            b = parseInt(shortHexMatch[3] + shortHexMatch[3], 16).toString();
        }
        else {
            throw new Error(`Cannot parse color - ${value} is not a valid color.`);
        }
        return new ThemeColor(r, g, b, a);
    }
    /**
     * Clone a theme color so it can be modified without affecting other places using the color
     * @param themeColor The original theme color to clone
     */
    static from(themeColor) {
        return new ThemeColor(themeColor.getRed(), themeColor.getGreen(), themeColor.getBlue(), themeColor.getAlpha());
    }
    /**
     * Determine if an object is an instance of a theme color.
     * Using a simple instanceof check will not always work in plunker
     * where the ThemeColor is from @ux-aspects/ux-aspects and the color
     * comes from @micro-focus/ux-aspects
     */
    static isInstanceOf(themeColor) {
        return (typeof themeColor === 'object' &&
            // eslint-disable-next-line no-prototype-builtins
            themeColor.hasOwnProperty('_r') &&
            // eslint-disable-next-line no-prototype-builtins
            themeColor.hasOwnProperty('_g') &&
            // eslint-disable-next-line no-prototype-builtins
            themeColor.hasOwnProperty('_b') &&
            // eslint-disable-next-line no-prototype-builtins
            themeColor.hasOwnProperty('_a'));
    }
    /**
     * Convert the theme color to a CSS hex color code
     */
    toHex() {
        let red = parseInt(this._r).toString(16);
        let green = parseInt(this._g).toString(16);
        let blue = parseInt(this._b).toString(16);
        if (red.length < 2) {
            red = '0' + red;
        }
        if (green.length < 2) {
            green = '0' + green;
        }
        if (blue.length < 2) {
            blue = '0' + blue;
        }
        return '#' + red + green + blue;
    }
    /**
     * Convert the theme color to a CSS rgb color code
     */
    toRgb() {
        return 'rgb(' + this._r + ', ' + this._g + ', ' + this._b + ')';
    }
    /**
     * Convert the theme color to a CSS rgbs color code
     */
    toRgba() {
        return 'rgba(' + this._r + ', ' + this._g + ', ' + this._b + ', ' + this._a + ')';
    }
    /**
     * Get the red value from the RGBA color value
     */
    getRed() {
        return this._r;
    }
    /**
     * Get the green value from the RGBA color value
     */
    getGreen() {
        return this._g;
    }
    /**
     * Get the blue value from the RGBA color value
     */
    getBlue() {
        return this._b;
    }
    /**
     * Get the alpha value from the RGBA color value
     */
    getAlpha() {
        return this._a;
    }
    /**
     * Set the red value from the RGBA color value
     */
    setRed(red) {
        this._r = red;
        return this;
    }
    /**
     * Set the green value from the RGBA color value
     */
    setGreen(green) {
        this._g = green;
        return this;
    }
    /**
     * Set the blue value from the RGBA color value
     */
    setBlue(blue) {
        this._b = blue;
        return this;
    }
    /**
     * Set the alpha value from the RGBA color value
     */
    setAlpha(alpha) {
        this._a = alpha.toString();
        return this;
    }
}

class ColorService {
    /** Allow the color set to be provided in a forRoot function otherwise set it to the Keppel theme by default */
    constructor() {
        this._colorSet = inject(COLOR_SET_TOKEN, { optional: true });
        // resolve the theme based on the colorset
        this._theme = this.getTheme(this._colorSet);
    }
    /**
     * Get a ThemeColor object from a color name
     * @param colorName The name of the color from the color palette
     */
    getColor(colorName) {
        // get the matching ThemeColor from the active theme
        const themeColor = this._theme[this.resolveColorName(colorName)];
        // if there is not a match then throw an error
        if (!themeColor) {
            throw new Error('Color not found: ' + colorName);
        }
        return new ThemeColor(themeColor.getRed(), themeColor.getGreen(), themeColor.getBlue(), themeColor.getAlpha());
    }
    /**
     * Get the active color set
     */
    getColorSet() {
        return this._colorSet;
    }
    /**
     * Define the current color set and produce a Theme from it
     */
    setColorSet(colorSet) {
        this._colorSet = colorSet;
        this._theme = this.getTheme(colorSet);
    }
    /**
     * Resolve a color value. This may be the name of a color from the color set
     * or it may simply be a hex or rgb(a) color value. This function will return
     * a CSS color value regardless of which one of these formats it is
     * @param value The color name, hex code or rgb(a) value to resolve
     * @returns If the color is the name of a color in the set, the `rgba` color will be returned, otherwise the original CSS value will be returned.
     */
    resolve(value) {
        if (!value) {
            return;
        }
        const colorName = this.resolveColorName(value);
        for (const color in this._theme) {
            if (colorName === color.toLowerCase()) {
                return this.getColor(colorName).toRgba();
            }
        }
        return value;
    }
    /**
     * Converts a color name to an appropriate ColorSet name. For example
     * a color may be written in lower-camel-case, however color sets are in
     * kebab-case. This will convert to the appropriate naming format
     * @param colorName The color name to resolve
     */
    resolveColorName(colorName = '') {
        return colorName.replace(/\s+/g, '-').toLowerCase();
    }
    /** Determine if the current colorset has a specific color */
    colorExists(name) {
        return !!Object.keys(this._theme).find(colorName => colorName === this.resolveColorName(name));
    }
    /** Create a theme from a colorset */
    getTheme(colorSet) {
        // create a new theme object
        const theme = {};
        // ensure we have a colorset
        if (!colorSet) {
            colorSet = colorSets.keppel;
        }
        // iterate over each hex code and convert it to a theme color
        for (const color in colorSet.colorValueSet) {
            theme[color] = ThemeColor.parse(colorSet.colorValueSet[color]);
        }
        return theme;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class ColorServiceModule {
    /**
     * The function allows the consuming applications to specify the applications
     * color set once in the app module, eg:
     * ```
     * ColorServiceModule.forRoot(colorSets.microFocus);
     * ```
     * @param colorSet The color set the application should use
     */
    static forRoot(colorSet) {
        return {
            ngModule: ColorServiceModule,
            providers: [
                { provide: COLOR_SET_TOKEN, useValue: colorSet ? colorSet : colorSets.keppel },
                ColorService,
            ],
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorServiceModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ColorServiceModule }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorServiceModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorServiceModule, decorators: [{
            type: NgModule,
            args: [{}]
        }] });

class ContrastService {
    /**
     * Calculate the contract ratio between two colors.
     * This uses the official WCAG Color Contrast Ratio
     * Algorithm: https://www.w3.org/TR/WCAG20-TECHS/G17.html
     */
    getContrastColor(backgroundColor, lightColor, darkColor) {
        // get a ThemeColor from the ColorPickerColor
        const themeColor = ThemeColor.parse(backgroundColor.toHex());
        const background = this.getLuminance(themeColor);
        const light = this.getLuminance(lightColor);
        const dark = this.getLuminance(darkColor);
        // determine the contrast for both black and white
        const whiteContrast = (light + 0.05) / (background + 0.05);
        const blackContrast = (background + 0.05) / (dark + 0.05);
        // return the color with the most contrast ratio
        return blackContrast > whiteContrast ? darkColor : lightColor;
    }
    getLuminance(color) {
        // normalize the colors
        let r = +color.getRed() / 255;
        let g = +color.getGreen() / 255;
        let b = +color.getBlue() / 255;
        // calculate the value required for each color component
        r = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
        g = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
        b = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
        // return the luminance
        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContrastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContrastService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ContrastService, decorators: [{
            type: Injectable
        }] });

class ColorContrastDirective {
    constructor() {
        this._colorService = inject(ColorService);
        this._contrastService = inject(ContrastService);
        /** Store the light color as a ThemeColor object */
        this._lightColor = ThemeColor.parse('#fff');
        /** Store the light color as a ThemeColor object */
        this._darkColor = ThemeColor.parse('#000');
    }
    /**
     * Define the background color for contrast comparison.
     * This can be a CSS color value or the name of a
     * color from the color palette.
     */
    set uxColorContrast(backgroundColor) {
        this._backgroundColor = ThemeColor.parse(this._colorService.resolve(backgroundColor));
    }
    /**
     * Define the light color for contrast comparison.
     * This can be a CSS color value or the name of a
     * color from the color palette.
     */
    set lightColor(lightColor) {
        this._lightColor = ThemeColor.parse(this._colorService.resolve(lightColor));
    }
    /**
     * Define the dark color for contrast comparison.
     * This can be a CSS color value or the name of a
     * color from the color palette.
     */
    set darkColor(darkColor) {
        this._darkColor = ThemeColor.parse(this._colorService.resolve(darkColor));
    }
    /** Determine the color to set based on the supplied parameters */
    get _color() {
        return this._backgroundColor
            ? this._contrastService
                .getContrastColor(this._backgroundColor, this._lightColor, this._darkColor)
                .toRgba()
            : null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorContrastDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ColorContrastDirective, isStandalone: false, selector: "[uxColorContrast]", inputs: { uxColorContrast: "uxColorContrast", lightColor: "lightColor", darkColor: "darkColor" }, host: { properties: { "style.color": "this._color" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorContrastDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxColorContrast]',
                    standalone: false,
                }]
        }], propDecorators: { uxColorContrast: [{
                type: Input
            }], lightColor: [{
                type: Input
            }], darkColor: [{
                type: Input
            }], _color: [{
                type: HostBinding,
                args: ['style.color']
            }] } });

/**
 * This directive can be used to target specific elements based on their CSS
 * class so we can control when the focus shows. This will help prevent us
 * polluting the FocusIndicatorDirective with an lot of selectors.
 *
 * If the button has a uxFocusIndicator, uxMenuTriggerFor or uxMenuNavigationToggle directive applied we should skip this
 */
class DefaultFocusIndicatorDirective extends FocusIndicatorDirective {
    constructor() {
        super();
        // Enable programmatic focus by default
        this.programmaticFocusIndicator = true;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DefaultFocusIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DefaultFocusIndicatorDirective, isStandalone: false, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])", usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DefaultFocusIndicatorDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '.btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])',
                    standalone: false,
                }]
        }], ctorParameters: () => [] });

class FocusIndicatorOptionsDirective {
    constructor() {
        this._options = inject(LocalFocusIndicatorOptions, { self: true });
    }
    /** If `true`, this element will receive a focus indicator when the element is clicked on. */
    set mouseFocusIndicator(mouseFocusIndicator) {
        this._options.mouseFocusIndicator = mouseFocusIndicator;
    }
    /** If `true`, this element will receive a focus indicator when the element is touched. */
    set touchFocusIndicator(touchFocusIndicator) {
        this._options.touchFocusIndicator = touchFocusIndicator;
    }
    /** If `true`, this element will receive a focus indicator when the element is focused using the keyboard. */
    set keyboardFocusIndicator(keyboardFocusIndicator) {
        this._options.keyboardFocusIndicator = keyboardFocusIndicator;
    }
    /** If `true`, this element will receive a focus indicator when the element is programmatically focused. */
    set programmaticFocusIndicator(programmaticFocusIndicator) {
        this._options.programmaticFocusIndicator = programmaticFocusIndicator;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOptionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FocusIndicatorOptionsDirective, isStandalone: false, selector: "[uxFocusIndicatorOptions]", inputs: { mouseFocusIndicator: "mouseFocusIndicator", touchFocusIndicator: "touchFocusIndicator", keyboardFocusIndicator: "keyboardFocusIndicator", programmaticFocusIndicator: "programmaticFocusIndicator" }, providers: [LocalFocusIndicatorOptions], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOptionsDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFocusIndicatorOptions]',
                    providers: [LocalFocusIndicatorOptions],
                    standalone: false,
                }]
        }], propDecorators: { mouseFocusIndicator: [{
                type: Input
            }], touchFocusIndicator: [{
                type: Input
            }], keyboardFocusIndicator: [{
                type: Input
            }], programmaticFocusIndicator: [{
                type: Input
            }] } });

/**
 * When working with component host elements
 * we cannot apply directives, eg. FocusIndicatorOriginDirective
 * however we may still want the functionality to be applied to
 * the host element. This class allows the host element to become
 * a focus indicator origin
 */
class FocusIndicatorOrigin {
    constructor(_focusIndicatorOrigin, elementRef, renderer) {
        this._focusIndicatorOrigin = _focusIndicatorOrigin;
        /** Store all event handlers */
        this._handlers = [];
        // add event handlers
        this._handlers = [
            renderer.listen(elementRef.nativeElement, 'click', () => this.onClick()),
            renderer.listen(elementRef.nativeElement, 'mousedown', () => this.onMouseDown()),
            renderer.listen(elementRef.nativeElement, 'keydown', () => this.onKeydown()),
        ];
    }
    /** Remove all event handlers */
    destroy() {
        this._handlers.forEach(handler => handler());
    }
    onMouseDown() {
        this._isMouseEvent = true;
    }
    onClick() {
        // if the click was triggered after a mousedown event then it is a keyboard event
        this._focusIndicatorOrigin.setOrigin(this._isMouseEvent ? 'mouse' : 'keyboard');
        // reset the mouse event flag
        this._isMouseEvent = false;
    }
    onKeydown() {
        this._isMouseEvent = false;
        this._focusIndicatorOrigin.setOrigin('keyboard');
    }
}

class FocusIndicatorOriginDirective {
    constructor() {
        this.focusOriginService = inject(FocusIndicatorOriginService);
        this.elementRef = inject(ElementRef);
        this.renderer = inject(Renderer2);
        this._focusOrigin = new FocusIndicatorOrigin(this.focusOriginService, this.elementRef, this.renderer);
    }
    ngOnDestroy() {
        this._focusOrigin.destroy();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOriginDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FocusIndicatorOriginDirective, isStandalone: false, selector: "[uxFocusIndicatorOrigin]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIndicatorOriginDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFocusIndicatorOrigin]',
                    standalone: false,
                }]
        }], ctorParameters: () => [] });

class FocusWithinDirective {
    /**
     * Note: We used to use @angular/cdk FocusMonitor here instead of manually listening
     * to focus blur events, however this was problematic as any child elements using the FocusMonitor,
     * eg: `uxFocusIndicator` which not get the correct `origin`, they will instead get a programmatic
     * origin even if it was clicked or focused via the keyboard.
     */
    constructor() {
        this._elementRef = inject(ElementRef);
        /** Emits when a child element gains focus */
        this.uxFocusWithin = new EventEmitter();
        /** Emits when a child element loses focus */
        this.uxBlurWithin = new EventEmitter();
        // We need to listen in capture phase since focus events don't bubble.
        this._elementRef.nativeElement.addEventListener('focus', this.onFocus.bind(this), true);
        this._elementRef.nativeElement.addEventListener('blur', this.onBlur.bind(this), true);
    }
    ngOnDestroy() {
        this._elementRef.nativeElement.removeEventListener('focus', this.onFocus.bind(this), true);
        this._elementRef.nativeElement.removeEventListener('blur', this.onBlur.bind(this), true);
    }
    onFocus() {
        this.uxFocusWithin.emit();
    }
    onBlur() {
        this.uxBlurWithin.emit();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusWithinDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FocusWithinDirective, isStandalone: false, selector: "[uxFocusWithin],[uxBlurWithin]", outputs: { uxFocusWithin: "uxFocusWithin", uxBlurWithin: "uxBlurWithin" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusWithinDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFocusWithin],[uxBlurWithin]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxFocusWithin: [{
                type: Output
            }], uxBlurWithin: [{
                type: Output
            }] } });

class ManagedFocusContainerService {
    constructor() {
        this.rendererFactory = inject(RendererFactory2);
        this._containers = [];
        // programmatically create a renderer as it can't be injected into a service
        this._renderer = this.rendererFactory.createRenderer(null, null);
    }
    /**
     * Create or get an existing object which manages the tabindex of descendants.
     * @param element The element containing focusable descendants.
     * @param component The component requesting the managed focus container.
     */
    register(element, component) {
        // Only create a new instance if no other component has created a container on the same element
        let containerRef = this._containers.find(ref => ref.container.element.isEqualNode(element));
        if (!containerRef) {
            containerRef = new ManagedFocusContainerWithReferences(new ManagedFocusContainer(element, this._renderer));
            this._containers.push(containerRef);
            // Start listening for focus
            containerRef.container.register();
        }
        // Track references to dispose correctly
        if (component) {
            containerRef.addReference(component);
        }
    }
    /**
     * Remove the container object. This will call `unregister` on the container if `component` is the last reference
     * to it.
     * @param element The element containing focusable descendants.
     * @param component The component requesting the managed focus container.
     */
    unregister(element, component) {
        // Remove the container's reference to the source component
        const containerRef = this._containers.find(ref => ref.container.element.isEqualNode(element));
        // technically this function can be called before the register function if ngOnDestroy runs before it
        // is fully initialized so we should stop here if there is no containRef.
        if (!containerRef) {
            return;
        }
        containerRef.removeReference(component);
        if (!containerRef.isAlive()) {
            // Last reference was removed, so unregister the listeners
            containerRef.container.unregister();
            // Clean up the reference tracking array
            this._containers = this._containers.filter(c => c !== containerRef);
        }
    }
    /**
     * Get an observable which can be used to determine when the element or one of its descendants has focus.
     * @param element The element containing focusable descendants.
     */
    hasFocus(element) {
        const container = this.getContainer(element);
        return container ? container.hasFocus$.asObservable() : null;
    }
    getContainer(element) {
        const containerRef = this._containers.find(ref => ref.container.element.isEqualNode(element));
        return containerRef ? containerRef.container : null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManagedFocusContainerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManagedFocusContainerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManagedFocusContainerService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });
class ManagedFocusContainer {
    constructor(element, _renderer) {
        this.element = element;
        this._renderer = _renderer;
        /** Whether the container or one of its descendants has focus. */
        this.hasFocus$ = new BehaviorSubject(false);
        this._modifiedElements = [];
        this._unlisten = [];
    }
    /** Start managing the focus of child elements. */
    register() {
        this._unlisten.push(this._renderer.listen(this.element, 'focusin', this.onFocusIn.bind(this)));
        this._unlisten.push(this._renderer.listen(this.element, 'focusout', this.onFocusOut.bind(this)));
        // Check if the container already has focus
        setTimeout(() => {
            if (!this.element.contains(document.activeElement)) {
                this.removeTabFocus();
            }
        });
    }
    /** Stop managing the focus of child elements. */
    unregister() {
        // Dispose the event handlers
        this._unlisten.forEach(unlisten => unlisten());
        this._unlisten = [];
        // Undo any tabindex modifications
        this.restoreTabFocus();
    }
    onFocusIn() {
        this.restoreTabFocus();
    }
    onFocusOut() {
        this.removeTabFocus();
    }
    removeTabFocus() {
        this.hasFocus$.next(false);
        // Clear the list of affected elements
        this._modifiedElements = [];
        // Get all focusable children
        const focusable = this.element.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
        // Remove focusable children from the tab ring
        Array.from(focusable).forEach(element => {
            const originalTabIndex = element.getAttribute('tabindex');
            this._renderer.setAttribute(element, 'tabindex', '-1');
            this._modifiedElements.push({
                element,
                tabindex: originalTabIndex,
            });
        });
    }
    restoreTabFocus() {
        this.hasFocus$.next(true);
        // Restore tab focus ability by removing the custom `tabindex` attribute
        this._modifiedElements.forEach(elementInfo => {
            if (elementInfo.tabindex === null) {
                this._renderer.removeAttribute(elementInfo.element, 'tabindex');
            }
            else {
                this._renderer.setAttribute(elementInfo.element, 'tabindex', elementInfo.tabindex);
            }
        });
        // Clear the list of affected elements
        this._modifiedElements = [];
    }
}
class ManagedFocusContainerWithReferences {
    constructor(container) {
        this.container = container;
        this._components = [];
    }
    addReference(component) {
        this._components.push(component);
    }
    removeReference(component) {
        this._components = this._components.filter(c => c !== component);
    }
    isAlive() {
        return this._components.length > 0;
    }
}

class ManagedFocusContainerDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._managedFocusContainerService = inject(ManagedFocusContainerService);
    }
    ngOnInit() {
        this._managedFocusContainerService.register(this._elementRef.nativeElement, this);
    }
    ngOnDestroy() {
        this._managedFocusContainerService.unregister(this._elementRef.nativeElement, this);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManagedFocusContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ManagedFocusContainerDirective, isStandalone: false, selector: "[uxManagedFocusContainer]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManagedFocusContainerDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxManagedFocusContainer]',
                    standalone: false,
                }]
        }] });

class SplitterAccessibilityDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._splitter = inject(SplitComponent);
        this._focusIndicatorService = inject(FocusIndicatorService);
        this._platform = inject(PLATFORM_ID);
        /** Emit an event whenever the gutter is moved using the keyboard */
        this.gutterKeydown = new EventEmitter();
        /** Store all the gutter elements */
        this._gutters = [];
        /** Teardown our observables on destroy */
        this._onDestroy = new Subject();
        /** Store references to all focus indicators */
        this._focusIndicators = [];
    }
    ngOnInit() {
        // update aria values when the a gutter is dragged
        this._splitter.dragProgress$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.updateGutterAttributes());
    }
    /** Once initialised make the gutters accessible */
    ngAfterViewInit() {
        // find the gutters
        this.onGutterChange();
        // if the number of split areas change then update the gutters and apply aria properties
        this.areas.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => this.onGutterChange());
        // we can't know when additional split-gutters appear using ContentChildren as the directive class is not exported and selector doesn't work - use mutation observer instead
        if (isPlatformBrowser(this._platform)) {
            // create the mutation observer
            this._observer = new MutationObserver(() => this.onGutterChange());
            // begin observing the child nodes
            this._observer.observe(this._elementRef.nativeElement, { childList: true });
        }
    }
    /** Destroy all observables and observers */
    ngOnDestroy() {
        if (this._observer) {
            this._observer.disconnect();
        }
        this._onDestroy.next();
        this._onDestroy.complete();
        // destroy all existing focus indicators
        this._focusIndicators.forEach(indicator => indicator.destroy());
    }
    /** We should focus the gutter when it is clicked */
    onClick(event) {
        if (this.isSplitterGutter(event.target)) {
            event.target.parentElement.focus();
        }
    }
    /** Find all the gutters and set their attributes */
    onGutterChange() {
        // destroy all existing focus indicators
        this._focusIndicators.forEach(indicator => indicator.destroy());
        // reset the array
        this._focusIndicators = [];
        // get the new gutter elements
        this._gutters = this.getGutters();
        // monitor the focus of each gutter
        this._gutters.forEach(gutter => this._focusIndicators.push(this._focusIndicatorService.monitor(gutter)));
        // apply all required accessibility attributes to the gutter elements
        this.setGutterAttributes();
    }
    /** Get all the gutter elements */
    getGutters() {
        // This function uses DOM accessing properties - which won't work if server side rendered
        if (isPlatformBrowser(this._platform)) {
            const gutters = [];
            for (let idx = 0; idx < this._elementRef.nativeElement.children.length; idx++) {
                const node = this._elementRef.nativeElement.children.item(idx);
                if (this.isSplitterGutter(node)) {
                    gutters.push(node);
                }
            }
            return gutters;
        }
        return [];
    }
    /** Set the appropriate attributes on the gutter elements */
    setGutterAttributes() {
        // apply attribute to every gutter
        this._gutters.forEach(gutter => {
            // apply the separator role
            this._renderer.setAttribute(gutter, 'role', 'separator');
            // make the gutters tabbable
            this._renderer.setAttribute(gutter, 'tabindex', '0');
            // set the value now aria property
            this.updateGutterAttributes();
        });
    }
    /** Apply the aria attribute values */
    updateGutterAttributes() {
        // update the value now properties of each gutter
        this._gutters.forEach((gutter, idx) => {
            this.setGutterValueNow(gutter, idx);
            this.setGutterValueMin(gutter, idx);
            this.setGutterValueMax(gutter, idx);
        });
    }
    /** Apply the value now aria attribute */
    setGutterValueNow(gutter, index) {
        // get the matching split area
        const area = this._splitter.displayedAreas[index];
        if (area.size === '*') {
            return;
        }
        // indicate the size
        this._renderer.setAttribute(gutter, 'aria-valuenow', `${Math.round(area.size)}`);
    }
    /** Apply the value min aria attribute */
    setGutterValueMin(gutter, index) {
        // get the matching split area
        const area = this.areas.toArray()[index];
        // indicate the minimum size
        this._renderer.setAttribute(gutter, 'aria-valuemin', `${Math.round(area.minSize)}`);
    }
    /** Apply the value max aria attribute */
    setGutterValueMax(gutter, index) {
        // get every other splitter area
        const availableSize = this.areas
            .filter((_area, idx) => index !== idx)
            .reduce((total, area) => total + area.minSize, 0);
        // indicate the minimum size
        this._renderer.setAttribute(gutter, 'aria-valuemax', `${100 - Math.round(availableSize)}`);
    }
    onKeydown(event) {
        if (this.isSplitterGutter(event.target)) {
            this.gutterKeydown.emit(event);
        }
    }
    onIncreaseKey(event) {
        // only perform a move if a gutter is focused
        if (this.isSplitterGutter(event.target)) {
            this.setGutterPosition(event.target, -1);
            // stop the browser from scrolling
            event.preventDefault();
        }
    }
    onDecreaseKey(event) {
        // only perform a move if a gutter is focused
        if (this.isSplitterGutter(event.target)) {
            this.setGutterPosition(event.target, 1);
            // stop the browser from scrolling
            event.preventDefault();
        }
    }
    onHomeKey(event) {
        if (this.isSplitterGutter(event.target)) {
            // get the affected panels
            const areas = this.getAreasFromGutter(event.target);
            if (areas.previous.size === '*') {
                return;
            }
            // set the previous area to it's minimum size
            const delta = areas.previous.size - areas.previous.minSize;
            // update the sizes accordingly
            this.setGutterPosition(event.target, delta);
            // stop the browser from scrolling
            event.preventDefault();
        }
    }
    onEndKey(event) {
        if (this.isSplitterGutter(event.target)) {
            // get the affected panels
            const areas = this.getAreasFromGutter(event.target);
            if (areas.next.size === '*') {
                return;
            }
            // set the next area to it's minimum size
            const delta = areas.next.size - areas.next.minSize;
            // update the sizes accordingly
            this.setGutterPosition(event.target, -delta);
            // stop the browser from scrolling
            event.preventDefault();
        }
    }
    /** Determine if an element is a gutter */
    isSplitterGutter(element) {
        return (element.classList.contains('as-split-gutter') ||
            element.classList.contains('as-split-gutter-icon'));
    }
    /** Update the gutter position */
    setGutterPosition(gutter, delta) {
        // get the affected panels
        const areas = this.getAreasFromGutter(gutter);
        if (areas.previous.size === '*' || areas.next.size === '*') {
            return;
        }
        // ensure we can perform the resize
        if (areas.previous.size - delta < areas.previous.minSize ||
            areas.next.size + delta < areas.next.minSize) {
            return;
        }
        // perform the resize
        areas.previous.size -= delta;
        areas.next.size += delta;
        // update the splitter - this is a private method but we need to call it
        this._splitter.refreshStyleSizes();
        // update the gutter aria values
        this.updateGutterAttributes();
    }
    /** Get the split areas associated with a given gutter */
    getAreasFromGutter(gutter) {
        const index = this._gutters.indexOf(gutter);
        return {
            previous: this._splitter.displayedAreas[index],
            next: this._splitter.displayedAreas[index + 1],
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SplitterAccessibilityDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SplitterAccessibilityDirective, isStandalone: false, selector: "as-split", outputs: { gutterKeydown: "gutterKeydown" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)", "keydown.ArrowDown": "onIncreaseKey($event)", "keydown.ArrowRight": "onIncreaseKey($event)", "keydown.ArrowUp": "onDecreaseKey($event)", "keydown.ArrowLeft": "onDecreaseKey($event)", "keydown.Home": "onHomeKey($event)", "keydown.End": "onEndKey($event)" } }, queries: [{ propertyName: "areas", predicate: SplitAreaDirective }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SplitterAccessibilityDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'as-split',
                    standalone: false,
                }]
        }], propDecorators: { gutterKeydown: [{
                type: Output
            }], areas: [{
                type: ContentChildren,
                args: [SplitAreaDirective]
            }], onClick: [{
                type: HostListener,
                args: ['click', ['$event']]
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }], onIncreaseKey: [{
                type: HostListener,
                args: ['keydown.ArrowDown', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.ArrowRight', ['$event']]
            }], onDecreaseKey: [{
                type: HostListener,
                args: ['keydown.ArrowUp', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.ArrowLeft', ['$event']]
            }], onHomeKey: [{
                type: HostListener,
                args: ['keydown.Home', ['$event']]
            }], onEndKey: [{
                type: HostListener,
                args: ['keydown.End', ['$event']]
            }] } });

class TabbableListService {
    constructor() {
        /** Indicate is this is being using on a hierarchichal set of items */
        this.hierarchy = false;
        /** Determine if we all the alt key */
        this.allowAltModifier = true;
        /** Determine if we all the ctrl key */
        this.allowCtrlModifier = true;
        /** Determine if we allow the Home/End keys */
        this.allowBoundaryKeys = false;
        /** Determine if we should scroll the item into view on focus */
        this.shouldScrollInView = true;
        /** Indicate if we should refocus an item on QueryList change - for use within virtual lists */
        this.shouldFocusOnChange = true;
        /** Emit whenever focus does not change but tabindexes have */
        this.onTabIndexChange = new Subject();
        /** Determine if focus is currently within the tabbable list */
        this.isFocused = false;
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this.onTabIndexChange.complete();
    }
    initialize(items, direction, wrap) {
        // store the items
        this._items = items;
        // create the new focus key manager
        this.focusKeyManager = new FocusKeyManager(items);
        // set the direction of the list
        direction === 'vertical'
            ? this.focusKeyManager.withVerticalOrientation()
            : this.focusKeyManager.withHorizontalOrientation('ltr');
        this._direction = direction;
        // enable wrapping if required
        if (wrap) {
            this.focusKeyManager.withWrap();
        }
        // make sure the first item in the list is tabbable
        this.setFirstItemTabbable();
        // call the init function on each item
        this._items.forEach(item => item.onInit());
        // if the list changes we need to ensure there is always at least one tabbable item
        this._items.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            // call the on init function on any new items
            this._items.filter(item => !item.initialized).forEach(item => item.onInit());
            // ensure we update the tab indexes
            this.onTabIndexChange.next();
            // ensure there is at least one item tabbable at all times
            this.ensureTabbableItem();
        });
    }
    /** Give and item focus or just make it the current tabbable item */
    activate(item, updateIndexOnly = false) {
        if (!item) {
            return;
        }
        // get the item index
        const index = this._items.toArray().indexOf(item);
        this.activateItemAtIndex(index, updateIndexOnly);
    }
    /** Give and item focus or just make it the current tabbable item */
    activateItemAtIndex(index, updateIndexOnly = false) {
        // if we only want to update the index
        if (updateIndexOnly) {
            return this.updateActiveItemIndex(index);
        }
        // update active the item only if it is not already active
        if (this.focusKeyManager.activeItemIndex !== index) {
            this.focusKeyManager.setActiveItem(index);
        }
    }
    isItemActive(item) {
        // if this is called before the items have been set then do nothing
        if (!this._items) {
            return false;
        }
        // find the index of the item
        const index = this._items.toArray().findIndex(_item => _item.id === item.id);
        // check if the item is active (we check against index as it can be updated without setting the activeItem)
        return this.focusKeyManager && this.focusKeyManager.activeItemIndex === index;
    }
    setFirstItemTabbable() {
        // find the first item that is not disabled
        const first = this._items.toArray().findIndex(item => !item.disabled);
        if (first !== -1) {
            this.updateActiveItemIndex(first);
        }
    }
    isAnyItemTabbable() {
        const tabbable = this._items.toArray().find(item => item.tabindex === 0);
        return tabbable ? true : false;
    }
    ensureTabbableItem() {
        // check to see if any item is tabbable
        const active = this._items.find(item => this.isItemActive(item));
        if (!active) {
            this.setFirstItemTabbable();
        }
    }
    focusTabbableItem() {
        if (!this._items) {
            return;
        }
        // find the item in the list with a tab index
        const index = this._items.toArray().findIndex(item => this.isItemActive(item));
        // if an item was found then focus it
        if (index !== -1) {
            this.focusKeyManager.setActiveItem(index);
        }
    }
    onKeydown(source, event) {
        // prevent anything happening when modifier keys are pressed if they have been disabled
        if ((!this.allowAltModifier && event.altKey) || (!this.allowCtrlModifier && event.ctrlKey)) {
            return;
        }
        this.focusKeyManager.onKeydown(event);
        // if the key is a boundary key and boundary keys are enabled
        if (this.allowBoundaryKeys) {
            switch (event.which) {
                case HOME:
                    this.focusKeyManager.setFirstItemActive();
                    event.preventDefault();
                    break;
                case END:
                    this.focusKeyManager.setLastItemActive();
                    event.preventDefault();
                    break;
            }
        }
        if (this.hierarchy) {
            if ((this._direction === 'horizontal' && event.keyCode === DOWN_ARROW) ||
                (this._direction === 'vertical' && event.keyCode === RIGHT_ARROW)) {
                source.keyboardExpanded$.next(true);
            }
            else if ((this._direction === 'horizontal' && event.keyCode === UP_ARROW) ||
                (this._direction === 'vertical' && event.keyCode === LEFT_ARROW)) {
                if (source.children.length > 0 && source.expanded) {
                    source.keyboardExpanded$.next(false);
                }
                else if (source.parent) {
                    source.parent.keyboardExpanded$.next(false);
                }
            }
        }
    }
    sortItemsByHierarchy(list) {
        const topLevel = [];
        // Populating children - clear previously generated collection
        list.forEach(item => (item.children = []));
        // Populating children - map from child -> parent relationship
        list.forEach(item => {
            if (item.parent) {
                item.parent.children.push(item);
            }
            else {
                topLevel.push(item);
            }
        });
        // Flatten the tree to produce the cursor key order
        return this.flattenHierarchy(topLevel);
    }
    /**
     * In a uxVirtualFor list cells can be resused. This means that when we scroll
     * the data associated with a given element may change and not the actual elements. If only the data changes
     * then the QueryList will not emit a change so we may show focus indicatator on the element that previously displayed
     * the correct data but no longer does.
     *
     * We need to handle this correctly here. We already have keys implements to handle virtual elements so we can check
     * if a key changes and use it to update the focused item even if the QueryList doesn't inform us that we have changed.
     */
    itemReferenceChange(previousKey, origin) {
        // find the item that now has the previously focused key
        const item = this.getItemByKey(previousKey);
        // if no key was found then we should ensure there is a tabbable item
        if (!item) {
            return this.ensureTabbableItem();
        }
        // get the item index
        const index = this._items.toArray().indexOf(item);
        // activate the item without side effects
        this.updateActiveItemIndex(index);
        // focus the item with the same origin that it previously had
        item.focusWithOrigin(origin);
    }
    /** Update the active item without causing focus */
    updateActiveItemIndex(index) {
        this.focusKeyManager.updateActiveItem(index);
        this.onTabIndexChange.next();
    }
    /** Determine if there is an item with a tabindex of 0 */
    hasTabbableItem() {
        return this.focusKeyManager && this.focusKeyManager.activeItemIndex >= 0;
    }
    getItemByKey(key) {
        return this._items.find(item => item.key === key);
    }
    flattenHierarchy(items) {
        const flatList = [];
        items.forEach(item => {
            item.children.sort((a, b) => a.rank - b.rank);
            flatList.push(item, ...this.flattenHierarchy(item.children));
        });
        return flatList;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListService, decorators: [{
            type: Injectable
        }] });

let nextId = 0;
let uniqueKey = 0;
class TabbableListItemDirective {
    /** Provide a unique key to help identify items when used in a virtual list */
    set key(key) {
        // store the previous key
        const previousKey = this._key;
        // check if the key has changed eg. via cell reuse
        const didChangeRef = previousKey && key !== previousKey;
        // update the current key
        this._key = key;
        // if this element was the previously tabbable item then update the reference
        if (didChangeRef && this.isTabbable()) {
            // allow the virtual scroll to update
            this._changeDetector.detectChanges();
            // this item should no longer be tabbable
            this._tabbableList.focusKeyManager.updateActiveItem(-1);
            // store the focus origin before we blur
            const origin = this._focusOrigin;
            // blur this item
            this._elementRef.nativeElement.blur();
            // update the reference
            this._tabbableList.itemReferenceChange(previousKey, origin);
        }
    }
    get key() {
        return this._key || this._defaultKey;
    }
    get tabindex() {
        return this._tabbableList.isItemActive(this) ? 0 : -1;
    }
    constructor() {
        /** Access the service to programmatically control focus indicators */
        this.focusIndicatorService = inject(FocusIndicatorService);
        /** Access the tabbable list service */
        this._tabbableList = inject(TabbableListService);
        /** Access the tabbable item element */
        this._elementRef = inject(ElementRef);
        /** Access the service responsible for handling focus in child elements */
        this._managedFocusContainerService = inject(ManagedFocusContainerService);
        /** Access the service which can provide us with browser identification */
        this._platform = inject(Platform);
        /** Access the change detector to ensure tabindex gets updated as expects */
        this._changeDetector = inject(ChangeDetectorRef);
        /** Access the focus origin if one is provided */
        this._focusOriginService = inject(FocusIndicatorOriginService);
        /** Access the renderer to make manual dom manipulations */
        this._renderer = inject(Renderer2);
        this.rank = 0;
        /** Indicate if this item is disabled */
        this.disabled = false;
        /** Indicate if the item is expanded if used as a hierarchical item. */
        this.expanded = false;
        /** Emit when the expanded state changes. */
        this.expandedChange = new EventEmitter();
        /** Emit when the element receives focus via the tabbable list. */
        this.activated = new EventEmitter();
        /** Give each tabbable item a unique id */
        this.id = nextId++;
        /** Each item in the list needs to be initialised by the service. When the item QueryList changes this is used to identify which items previously existed and which are new */
        this.initialized = false;
        /** Store a list of all child tabbable items */
        this.children = [];
        /** Emit whenever the expanded state changes */
        this.keyboardExpanded$ = new Subject();
        /** Automatically unsubscribe from all observables */
        this._onDestroy = new Subject();
        /** Store a default key to use if one is not provided */
        this._defaultKey = `tabbable-list-key-${uniqueKey++}`;
        /** Determine if this element has a focus indicator visible */
        this._focusOrigin = null;
        // create the focus indicator
        this._focusIndicator = this.focusIndicatorService.monitor(this._elementRef.nativeElement);
        // store the most current focus origin
        this._focusIndicator.origin$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(origin => (this._focusOrigin = origin));
        // watch for changes to tabindexes
        this._tabbableList.onTabIndexChange
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.setTabIndex());
        this.keyboardExpanded$.pipe(tick(), takeUntil(this._onDestroy)).subscribe(expanded => {
            // Emit event which may alter the DOM
            this.expandedChange.emit(expanded);
            // Activate the appropriate item
            if (expanded) {
                if (this.children.length > 0) {
                    this._tabbableList.activate(this.children[0]);
                }
            }
            else {
                this._tabbableList.activate(this);
            }
        });
    }
    onInit() {
        this.initialized = true;
        // Watch for focus within the container element and manage tabindex of descendants
        this._managedFocusContainerService.register(this._elementRef.nativeElement, this);
        // ensure the tab index is initially set
        this.setTabIndex();
    }
    ngOnDestroy() {
        // check if this is the currently focused item - if so we need to make another item tabbable
        if (this.tabindex === 0) {
            this._tabbableList.setFirstItemTabbable();
        }
        this._onDestroy.next();
        this._onDestroy.complete();
        this._focusIndicator.destroy();
        this._managedFocusContainerService.unregister(this._elementRef.nativeElement, this);
    }
    focus() {
        // check if there are currently any items that are tabbable
        const hasTabbableItem = this._tabbableList.hasTabbableItem();
        // determine the focus origin
        const origin = hasTabbableItem
            ? this._focusOriginService.getOrigin() || 'keyboard'
            : 'keyboard';
        // apply focus to the element
        this.focusWithOrigin(origin, !this._tabbableList.shouldScrollInView);
        // ensure the focus key manager updates the active item correctly
        this._tabbableList.activate(this, hasTabbableItem);
        // emit the focus event
        this.activated.emit(origin);
    }
    onFocus() {
        // if this item is not currently focused in the focusKeyManager set it as the active item
        if (!this._tabbableList.isItemActive(this)) {
            this._tabbableList.activate(this, true);
        }
        // also inform the service that an item within the list is now focused
        this._tabbableList.isFocused = true;
    }
    onBlur() {
        // if this is the current active item and it is blurred then update the isFocused state
        if (this._tabbableList.isItemActive(this)) {
            this._tabbableList.isFocused = false;
        }
    }
    onKeydown(event) {
        this._tabbableList.onKeydown(this, event);
    }
    getFocused() {
        return this._elementRef.nativeElement === document.activeElement;
    }
    /** We can programmatically focus an element but may want a different origin than 'programmatic' */
    focusWithOrigin(origin, preventScroll = true) {
        if (origin) {
            const scrollTop = this._tabbableList.containerRef.scrollTop;
            // focus the item with a given origin
            this._focusIndicator.focus(origin, { preventScroll });
            // IE and Firefox don't support prevent scroll
            if (preventScroll && !this._platform.WEBKIT) {
                this._tabbableList.containerRef.scrollTop = scrollTop;
            }
        }
    }
    isTabbable() {
        return this.tabindex === 0;
    }
    setTabIndex() {
        // update the tabindex attribute
        this._renderer.setAttribute(this._elementRef.nativeElement, 'tabindex', this.tabindex.toString());
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TabbableListItemDirective, isStandalone: false, selector: "[uxTabbableListItem]", inputs: { parent: "parent", rank: "rank", disabled: "disabled", expanded: "expanded", key: "key" }, outputs: { expandedChange: "expandedChange", activated: "activated" }, host: { listeners: { "focus": "onFocus()", "click": "onFocus()", "blur": "onBlur()", "keydown": "onKeydown($event)" } }, exportAs: ["ux-tabbable-list-item"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTabbableListItem]',
                    exportAs: 'ux-tabbable-list-item',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { parent: [{
                type: Input
            }], rank: [{
                type: Input
            }], disabled: [{
                type: Input
            }], expanded: [{
                type: Input
            }], key: [{
                type: Input
            }], expandedChange: [{
                type: Output
            }], activated: [{
                type: Output
            }], onFocus: [{
                type: HostListener,
                args: ['focus']
            }, {
                type: HostListener,
                args: ['click']
            }], onBlur: [{
                type: HostListener,
                args: ['blur']
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class TabbableListDirective {
    /** Enabling handling of hierarchical lists via use of the `TabbableListItemDirective.parent` property. */
    set hierarchy(value) {
        this._tabbableList.hierarchy = value;
    }
    /** Prevent keyboard interaction when alt modifier key is pressed */
    set allowAltModifier(value) {
        this._tabbableList.allowAltModifier = value;
    }
    /** Prevent keyboard interaction when ctrl modifier key is pressed */
    set allowCtrlModifier(value) {
        this._tabbableList.allowCtrlModifier = value;
    }
    /** Focus the first or last item when Home or End keys are pressed */
    set allowBoundaryKeys(value) {
        this._tabbableList.allowBoundaryKeys = value;
    }
    get focusKeyManager() {
        return this._tabbableList.focusKeyManager;
    }
    constructor() {
        /** Access the tabbable list service */
        this._tabbableList = inject(TabbableListService);
        /** Access the native dom element */
        this.elementRef = inject(ElementRef);
        /** Determine whether the up/down arrows should be used or the left/right arrows */
        this.direction = 'vertical';
        /** Indicate whether or not focus should loop back to the first element after the last */
        this.wrap = true;
        /** Indicate whether or not the first item should receive focus on show - useful for modals and popovers */
        this.focusOnShow = false;
        /** Indicate whether or not focus should be returned to the previous element (only applicable when using focusOnShow) */
        this.returnFocus = false;
        /** Unsubscribe from all observables automatically on destroy */
        this._onDestroy = new Subject();
        // store a reference to the container element
        this._tabbableList.containerRef = this.elementRef.nativeElement;
    }
    ngAfterContentInit() {
        // store the currently focused element
        this._focusedElement = document.activeElement;
        this._orderedItems = new QueryList();
        if (this._tabbableList.hierarchy) {
            // Sort items in a hierarchy
            this._orderedItems.reset(this._tabbableList.sortItemsByHierarchy(this.items));
            // Ensure that the child items remain sorted
            this.items.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => {
                this._orderedItems.reset(this._tabbableList.sortItemsByHierarchy(this.items));
                this._orderedItems.notifyOnChanges();
            });
        }
        else {
            // Items are already in order
            this._orderedItems = this.items;
            // Ensure we reselect a selected item after the querylist has changed
            this.items.changes
                .pipe(filter(() => this._tabbableList.shouldFocusOnChange && this._tabbableList.isFocused), takeUntil(this._onDestroy))
                .subscribe((items) => {
                // check if an item is currently focused
                const activeItem = this._tabbableList.focusKeyManager.activeItem;
                // restore the selected item if there was one and it is still visible
                if (activeItem) {
                    // find the matching index
                    const index = items.toArray().findIndex(item => item.key === activeItem.key);
                    // if the item is still in the list we want to focus it
                    if (index > -1) {
                        // however we are refocusing an item that was focused so we dont want to scroll into view again as this can prevent wheel scrolling
                        this._tabbableList.shouldScrollInView = false;
                        // refocus the item again
                        this._tabbableList.activateItemAtIndex(index, !this._tabbableList.isFocused);
                        // re-enable scrolling into view
                        this._tabbableList.shouldScrollInView = true;
                    }
                }
            });
        }
        // Set up the focus monitoring
        this._tabbableList.initialize(this._orderedItems, this.direction, this.wrap);
        // focus the first element if specified
        if (this.focusOnShow) {
            this._tabbableList.focusKeyManager.setFirstItemActive();
        }
    }
    ngOnDestroy() {
        if (this.returnFocus && this._focusedElement instanceof HTMLElement) {
            setTimeout(() => this._focusedElement.focus());
        }
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    setFirstItemTabbable() {
        if (!this._tabbableList.isAnyItemTabbable()) {
            this._tabbableList.setFirstItemTabbable();
        }
    }
    focus() {
        if (this._tabbableList.focusKeyManager && this._tabbableList.focusKeyManager.activeItem) {
            this._tabbableList.focusKeyManager.activeItem.focus();
        }
    }
    focusTabbableItem() {
        this._tabbableList.focusTabbableItem();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TabbableListDirective, isStandalone: false, selector: "[uxTabbableList]", inputs: { direction: "direction", wrap: "wrap", focusOnShow: "focusOnShow", returnFocus: "returnFocus", hierarchy: "hierarchy", allowAltModifier: "allowAltModifier", allowCtrlModifier: "allowCtrlModifier", allowBoundaryKeys: "allowBoundaryKeys" }, providers: [TabbableListService], queries: [{ propertyName: "items", predicate: TabbableListItemDirective, descendants: true }], exportAs: ["ux-tabbable-list"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabbableListDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTabbableList]',
                    exportAs: 'ux-tabbable-list',
                    providers: [TabbableListService],
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { direction: [{
                type: Input
            }], wrap: [{
                type: Input
            }], focusOnShow: [{
                type: Input
            }], returnFocus: [{
                type: Input
            }], hierarchy: [{
                type: Input
            }], allowAltModifier: [{
                type: Input
            }], allowCtrlModifier: [{
                type: Input
            }], allowBoundaryKeys: [{
                type: Input
            }], items: [{
                type: ContentChildren,
                args: [TabbableListItemDirective, { descendants: true }]
            }] } });

class AccessibilityModule {
    static forRoot(options) {
        return {
            ngModule: AccessibilityModule,
            providers: [{ provide: ACCESSIBILITY_OPTIONS_TOKEN, useValue: options }],
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityModule, declarations: [DefaultFocusIndicatorDirective,
            FocusIndicatorDirective,
            FocusIndicatorOptionsDirective,
            FocusIndicatorOriginDirective,
            FocusWithinDirective,
            ManagedFocusContainerDirective,
            SplitterAccessibilityDirective,
            TabbableListDirective,
            TabbableListItemDirective,
            FocusIndicatorOriginDirective,
            ColorContrastDirective], imports: [A11yModule, ColorServiceModule, PlatformModule], exports: [DefaultFocusIndicatorDirective,
            FocusIndicatorDirective,
            FocusIndicatorOptionsDirective,
            FocusIndicatorOriginDirective,
            FocusWithinDirective,
            ManagedFocusContainerDirective,
            SplitterAccessibilityDirective,
            TabbableListDirective,
            TabbableListItemDirective,
            FocusIndicatorOriginDirective,
            ColorContrastDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityModule, providers: [
            AccessibilityOptionsService,
            ContrastService,
            FocusIndicatorService,
            ManagedFocusContainerService,
        ], imports: [A11yModule, ColorServiceModule, PlatformModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccessibilityModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        DefaultFocusIndicatorDirective,
                        FocusIndicatorDirective,
                        FocusIndicatorOptionsDirective,
                        FocusIndicatorOriginDirective,
                        FocusWithinDirective,
                        ManagedFocusContainerDirective,
                        SplitterAccessibilityDirective,
                        TabbableListDirective,
                        TabbableListItemDirective,
                        FocusIndicatorOriginDirective,
                        ColorContrastDirective,
                    ],
                    imports: [A11yModule, ColorServiceModule, PlatformModule],
                    exports: [
                        DefaultFocusIndicatorDirective,
                        FocusIndicatorDirective,
                        FocusIndicatorOptionsDirective,
                        FocusIndicatorOriginDirective,
                        FocusWithinDirective,
                        ManagedFocusContainerDirective,
                        SplitterAccessibilityDirective,
                        TabbableListDirective,
                        TabbableListItemDirective,
                        FocusIndicatorOriginDirective,
                        ColorContrastDirective,
                    ],
                    providers: [
                        AccessibilityOptionsService,
                        ContrastService,
                        FocusIndicatorService,
                        ManagedFocusContainerService,
                    ],
                }]
        }] });

class AccordionModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AccordionModule, declarations: [AccordionComponent, AccordionPanelComponent, AccordionPanelHeadingDirective], imports: [AccessibilityModule, CommonModule], exports: [AccordionComponent, AccordionPanelComponent, AccordionPanelHeadingDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionModule, imports: [AccessibilityModule, CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AccordionModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule],
                    declarations: [AccordionComponent, AccordionPanelComponent, AccordionPanelHeadingDirective],
                    exports: [AccordionComponent, AccordionPanelComponent, AccordionPanelHeadingDirective],
                }]
        }] });

class AlertIconDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: AlertIconDirective, isStandalone: false, selector: "[uxAlertIcon]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertIconDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxAlertIcon]',
                    standalone: false,
                }]
        }] });

const ICON_OPTIONS_TOKEN = new InjectionToken('ICON_OPTIONS_TOKEN');

/** AUTOGENERATED: DO NOT MODIFY **/
const commonIcons = [
    '3d',
    'achievement',
    'action',
    'actions',
    'active',
    'add',
    'address-book',
    'advanced-search',
    'aggregate',
    'alarm',
    'alert-filled',
    'alert',
    'analytics',
    'announcement',
    'app',
    'archive',
    'article',
    'ascend',
    'assistant',
    'attachment',
    'bar-chart',
    'blog',
    'bloomberg',
    'book',
    'bookmark-filled',
    'bookmark',
    'bundle',
    'calculator',
    'calendar',
    'camera-filled',
    'camera',
    'capacity',
    'caret-down-filled',
    'caret-down',
    'caret-next-filled',
    'caret-next',
    'caret-previous-filled',
    'caret-previous',
    'caret-up-filled',
    'caret-up',
    'catalog',
    'change-password',
    'chapter-add',
    'chapter-next-filled',
    'chapter-next',
    'chapter-previous-filled',
    'chapter-previous',
    'chart-organization',
    'chart-partition',
    'chart-sankey',
    'chat-attachment',
    'chat',
    'checkbox-selected',
    'checkbox',
    'checkmark',
    'chevron-down',
    'chevron-left-double',
    'chevron-left',
    'chevron-right-double',
    'chevron-right',
    'chevron-up',
    'chorus',
    'circular-view',
    'clipboard',
    'clone',
    'close',
    'cloud-computer',
    'cloud-download',
    'cloud-software',
    'cloud-upload',
    'cloud',
    'cluster',
    'code',
    'command-line',
    'compare',
    'compass',
    'compliance',
    'computer-personal',
    'configuration-filled',
    'configuration',
    'confluence',
    'connect',
    'contact-card',
    'contact-us-filled',
    'contact-us',
    'contract',
    'copy',
    'cube-filled',
    'cube',
    'cubes',
    'cursor-filled',
    'cursor',
    'cut',
    'cycle',
    'dashboard',
    'database',
    'defect',
    'deliver',
    'deployment',
    'descend',
    'desktop',
    'detach',
    'directions',
    'dislike-filled',
    'dislike',
    'divide-four',
    'divide-right',
    'divide-three',
    'divide',
    'document-cloud',
    'document-compress',
    'document-config',
    'document-csv',
    'document-data',
    'document-download',
    'document-excel',
    'document-executable',
    'document-html',
    'document-image',
    'document-import',
    'document-locked',
    'document-missing',
    'document-notes',
    'document-outlook',
    'document-pdf',
    'document-performance',
    'document-powerpoint',
    'document-rtf',
    'document-sound',
    'document-test',
    'document-text',
    'document-threat',
    'document-time',
    'document-transfer',
    'document-txt',
    'document-update',
    'document-upload',
    'document-user',
    'document-verified',
    'document-video',
    'document-word',
    'document',
    'domain',
    'down',
    'download',
    'drag',
    'drive-cage',
    'duplicate',
    'edit-filled',
    'edit',
    'efax',
    'eject-filled',
    'eject',
    'expand',
    'export',
    'fan',
    'fast-forward-filled',
    'fast-forward',
    'favorite-filled',
    'favorite',
    'filter-filled',
    'filter',
    'first-aid',
    'flag-filled',
    'flag',
    'folder-cycle',
    'folder-open',
    'folder',
    'gallery-filled',
    'gallery',
    'globe',
    'grid',
    'group-add',
    'group',
    'grow',
    'halt',
    'help-circle',
    'help',
    'highlighting-remove',
    'highlighting',
    'history',
    'home-filled',
    'home',
    'host-maintenance',
    'host',
    'image-filled',
    'image',
    'impact',
    'import',
    'in-progress',
    'inactive',
    'inbox',
    'indicator-filled',
    'indicator',
    'information-filled',
    'information',
    'inherit-filled',
    'inherit',
    'input-to-process',
    'install',
    'integration',
    'iteration-filled',
    'iteration',
    'java-filled',
    'java',
    'language',
    'launch',
    'license-filled',
    'license',
    'like-filled',
    'like',
    'line-chart',
    'link-bottom',
    'link-down',
    'link-next',
    'link-previous',
    'link-top',
    'link-up',
    'link',
    'list',
    'location-filled',
    'location-pin-filled',
    'location-pin',
    'location',
    'lock',
    'login',
    'logout',
    'mail-attachment',
    'mail-filled',
    'mail',
    'manual',
    'map-location',
    'map',
    'menu',
    'microphone-filled',
    'microphone',
    'monitor',
    'more',
    'multiple',
    'navigate',
    'new-window',
    'new',
    'news-aggregation',
    'news-collection',
    'news-content',
    'news',
    'next',
    'notes',
    'notification-filled',
    'notification',
    'optimization',
    'organization',
    'overview',
    'pan',
    'pause-filled',
    'pause',
    'payment-google-wallet',
    'payment-mastercard',
    'payment-paypal',
    'payment-square',
    'payment-visa',
    'pin-filled',
    'pin',
    'plan',
    'platform-apple',
    'platform-chrome',
    'platform-dropbox',
    'platform-edge',
    'platform-firefox',
    'platform-internet-explorer',
    'platform-kubernetes',
    'platform-skype',
    'platform-windows',
    'play-filled',
    'play',
    'power',
    'previous',
    'print',
    'quick-view',
    'radial-selected',
    'radial',
    'redo',
    'refresh',
    'resources',
    'reuters',
    'rewind-filled',
    'rewind',
    'risk',
    'rss',
    'satellite',
    'save-filled',
    'save',
    'scale-out-repository',
    'schedule-clone',
    'schedule-new',
    'schedule-play',
    'schedule',
    'scorecard',
    'search',
    'secure',
    'select-left',
    'select',
    'server-cluster',
    'server-started',
    'server',
    'servers',
    'service-business',
    'service-start',
    'share',
    'shield-configure',
    'shield-filled',
    'shield',
    'shift',
    'shop-basket',
    'shop-cart',
    'show-less',
    'show-more',
    'sms',
    'soa',
    'social-cisco-jabber',
    'social-email',
    'social-facebook-workplace',
    'social-facebook',
    'social-github',
    'social-instagram',
    'social-instant-message',
    'social-jira',
    'social-linkedin',
    'social-medium',
    'social-ms-teams',
    'social-pinterest',
    'social-reddit',
    'social-salesforce-filled',
    'social-salesforce',
    'social-sharepoint',
    'social-skype-for-business',
    'social-slack',
    'social-tumblr',
    'social-twitter',
    'social-vimeo',
    'social-we-chat',
    'social-whats-app',
    'social-yammer',
    'social-youtube',
    'social-zoom',
    'sort',
    'stakeholder',
    'star-filled',
    'star-half',
    'star',
    'status-approved-filled',
    'status-error-filled',
    'status-information-filled',
    'status-information',
    'status-warning-filled',
    'steps-filled',
    'steps',
    'storage',
    'street-view-filled',
    'street-view',
    'subtitles',
    'subtract',
    'support',
    'symphony',
    'sync',
    'system',
    'tab-next',
    'tab-previous',
    'tab-up',
    'table-add',
    'table',
    'tag-filled',
    'tag',
    'target',
    'task',
    'template',
    'test-desktop',
    'test',
    'text-wrap',
    'threats',
    'ticket',
    'tools',
    'tooltip',
    'transaction-filled',
    'transaction',
    'translate',
    'trash-filled',
    'trash',
    'tree',
    'trigger',
    'trophy-filled',
    'trophy',
    'troubleshooting',
    'undo',
    'unlock',
    'up',
    'update',
    'upgrade-filled',
    'upgrade',
    'upload',
    'user-add-filled',
    'user-add',
    'user-admin',
    'user-expert',
    'user-female-filled',
    'user-female',
    'user-filled',
    'user-manager',
    'user-new',
    'user-police',
    'user-settings',
    'user-worker',
    'user',
    'validation-filled',
    'validation',
    'video-filled',
    'video',
    'view-filled',
    'view',
    'virtual-machine',
    'vm-maintenance',
    'voltage',
    'volume-filled',
    'volume-low-filled',
    'volume-low',
    'volume-mute-filled',
    'volume-mute',
    'volume',
    'vulnerability',
    'waypoint-filled',
    'waypoint',
    'workshop',
    'zoom-in',
    'zoom-out',
];

/** We generate the iconset definition as hardcoding it increases bundle size by ~40kb per iconset */
const uxIconset = [
    ...commonIcons.map(uxIconMapper),
];
function uxIconMapper(icon) {
    return { name: icon, iconset: 'ux-icon', icon: `ux-icon-${icon}` };
}

class IconService {
    /** Inject a parent service if one exists */
    constructor() {
        this.options = inject(ICON_OPTIONS_TOKEN, { optional: true });
        this._iconService = inject(IconService, { optional: true, skipSelf: true });
        /** Emit whenever the iconset changes */
        this.iconsChanged$ = new Subject();
        /** Store a list of all icon */
        this._icons = [...uxIconset];
        // if the iconset was defined at the root or child module level apply this configuration
        if (this.options && this.options.icons) {
            this.setIcons(this.options.icons);
        }
    }
    /** Define multiple icon definitions. This will override icon definitions if a name and size collision occurs */
    setIcons(icons) {
        icons.forEach(icon => this.setIcon(icon));
    }
    /** Provide an icon definition which will override if necessary */
    setIcon({ name, icon, iconset, size }) {
        // if there are multiple sizes specified add them all as individual records
        if (Array.isArray(size)) {
            return size.forEach(variant => this.setIcon({ name, icon, iconset, size: variant }));
        }
        // remove any existing definition with the same parameters
        this._icons = this._icons.filter(definition => !(definition.name === name && definition.size === size));
        // insert the new definition
        this._icons = [...this._icons, { name, icon, iconset, size }];
        // emit the icon change
        this.iconsChanged$.next({ name, size });
    }
    /** Find an icon based on the given name and size if provided */
    getIcon(name, size) {
        // if no name was specified then do nothing (this can occur if the name input on the component is not initially defined)
        if (!name) {
            return;
        }
        // if there is a size specified then check for an exact match
        if (size) {
            // get an icon definition that matches both name and size
            const sizedIcon = this._icons.find(definition => definition.name === name && definition.size === size);
            // if there is a match then return otherwise fallthrough to the default
            if (sizedIcon) {
                return sizedIcon;
            }
        }
        // find a general match with no size constraint
        const icon = this._icons.find(definition => definition.name === name && definition.size === undefined);
        // if no match is found and there is a parent service then we should check it
        if (!icon && this._iconService) {
            return this._iconService.getIcon(name, size);
        }
        else if (!icon) {
            console.warn(`Icon '${name}' was not found.`);
        }
        return icon;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class IconComponent {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._iconService = inject(IconService);
        /** Store the boolean value of flip vertical */
        this._flipVertical = false;
        /** Store the boolean value of flip horizontal */
        this._flipHorizontal = false;
        /** Automatically unsubscribe from observables */
        this._onDestroy = new Subject();
    }
    /** The number of degrees to rotate the icon */
    set rotate(rotation) {
        this._rotate = coerceNumberProperty(rotation);
    }
    get rotate() {
        return this._rotate;
    }
    /** Define if the icon should be horizontally flipped */
    set flipHorizontal(flipHorizontal) {
        this._flipHorizontal = coerceBooleanProperty(flipHorizontal);
    }
    get flipHorizontal() {
        return this._flipHorizontal;
    }
    /** Define if the icon should be horizontally flipped */
    set flipVertical(flipVertical) {
        this._flipVertical = coerceBooleanProperty(flipVertical);
    }
    get flipVertical() {
        return this._flipVertical;
    }
    /** When inputs change ensure we have the best icon definition */
    ngOnChanges(changes) {
        // if the name or size changes then update the icon
        if ((changes.name && changes.name.currentValue !== changes.name.previousValue) ||
            (changes.size && changes.size.currentValue !== changes.size.previousValue)) {
            this.updateIcon();
        }
    }
    /** Watch for changes to the iconset */
    ngAfterViewInit() {
        // watch for changes to the iconset to check if we need to update.
        this._iconService.iconsChanged$
            .pipe(filter(event => this._icon && event.name === this._icon.name), takeUntil(this._onDestroy))
            .subscribe(() => this.updateIcon());
    }
    /** Cleanup on component destroy */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** get the icon definition based on the name and size specified */
    updateIcon() {
        // remove the current icon set and icon classes of the old icon.
        // note we are using the renderer and not HostBindings as a HostBinding
        // on the `class` property will override any user added classes which is
        // not desirable.
        if (this._icon) {
            this._renderer.removeClass(this._elementRef.nativeElement, this._icon.iconset);
            this._renderer.removeClass(this._elementRef.nativeElement, this._icon.icon);
        }
        // update the stored icon definition with the best match based on name and size
        this._icon = this._iconService.getIcon(this.name, this.size);
        // add the new icon classes, again using the renderer to avoid overriding user classes
        if (this._icon) {
            this._renderer.addClass(this._elementRef.nativeElement, this._icon.iconset);
            this._renderer.addClass(this._elementRef.nativeElement, this._icon.icon);
        }
        else if (this.name) {
            console.warn(`The icon ${this.name} could not be found. Ensure you are using the correct iconset.`);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: IconComponent, isStandalone: false, selector: "ux-icon", inputs: { name: "name", size: "size", rotate: "rotate", flipHorizontal: "flipHorizontal", flipVertical: "flipVertical" }, host: { properties: { "style.font-size": "size", "class.ux-flip-horizontal": "flipHorizontal", "class.ux-flip-vertical": "flipVertical", "class.ux-rotate-90": "rotate == 90", "class.ux-rotate-180": "rotate == 180", "class.ux-rotate-270": "rotate == 270" } }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'ux-icon',
                    template: '',
                    changeDetection: ChangeDetectionStrategy.OnPush,
                    host: {
                        '[style.font-size]': 'size',
                        '[class.ux-flip-horizontal]': 'flipHorizontal',
                        '[class.ux-flip-vertical]': 'flipVertical',
                        '[class.ux-rotate-90]': 'rotate == 90',
                        '[class.ux-rotate-180]': 'rotate == 180',
                        '[class.ux-rotate-270]': 'rotate == 270',
                    },
                    standalone: false,
                }]
        }], propDecorators: { name: [{
                type: Input
            }], size: [{
                type: Input
            }], rotate: [{
                type: Input
            }], flipHorizontal: [{
                type: Input
            }], flipVertical: [{
                type: Input
            }] } });

class AlertComponent {
    constructor() {
        this.colorService = inject(ColorService);
        /** Determine the style of the alert */
        this.type = 'info';
        /** Determine the the alert can be dismissed */
        this.dismissible = false;
        /** Define a custom aria label for the dismiss button */
        this.dismissAriaLabel = 'Dismiss Alert';
        /** Emit when the dismiss button is pressed */
        this.dismiss = new EventEmitter();
    }
    /** Resolve the background color from the color set */
    get _backgroundColor() {
        return this.backgroundColor ? this.getColor(this.backgroundColor) : null;
    }
    /** Resolve the foreground color from the color set */
    get _foregroundColor() {
        return this.foregroundColor ? this.getColor(this.foregroundColor) : null;
    }
    /** Determine if we are using a prefined type or custom colors */
    get _isCustomColor() {
        return !!this.backgroundColor && !!this.foregroundColor;
    }
    getColor(color) {
        // check if it is a color name from the color palette or just return the CSS color value
        return this.colorService.resolve(color);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: AlertComponent, isStandalone: false, selector: "ux-alert", inputs: { type: "type", dismissible: "dismissible", backgroundColor: "backgroundColor", foregroundColor: "foregroundColor", dismissAriaLabel: "dismissAriaLabel" }, outputs: { dismiss: "dismiss" }, host: { attributes: { "role": "alert" }, properties: { "class.ux-alert-info": "type === \"info\" && !_isCustomColor", "class.ux-alert-error": "type === \"error\" && !_isCustomColor", "class.ux-alert-warning": "type === \"warning\" && !_isCustomColor", "class.ux-alert-success": "type === \"success\" && !_isCustomColor", "class.ux-alert-dark": "type === \"dark\" && !_isCustomColor", "style.background-color": "_backgroundColor", "style.color": "_foregroundColor" } }, queries: [{ propertyName: "icon", first: true, predicate: AlertIconDirective, descendants: true }], ngImport: i0, template: "@if (icon) {\n  <div class=\"alert-icon\">\n    <ng-content select=\"[uxAlertIcon]\"></ng-content>\n  </div>\n}\n\n<div class=\"alert-content\">\n  <ng-content></ng-content>\n</div>\n\n@if (dismissible) {\n  <button\n    uxFocusIndicator\n    class=\"alert-close\"\n    type=\"button\"\n    (click)=\"dismiss.emit()\"\n    [attr.aria-label]=\"dismissAriaLabel\"\n  >\n    <ux-icon name=\"close\" class=\"alert-close-icon\"></ux-icon>\n  </button>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-alert', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'alert',
                        '[class.ux-alert-info]': 'type === "info" && !_isCustomColor',
                        '[class.ux-alert-error]': 'type === "error" && !_isCustomColor',
                        '[class.ux-alert-warning]': 'type === "warning" && !_isCustomColor',
                        '[class.ux-alert-success]': 'type === "success" && !_isCustomColor',
                        '[class.ux-alert-dark]': 'type === "dark" && !_isCustomColor',
                        '[style.background-color]': '_backgroundColor',
                        '[style.color]': '_foregroundColor',
                    }, standalone: false, template: "@if (icon) {\n  <div class=\"alert-icon\">\n    <ng-content select=\"[uxAlertIcon]\"></ng-content>\n  </div>\n}\n\n<div class=\"alert-content\">\n  <ng-content></ng-content>\n</div>\n\n@if (dismissible) {\n  <button\n    uxFocusIndicator\n    class=\"alert-close\"\n    type=\"button\"\n    (click)=\"dismiss.emit()\"\n    [attr.aria-label]=\"dismissAriaLabel\"\n  >\n    <ux-icon name=\"close\" class=\"alert-close-icon\"></ux-icon>\n  </button>\n}\n" }]
        }], propDecorators: { type: [{
                type: Input
            }], dismissible: [{
                type: Input
            }], backgroundColor: [{
                type: Input
            }], foregroundColor: [{
                type: Input
            }], dismissAriaLabel: [{
                type: Input
            }], dismiss: [{
                type: Output
            }], icon: [{
                type: ContentChild,
                args: [AlertIconDirective, { static: false }]
            }] } });

class IconModule {
    /** Allow configuration at AppModule level */
    static forRoot(options) {
        return {
            ngModule: IconModule,
            providers: [{ provide: ICON_OPTIONS_TOKEN, useValue: options }],
        };
    }
    /** Allow configuration at a child module level */
    static forChild(options) {
        // the `forChild` does the same as `forRoot` however this having
        // `forChild` follows the correct conventions as we should never
        // import `forRoot` in a child module
        return IconModule.forRoot(options);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: IconModule, declarations: [IconComponent], exports: [IconComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconModule, providers: [IconService] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IconModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [IconComponent],
                    exports: [IconComponent],
                    providers: [IconService],
                }]
        }] });

class AlertModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AlertModule, declarations: [AlertComponent, AlertIconDirective], imports: [AccessibilityModule, CommonModule, IconModule], exports: [AlertComponent, AlertIconDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertModule, imports: [AccessibilityModule, CommonModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlertModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, IconModule],
                    declarations: [AlertComponent, AlertIconDirective],
                    exports: [AlertComponent, AlertIconDirective],
                }]
        }] });

class BreadcrumbsComponent {
    clickCrumb(event, crumb) {
        if (crumb.onClick) {
            crumb.onClick.call(null, event);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: BreadcrumbsComponent, isStandalone: false, selector: "ux-breadcrumbs", inputs: { crumbs: "crumbs" }, ngImport: i0, template: "<nav aria-label=\"Breadcrumb\">\n  <ol class=\"breadcrumb\">\n    @for (crumb of crumbs; track crumb) {\n      <li>\n        <!-- If there is a router link or an onClick function then use a tag -->\n        @if (crumb.routerLink) {\n          <a\n            uxFocusIndicator\n            tabindex=\"0\"\n            [routerLink]=\"crumb.routerLink\"\n            [fragment]=\"crumb.fragment\"\n            [queryParams]=\"crumb.queryParams\"\n            (click)=\"clickCrumb($event, crumb)\"\n          >\n            {{ crumb.title }}\n          </a>\n        }\n        @if (crumb.onClick && !crumb.routerLink) {\n          <a uxFocusIndicator tabindex=\"0\" (click)=\"clickCrumb($event, crumb)\">\n            {{ crumb.title }}\n          </a>\n        }\n        <!-- If there is no router link or onClick function then display text in a span -->\n        @if (!crumb.routerLink && !crumb.onClick) {\n          <span>{{ crumb.title }}</span>\n        }\n      </li>\n    }\n  </ol>\n</nav>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-breadcrumbs', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<nav aria-label=\"Breadcrumb\">\n  <ol class=\"breadcrumb\">\n    @for (crumb of crumbs; track crumb) {\n      <li>\n        <!-- If there is a router link or an onClick function then use a tag -->\n        @if (crumb.routerLink) {\n          <a\n            uxFocusIndicator\n            tabindex=\"0\"\n            [routerLink]=\"crumb.routerLink\"\n            [fragment]=\"crumb.fragment\"\n            [queryParams]=\"crumb.queryParams\"\n            (click)=\"clickCrumb($event, crumb)\"\n          >\n            {{ crumb.title }}\n          </a>\n        }\n        @if (crumb.onClick && !crumb.routerLink) {\n          <a uxFocusIndicator tabindex=\"0\" (click)=\"clickCrumb($event, crumb)\">\n            {{ crumb.title }}\n          </a>\n        }\n        <!-- If there is no router link or onClick function then display text in a span -->\n        @if (!crumb.routerLink && !crumb.onClick) {\n          <span>{{ crumb.title }}</span>\n        }\n      </li>\n    }\n  </ol>\n</nav>\n" }]
        }], propDecorators: { crumbs: [{
                type: Input
            }] } });

class BreadcrumbsModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsModule, declarations: [BreadcrumbsComponent], imports: [AccessibilityModule, CommonModule, RouterModule], exports: [BreadcrumbsComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsModule, imports: [AccessibilityModule, CommonModule, RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BreadcrumbsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, RouterModule],
                    exports: [BreadcrumbsComponent],
                    declarations: [BreadcrumbsComponent],
                }]
        }] });

class ResizeService {
    constructor() {
        this._zone = inject(NgZone);
        this._observer = new ResizeObserver(this.elementDidResize.bind(this));
        this._targets = new WeakMap();
    }
    ngOnDestroy() {
        this._observer.disconnect();
    }
    addResizeListener(target) {
        this._zone.runOutsideAngular(() => this._observer.observe(target));
        if (this._targets.has(target)) {
            return this._targets.get(target);
        }
        else {
            const emitter = new ReplaySubject();
            this._targets.set(target, emitter);
            return emitter;
        }
    }
    removeResizeListener(target) {
        this._observer.unobserve(target);
    }
    elementDidResize(entries) {
        this._zone.run(() => {
            for (const entry of entries) {
                if (this._targets.has(entry.target)) {
                    const emitter = this._targets.get(entry.target);
                    emitter.next({
                        width: entry.target.offsetWidth,
                        height: entry.target.offsetHeight,
                    });
                }
            }
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeService, decorators: [{
            type: Injectable
        }] });

class ResizeDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._resizeService = inject(ResizeService);
        this._ngZone = inject(NgZone);
        /** Debounce the resize event emitter */
        this.throttle = 0;
        /** Emits whenever a resize event occurs */
        this.uxResize = new EventEmitter();
        /** Remove all subscriptions on component destroy */
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy), debounceTime(this.throttle))
            .subscribe((event) => this._ngZone.run(() => this.uxResize.emit(event)));
    }
    ngOnDestroy() {
        this._resizeService.removeResizeListener(this._elementRef.nativeElement);
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ResizeDirective, isStandalone: false, selector: "[uxResize]", inputs: { throttle: "throttle" }, outputs: { uxResize: "uxResize" }, providers: [ResizeService], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxResize]',
                    providers: [ResizeService],
                    standalone: false,
                }]
        }], propDecorators: { throttle: [{
                type: Input
            }], uxResize: [{
                type: Output
            }] } });

class ResizeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ResizeModule, declarations: [ResizeDirective], exports: [ResizeDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeModule, providers: [ResizeService] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizeModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [ResizeDirective],
                    declarations: [ResizeDirective],
                    providers: [ResizeService],
                }]
        }] });

class CardTabContentDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: CardTabContentDirective, isStandalone: false, selector: "[uxCardTabContent]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabContentDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxCardTabContent]',
                    standalone: false,
                }]
        }] });

class CardTabsService {
    constructor() {
        this.tab$ = new BehaviorSubject(null);
        this.tabs$ = new BehaviorSubject([]);
        this.position$ = new BehaviorSubject('top');
        // when a tab is added or removed ensure we always select one if any are available
        this._subscription = this.tabs$
            .pipe(filter(tabs => !this.tab$.value || !tabs.find(tab => tab === this.tab$.value)))
            .subscribe(tabs => this.tab$.next(tabs.length > 0 ? tabs[0] : null));
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    /**
     * Add a tab to the list of tabs
     */
    addTab(tab) {
        this.tabs$.next([...this.tabs$.value, tab]);
    }
    /**
     * Remove a tab from the list
     */
    removeTab(tab) {
        this.tabs$.next(this.tabs$.value.filter(_tab => _tab !== tab));
    }
    /**
     * Select the tab
     */
    select(tab) {
        this.tab$.next(tab);
    }
    /**
     * Set the position of the tab content
     */
    setPosition(position) {
        this.position$.next(position);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class CardTabComponent {
    constructor() {
        this._tabService = inject(CardTabsService);
        this.active$ = this._tabService.tab$.pipe(map(tab => tab === this));
        this._tabService.addTab(this);
    }
    ngOnDestroy() {
        this._tabService.removeTab(this);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: CardTabComponent, isStandalone: false, selector: "ux-card-tab", queries: [{ propertyName: "content", first: true, predicate: CardTabContentDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: "@if (active$ | async) {\n  <ng-content></ng-content>\n}\n", dependencies: [{ kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-card-tab', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@if (active$ | async) {\n  <ng-content></ng-content>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { content: [{
                type: ContentChild,
                args: [CardTabContentDirective, { read: TemplateRef, static: false }]
            }] } });

class CardTabsetComponent {
    constructor() {
        this.tabService = inject(CardTabsService);
        this.offset = 0;
        this.bounds = { lower: 0, upper: 0 };
    }
    set position(direction) {
        this.tabService.setPosition(direction);
    }
    get position() {
        return this.tabService.position$.getValue();
    }
    select(tab, element) {
        // select the tab
        this.tabService.select(tab);
        // ensure the tab is moved into view if required
        this.moveIntoView(element);
    }
    resize(dimensions) {
        this._width = dimensions.width;
        this._innerWidth = this.tablist.nativeElement.scrollWidth;
        this.bounds.lower = 0;
        this.bounds.upper = -(this._innerWidth - this._width);
    }
    previous() {
        this.offset += this._width;
        // ensure it remains within the allowed bounds
        this.offset = Math.min(this.offset, this.bounds.lower);
    }
    next() {
        this.offset -= this._width;
        // ensure it remains within the allowed bounds
        this.offset = Math.max(this.offset, this.bounds.upper);
    }
    moveIntoView(element) {
        // if we dont have the dimensions we cant check
        if (!this._width || !this._innerWidth) {
            return;
        }
        // get the current element bounds
        const { offsetLeft, offsetWidth } = element;
        const { marginLeft, marginRight } = getComputedStyle(element);
        // calculate the visible area
        const viewportStart = Math.abs(this.offset);
        const viewportEnd = viewportStart + this._width;
        const cardWidth = parseFloat(marginLeft) + offsetWidth + parseFloat(marginRight);
        // if we need to move to the left - figure out how much
        if (offsetLeft < viewportStart) {
            this.offset -= offsetLeft - parseFloat(marginLeft) - viewportStart;
        }
        // if we need to move to the right - figure out how much
        if (offsetLeft + cardWidth > viewportEnd) {
            this.offset -= offsetLeft + cardWidth - viewportEnd;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: CardTabsetComponent, isStandalone: false, selector: "ux-card-tabset", inputs: { position: "position" }, host: { properties: { "class": "this.position" } }, providers: [CardTabsService], viewQueries: [{ propertyName: "tablist", first: true, predicate: ["tablist"], descendants: true, static: true }], ngImport: i0, template: "@if (tabService.tab$ | async) {\n  <div class=\"card-tab-content\" role=\"tabpanel\">\n    <ng-content></ng-content>\n  </div>\n}\n\n<div class=\"card-tabs\" #tabs>\n  @if (offset < bounds.lower) {\n    <button\n      type=\"button\"\n      class=\"card-tabs-paging-btn card-tabs-paging-btn-previous\"\n      aria-label=\"Previous Tabs\"\n      (click)=\"previous()\"\n    >\n      <ux-icon name=\"previous\"></ux-icon>\n    </button>\n  }\n\n  <div\n    class=\"card-tabs-list\"\n    role=\"tablist\"\n    #tablist\n    (uxResize)=\"resize($event)\"\n    [style.transform]=\"'translateX(' + offset + 'px)'\"\n  >\n    @for (tab of tabService.tabs$ | async; track tab) {\n      <div\n        class=\"card-tab\"\n        role=\"tab\"\n        tabindex=\"0\"\n        #card\n        [ngClass]=\"tabService.position$ | async\"\n        [class.active]=\"tab.active$ | async\"\n        [attr.aria-selected]=\"tab.active$ | async\"\n        (click)=\"select(tab, card)\"\n        (focus)=\"tabs.scrollLeft = 0\"\n        (keydown.enter)=\"select(tab, card)\"\n      >\n        <ng-container [ngTemplateOutlet]=\"tab.content\"></ng-container>\n      </div>\n    }\n  </div>\n\n  @if (offset > bounds.upper) {\n    <button\n      type=\"button\"\n      class=\"card-tabs-paging-btn card-tabs-paging-btn-next\"\n      aria-label=\"Next Tabs\"\n      (click)=\"next()\"\n    >\n      <ux-icon name=\"next\"></ux-icon>\n    </button>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ResizeDirective, selector: "[uxResize]", inputs: ["throttle"], outputs: ["uxResize"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-card-tabset', providers: [CardTabsService], standalone: false, template: "@if (tabService.tab$ | async) {\n  <div class=\"card-tab-content\" role=\"tabpanel\">\n    <ng-content></ng-content>\n  </div>\n}\n\n<div class=\"card-tabs\" #tabs>\n  @if (offset < bounds.lower) {\n    <button\n      type=\"button\"\n      class=\"card-tabs-paging-btn card-tabs-paging-btn-previous\"\n      aria-label=\"Previous Tabs\"\n      (click)=\"previous()\"\n    >\n      <ux-icon name=\"previous\"></ux-icon>\n    </button>\n  }\n\n  <div\n    class=\"card-tabs-list\"\n    role=\"tablist\"\n    #tablist\n    (uxResize)=\"resize($event)\"\n    [style.transform]=\"'translateX(' + offset + 'px)'\"\n  >\n    @for (tab of tabService.tabs$ | async; track tab) {\n      <div\n        class=\"card-tab\"\n        role=\"tab\"\n        tabindex=\"0\"\n        #card\n        [ngClass]=\"tabService.position$ | async\"\n        [class.active]=\"tab.active$ | async\"\n        [attr.aria-selected]=\"tab.active$ | async\"\n        (click)=\"select(tab, card)\"\n        (focus)=\"tabs.scrollLeft = 0\"\n        (keydown.enter)=\"select(tab, card)\"\n      >\n        <ng-container [ngTemplateOutlet]=\"tab.content\"></ng-container>\n      </div>\n    }\n  </div>\n\n  @if (offset > bounds.upper) {\n    <button\n      type=\"button\"\n      class=\"card-tabs-paging-btn card-tabs-paging-btn-next\"\n      aria-label=\"Next Tabs\"\n      (click)=\"next()\"\n    >\n      <ux-icon name=\"next\"></ux-icon>\n    </button>\n  }\n</div>\n" }]
        }], propDecorators: { position: [{
                type: HostBinding,
                args: ['class']
            }, {
                type: Input
            }], tablist: [{
                type: ViewChild,
                args: ['tablist', { static: true }]
            }] } });

class CardTabsModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: CardTabsModule, declarations: [CardTabsetComponent, CardTabComponent, CardTabContentDirective], imports: [CommonModule, ResizeModule, IconModule], exports: [CardTabsetComponent, CardTabComponent, CardTabContentDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsModule, imports: [CommonModule, ResizeModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CardTabsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, ResizeModule, IconModule],
                    declarations: [CardTabsetComponent, CardTabComponent, CardTabContentDirective],
                    exports: [CardTabsetComponent, CardTabComponent, CardTabContentDirective],
                }]
        }] });

class MenuDividerComponent {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuDividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: MenuDividerComponent, isStandalone: false, selector: "ux-menu-divider", host: { attributes: { "role": "separator" } }, ngImport: i0, template: "", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuDividerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-menu-divider', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'separator',
                    }, standalone: false, template: "" }]
        }] });

/**
 * This is used to avoid having to do an `instanceof` check
 * which would cause a circular dependency between the
 * `MenuComponent` and `MenuItemComponent`
 */
var MenuItemType;
(function (MenuItemType) {
    MenuItemType[MenuItemType["Default"] = 0] = "Default";
    MenuItemType[MenuItemType["Custom"] = 1] = "Custom";
})(MenuItemType || (MenuItemType = {}));

const MENU_OPTIONS_TOKEN = new InjectionToken('MENU_OPTIONS_TOKEN');

let uniqueId$d = 0;
class MenuComponent {
    constructor() {
        this._options = inject(MENU_OPTIONS_TOKEN, { optional: true });
        this._changeDetector = inject(ChangeDetectorRef);
        /** A unique id for the component. */
        this.id = `ux-menu-${++uniqueId$d}`;
        /** Define the position of the menu */
        this.placement = 'bottom';
        /** Define the alignment of the menu */
        this.alignment = 'start';
        /** Define if we should animate the menu */
        // eslint-disable-next-line no-prototype-builtins
        this.animate = this._options && 'animate' in this._options ? this._options.animate : true;
        /** Emit when the opening has begun (the opened EventEmitter waits until the animation has finished) */
        this.opening = new EventEmitter();
        /** Emit when the menu is opened */
        this.opened = new EventEmitter();
        /** Emit whenever closing has begun (the closed EventEmitter waits until animation has finished) */
        this.closing = new EventEmitter();
        /** Emit when the menu is closed */
        this.closed = new EventEmitter();
        /** Store the menu open state */
        this.isMenuOpen = false;
        /** Store the animation state */
        this._isAnimating = false;
        /** Determine if this is a submenu */
        this._isSubMenu = false;
        /** Whether this menu should close when it loses focus */
        this._closeOnBlur = false;
        /** Emit when the focused item changes (we use this as the key manager is not instantiated until a late lifecycle hook) */
        this._activeItem$ = new BehaviorSubject(null);
        /** Access allow a close event to propagate all the way up the submenus */
        this._closeAll$ = new Subject();
        /** Emit keyboard events */
        this._onKeydown$ = new Subject();
        /** Emit hover events */
        this._isHovering$ = new BehaviorSubject(false);
        /** Emit focus events */
        this._isFocused$ = new BehaviorSubject(false);
        /** Emit placement change */
        this._placement$ = new BehaviorSubject('bottom');
        this._alignment$ = new BehaviorSubject('start');
        /** Access all child menu items for accessibility purposes */
        this._items$ = new BehaviorSubject([]);
        /** Automatically unsubscribe when the component is destroyed */
        this._onDestroy$ = new Subject();
        this._isTabPressed = false;
        /** Create an internal querylist to store the menu items */
        this._itemsList = new QueryList();
    }
    /** Get innerId for use for accessibility  */
    get innerId() {
        return `${this.id}-menu`;
    }
    get _isExpanded() {
        return this._menuItems.pipe(switchMap(items => merge(...items.map(item => item.isExpanded$))), takeUntil(this._onDestroy$));
    }
    get _menuItemClick() {
        return this._menuItems.pipe(switchMap(items => merge(...items.map(item => item.onClick$))), takeUntil(this._onDestroy$));
    }
    /** Return only menu items an not custom tabbable items */
    get _menuItems() {
        return this._items$.pipe(map(items => items.filter(item => item.type === MenuItemType.Default)));
    }
    ngAfterContentInit() {
        // initialise the query list with the items
        this._items$.pipe(takeUntil(this._onDestroy$)).subscribe(items => {
            // if no items has been marked as tabbable then this should be
            if (!this._activeItem$.value && items.length > 0) {
                this._activeItem$.next(items[0]);
            }
            this._itemsList.reset(items);
            this._itemsList.notifyOnChanges();
        });
        // setup keyboard functionality
        this._keyManager = new FocusKeyManager(this._itemsList)
            .withVerticalOrientation()
            .withTypeAhead()
            .withWrap();
        // emit the tabbable item on change
        this._keyManager.change
            .pipe(map(() => this._keyManager.activeItem), takeUntil(this._onDestroy$))
            .subscribe(item => this._activeItem$.next(item));
    }
    ngOnChanges(changes) {
        if (changes.placement && changes.placement.currentValue !== changes.placement.previousValue) {
            this._placement$.next(changes.placement.currentValue);
        }
        if (changes.alignment && changes.alignment.currentValue !== changes.alignment.previousValue) {
            this._alignment$.next(changes.alignment.currentValue);
        }
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
        this._closeAll$.complete();
        this._isHovering$.complete();
        this._isFocused$.complete();
        this._activeItem$.complete();
        this._items$.complete();
        this._placement$.complete();
    }
    /** Register a menu item - we do this do avoid `@ContentChildren` detecting submenu items */
    _addItem(item) {
        if (!this.hasItem(item)) {
            this._items$.next([...this._items$.value, item]);
        }
    }
    /** Remove an item */
    _removeItem(item) {
        if (this.hasItem(item)) {
            this._items$.next(this._items$.value.filter(_item => _item !== item));
        }
    }
    /** Determine if an item exists */
    hasItem(item) {
        return !!this._items$.value.find(_item => _item === item);
    }
    /** Internal function to set the open state and run change detection */
    _setMenuOpen(menuOpen) {
        // store the open state
        this.isMenuOpen = menuOpen;
        // if we are closing the menu reset some values
        if (!menuOpen) {
            this._isHovering$.next(false);
            this._isFocused$.next(false);
        }
        // the change detector is actually an instance of a ViewRef (which extends ChangeDetectorRef) when used within a component
        // and the ViewRef contains the destroyed state of the component which is more reliable
        // than setting a flag in ngOnDestroy as the component can be destroyed before
        // the lifecycle hook is called
        const viewRef = this._changeDetector;
        // check for changes - required to show the menu as we are using `*ngIf`
        if (!viewRef.destroyed) {
            this._changeDetector.detectChanges();
        }
        // emit the closing event
        menuOpen ? this.opening.emit() : this.closing.emit();
    }
    /** Close the menu if the focus event is null */
    _focusChange(event) {
        if (event === null && this._closeOnBlur && this._isTabPressed) {
            this._closeAll$.next('tabout');
        }
    }
    /** Track the animation state */
    _onAnimationStart() {
        this._isAnimating = true;
    }
    /** Track animation state and emit event when opening or closing */
    _onAnimationDone() {
        this._isAnimating = false;
        if (this.isMenuOpen) {
            this.opened.emit();
        }
        else {
            this.closed.emit();
        }
    }
    _closeMenu() {
        this._setMenuOpen(false);
    }
    /** Forward any keyboard events to the key manage for accessibility */
    _onKeydown(event) {
        this._keyManager.setFocusOrigin('keyboard').onKeydown(event);
        // emit the keydown event
        this._onKeydown$.next(event);
    }
    _onHoverStart() {
        this._isHovering$.next(true);
    }
    _onHoverEnd() {
        this._isHovering$.next(false);
    }
    _onFocus() {
        this._isFocused$.next(true);
    }
    _onBlur() {
        this._isFocused$.next(false);
    }
    _onKeyDown(event) {
        this._isTabPressed = event.keyCode === TAB;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MenuComponent, isStandalone: false, selector: "ux-menu", inputs: { id: "id", placement: "placement", alignment: "alignment", animate: "animate", menuClass: "menuClass" }, outputs: { opening: "opening", opened: "opened", closing: "closing", closed: "closed" }, host: { properties: { "attr.id": "this.id" } }, viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-template>\n  @if (isMenuOpen) {\n    <div\n      class=\"ux-menu\"\n      role=\"menu\"\n      [id]=\"innerId\"\n      [class.ux-sub-menu]=\"_isSubMenu\"\n      [ngClass]=\"menuClass\"\n      @menuAnimation\n      [@.disabled]=\"!animate\"\n      (@menuAnimation.start)=\"_onAnimationStart()\"\n      (@menuAnimation.done)=\"_onAnimationDone()\"\n      (mouseenter)=\"_onHoverStart()\"\n      (mouseover)=\"_onHoverStart()\"\n      (mouseleave)=\"_onHoverEnd()\"\n      (focusin)=\"_onFocus()\"\n      (focusout)=\"_onBlur()\"\n      (keydown)=\"_onKeyDown($event)\"\n      cdkMonitorSubtreeFocus\n      [cdkTrapFocus]=\"_closeOnBlur\"\n      (cdkFocusChange)=\"_focusChange($event)\"\n    >\n      <ng-content></ng-content>\n    </div>\n  }\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1$1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i1$1.CdkMonitorFocus, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: ["cdkFocusChange"], exportAs: ["cdkMonitorFocus"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], animations: [
            trigger('menuAnimation', [
                transition(':enter', [
                    style({ opacity: 0, transform: 'scaleY(0)' }),
                    animate('200ms ease-out', style({ opacity: 1, transform: 'none' })),
                ]),
                transition(':leave', [
                    animate('200ms ease-out', style({ opacity: 0, transform: 'scaleY(0)' })),
                ]),
            ]),
        ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-menu', changeDetection: ChangeDetectionStrategy.OnPush, animations: [
                        trigger('menuAnimation', [
                            transition(':enter', [
                                style({ opacity: 0, transform: 'scaleY(0)' }),
                                animate('200ms ease-out', style({ opacity: 1, transform: 'none' })),
                            ]),
                            transition(':leave', [
                                animate('200ms ease-out', style({ opacity: 0, transform: 'scaleY(0)' })),
                            ]),
                        ]),
                    ], standalone: false, template: "<ng-template>\n  @if (isMenuOpen) {\n    <div\n      class=\"ux-menu\"\n      role=\"menu\"\n      [id]=\"innerId\"\n      [class.ux-sub-menu]=\"_isSubMenu\"\n      [ngClass]=\"menuClass\"\n      @menuAnimation\n      [@.disabled]=\"!animate\"\n      (@menuAnimation.start)=\"_onAnimationStart()\"\n      (@menuAnimation.done)=\"_onAnimationDone()\"\n      (mouseenter)=\"_onHoverStart()\"\n      (mouseover)=\"_onHoverStart()\"\n      (mouseleave)=\"_onHoverEnd()\"\n      (focusin)=\"_onFocus()\"\n      (focusout)=\"_onBlur()\"\n      (keydown)=\"_onKeyDown($event)\"\n      cdkMonitorSubtreeFocus\n      [cdkTrapFocus]=\"_closeOnBlur\"\n      (cdkFocusChange)=\"_focusChange($event)\"\n    >\n      <ng-content></ng-content>\n    </div>\n  }\n</ng-template>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.id']
            }], placement: [{
                type: Input
            }], alignment: [{
                type: Input
            }], animate: [{
                type: Input
            }], menuClass: [{
                type: Input
            }], opening: [{
                type: Output
            }], opened: [{
                type: Output
            }], closing: [{
                type: Output
            }], closed: [{
                type: Output
            }], templateRef: [{
                type: ViewChild,
                args: [TemplateRef, { static: false }]
            }] } });

class MenuInitialFocusDirective {
    constructor() {
        this._menu = inject(MenuComponent);
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        this.ensureFocusable();
        // Focus the host element when the parent menu is opened.
        this._menu.opened.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            this._elementRef.nativeElement.focus();
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Apply tabindex="0" to the element if it's not already focusable. */
    ensureFocusable() {
        if (this._elementRef.nativeElement.tabIndex >= 0) {
            return;
        }
        this._renderer.setAttribute(this._elementRef.nativeElement, 'tabindex', '0');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuInitialFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuInitialFocusDirective, isStandalone: false, selector: "[uxMenuInitialFocus]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuInitialFocusDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuInitialFocus]',
                    standalone: false,
                }]
        }] });

class MenuTabbableItemDirective {
    constructor() {
        this._menu = inject(MenuComponent);
        this._elementRef = inject(ElementRef);
        this._focusIndicatorService = inject(FocusIndicatorService);
        this._renderer = inject(Renderer2);
        /** Define if this item is disabled or not */
        this.disabled = false;
        /** Indicate the type of the menu item */
        this.type = MenuItemType.Default;
        /** Automatically unsubscribe when directive is destroyed */
        this._onDestroy$ = new Subject();
    }
    ngOnInit() {
        // register this item in the MenuComponent
        this._menu._addItem(this);
        // we only want to show the focus indicator whenever the keyboard is used
        this.focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement);
        // subscribe to active item changes
        this._menu._activeItem$
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(item => this.setTabIndex(item === this));
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
        this.focusIndicator.destroy();
    }
    /** Focus this item with a given origin */
    focus(origin) {
        this.focusIndicator.focus(origin);
    }
    /** This function is built into the CDK manager to allow jumping to items based on text content */
    getLabel() {
        return this._elementRef.nativeElement.textContent.trim();
    }
    /** Forward any keyboard events to the MenuComponent for accessibility */
    _onKeydown(event) {
        this._menu._onKeydown(event);
    }
    /** Update the tab index on this item */
    setTabIndex(isTabbable) {
        this._renderer.setAttribute(this._elementRef.nativeElement, 'tabindex', isTabbable ? '0' : '-1');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuTabbableItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuTabbableItemDirective, isStandalone: false, selector: "[uxMenuTabbableItem]", inputs: { disabled: "disabled" }, host: { listeners: { "keydown": "_onKeydown($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuTabbableItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuTabbableItem]',
                    standalone: false,
                }]
        }], propDecorators: { disabled: [{
                type: Input
            }], _onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

const FocusableItemToken = new InjectionToken('Focusable Option');
class MenuItemCustomControlDirective extends MenuTabbableItemDirective {
    constructor() {
        super();
        this._focusableControl = inject(FocusableItemToken, { optional: true });
        /** Indicate the type of the menu item */
        this.type = MenuItemType.Custom;
    }
    ngOnInit() {
        // register this item in the MenuComponent
        super.ngOnInit();
        this._menu.opened.pipe(takeUntil(this._onDestroy$)).subscribe(() => {
            // remove any existing tab index on component instance and have it handled by this directive
            this._focusableControl?.setInputTabIndex(-1);
        });
    }
    /** Focus this item with a given origin */
    focus(origin) {
        super.focus(origin);
        this._focusableControl
            ? this._focusableControl.focus(origin)
            : this._elementRef.nativeElement.focus();
    }
    /** We want to remove the ability to shift+tab back into the parent element */
    setTabIndex() {
        super.setTabIndex(false);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuItemCustomControlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuItemCustomControlDirective, isStandalone: false, selector: "[uxMenuItemCustomControl]", host: { attributes: { "role": "menuitem" }, properties: { "class.ux-menu-item": "true" } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuItemCustomControlDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuItemCustomControl]',
                    host: {
                        '[class.ux-menu-item]': 'true',
                        role: 'menuitem',
                    },
                    standalone: false,
                }]
        }], ctorParameters: () => [] });

class MenuItemComponent {
    constructor() {
        this._menu = inject(MenuComponent);
        this._elementRef = inject(ElementRef);
        this._focusIndicatorService = inject(FocusIndicatorService);
        this._renderer = inject(Renderer2);
        /** Define the role of the element */
        this.role = 'menuitem';
        /** Emits when the menu item is clicked or the enter key is pressed. */
        this.activate = new EventEmitter();
        /** Indicate the type of the menu item */
        this.type = MenuItemType.Default;
        /** Store the current hover state */
        this.isHovered$ = new BehaviorSubject(false);
        /** Store the current focus state */
        this.isFocused$ = new BehaviorSubject(false);
        /** Store the current expanded state */
        this.isExpanded$ = new BehaviorSubject(false);
        /** Emit when an item is clicked */
        this.onClick$ = new Subject();
        /** Automatically unsubscribe from observables on destroy */
        this._onDestroy$ = new Subject();
        this._disabled = false;
        this._closeOnSelect = true;
    }
    /** Define if this item is disabled or not */
    set disabled(disabled) {
        this._disabled = coerceBooleanProperty(disabled);
    }
    get disabled() {
        return this._disabled;
    }
    /** Determine if the menu should close on item click/enter.*/
    set closeOnSelect(value) {
        this._closeOnSelect = coerceBooleanProperty(value);
    }
    get closeOnSelect() {
        return this._closeOnSelect;
    }
    /** Access the open state */
    get isOpen() {
        return this._menu.isMenuOpen;
    }
    ngOnInit() {
        // register this item in the MenuComponent
        this._menu._addItem(this);
        // we only want to show the focus indicator whenever the keyboard is used
        this._focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement);
        // subscribe to active item changes
        this._menu._activeItem$
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(item => this.setTabIndex(item === this));
    }
    ngOnDestroy() {
        this._menu._removeItem(this);
        this.isHovered$.complete();
        this.isExpanded$.complete();
        this.isFocused$.complete();
        this.onClick$.complete();
        this._focusIndicator.destroy();
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    focus(origin) {
        this._focusIndicator.focus(origin);
    }
    /** This function is built into the CDK manager to allow jumping to items based on text content */
    getLabel() {
        return this._elementRef.nativeElement.textContent.trim();
    }
    _onMouseEnter() {
        this.isHovered$.next(true);
    }
    _onMouseLeave() {
        this.isHovered$.next(false);
    }
    _onFocus() {
        this.isFocused$.next(true);
    }
    _onBlur() {
        this.isFocused$.next(false);
    }
    _onClick(event) {
        if (!this.disabled) {
            if (this.closeOnSelect) {
                this.onClick$.next(isKeyboardTrigger(event) ? 'keyboard' : 'mouse');
            }
            this.activate.emit(event);
        }
    }
    /** Forward any keyboard events to the MenuComponent for accessibility */
    _onKeydown(event) {
        this._menu._onKeydown(event);
    }
    /** Update the tab index on this item */
    setTabIndex(isTabbable) {
        this._renderer.setAttribute(this._elementRef.nativeElement, 'tabindex', isTabbable ? '0' : '-1');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: MenuItemComponent, isStandalone: false, selector: "[uxMenuItem]", inputs: { disabled: "disabled", closeOnSelect: "closeOnSelect", role: "role" }, outputs: { activate: "activate" }, host: { listeners: { "mouseenter": "_onMouseEnter()", "mouseleave": "_onMouseLeave()", "focus": "_onFocus()", "blur": "_onBlur()", "click": "_onClick($event)", "keydown.enter": "_onClick($event)", "keydown": "_onKeydown($event)" }, properties: { "attr.disabled": "disabled ? \"disabled\" : null", "attr.role": "role", "class.disabled": "disabled", "class.ux-menu-item": "true", "class.open": "isOpen" } }, ngImport: i0, template: "<ng-content></ng-content>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuItemComponent, decorators: [{
            type: Component,
            args: [{ selector: '[uxMenuItem]', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[attr.disabled]': 'disabled ? "disabled" : null',
                        '[attr.role]': 'role',
                        '[class.disabled]': 'disabled',
                        '[class.ux-menu-item]': 'true',
                        '[class.open]': 'isOpen',
                    }, standalone: false, template: "<ng-content></ng-content>\n" }]
        }], propDecorators: { disabled: [{
                type: Input
            }], closeOnSelect: [{
                type: Input
            }], role: [{
                type: Input
            }], activate: [{
                type: Output
            }], _onMouseEnter: [{
                type: HostListener,
                args: ['mouseenter']
            }], _onMouseLeave: [{
                type: HostListener,
                args: ['mouseleave']
            }], _onFocus: [{
                type: HostListener,
                args: ['focus']
            }], _onBlur: [{
                type: HostListener,
                args: ['blur']
            }], _onClick: [{
                type: HostListener,
                args: ['click', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.enter', ['$event']]
            }], _onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class OverlayPlacementService {
    /** Updates the position of the current menu. */
    updatePosition(overlayRef, placement, alignment, customFallbackPlacement) {
        const position = overlayRef.getConfig().positionStrategy;
        const origin = this.getOrigin(placement, alignment);
        const overlay = this.getOverlayPosition(placement, alignment);
        position.withPositions(this.addPositions(origin, overlay, customFallbackPlacement));
    }
    /** Apply position to position strategy */
    addPositions(origin, overlay, customFallbackPlacement) {
        if (customFallbackPlacement) {
            return [
                { ...origin.main, ...overlay.main },
                this.getFallbackPosition(customFallbackPlacement),
            ];
        }
        else {
            return [
                { ...origin.main, ...overlay.main },
                { ...origin.fallback, ...overlay.fallback },
                {
                    ...{
                        originX: origin.main.originX,
                        originY: this.invertHorizontalPosition(origin.main.originY),
                    },
                    ...{
                        overlayX: overlay.main.overlayX,
                        overlayY: this.invertHorizontalPosition(overlay.main.overlayY),
                    },
                },
                {
                    ...{
                        originX: origin.fallback.originX,
                        originY: this.invertHorizontalPosition(origin.fallback.originY),
                    },
                    ...{
                        overlayX: overlay.fallback.overlayX,
                        overlayY: this.invertHorizontalPosition(overlay.fallback.overlayY),
                    },
                },
            ];
        }
    }
    /** Get the origin position based on the specified tooltip placement */
    getOrigin(initialPlacement, alignment) {
        // ensure placement is defined
        const placement = initialPlacement || 'bottom';
        let originPosition;
        if (placement === 'top' || placement === 'bottom') {
            originPosition = { originX: alignment, originY: placement };
        }
        if (placement === 'left') {
            originPosition = { originX: 'start', originY: this.getVerticalAlignment(alignment) };
        }
        if (placement === 'right') {
            originPosition = { originX: 'end', originY: this.getVerticalAlignment(alignment) };
        }
        const { x, y } = this.invertPosition(placement, originPosition.originX, originPosition.originY);
        return {
            main: originPosition,
            fallback: { originX: x, originY: y },
        };
    }
    /** Calculate the overlay position based on the specified tooltip placement */
    getOverlayPosition(initialPlacement, alignment) {
        // ensure placement is defined
        const placement = initialPlacement || 'top';
        let overlayPosition;
        if (placement === 'top') {
            overlayPosition = { overlayX: alignment, overlayY: 'bottom' };
        }
        if (placement === 'bottom') {
            overlayPosition = { overlayX: alignment, overlayY: 'top' };
        }
        if (placement === 'left') {
            overlayPosition = { overlayX: 'end', overlayY: this.getVerticalAlignment(alignment) };
        }
        if (placement === 'right') {
            overlayPosition = { overlayX: 'start', overlayY: this.getVerticalAlignment(alignment) };
        }
        const { x, y } = this.invertPosition(placement, overlayPosition.overlayX, overlayPosition.overlayY);
        return {
            main: overlayPosition,
            fallback: { overlayX: x, overlayY: y },
        };
    }
    /** Convert the alignment property to a valid CDK alignment value */
    getVerticalAlignment(alignment) {
        switch (alignment) {
            case 'start':
                return 'top';
            case 'end':
                return 'bottom';
            default:
                return alignment;
        }
    }
    /** Inverts an overlay position. */
    invertPosition(placement, x, y) {
        if (placement === 'top' || placement === 'bottom') {
            if (y === 'top') {
                y = 'bottom';
            }
            else if (y === 'bottom') {
                y = 'top';
            }
        }
        else {
            if (x === 'end') {
                x = 'start';
            }
            else if (x === 'start') {
                x = 'end';
            }
        }
        return { x, y };
    }
    invertHorizontalPosition(y) {
        return y === 'top' ? 'bottom' : 'top';
    }
    getFallbackPosition(fallbackPlacement) {
        switch (fallbackPlacement) {
            case 'left':
                return { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center' };
            case 'right':
                return { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center' };
            case 'top':
                return { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom' };
            case 'bottom':
                return { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top' };
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OverlayPlacementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OverlayPlacementService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OverlayPlacementService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class MenuTriggerDirective {
    constructor() {
        this._overlay = inject(Overlay);
        this._elementRef = inject(ElementRef);
        this._viewContainerRef = inject(ViewContainerRef);
        this._focusOrigin = inject(FocusIndicatorOriginService);
        this._focusIndicatorService = inject(FocusIndicatorService);
        this._overlayPlacement = inject(OverlayPlacementService);
        this._parentMenu = inject(MenuComponent, { optional: true });
        this._menuItem = inject(MenuItemComponent, { optional: true, self: true });
        /** Determine if we should disable the trigger */
        this.disabled = false;
        /** Emit when the menu is closed */
        this.closed = new EventEmitter();
        /** Automatically unsubscribe on directive destroy */
        this._onDestroy$ = new Subject();
        /** Reference to the menu should close when it loses focus */
        this._closeOnBlur = false;
        this._debounceTime = 50;
    }
    /** Determine if the menu should close when it loses focus */
    set closeOnBlur(value) {
        this._closeOnBlur = coerceBooleanProperty(value);
    }
    get closeOnBlur() {
        return this._closeOnBlur;
    }
    /** Get the aria controls for accessibility */
    get ariaControls() {
        return this.menu?.isMenuOpen ? this.menu?.innerId : null;
    }
    /** Determine if this triggers a submenu */
    get _isSubmenuTrigger() {
        return !!this._parentMenu;
    }
    /** Determine if this is the root trigger */
    get _isRootTrigger() {
        return !this._isSubmenuTrigger;
    }
    /** If this is a submenu we want to know when the mouse leaves the items or parent item */
    get _menuShouldClose() {
        if (!this._isSubmenuTrigger) {
            return of();
        }
        // This combined observable will essentially check for all of the combinations of events that can cause a menu
        // to remain open, for example:
        //
        // 1. Hovering over any item in the menu should keep the menu open
        // 2. Having any item in the menu focused should keep the menu open
        // 3. Having the parent menu item hovered should keep a submenu open
        // 4. Having the parent menu item focused should keep a submenu open
        // 5. Having a submenu open should keep the parent open (if the submenu meets one of the above conditions)
        //
        // We also debounce this because there is often a delay between a blur and a focus event or moving the mouse
        // from a menu item to a sub menu item, so we add this buffer time to prevent the menu from closing unexpectedly
        return combineLatest([
            this.menu._isHovering$,
            this.menu._isFocused$,
            this._menuItem.isHovered$,
            this.menu._isExpanded,
            this._menuItem.isFocused$,
        ]).pipe(debounceTime(50), filter(([isHovered, isFocused, isItemHovered, isExpanded, isItemFocused]) => !isHovered && !isFocused && !isItemHovered && !isExpanded && !isItemFocused));
    }
    ngOnInit() {
        // set up focus indicator handling
        this._focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement);
        // if there is a parent menu then we should override the default initial
        // position to be to the right rather than beneath. Note this gets called
        // before ngOnInit in the MenuComponent so if the user specifies an explicit
        // position then it will still take precendence
        if (this._isSubmenuTrigger) {
            this.menu._isSubMenu = true;
            this.menu.placement = this.getSubMenuPlacement(this.menu.placement);
        }
        // listen for the menu to open (after animation so we can focus the first item)
        this.menu.opened.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.menuDidOpen());
        // propagate the close event if it is triggered
        this.menu._closeAll$.pipe(takeUntil(this._onDestroy$)).subscribe(origin => {
            if (origin === 'tabout' && this._isRootTrigger) {
                this.closeMenu('keyboard', true, true, true);
            }
            else {
                this.closeMenu(origin, true);
            }
        });
        // handle keyboard events in the menu
        this.menu._onKeydown$
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(event => this.onMenuKeydown(event));
        combineLatest([this.menu._placement$, this.menu._alignment$])
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(() => {
            if (this._isSubmenuTrigger) {
                this.menu.placement = this.getSubMenuPlacement(this.menu.placement);
            }
            this.getOverlay(true);
        });
    }
    ngOnDestroy() {
        this.destroyMenu();
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    /** Focus the next focusable element */
    focusNextElement() {
        // add elements we want to include in our selection
        const focusableElements = 'a:not([disabled]), button:not([disabled]), input:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
        if (document.activeElement) {
            const focusable = document.querySelectorAll(focusableElements);
            const suitableElements = [];
            focusable.forEach(element => {
                if ((element.offsetWidth > 0 ||
                    element.offsetHeight > 0 ||
                    element === document.activeElement) &&
                    !element.classList.contains('cdk-visually-hidden')) {
                    suitableElements.push(element);
                }
            });
            const index = suitableElements.indexOf(document.activeElement);
            if (index > -1) {
                const nextElement = suitableElements[index + 1] || focusable[0];
                nextElement.focus();
            }
        }
    }
    /** Open the menu */
    openMenu() {
        // if the menu is already open then do nothing
        if (this.menu.isMenuOpen || this.disabled) {
            return;
        }
        this.menu._closeOnBlur = this.closeOnBlur;
        // get or create an overlayRef
        const overlayRef = this.getOverlay();
        const portal = this.getPortal();
        // if the overlay is already attached do nothing
        if (overlayRef.hasAttached()) {
            return;
        }
        // attach the menu to the DOM
        overlayRef.attach(portal);
        // mark the menu as open
        this.menu._setMenuOpen(true);
        if (this._menuItem) {
            // timer is needed because isExpanded$ will get set to false
            // prematurely due to the debounceTime on the menuShouldClose.
            timer(this._debounceTime)
                .pipe(takeUntil(this._onDestroy$))
                .subscribe(() => this._menuItem.isExpanded$.next(true));
        }
        // listen for a menu item to be selected
        this.menu._menuItemClick
            .pipe(take(1), takeUntil(this._onDestroy$))
            .subscribe(origin => this.closeMenu(origin, true));
        // subscribe to any close events
        this.didMenuClose()
            .pipe(take(1), takeUntil(this._onDestroy$))
            .subscribe(() => this.closeMenu());
        // listen for the menu to animate closed then destroy it, if submenu wait for it to start closing to destroy.
        if (this._isSubmenuTrigger) {
            this.menu.closing
                .pipe(take(1), takeUntil(this._onDestroy$))
                .subscribe(() => this.destroyMenu());
        }
        else {
            this.menu.closed
                .pipe(take(1), takeUntil(this._onDestroy$))
                .subscribe(() => this.destroyMenu());
        }
    }
    /** Close a menu or submenu */
    closeMenu(origin, closeParents = false, focusTrigger = true, focusNextElement = false) {
        if (!this._overlayRef.hasAttached()) {
            return;
        }
        // update the menu state
        this.menu._setMenuOpen(false);
        if (this._menuItem) {
            this._menuItem.isExpanded$.next(false);
        }
        // if we should close parents then propagate the event
        if (closeParents && this._parentMenu) {
            this._parentMenu._closeAll$.next(origin);
        }
        // we should focus the trigger element if this is the root trigger unless otherwise specified
        if (this._isRootTrigger && focusTrigger) {
            this._focusIndicator.focus(origin);
        }
        if (focusNextElement) {
            this.focusNextElement();
        }
        this.closed.emit();
        return this.menu.closed;
    }
    /** Toggle the open state of a menu */
    toggleMenu(event) {
        // if this occurs on a submenu trigger then we can skip
        if (this._isSubmenuTrigger) {
            return;
        }
        if (!this.menu._isAnimating) {
            // determine the focus origin based on whether or not a keyboard was used
            const origin = event instanceof KeyboardEvent ? 'keyboard' : 'mouse';
            // set the correct focus origin - if triggered by an event then use the source otherwise it was programmatic
            this._focusOrigin.setOrigin(event ? origin : 'program');
            // toggle the menu open state
            this.menu.isMenuOpen ? this.closeMenu(origin, true) : this.openMenu();
        }
        // the enter key will trigger the click event and therefore set the wrong focus origin
        // so we nee to ensure this doesn't happen
        if (event) {
            event.preventDefault();
        }
    }
    /** Submenus should be opened by hovering on the menu item */
    _onMouseEnter() {
        if (this._isSubmenuTrigger && !this._parentMenu._isAnimating) {
            this.openMenu();
        }
    }
    _onMouseMove() {
        if (this._isSubmenuTrigger && !this._parentMenu._isAnimating) {
            setTimeout(() => {
                this.openMenu();
            }, this._debounceTime);
        }
    }
    /** Pressing the escape key should close all menus */
    _onEscape() {
        if (this.menu.isMenuOpen) {
            this.closeMenu();
            // refocus the root trigger and show the focus ring
            if (this._isRootTrigger) {
                this._focusIndicator.focus('keyboard');
            }
        }
    }
    /** Handle keyboard events for opening submenus */
    _onKeydown(event) {
        // arrow key in the correct direction should open the menu
        if ((this.menu.placement === 'right' && event.keyCode === RIGHT_ARROW) ||
            (this.menu.placement === 'left' && event.keyCode === LEFT_ARROW) ||
            (this.menu.placement === 'top' && event.keyCode === UP_ARROW) ||
            (this.menu.placement === 'bottom' && event.keyCode === DOWN_ARROW)) {
            this._focusOrigin.setOrigin('keyboard');
            // if the menu was opened by a click but we subsequently use the arrow keys focus the first item
            if (this.menu.isMenuOpen) {
                this.menu._keyManager.setFocusOrigin('keyboard').setFirstItemActive();
            }
            else {
                // otherwise open the menu
                this.openMenu();
            }
            // prevent the browser from scrolling
            event.preventDefault();
        }
    }
    /** Blurring the trigger should check if the menu has focus and close it if not */
    _onBlur() {
        if (this.closeOnBlur) {
            this.closeOnFocusout();
        }
    }
    /** Remove the menu from the DOM */
    destroyMenu() {
        // if the menu has been destroyed already then do nothing
        if (!this._overlayRef) {
            return;
        }
        // remove the overlay
        this._overlayRef.detach();
    }
    /** Create an overlay or return an existing instance */
    getOverlay(recreateOverlay = false) {
        // if we have already created the overlay then reuse it
        if (this._overlayRef && !recreateOverlay) {
            return this._overlayRef;
        }
        const strategy = this._overlay
            .position()
            .flexibleConnectedTo(this.parent ?? this._elementRef)
            .withFlexibleDimensions(false)
            .withPush(false)
            .withTransformOriginOn('.ux-menu');
        // otherwise create a new one
        this._overlayRef = this._overlay.create({
            hasBackdrop: !this._isSubmenuTrigger,
            backdropClass: 'cdk-overlay-transparent-backdrop',
            scrollStrategy: this._overlay.scrollStrategies.reposition({ scrollThrottle: 0 }),
            positionStrategy: strategy,
        });
        this._overlayPlacement.updatePosition(this._overlayRef, this.menu.placement, this.menu.alignment, undefined);
        return this._overlayRef;
    }
    /** Create a Template portal if one does not already exist (or the template has changed) */
    getPortal() {
        // if there is no portal or the templateRef has changed then create a new one
        if (!this._portal || this.menu.templateRef !== this._portal.templateRef) {
            this._portal = new TemplatePortal(this.menu.templateRef, this._viewContainerRef);
        }
        return this._portal;
    }
    /** Get an observable that emits on any of the triggers that close a menu */
    didMenuClose() {
        return merge(this._overlayRef.backdropClick(), this._parentMenu ? this._parentMenu.closing : of(), this._menuShouldClose);
    }
    /** When the menu opens we want to focus the first item in the list */
    menuDidOpen() {
        // if the keyboard is used we should always focus and show the indicator
        // regardless of it this is the root menu or not
        if (this._focusOrigin.getOrigin() === 'keyboard') {
            this.menu._keyManager.setFocusOrigin('keyboard').setFirstItemActive();
        }
    }
    /** Handle keypresses in submenus where we may want to close them */
    onMenuKeydown(event) {
        // if we are a submenu and the user presses an arrow key in the opposite
        // direction than it is positioned from its parents then we should close the menu
        if (this._parentMenu) {
            if ((this.menu.placement === 'right' && event.keyCode === LEFT_ARROW) ||
                (this.menu.placement === 'left' && event.keyCode === RIGHT_ARROW) ||
                (this.menu.placement === 'top' && event.keyCode === DOWN_ARROW) ||
                (this.menu.placement === 'bottom' && event.keyCode === UP_ARROW)) {
                this.closeMenu();
                // refocus the parent menu item
                this._menuItem.focus('keyboard');
            }
        }
    }
    /** Check whether the overlay has focus */
    hasFocus() {
        let check = false;
        document.querySelectorAll('.cdk-overlay-container .ux-menu').forEach(el => {
            if (el.contains(document.activeElement)) {
                check = true;
            }
        });
        return check;
    }
    /** Close the menu if there is no element focused */
    closeOnFocusout() {
        if (this.menu.isMenuOpen) {
            setTimeout(() => {
                if (!this.hasFocus()) {
                    this.closeMenu(undefined, true, false);
                }
            }, this._debounceTime);
        }
    }
    getSubMenuPlacement(placement) {
        return placement === 'left' ? 'left' : 'right';
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuTriggerDirective, isStandalone: false, selector: "[uxMenuTriggerFor]", inputs: { menu: ["uxMenuTriggerFor", "menu"], disabled: "disabled", parent: ["uxMenuParent", "parent"], closeOnBlur: "closeOnBlur" }, outputs: { closed: "closed" }, host: { listeners: { "click": "toggleMenu($event)", "keydown.enter": "toggleMenu($event)", "keydown.space": "toggleMenu($event)", "mouseenter": "_onMouseEnter()", "mousemove": "_onMouseMove()", "document:keydown.escape": "_onEscape()", "keydown": "_onKeydown($event)", "blur": "_onBlur()" }, properties: { "attr.disabled": "disabled ? true : null", "attr.aria-haspopup": "!!menu", "attr.aria-expanded": "menu?.isMenuOpen", "attr.aria-controls": "ariaControls" } }, queries: [{ propertyName: "menuTriggers", predicate: i0.forwardRef(() => MenuTriggerDirective) }], exportAs: ["ux-menu-trigger"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuTriggerDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuTriggerFor]',
                    exportAs: 'ux-menu-trigger',
                    host: {
                        '[attr.disabled]': 'disabled ? true : null',
                        '[attr.aria-haspopup]': '!!menu',
                        '[attr.aria-expanded]': 'menu?.isMenuOpen',
                        '[attr.aria-controls]': 'ariaControls',
                    },
                    standalone: false,
                }]
        }], propDecorators: { menu: [{
                type: Input,
                args: ['uxMenuTriggerFor']
            }], disabled: [{
                type: Input
            }], parent: [{
                type: Input,
                args: ['uxMenuParent']
            }], closeOnBlur: [{
                type: Input
            }], closed: [{
                type: Output
            }], menuTriggers: [{
                type: ContentChildren,
                args: [forwardRef(() => MenuTriggerDirective)]
            }], toggleMenu: [{
                type: HostListener,
                args: ['click', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.enter', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.space', ['$event']]
            }], _onMouseEnter: [{
                type: HostListener,
                args: ['mouseenter']
            }], _onMouseMove: [{
                type: HostListener,
                args: ['mousemove']
            }], _onEscape: [{
                type: HostListener,
                args: ['document:keydown.escape']
            }], _onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }], _onBlur: [{
                type: HostListener,
                args: ['blur']
            }] } });

class MenuModule {
    static forRoot(options) {
        return {
            ngModule: MenuModule,
            providers: [{ provide: MENU_OPTIONS_TOKEN, useValue: options }],
        };
    }
    /** Support options at a child module level (implementation is the same as `forRoot`) */
    static forChild(options) {
        return MenuModule.forRoot(options);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: MenuModule, declarations: [MenuComponent,
            MenuTriggerDirective,
            MenuItemComponent,
            MenuDividerComponent,
            MenuTabbableItemDirective,
            MenuInitialFocusDirective,
            MenuItemCustomControlDirective], imports: [A11yModule, AccessibilityModule, CommonModule, OverlayModule], exports: [MenuComponent,
            MenuTriggerDirective,
            MenuItemComponent,
            MenuDividerComponent,
            MenuTabbableItemDirective,
            MenuInitialFocusDirective,
            MenuItemCustomControlDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuModule, imports: [A11yModule, AccessibilityModule, CommonModule, OverlayModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        MenuComponent,
                        MenuTriggerDirective,
                        MenuItemComponent,
                        MenuDividerComponent,
                        MenuTabbableItemDirective,
                        MenuInitialFocusDirective,
                        MenuItemCustomControlDirective,
                    ],
                    imports: [A11yModule, AccessibilityModule, CommonModule, OverlayModule],
                    exports: [
                        MenuComponent,
                        MenuTriggerDirective,
                        MenuItemComponent,
                        MenuDividerComponent,
                        MenuTabbableItemDirective,
                        MenuInitialFocusDirective,
                        MenuItemCustomControlDirective,
                    ],
                }]
        }] });

const CHECKBOX_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CheckboxComponent),
    multi: true,
};
let uniqueCheckboxId = 0;
class CheckboxComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Provide a default unique id value for the checkbox */
        this._checkboxId = `ux-checkbox-${++uniqueCheckboxId}`;
        /** Determines if the checkbox should be checked, unchecked or indeterminate. */
        this.id = this._checkboxId;
        /** Determines if the checkbox should be checked, unchecked or indeterminate. */
        this.value = false;
        /** Specifies the tabindex of the input. */
        this.tabindex = 0;
        /** If set to `true` the checkbox will not toggle state when clicked. */
        this.clickable = true;
        /** If set to `true` the checkbox will be displayed without a border and background. */
        this.simplified = false;
        /**
         * If `value` is set to the indeterminate value specified using this attribute, it will neither
         * display the checkbox as checked or unchecked, and will instead show the indeterminate variation.
         */
        this.indeterminateValue = -1;
        /** Specify if the checkbox should be disabled. */
        this.disabled = false;
        /** Provide an aria label for the checkbox. */
        this.ariaLabel = '';
        /** Provide an aria-labelled by property for the checkbox. */
        this.ariaLabelledby = null;
        /** Emits when `value` has been changed. */
        this.valueChange = new EventEmitter();
        /** Determine if the underlying input component has been focused with the keyboard */
        this._focused = false;
        /** Used to inform Angular forms that the component has been touched */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        /** Used to inform Angular forms that the component value has changed */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
    }
    /** Toggle the current state of the checkbox */
    toggle() {
        if (this.disabled || !this.clickable) {
            return;
        }
        if (this.value === this.indeterminateValue) {
            this.value = true;
        }
        else {
            // toggle the checked state
            this.value = !this.value;
        }
        // emit the value
        this.valueChange.emit(this.value);
        // update the value if used within a form control
        this.onChangeCallback(this.value);
        // mark the component as touched
        this.onTouchedCallback();
    }
    // Functions required to update ngModel
    writeValue(value) {
        if (value !== this.value) {
            this.value = value;
            this._changeDetector.markForCheck();
        }
    }
    /** Allow Angular forms for provide us with a callback for when the input value changes */
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    /** Allow Angular forms for provide us with a callback for when the touched state changes */
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    /** Allow Angular forms to disable the component */
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    /** Focus the input element */
    focus(origin) {
        this._focusIndicator.focus(origin);
    }
    setInputTabIndex(tabindex) {
        this.tabindex = tabindex;
        this._changeDetector.markForCheck();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CheckboxComponent, isStandalone: false, selector: "ux-checkbox", inputs: { id: "id", name: "name", value: "value", required: "required", tabindex: "tabindex", clickable: "clickable", simplified: "simplified", indeterminateValue: "indeterminateValue", disabled: "disabled", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"] }, outputs: { valueChange: "valueChange" }, providers: [
            CHECKBOX_VALUE_ACCESSOR,
            {
                provide: FocusableItemToken,
                useExisting: CheckboxComponent,
            },
        ], viewQueries: [{ propertyName: "_focusIndicator", first: true, predicate: FocusIndicatorDirective, descendants: true }], ngImport: i0, template: "<label\n  [attr.for]=\"(id || _checkboxId) + '-input'\"\n  class=\"ux-checkbox\"\n  [class.ux-checkbox-checked]=\"value === true\"\n  [class.ux-checkbox-indeterminate]=\"value === indeterminateValue\"\n  [class.ux-checkbox-simplified]=\"simplified\"\n  [class.ux-checkbox-disabled]=\"disabled\"\n  [class.ux-checkbox-focused]=\"_focused\"\n>\n  <div class=\"ux-checkbox-container\">\n    <input\n      #input\n      type=\"checkbox\"\n      uxFocusIndicator\n      class=\"ux-checkbox-input\"\n      [id]=\"(id || _checkboxId) + '-input'\"\n      [required]=\"required\"\n      [checked]=\"value\"\n      [attr.value]=\"value\"\n      [disabled]=\"disabled\"\n      [attr.name]=\"name\"\n      [tabindex]=\"tabindex\"\n      [indeterminate]=\"value === indeterminateValue\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      [attr.aria-checked]=\"value === indeterminateValue ? 'mixed' : value\"\n      (indicator)=\"_focused = $event\"\n      (change)=\"$event.stopPropagation()\"\n      (click)=\"toggle()\"\n    />\n  </div>\n\n  <span class=\"ux-checkbox-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CheckboxComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-checkbox', providers: [
                        CHECKBOX_VALUE_ACCESSOR,
                        {
                            provide: FocusableItemToken,
                            useExisting: CheckboxComponent,
                        },
                    ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<label\n  [attr.for]=\"(id || _checkboxId) + '-input'\"\n  class=\"ux-checkbox\"\n  [class.ux-checkbox-checked]=\"value === true\"\n  [class.ux-checkbox-indeterminate]=\"value === indeterminateValue\"\n  [class.ux-checkbox-simplified]=\"simplified\"\n  [class.ux-checkbox-disabled]=\"disabled\"\n  [class.ux-checkbox-focused]=\"_focused\"\n>\n  <div class=\"ux-checkbox-container\">\n    <input\n      #input\n      type=\"checkbox\"\n      uxFocusIndicator\n      class=\"ux-checkbox-input\"\n      [id]=\"(id || _checkboxId) + '-input'\"\n      [required]=\"required\"\n      [checked]=\"value\"\n      [attr.value]=\"value\"\n      [disabled]=\"disabled\"\n      [attr.name]=\"name\"\n      [tabindex]=\"tabindex\"\n      [indeterminate]=\"value === indeterminateValue\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      [attr.aria-checked]=\"value === indeterminateValue ? 'mixed' : value\"\n      (indicator)=\"_focused = $event\"\n      (change)=\"$event.stopPropagation()\"\n      (click)=\"toggle()\"\n    />\n  </div>\n\n  <span class=\"ux-checkbox-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], name: [{
                type: Input
            }], value: [{
                type: Input
            }], required: [{
                type: Input
            }], tabindex: [{
                type: Input
            }], clickable: [{
                type: Input
            }], simplified: [{
                type: Input
            }], indeterminateValue: [{
                type: Input
            }], disabled: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], ariaLabelledby: [{
                type: Input,
                args: ['aria-labelledby']
            }], valueChange: [{
                type: Output
            }], _focusIndicator: [{
                type: ViewChild,
                args: [FocusIndicatorDirective]
            }] } });

class CheckboxModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CheckboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: CheckboxModule, declarations: [CheckboxComponent], imports: [AccessibilityModule, FormsModule], exports: [CheckboxComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CheckboxModule, imports: [AccessibilityModule, FormsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CheckboxModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, FormsModule],
                    exports: [CheckboxComponent],
                    declarations: [CheckboxComponent],
                }]
        }] });

/**
 * Type representing a color, including its descriptive name.
 */
class ColorPickerColor {
    /**
     * Hex value of the color, e.g. `#ffffff`.
     */
    get hex() {
        return this._originalHexValue ? this._originalHexValue : this._color.toHex();
    }
    /**
     * RGBA value of the color, e.g. `rgba(255, 255, 255, 1)`.
     */
    get rgba() {
        return this._originalRgbaValue ? this._originalRgbaValue : this._color.toRgba();
    }
    get r() {
        return parseInt(this._color.getRed());
    }
    get g() {
        return parseInt(this._color.getGreen());
    }
    get b() {
        return parseInt(this._color.getBlue());
    }
    get a() {
        return parseFloat(this._color.getAlpha());
    }
    constructor(name, value, inputMode) {
        this.name = name;
        this._color = ThemeColor.parse(value);
        // Preserve the format entered by the user if it's valid
        if (inputMode === 'hex') {
            this._originalHexValue = value;
        }
        else if (inputMode === 'rgba') {
            this._originalRgbaValue = value;
        }
    }
    toString() {
        return this._color.toRgba();
    }
}

let uniqueTooltipId = 0;
class TooltipComponent {
    constructor() {
        this._changeDetectorRef = inject(ChangeDetectorRef);
        /** Define a unique id for each tooltip */
        this.id = `ux-tooltip-${++uniqueTooltipId}`;
        /** Define the tooltip role */
        this.role = 'tooltip';
        /** Allow a custom class to be added to the tooltip to allow custom styling */
        this.customClass = '';
        /** Emit when the tooltip need to update it's position */
        this.reposition$ = new Subject();
        /** The name of the css class to use for the tooltip direction */
        this._positionClass = '';
    }
    /** Indicates whether or not the content is a string or a TemplateRef */
    get isTemplateRef() {
        return this.content instanceof TemplateRef;
    }
    get positionClass() {
        return this._positionClass;
    }
    set positionClass(positionClass) {
        this._positionClass = positionClass;
        this._changeDetectorRef.detectChanges();
    }
    /** Cleanup after the component is destroyed */
    ngOnDestroy() {
        this.reposition$.complete();
    }
    /** Inform the parent directive that it needs to recalulate the position */
    reposition() {
        this.reposition$.next();
    }
    /** This will update the content of the tooltip and trigger change detection */
    setContent(content) {
        this.content = content;
        this._changeDetectorRef.markForCheck();
    }
    /** This will update the tooltip placement and trigger change detection */
    setPlacement(placement) {
        if (!placement) {
            return;
        }
        this.placement = placement;
        this._changeDetectorRef.markForCheck();
    }
    /** This will update the tooltip alignment and trigger change detection */
    setAlignment(alignment) {
        if (!alignment) {
            return;
        }
        this.alignment = alignment;
        this._changeDetectorRef.markForCheck();
    }
    /** This will set a custom class on the tooltip and trigger change detection */
    setClass(customClass) {
        if (!customClass) {
            return;
        }
        this.customClass = customClass;
        this._changeDetectorRef.markForCheck();
    }
    /** Updates the context used by the TemplateRef */
    setContext(context) {
        if (!context) {
            return;
        }
        this.context = context;
        this._changeDetectorRef.markForCheck();
    }
    /** Specify the tooltip role attribute */
    setRole(role) {
        if (!role) {
            return;
        }
        this.role = role;
        this._changeDetectorRef.markForCheck();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TooltipComponent, isStandalone: false, selector: "ux-tooltip", inputs: { content: "content", context: "context", placement: "placement", alignment: "alignment" }, ngImport: i0, template: "<div class=\"tooltip in\" [id]=\"id\" [attr.role]=\"role\" [ngClass]=\"[positionClass, customClass]\">\n  <div class=\"tooltip-arrow\"></div>\n  <div class=\"tooltip-inner\" (cdkObserveContent)=\"reposition()\">\n    @if (!isTemplateRef) {\n      {{ content }}\n    }\n    @if (isTemplateRef) {\n      <ng-container\n        [ngTemplateOutlet]=\"$any(content)\"\n        [ngTemplateOutletContext]=\"context\"\n      ></ng-container>\n    }\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.CdkObserveContent, selector: "[cdkObserveContent]", inputs: ["cdkObserveContentDisabled", "debounce"], outputs: ["cdkObserveContent"], exportAs: ["cdkObserveContent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"tooltip in\" [id]=\"id\" [attr.role]=\"role\" [ngClass]=\"[positionClass, customClass]\">\n  <div class=\"tooltip-arrow\"></div>\n  <div class=\"tooltip-inner\" (cdkObserveContent)=\"reposition()\">\n    @if (!isTemplateRef) {\n      {{ content }}\n    }\n    @if (isTemplateRef) {\n      <ng-container\n        [ngTemplateOutlet]=\"$any(content)\"\n        [ngTemplateOutletContext]=\"context\"\n      ></ng-container>\n    }\n  </div>\n</div>\n" }]
        }], propDecorators: { content: [{
                type: Input
            }], context: [{
                type: Input
            }], placement: [{
                type: Input
            }], alignment: [{
                type: Input
            }] } });

class TooltipService {
    constructor() {
        this.shown$ = new Subject();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipService, decorators: [{
            type: Injectable
        }] });

class TooltipDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._viewContainerRef = inject(ViewContainerRef);
        this._overlay = inject(Overlay);
        this._scrollDispatcher = inject(ScrollDispatcher);
        this._changeDetectorRef = inject(ChangeDetectorRef);
        this._renderer = inject(Renderer2);
        this._tooltipService = inject(TooltipService);
        this._overlayFallback = inject(OverlayPlacementService);
        /** All the user to add a custom class to the tooltip */
        this.customClass = '';
        /** All the user to add a role to the tooltip - default is tooltip */
        this.role = 'tooltip';
        /** Provide the TemplateRef a context object */
        this.context = {};
        /** Delay the showing of the tooltip by a number of miliseconds */
        this.delay = 0;
        /** Programmatically show and hide the tooltip */
        this.isOpen = false;
        /** Customize how the tooltip should be positioned relative to the element */
        this.placement = 'top';
        /** Customize the position of the callout */
        this.alignment = 'center';
        /** Specify which events should show the tooltip */
        this.showTriggers = ['mouseenter', 'focus'];
        /** Specify which events should hide the tooltip */
        this.hideTriggers = ['mouseleave', 'blur'];
        /** Emits an event when the tooltip is shown */
        this.shown = new EventEmitter();
        /** Emits a event when the tooltip is hidden */
        this.hidden = new EventEmitter();
        /** Allow two way binding to track the visibility of the tooltip */
        this.isOpenChange = new EventEmitter();
        /** Keep track of the tooltip visibility */
        this.isVisible = false;
        /** The name of the css class to use for the tooltip direction */
        this.positionClass = this.placement;
        /** Define the overlay class */
        this._overlayClass = 'ux-tooltip-pane';
        /** This will emit when the directive is destroyed allowing us to unsubscribe all subscriptions automatically */
        this._onDestroy = new Subject();
        /** Store the timeout interval for cancelation */
        this._showTimeoutId = null;
        /** Internally store the type of this component - usual for distinctions when extending this class */
        this._type = 'tooltip';
    }
    /** Set up the triggers and bind to the show/hide events to keep visibility in sync */
    ngOnInit() {
        // set up show and hide event triggers
        fromEvent(this._elementRef.nativeElement, 'click')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onClick.bind(this));
        fromEvent(this._elementRef.nativeElement, 'mouseenter')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onMouseEnter.bind(this));
        fromEvent(this._elementRef.nativeElement, 'mouseleave')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onMouseLeave.bind(this));
        fromEvent(this._elementRef.nativeElement, 'focus')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onFocus.bind(this));
        fromEvent(this._elementRef.nativeElement, 'blur')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onBlur.bind(this));
        // when any other tooltips open hide this one
        this._tooltipService.shown$
            .pipe(filter(() => this._type === 'tooltip'), filter(tooltip => tooltip !== this._instance), takeUntil(this._onDestroy))
            .subscribe(this.hide.bind(this));
        // if the tooltip should be initially visible then open it
        if (this.isOpen) {
            this.show();
        }
    }
    /**
     * We need to send input changes to the tooltip component
     * We can't use setters as they may trigger before tooltip initialised and can't resend once initialised
     **/
    ngOnChanges(changes) {
        // we can ignore the first change as it's handled in ngOnInit
        if (changes.isOpen &&
            !changes.isOpen.firstChange &&
            changes.isOpen.currentValue !== this.isVisible) {
            changes.isOpen.currentValue ? this.show() : this.hide();
        }
        // destroy the overlay ref so a new correctly positioned instance will be created next time
        if (changes.placement) {
            this.destroyOverlay();
        }
        if (this._instance && changes.placement) {
            this._instance.setPlacement(changes.placement.currentValue);
        }
        if (this._instance && changes.alignment) {
            this._instance.setAlignment(changes.alignment.currentValue);
        }
        if (this._instance && changes.content) {
            this._instance.setContent(changes.content.currentValue);
        }
        if (this._instance && changes.customClass) {
            this._instance.setClass(changes.customClass.currentValue);
        }
        if (this._instance && changes.context) {
            this._instance.setContext(changes.context.currentValue);
        }
        if (this._instance && changes.role) {
            this._instance.setContext(changes.role.currentValue);
        }
    }
    /** Ensure we clean up after ourselves */
    ngOnDestroy() {
        // ensure we close the tooltip when the host is destroyed
        if (this._overlayRef) {
            this._overlayRef.dispose();
            this._instance = null;
        }
        // clear any pending timeouts
        this.cancelTooltip();
        // emit this event to automatically unsubscribe from all subscriptions
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Make the tooltip open */
    show() {
        // if the tooltip is disabled then do nothing
        if (this.disabled || this.isVisible || this._showTimeoutId || !this.content) {
            return;
        }
        // delay the show by the delay amount
        this._showTimeoutId = window.setTimeout(() => {
            // create the tooltip and get the overlay ref
            const overlayRef = this.createOverlay();
            // create the portal to create the tooltip component
            this._portal = this.createPortal();
            this._instance = this.createInstance(overlayRef);
            // watch for any changes to the content
            this._instance.reposition$
                .pipe(takeUntil(this._onDestroy))
                .subscribe(this.reposition.bind(this));
            // store the visible state
            this.isVisible = true;
            // ensure the overlay has the correct initial position
            this.reposition();
            // emit the show events
            this.shown.emit();
            this.isOpenChange.next(true);
            // clear the interval id
            this._showTimeoutId = null;
            // emit the show event to close any other tooltips
            this._tooltipService.shown$.next(this._instance);
            // ensure change detection is run
            this._changeDetectorRef.detectChanges();
        }, this.delay);
    }
    /** If a tooltip exists and is visible, hide it */
    hide() {
        // if we are waiting to show a tooltip then cancel the pending timeout
        if (this._showTimeoutId) {
            window.clearTimeout(this._showTimeoutId);
            this._showTimeoutId = null;
            return;
        }
        // if the tooltip is hidden then do nothing
        if (!this.isVisible) {
            return;
        }
        if (this._overlayRef && this._overlayRef.hasAttached()) {
            this._overlayRef.detach();
        }
        this.setAriaDescribedBy(null);
        this._instance = null;
        // store the visible state
        this.isVisible = false;
        // emit the hide events
        this.hidden.emit();
        this.isOpenChange.next(false);
        // ensure change detection is run
        this._changeDetectorRef.detectChanges();
    }
    /** Toggle the visibility of the tooltip */
    toggle() {
        this.isVisible ? this.hide() : this.show();
    }
    /** Recalculate the position of the popover */
    reposition() {
        if (this.isVisible && this._overlayRef) {
            this._overlayRef.updatePosition();
        }
    }
    /** Create an instance from the overlay ref - allows overriding and additional logic here */
    createInstance(overlayRef) {
        const instance = overlayRef.attach(this._portal).instance;
        // supply the tooltip with the correct properties
        instance.setContent(this.content);
        instance.setPlacement(this.placement);
        instance.setAlignment(this.alignment);
        instance.setClass(this.customClass);
        instance.setContext(this.context);
        instance.setRole(this.role);
        // Update the aria-describedby attribute
        this.setAriaDescribedBy(instance.id);
        return instance;
    }
    /** Create the component portal - allows overriding to allow other portals eg. popovers */
    createPortal() {
        return this._portal || new ComponentPortal(TooltipComponent, this._viewContainerRef);
    }
    /** Create the overlay and set up the scroll handling behavior */
    createOverlay() {
        // if the tooltip has already been created then just return the existing instance
        if (this._overlayRef) {
            return this._overlayRef;
        }
        const strategy = this._overlay
            .position()
            .flexibleConnectedTo(this._elementRef)
            .withFlexibleDimensions(false)
            .withPush(false);
        this._overlayRef = this._overlay.create({
            positionStrategy: strategy,
            panelClass: this._overlayClass,
            scrollStrategy: this._overlay.scrollStrategies.reposition({ scrollThrottle: 0 }),
            hasBackdrop: false,
        });
        this._overlayFallback.updatePosition(this._overlayRef, this.placement, this.alignment, this.fallbackPlacement);
        strategy.positionChanges.subscribe(positionChange => {
            const currentPosition = positionChange.connectionPair;
            this.getPositionClass(currentPosition);
        });
        return this._overlayRef;
    }
    /** Recreate the overlay ref using the updated origin and overlay positions */
    destroyOverlay() {
        // destroy the existing overlay
        if (this._overlayRef && this._overlayRef.hasAttached()) {
            this._overlayRef.detach();
        }
        if (this._overlayRef) {
            this._overlayRef.dispose();
            this._overlayRef = null;
        }
        this.isVisible = false;
    }
    getPositionClass(currentPosition) {
        let positionClass = this.placement;
        if (currentPosition.originX === 'center') {
            positionClass = currentPosition.originY === 'top' ? 'top' : 'bottom';
        }
        else if (currentPosition.originY === 'center') {
            positionClass = currentPosition.originX === 'start' ? 'left' : 'right';
        }
        this._instance.positionClass = positionClass;
        this._changeDetectorRef.detectChanges();
    }
    /**
     * Simple utility method - because IE doesn't support array.includes
     * And it isn't included in the core-js/es6 polyfills which are the
     * only ones required by Angular and guaranteed to be there
     **/
    includes(array, value) {
        return Array.isArray(array) && !!array.find(item => item === value);
    }
    /** Handle the click event - show or hide accordingly */
    onClick() {
        // if its not visible and click is a show trigger open it
        if (!this.isVisible &&
            this.includes(this.showTriggers, 'click') &&
            this._showTimeoutId === null) {
            return this.show();
        }
        // if its visible and click is a hide trigger close it
        if (this.isVisible &&
            this.includes(this.hideTriggers, 'click') &&
            this._showTimeoutId === null) {
            return this.hide();
        }
        // if its not visible and click is a hide trigger close it and there is a pending tooltip
        if (!this.isVisible &&
            this.includes(this.hideTriggers, 'click') &&
            this._showTimeoutId !== null) {
            return this.cancelTooltip();
        }
    }
    /** Handle the mouse enter event - show or hide accordingly */
    onMouseEnter() {
        // this is an show only trigger - if already open or it isn't a trigger do nothing
        if (this.isVisible || !this.includes(this.showTriggers, 'mouseenter')) {
            return;
        }
        // otherwise open the tooltip
        this.show();
    }
    /** Handle the mouse leave event - show or hide accordingly */
    onMouseLeave() {
        // If the tooltip is pending then cancel showing it
        if (!this.isVisible &&
            this.includes(this.hideTriggers, 'mouseleave') &&
            this._showTimeoutId !== null) {
            return this.cancelTooltip();
        }
        // if the tooltip is not visible or mouseleave isn't a hide trigger then do nothing
        if (!this.isVisible || !this.includes(this.hideTriggers, 'mouseleave')) {
            return;
        }
        // otherwise close the tooltip
        this.hide();
    }
    /** Handle the focus event - show or hide accordingly */
    onFocus() {
        // this is an show only trigger - if already open or it isn't a trigger do nothing
        if (this.isVisible || !this.includes(this.showTriggers, 'focus')) {
            return;
        }
        // otherwise open the tooltip
        this.show();
    }
    /** Handle the blur event - show or hide accordingly */
    onBlur() {
        // If the tooltip is pending then cancel showing it
        if (!this.isVisible &&
            this.includes(this.hideTriggers, 'blur') &&
            this._showTimeoutId !== null) {
            return this.cancelTooltip();
        }
        // this is an hide only trigger - if not open or it isn't a trigger do nothing
        if (!this.isVisible || !this.includes(this.hideTriggers, 'blur')) {
            return;
        }
        // otherwise close the tooltip
        this.hide();
    }
    /** Programmatically update the aria-describedby property */
    setAriaDescribedBy(id) {
        if (id === null) {
            this._renderer.removeAttribute(this._elementRef.nativeElement, 'aria-describedby');
        }
        else {
            this._renderer.setAttribute(this._elementRef.nativeElement, 'aria-describedby', id);
        }
    }
    /** Cancel any pending tooltip (waiting on delay ellapsing) */
    cancelTooltip() {
        if (this._showTimeoutId !== null) {
            window.clearTimeout(this._showTimeoutId);
            this._showTimeoutId = null;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TooltipDirective, isStandalone: false, selector: "[uxTooltip]", inputs: { content: ["uxTooltip", "content"], disabled: ["tooltipDisabled", "disabled"], customClass: ["tooltipClass", "customClass"], role: ["tooltipRole", "role"], context: ["tooltipContext", "context"], delay: ["tooltipDelay", "delay"], isOpen: "isOpen", placement: "placement", fallbackPlacement: "fallbackPlacement", alignment: "alignment", showTriggers: "showTriggers", hideTriggers: "hideTriggers" }, outputs: { shown: "shown", hidden: "hidden", isOpenChange: "isOpenChange" }, exportAs: ["ux-tooltip"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTooltip]',
                    exportAs: 'ux-tooltip',
                    standalone: false,
                }]
        }], propDecorators: { content: [{
                type: Input,
                args: ['uxTooltip']
            }], disabled: [{
                type: Input,
                args: ['tooltipDisabled']
            }], customClass: [{
                type: Input,
                args: ['tooltipClass']
            }], role: [{
                type: Input,
                args: ['tooltipRole']
            }], context: [{
                type: Input,
                args: ['tooltipContext']
            }], delay: [{
                type: Input,
                args: ['tooltipDelay']
            }], isOpen: [{
                type: Input
            }], placement: [{
                type: Input
            }], fallbackPlacement: [{
                type: Input
            }], alignment: [{
                type: Input
            }], showTriggers: [{
                type: Input
            }], hideTriggers: [{
                type: Input
            }], shown: [{
                type: Output
            }], hidden: [{
                type: Output
            }], isOpenChange: [{
                type: Output
            }] } });

// Values corresponding to stylesheet
const BUTTON_MARGIN = 8;
const BUTTON_WIDTHS = {
    sm: 26,
    md: 32,
    lg: 40,
};
let uniqueId$c = 0;
class ColorPickerComponent {
    constructor() {
        this.id = `ux-color-picker-${uniqueId$c++}`;
        /** The style of the color swatch buttons. */
        this.buttonStyle = 'circle';
        /** Whether to show tooltips above the color swatch buttons. These contain the color name if provided; otherwise the color hex/RGBA value. */
        this.showTooltips = false;
        /** Whether to show the hex/RGBA input panel. */
        this.showInput = false;
        /** The default input mode to display in the input panel. The user can switch modes using the toggle button. */
        this.inputMode = 'hex';
        /** Defines a function that returns an aria-label for ColorPickerColor. */
        this.colorAriaLabel = this.getColorAriaLabel;
        /** Defines a function that returns an aria-label for the button that switches input modes. */
        this.switchModeAriaLabel = this.getSwitchModeAriaLabel;
        /** Define a function that returns an aria-label for the input control. */
        this.inputAriaLabel = this.getInputAriaLabel;
        /** Emitted when the user changes the selected color, either by clicking a color swatch button, or entering a valid color value into the input panel text field. */
        this.selectedChange = new EventEmitter();
        /** Emitted when the user changes the colour input mode */
        this.inputModeChange = new EventEmitter();
        /** Emitted when the user presses enter in the input panel text field. This can be used to commit a color change and/or close a popup. */
        this.inputSubmit = new EventEmitter();
        this.cssWidth = 'auto';
        this.colors = [];
        this.selected$ = new BehaviorSubject(null);
        this.columns$ = new BehaviorSubject(-1);
        this.buttonSize$ = new BehaviorSubject('md');
        this.inputPatterns = {
            hex: /^#(?:[\da-fA-F]{3}){1,2}$/,
            rgba: /^(?:rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\))|(?:rgba\(\d{1,3},\s*\d{1,3},\s*\d{1,3},\s*\d(\.\d+)?\))$/,
        };
        this._onDestroy = new Subject();
    }
    /**
     * The collection of colors to display in the color swatch.
     *
     * Colors can be specified either as a string, which is the hex or RGBA value of the color; or as a `ColorPickerColor` object,
     * which allows a name to be associated with the color. See below for details of the `ColorPickerColor` class.
     * This property is either a one-dimensional or two-dimensional array. If a two-dimensional array is provided,
     * the colors will be split into rows, providing more control over the appearance of the swatch.
     */
    set inputColors(colors) {
        let normalizedColors;
        // If it's a 1d array, convert it to 2d
        if (colors.length === 0 || !Array.isArray(colors[0])) {
            normalizedColors = [colors];
        }
        else {
            normalizedColors = colors;
        }
        // Convert any string colors to ColorPickerColor
        this.colors = normalizedColors.map(row => {
            return row.map(color => color instanceof ColorPickerColor ? color : new ColorPickerColor(color, color));
        });
    }
    /**
     * The currently selected color. If this is one of the `colors` in the colors collection, it will be visually
     * highlighted in the swatch. It will also be shown in the input panel, if enabled (see showInput).
     * Note that this will always be a `ColorPickerColor` object, even if plain strings are provided to the colors property.
     * See below for details of the `ColorPickerColor` class.
     */
    set selected(selected) {
        this.selected$.next(selected);
    }
    /**
     * The number of columns to display in the color swatch. Set this to -1 if the width should be specified by a stylesheet
     * instead, e.g. to provide a responsive layout.
     */
    set columns(columns) {
        this.columns$.next(columns);
    }
    /** The size of the color swatch buttons. Three size variants are currently supported. */
    set buttonSize(buttonSize) {
        this.buttonSize$.next(buttonSize);
    }
    ngOnInit() {
        // Skip emitting the initial selectedChange
        this.selected$.pipe(pairwise(), takeUntil(this._onDestroy)).subscribe(([prev, curr]) => {
            if (prev) {
                this.selectedChange.emit(curr);
            }
        });
        // Set the width based on column count and button size
        combineLatest([this.columns$, this.buttonSize$])
            .pipe(takeUntil(this._onDestroy))
            .subscribe(([columns, buttonSize]) => {
            if (columns > 0) {
                const w = columns * (BUTTON_WIDTHS[buttonSize] + 2 * BUTTON_MARGIN);
                this.cssWidth = `${w}px`;
            }
            else {
                this.cssWidth = 'auto';
            }
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onFocus() {
        // Forward focus to the color grid
        this.tabbableList.focusTabbableItem();
    }
    updateColorValue(input, mode) {
        if (this.inputPatterns[mode].test(input)) {
            this.selected$.next(new ColorPickerColor('Custom', input, mode));
        }
    }
    toggleColorEntryType() {
        // update the input mode
        this.inputMode = this.inputMode === 'hex' ? 'rgba' : 'hex';
        // emit the new input mode
        this.inputModeChange.emit(this.inputMode);
        // get the current color value if there is one
        const color = this.selected$.value;
        // if there is no selected color property then skip
        if (color) {
            // get the new color value
            const newColor = this.inputMode === 'hex' ? color.hex : color.rgba;
            // update the selected color value based on the input mode
            this.updateColorValue(newColor, this.inputMode);
            // forcibly update the validation status of ngModel to prevent any
            // incorrect error states in the underlying form control
            // (running change detection will not suffice)
            this.inputFormControl.control.setValue(newColor);
        }
    }
    getColorAriaLabel(color) {
        return `Select color ${color.name}`;
    }
    getSwitchModeAriaLabel(mode) {
        return `Switch input mode to ${mode === 'hex' ? 'RGBA' : 'hex'}`;
    }
    getInputAriaLabel(mode) {
        return `Edit ${mode} color value`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ColorPickerComponent, isStandalone: false, selector: "ux-color-picker", inputs: { id: "id", inputColors: ["colors", "inputColors"], selected: "selected", columns: "columns", buttonStyle: "buttonStyle", buttonSize: "buttonSize", showTooltips: "showTooltips", showInput: "showInput", inputMode: "inputMode", colorAriaLabel: "colorAriaLabel", switchModeAriaLabel: "switchModeAriaLabel", inputAriaLabel: "inputAriaLabel" }, outputs: { selectedChange: "selectedChange", inputModeChange: "inputModeChange", inputSubmit: "inputSubmit" }, host: { attributes: { "tabindex": "0" }, listeners: { "focus": "onFocus($event)" }, properties: { "attr.id": "this.id", "style.width": "this.cssWidth" } }, viewQueries: [{ propertyName: "inputFormControl", first: true, predicate: ["inputField"], descendants: true }, { propertyName: "tabbableList", first: true, predicate: TabbableListDirective, descendants: true }], exportAs: ["ux-color-picker"], ngImport: i0, template: "<div class=\"ux-color-picker\" uxTabbableList direction=\"horizontal\">\n  <div class=\"ux-color-picker-swatch\">\n    @for (row of colors; track row) {\n      <div class=\"ux-color-picker-swatch-row\">\n        @for (color of row; track color) {\n          <div\n            #colorPickerColor\n            class=\"ux-color-picker-color\"\n            [class.ux-small]=\"(buttonSize$ | async) === 'sm'\"\n            [class.ux-large]=\"(buttonSize$ | async) === 'lg'\"\n            [class.ux-circle]=\"buttonStyle === 'circle'\"\n            [class.ux-selected]=\"color === (selected$ | async)\"\n          >\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              [attr.aria-label]=\"colorAriaLabel(color)\"\n              [attr.aria-selected]=\"color === (selected$ | async)\"\n              class=\"btn btn-icon\"\n              [uxColorContrast]=\"color.hex\"\n              [style.background-color]=\"color.rgba\"\n              (click)=\"selected$.next(color)\"\n              uxTabbableListItem\n              [uxTooltip]=\"color.name\"\n              [tooltipDisabled]=\"!showTooltips\"\n            >\n              <ux-icon name=\"checkmark\"></ux-icon>\n            </button>\n          </div>\n        }\n      </div>\n    }\n  </div>\n\n  @if (showInput) {\n    <div class=\"ux-color-picker-input-panel\">\n      <div class=\"ux-color-picker-input-header\">\n        <div\n          class=\"ux-color-picker-preview\"\n          [style.background-color]=\"(selected$ | async).rgba\"\n          [class.ux-circle]=\"buttonStyle === 'circle'\"\n        ></div>\n        @if (inputMode === 'hex') {\n          <label attr.for=\"{{ id }}-input-field\">HEX</label>\n        }\n        @if (inputMode === 'rgba') {\n          <label attr.for=\"{{ id }}-input-field\">RGBA</label>\n        }\n        <button\n          type=\"button\"\n          [attr.aria-label]=\"switchModeAriaLabel(inputMode)\"\n          class=\"btn btn-link btn-icon button-secondary ux-color-picker-input-toggle\"\n          (click)=\"toggleColorEntryType(); $event.stopPropagation()\"\n        >\n          <ux-icon name=\"chevron-right\"></ux-icon>\n        </button>\n      </div>\n      <div\n        class=\"ux-color-picker-input\"\n        [class.has-error]=\"inputField.errors\"\n        [class.has-feedback]=\"inputField.errors\"\n      >\n        <input\n          type=\"text\"\n          attr.id=\"{{ id }}-input-field\"\n          [attr.aria-description]=\"inputAriaLabel(inputMode)\"\n          class=\"form-control\"\n          #inputField=\"ngModel\"\n          [ngModel]=\"(selected$ | async)[inputMode]\"\n          (ngModelChange)=\"updateColorValue($event, inputMode)\"\n          [pattern]=\"inputPatterns[inputMode].source\"\n          (keyup.enter)=\"inputSubmit.emit()\"\n        />\n        <ux-icon class=\"form-control-feedback\" name=\"alert\"></ux-icon>\n      </div>\n    </div>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: ColorContrastDirective, selector: "[uxColorContrast]", inputs: ["uxColorContrast", "lightColor", "darkColor"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-color-picker', exportAs: 'ux-color-picker', host: {
                        tabindex: '0',
                    }, standalone: false, template: "<div class=\"ux-color-picker\" uxTabbableList direction=\"horizontal\">\n  <div class=\"ux-color-picker-swatch\">\n    @for (row of colors; track row) {\n      <div class=\"ux-color-picker-swatch-row\">\n        @for (color of row; track color) {\n          <div\n            #colorPickerColor\n            class=\"ux-color-picker-color\"\n            [class.ux-small]=\"(buttonSize$ | async) === 'sm'\"\n            [class.ux-large]=\"(buttonSize$ | async) === 'lg'\"\n            [class.ux-circle]=\"buttonStyle === 'circle'\"\n            [class.ux-selected]=\"color === (selected$ | async)\"\n          >\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              [attr.aria-label]=\"colorAriaLabel(color)\"\n              [attr.aria-selected]=\"color === (selected$ | async)\"\n              class=\"btn btn-icon\"\n              [uxColorContrast]=\"color.hex\"\n              [style.background-color]=\"color.rgba\"\n              (click)=\"selected$.next(color)\"\n              uxTabbableListItem\n              [uxTooltip]=\"color.name\"\n              [tooltipDisabled]=\"!showTooltips\"\n            >\n              <ux-icon name=\"checkmark\"></ux-icon>\n            </button>\n          </div>\n        }\n      </div>\n    }\n  </div>\n\n  @if (showInput) {\n    <div class=\"ux-color-picker-input-panel\">\n      <div class=\"ux-color-picker-input-header\">\n        <div\n          class=\"ux-color-picker-preview\"\n          [style.background-color]=\"(selected$ | async).rgba\"\n          [class.ux-circle]=\"buttonStyle === 'circle'\"\n        ></div>\n        @if (inputMode === 'hex') {\n          <label attr.for=\"{{ id }}-input-field\">HEX</label>\n        }\n        @if (inputMode === 'rgba') {\n          <label attr.for=\"{{ id }}-input-field\">RGBA</label>\n        }\n        <button\n          type=\"button\"\n          [attr.aria-label]=\"switchModeAriaLabel(inputMode)\"\n          class=\"btn btn-link btn-icon button-secondary ux-color-picker-input-toggle\"\n          (click)=\"toggleColorEntryType(); $event.stopPropagation()\"\n        >\n          <ux-icon name=\"chevron-right\"></ux-icon>\n        </button>\n      </div>\n      <div\n        class=\"ux-color-picker-input\"\n        [class.has-error]=\"inputField.errors\"\n        [class.has-feedback]=\"inputField.errors\"\n      >\n        <input\n          type=\"text\"\n          attr.id=\"{{ id }}-input-field\"\n          [attr.aria-description]=\"inputAriaLabel(inputMode)\"\n          class=\"form-control\"\n          #inputField=\"ngModel\"\n          [ngModel]=\"(selected$ | async)[inputMode]\"\n          (ngModelChange)=\"updateColorValue($event, inputMode)\"\n          [pattern]=\"inputPatterns[inputMode].source\"\n          (keyup.enter)=\"inputSubmit.emit()\"\n        />\n        <ux-icon class=\"form-control-feedback\" name=\"alert\"></ux-icon>\n      </div>\n    </div>\n  }\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.id']
            }], inputColors: [{
                type: Input,
                args: ['colors']
            }], selected: [{
                type: Input
            }], columns: [{
                type: Input
            }], buttonStyle: [{
                type: Input
            }], buttonSize: [{
                type: Input
            }], showTooltips: [{
                type: Input
            }], showInput: [{
                type: Input
            }], inputMode: [{
                type: Input
            }], colorAriaLabel: [{
                type: Input
            }], switchModeAriaLabel: [{
                type: Input
            }], inputAriaLabel: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], inputModeChange: [{
                type: Output
            }], inputSubmit: [{
                type: Output
            }], cssWidth: [{
                type: HostBinding,
                args: ['style.width']
            }], inputFormControl: [{
                type: ViewChild,
                args: ['inputField', { static: false }]
            }], tabbableList: [{
                type: ViewChild,
                args: [TabbableListDirective]
            }], onFocus: [{
                type: HostListener,
                args: ['focus', ['$event']]
            }] } });

let uniqueId$b = 0;
const NUMBER_PICKER_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => NumberPickerComponent),
    multi: true,
};
class NumberPickerComponent {
    constructor() {
        this._formGroup = inject(FormGroupDirective, { optional: true });
        this._changeDetector = inject(ChangeDetectorRef);
        this._step = 1;
        this._disabled = false;
        this._value = 0;
        this._focused = false;
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._propagateChange = (_) => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._touchedChange = () => { };
        /** Sets the id of the number picker. The child input will have this value with a -input suffix as its id. */
        this.id = `ux-number-picker-${uniqueId$b++}`;
        /** Define the precision of floating point values */
        this.precision = Number.MAX_SAFE_INTEGER.toString().length - 1;
        /** If two way binding is used this value will be updated any time the number picker value changes. */
        this.valueChange = new EventEmitter();
        /** Store the current valid state */
        this._valid = true;
        /** This is a flag to indicate when the component has been destroyed to avoid change detection being made after the component
         *  is no longer instantiated. A workaround for Angular Forms bug (https://github.com/angular/angular/issues/27803) */
        this._isDestroyed = false;
        this._readonly = false;
    }
    /** Specified if this is a readonly input. */
    set readonly(value) {
        this._readonly = coerceBooleanProperty(value);
    }
    get readonly() {
        return this._readonly;
    }
    /** Sets the value displayed in the number picker component. */
    get value() {
        return this._value;
    }
    set value(value) {
        if (this._value !== value) {
            this._value = value;
            this._valid = this.isValid();
        }
    }
    /** Defines the minimum value the number picker can set. */
    get min() {
        return this._min;
    }
    set min(value) {
        this._min = coerceNumberProperty(value);
    }
    /** Defines the maximum value the number picker can set. */
    get max() {
        return this._max;
    }
    set max(value) {
        this._max = coerceNumberProperty(value);
    }
    /** Defines the amount the number picker should increase or decrease when the buttons or arrow keys are used. */
    set step(value) {
        if (typeof value === 'function') {
            this._step = value;
        }
        else {
            this._step = coerceNumberProperty(value);
        }
    }
    /** Indicate if the number picker is disabled or not. */
    get disabled() {
        return this._disabled;
    }
    set disabled(value) {
        this._disabled = coerceBooleanProperty(value);
    }
    get inputId() {
        return this.id + '-input';
    }
    ngOnChanges() {
        this._valid = this.isValid();
    }
    ngOnDestroy() {
        this._isDestroyed = true;
    }
    getStep(direction) {
        return typeof this._step === 'number' ? this._step : this._step(this.value, direction);
    }
    increment(event) {
        if (event) {
            event.preventDefault();
        }
        if (!this.disabled && !this.readonly) {
            this.value = Math.max(Math.min(this.value + this.getStep(StepDirection.Increment), this.getMaxValueForComparison()), this.getMinValueForComparison());
            // account for javascripts terrible handling of floating point numbers
            this.value = parseFloat(this.value.toPrecision(this.precision));
            // emit the value to the Output and Angular forms
            this._emitValueChange(this.value);
        }
    }
    decrement(event) {
        if (event) {
            event.preventDefault();
        }
        if (!this.disabled && !this.readonly) {
            this.value = Math.min(Math.max(this.value - this.getStep(StepDirection.Decrement), this.getMinValueForComparison()), this.getMaxValueForComparison());
            // account for javascripts terrible handling of floating point numbers
            this.value = parseFloat(this.value.toPrecision(this.precision));
            // emit the value to the Output and Angular forms
            this._emitValueChange(this.value);
        }
    }
    isValid() {
        return (this.value >= this.getMinValueForComparison() && this.value <= this.getMaxValueForComparison());
    }
    onScroll(event) {
        if (!this._focused) {
            return;
        }
        // get the distance scrolled
        const scrollValue = event.deltaY || event.wheelDelta;
        // increment or decrement accordingly
        scrollValue < 0 ? this.increment(event) : this.decrement(event);
    }
    writeValue(value) {
        if (value !== undefined) {
            this._value = value;
            this._valid = this.isValid();
            // if the component is not destroyed then run change detection
            // workaround for Angular bug (https://portal.digitalsafe.net/browse/EL-3694)
            if (!this._isDestroyed) {
                this._changeDetector.detectChanges();
            }
        }
    }
    registerOnChange(fn) {
        this._propagateChange = fn;
    }
    registerOnTouched(fn) {
        this._touchedChange = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    /** Set the value and emit the change to the output and Angular forms. */
    _emitValueChange(value) {
        // This is a workaround for angular bug https://github.com/angular/angular/issues/12540
        if (value === this._lastValue) {
            return;
        }
        this._lastValue = value;
        this.valueChange.emit(value);
        this._propagateChange(value);
    }
    getMaxValueForComparison() {
        return this.max === undefined || this.max === null ? Infinity : this.max;
    }
    getMinValueForComparison() {
        return this.min === undefined || this.min === null ? -Infinity : this.min;
    }
    onFocusIn() {
        this._focused = true;
    }
    onFocusOut() {
        this._focused = false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: NumberPickerComponent, isStandalone: false, selector: "ux-number-picker, ux-number-picker-inline", inputs: { id: "id", ariaLabel: ["aria-label", "ariaLabel"], labelledBy: ["aria-labelledby", "labelledBy"], precision: "precision", placeholder: "placeholder", required: "required", readonly: "readonly", value: "value", min: "min", max: "max", step: "step", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusOut()" }, properties: { "class.ux-number-picker-invalid": "!_valid && !disabled && !_formGroup" } }, providers: [NUMBER_PICKER_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<input\n  type=\"number\"\n  [id]=\"inputId\"\n  role=\"spinbutton\"\n  class=\"form-control number-picker-input\"\n  [(ngModel)]=\"value\"\n  (ngModelChange)=\"_emitValueChange($event)\"\n  [min]=\"min\"\n  [max]=\"max\"\n  [required]=\"required\"\n  [attr.placeholder]=\"placeholder\"\n  [readonly]=\"readonly\"\n  (blur)=\"_touchedChange()\"\n  (keydown.ArrowDown)=\"decrement($event)\"\n  (keydown.ArrowUp)=\"increment($event)\"\n  (wheel)=\"onScroll($event)\"\n  step=\"any\"\n  [disabled]=\"disabled\"\n  [attr.aria-valuemin]=\"min\"\n  [attr.aria-valuenow]=\"value\"\n  [attr.aria-valuemax]=\"max\"\n  [attr.aria-label]=\"ariaLabel\"\n  [attr.aria-labelledby]=\"labelledBy\"\n/>\n\n<div class=\"number-picker-controls\">\n  <div\n    class=\"number-picker-control number-picker-control-up\"\n    (click)=\"increment($event); _touchedChange()\"\n    [class.disabled]=\"disabled || value >= max || readonly\"\n  >\n    <ux-icon name=\"up\"></ux-icon>\n  </div>\n\n  <div\n    class=\"number-picker-control number-picker-control-down\"\n    (click)=\"decrement($event); _touchedChange()\"\n    [class.disabled]=\"disabled || value <= min || readonly\"\n  >\n    <ux-icon name=\"down\"></ux-icon>\n  </div>\n</div>\n", dependencies: [{ kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-number-picker, ux-number-picker-inline', providers: [NUMBER_PICKER_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.ux-number-picker-invalid]': '!_valid && !disabled && !_formGroup',
                    }, standalone: false, template: "<input\n  type=\"number\"\n  [id]=\"inputId\"\n  role=\"spinbutton\"\n  class=\"form-control number-picker-input\"\n  [(ngModel)]=\"value\"\n  (ngModelChange)=\"_emitValueChange($event)\"\n  [min]=\"min\"\n  [max]=\"max\"\n  [required]=\"required\"\n  [attr.placeholder]=\"placeholder\"\n  [readonly]=\"readonly\"\n  (blur)=\"_touchedChange()\"\n  (keydown.ArrowDown)=\"decrement($event)\"\n  (keydown.ArrowUp)=\"increment($event)\"\n  (wheel)=\"onScroll($event)\"\n  step=\"any\"\n  [disabled]=\"disabled\"\n  [attr.aria-valuemin]=\"min\"\n  [attr.aria-valuenow]=\"value\"\n  [attr.aria-valuemax]=\"max\"\n  [attr.aria-label]=\"ariaLabel\"\n  [attr.aria-labelledby]=\"labelledBy\"\n/>\n\n<div class=\"number-picker-controls\">\n  <div\n    class=\"number-picker-control number-picker-control-up\"\n    (click)=\"increment($event); _touchedChange()\"\n    [class.disabled]=\"disabled || value >= max || readonly\"\n  >\n    <ux-icon name=\"up\"></ux-icon>\n  </div>\n\n  <div\n    class=\"number-picker-control number-picker-control-down\"\n    (click)=\"decrement($event); _touchedChange()\"\n    [class.disabled]=\"disabled || value <= min || readonly\"\n  >\n    <ux-icon name=\"down\"></ux-icon>\n  </div>\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], labelledBy: [{
                type: Input,
                args: ['aria-labelledby']
            }], precision: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], required: [{
                type: Input
            }], readonly: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], value: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }], step: [{
                type: Input
            }], disabled: [{
                type: Input
            }], onFocusIn: [{
                type: HostListener,
                args: ['focusin']
            }], onFocusOut: [{
                type: HostListener,
                args: ['focusout']
            }] } });
var StepDirection;
(function (StepDirection) {
    StepDirection[StepDirection["Increment"] = 0] = "Increment";
    StepDirection[StepDirection["Decrement"] = 1] = "Decrement";
})(StepDirection || (StepDirection = {}));

class NumberPickerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerModule, declarations: [NumberPickerComponent], imports: [CommonModule, IconModule, FormsModule], exports: [NumberPickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerModule, imports: [CommonModule, IconModule, FormsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NumberPickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, IconModule, FormsModule],
                    exports: [NumberPickerComponent],
                    declarations: [NumberPickerComponent],
                }]
        }] });

class TooltipModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TooltipModule, declarations: [TooltipComponent, TooltipDirective], imports: [CommonModule, OverlayModule, ObserversModule$1], exports: [TooltipDirective, TooltipComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipModule, providers: [TooltipService], imports: [CommonModule, OverlayModule, ObserversModule$1] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TooltipModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, OverlayModule, ObserversModule$1],
                    exports: [TooltipDirective, TooltipComponent],
                    declarations: [TooltipComponent, TooltipDirective],
                    providers: [TooltipService],
                }]
        }] });

class ColorPickerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerModule, declarations: [ColorPickerComponent], imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            NumberPickerModule,
            TooltipModule,
            IconModule], exports: [ColorPickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerModule, imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            NumberPickerModule,
            TooltipModule,
            IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColorPickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        FormsModule,
                        NumberPickerModule,
                        TooltipModule,
                        IconModule,
                    ],
                    exports: [ColorPickerComponent],
                    declarations: [ColorPickerComponent],
                }]
        }] });

class ColumnSortingDirective {
    constructor() {
        /** Emit the current sort state for all columns within the table */
        this.events = new Subject();
        /** Store the current sort state for all columns within the table */
        this.order = [];
    }
    ngOnDestroy() {
        this.events.complete();
    }
    /** Toggle the sorting state of a column */
    toggleColumn(sorting) {
        // apply sorting based on the single or multiple sort
        this.order = this.singleSort
            ? this.toggleSingleColumn(sorting)
            : this.toggleMultipleColumn(sorting);
        // emit the latest order
        this.events.next(this.order);
        return this.order;
    }
    /** Explicitly set the column state */
    setColumnState(key, state) {
        // check if the sorting has actually changed
        if (this.order.find(column => column.key === key && column.state === state)) {
            return;
        }
        // if only one column can be sorted and the current column has a sort direction remove all others
        if (this.singleSort && state !== ColumnSortingState.NoSort) {
            this.order = [];
        }
        else {
            // remove the item from the state if present
            this.order = this.order.filter(column => column.key !== key);
        }
        // if the column has active sorting then we should add it to the array again
        if (state === ColumnSortingState.Ascending || state === ColumnSortingState.Descending) {
            this.order = [...this.order, { key, state }];
        }
    }
    /** Toggle the sorting state of a column when using single select */
    toggleSingleColumn(sorting) {
        return sorting.state === ColumnSortingState.NoSort
            ? []
            : [{ key: sorting.key, state: sorting.state }];
    }
    /** Toggle the sorting state of a column when using multiple select */
    toggleMultipleColumn(sorting) {
        // reorder columns here
        const idx = this.order.findIndex(column => column.key === sorting.key);
        // if wasn't previously selected add to list and it is being sorted
        if (idx === -1 && sorting.state !== ColumnSortingState.NoSort) {
            return [...this.order, { key: sorting.key, state: sorting.state }];
        }
        // if we are sorting it change the sorting order
        if (sorting.state === ColumnSortingState.Ascending ||
            sorting.state === ColumnSortingState.Descending) {
            return [
                ...this.order.filter(_column => _column.key !== sorting.key),
                { key: sorting.key, state: sorting.state },
            ];
        }
        // Otherwise remove the item
        return this.order.filter(_column => _column.key !== sorting.key);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ColumnSortingDirective, isStandalone: false, selector: "[uxColumnSorting]", inputs: { singleSort: "singleSort", sortIndicator: "sortIndicator" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxColumnSorting]',
                    standalone: false,
                }]
        }], propDecorators: { singleSort: [{
                type: Input
            }], sortIndicator: [{
                type: Input
            }] } });
var ColumnSortingState;
(function (ColumnSortingState) {
    ColumnSortingState["Ascending"] = "ascending";
    ColumnSortingState["Descending"] = "descending";
    ColumnSortingState["NoSort"] = "none";
})(ColumnSortingState || (ColumnSortingState = {}));

class ColumnSortingComponent {
    constructor() {
        this._sorter = inject(ColumnSortingDirective);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Defines the sorting order of a column: `NoSort`, `Ascending` or `Descending`. */
        this.state = ColumnSortingState.NoSort;
        /** Determine if a column can have a `NoSort` state */
        this.allowNoSort = true;
        /** Specifies name of the ascending icon */
        this.ascendingIcon = 'ascend';
        /** Specifies name of the descending icon */
        this.descendingIcon = 'descend';
        /**
         * Changes the state of the sorting on the column between `NoSort`, `Ascending` and `Descending`.
         * This returns an array of objects for each column being sorted containing `key: string` and `state: ColumnSortingState`.
         * State can be used to find the current sorting state of the column eg. `(state === ColumnSortingState.Ascending)`.
         * The `ColumnSortingOrder` interface has been provided for objects in the array.
         */
        this.stateChange = new EventEmitter();
        /** Emit whenever the order changes */
        this.orderChange = new EventEmitter();
        /** Expose the sorting state enum to the view */
        this.ColumnSortingState = ColumnSortingState;
        /** Unsubscribe from all observables on component destroy */
        this._onDestroy$ = new Subject();
    }
    /** Access the custom sort indicator if one was provided */
    get _sortIndicator() {
        return this._sorter.sortIndicator;
    }
    ngOnInit() {
        // listen for changes triggered by the directive
        this._sorter.events
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(columns => this.updateState(columns));
    }
    ngOnChanges(changes) {
        // if the state input is changed then apply the change
        if (changes.state && changes.state.currentValue !== changes.state.previousValue) {
            this._sorter.setColumnState(this.key, this.state);
        }
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    /** Toggle the sorting state of a column - this is designed to be programmatically called by the consuming component */
    changeState() {
        switch (this.state) {
            case ColumnSortingState.Ascending:
                this.state = ColumnSortingState.Descending;
                break;
            case ColumnSortingState.Descending:
                this.state = this.allowNoSort ? ColumnSortingState.NoSort : ColumnSortingState.Ascending;
                break;
            default:
                this.state = ColumnSortingState.Ascending;
        }
        // change detection should be run
        this._changeDetector.markForCheck();
        // inform parent (internally we use a ReadonlyArray but are returning a standard array to prevent breaking changes to the public API)
        return this._sorter.toggleColumn({ key: this.key, state: this.state });
    }
    /** Update the state based on column order */
    updateState(columns) {
        // if we are sorting this column then find the matching data
        const columnIdx = columns.findIndex(_column => _column.key === this.key);
        // if we are not sorting this column then mark it as NoSort
        if (columnIdx === -1) {
            this.state = ColumnSortingState.NoSort;
        }
        // only store the number if we have 2 or more columns being sorted
        this.order = columns.length < 2 || columnIdx === -1 ? null : columnIdx + 1;
        // emit the latest order value
        if (typeof this.order === 'number') {
            this.orderChange.emit(this.order);
        }
        // change detection should be run
        this._changeDetector.markForCheck();
        // Emit the latest change
        this.stateChange.emit(this.state);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ColumnSortingComponent, isStandalone: false, selector: "ux-column-sorting", inputs: { state: "state", key: "key", order: "order", allowNoSort: "allowNoSort", ascendingIcon: "ascendingIcon", descendingIcon: "descendingIcon" }, outputs: { stateChange: "stateChange", orderChange: "orderChange" }, exportAs: ["ux-column-sorting"], usesOnChanges: true, ngImport: i0, template: "<div\n  class=\"ux-column-sorting\"\n  [class.ux-column-sorting-hidden]=\"state === ColumnSortingState.NoSort\"\n>\n  <!-- The default sort indicator -->\n  @if (!_sortIndicator) {\n    <ux-icon\n      class=\"ux-column-sorting-icon\"\n      [class.column-sorting-icon-hidden]=\"state === ColumnSortingState.NoSort\"\n      [name]=\"state === ColumnSortingState.Descending ? descendingIcon : ascendingIcon\"\n    >\n    </ux-icon>\n    <p class=\"ux-column-sorting-number\" aria-hidden=\"true\">{{ order }}</p>\n  }\n\n  <!-- Custom sort indicator -->\n  @if (_sortIndicator) {\n    <ng-container\n      [ngTemplateOutlet]=\"_sortIndicator\"\n      [ngTemplateOutletContext]=\"{ state: state, order: order }\"\n    >\n    </ng-container>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-column-sorting', exportAs: 'ux-column-sorting', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div\n  class=\"ux-column-sorting\"\n  [class.ux-column-sorting-hidden]=\"state === ColumnSortingState.NoSort\"\n>\n  <!-- The default sort indicator -->\n  @if (!_sortIndicator) {\n    <ux-icon\n      class=\"ux-column-sorting-icon\"\n      [class.column-sorting-icon-hidden]=\"state === ColumnSortingState.NoSort\"\n      [name]=\"state === ColumnSortingState.Descending ? descendingIcon : ascendingIcon\"\n    >\n    </ux-icon>\n    <p class=\"ux-column-sorting-number\" aria-hidden=\"true\">{{ order }}</p>\n  }\n\n  <!-- Custom sort indicator -->\n  @if (_sortIndicator) {\n    <ng-container\n      [ngTemplateOutlet]=\"_sortIndicator\"\n      [ngTemplateOutletContext]=\"{ state: state, order: order }\"\n    >\n    </ng-container>\n  }\n</div>\n" }]
        }], propDecorators: { state: [{
                type: Input
            }], key: [{
                type: Input
            }], order: [{
                type: Input
            }], allowNoSort: [{
                type: Input
            }], ascendingIcon: [{
                type: Input
            }], descendingIcon: [{
                type: Input
            }], stateChange: [{
                type: Output
            }], orderChange: [{
                type: Output
            }] } });

class ColumnSortingModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingModule, declarations: [ColumnSortingComponent, ColumnSortingDirective], imports: [CommonModule, IconModule], exports: [ColumnSortingComponent, ColumnSortingDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingModule, imports: [CommonModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnSortingModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, IconModule],
                    exports: [ColumnSortingComponent, ColumnSortingDirective],
                    declarations: [ColumnSortingComponent, ColumnSortingDirective],
                }]
        }] });

class ConduitSubject {
    constructor(conduit, _zone, zoneId) {
        this.conduit = conduit;
        this._zone = _zone;
        this.zoneId = zoneId;
        this._onDestroy = new Subject();
        // store the target subject object
        this._subject = conduit.subject;
        // check if there are any conduits that have supplied an initial value
        this.getInitialValue();
        // subscribe to changes to the source subject
        this._subject
            .pipe(distinctUntilChanged(conduit.changeDetection), takeUntil(this._onDestroy))
            .subscribe(this.onOutput.bind(this));
        // subscribe to the zone events and root zone events
        _zone
            .getEvents()
            .pipe(filter(event => event.conduit.id === conduit.id), takeUntil(this._onDestroy))
            .subscribe(this.onInput.bind(this));
    }
    /** Check all allow inputs to see if there is a value we should initially set the conduit to */
    getInitialValue() {
        // if we do not accept inputs then do nothing
        if (this.conduit.acceptsInput === false) {
            return;
        }
        // return all subjects that are 1) Not itself 2) In a zone that is listed in acceptsInput 3) Have a currentValue set
        const subjects = this._zone.getSubjects().filter(subject => {
            // If this is itself or if it has not value to give us then do nothing
            if (subject === this ||
                subject.conduit.id !== this.conduit.id ||
                !('currentValue' in subject.conduit)) {
                return false;
            }
            // if acceptsInput is true then we return every time
            if (this.conduit.acceptsInput === true) {
                return true;
            }
            if (Array.isArray(this.conduit.acceptsInput)) {
                return this.conduit.acceptsInput.indexOf(subject.zoneId) !== -1;
            }
        });
        // if there are no matches then do nothing
        if (subjects.length === 0) {
            return;
        }
        // otherwise sort by the last modified field
        subjects.sort((subjectOne, subjectTwo) => subjectOne.conduit.lastModified.getTime() < subjectTwo.conduit.lastModified.getTime() ? 1 : -1);
        // get the most recent value
        this._subject.next(subjects[0].conduit.currentValue);
    }
    /** This will be triggered when a conduits value has changed */
    onInput(event) {
        // if we dont accept input or we emitted this value then do nothing
        if (this.conduit.acceptsInput === false || event.conduit === this.conduit) {
            return;
        }
        // check if the conduit produces output - if not we only do something if we are in the same zone
        if (event.conduit.producesOutput === false && event.zoneId !== this.zoneId) {
            return;
        }
        // check if we only accept inputs from specific zones
        if (Array.isArray(this.conduit.acceptsInput)) {
            // check if the event came from an acceptable zone
            if (!this.conduit.acceptsInput.find(zone => zone === event.zoneId)) {
                return;
            }
        }
        // if required transform the value
        const outputValue = this.conduit.map ? this.conduit.map(event.value) : event.value;
        // update the subject
        this._subject.next(outputValue);
    }
    /** This will be fired when this conduit emits a new value */
    onOutput(value) {
        // store the most recent value and when it was modified - can be used for any new conduits to lookup a value
        this.conduit.currentValue = value;
        this.conduit.lastModified = new Date();
        // check if this should produce output
        if (this.conduit.producesOutput) {
            this._zone.emit({ conduit: this.conduit, zoneId: this.zoneId, value });
        }
    }
    /** Unsubscribe once this subject is destroyed */
    destroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
}

class ConduitZone {
    /** Create a global subject store */
    static { this.subjects = []; }
    /** Expose an event stream of new values */
    static { this.events = new Subject(); }
    ngOnDestroy() {
        // find all conduit subjects that are part of this zone
        ConduitZone.subjects
            .filter(_subject => _subject.zoneId === this._zoneId)
            .forEach(_subject => this.unregisterConduit(_subject.conduit));
    }
    /** Store reference to the repository and begin watching for and emitting changes */
    registerConduit(conduit) {
        ConduitZone.subjects.push(new ConduitSubject(conduit, this, this._zoneId));
    }
    /** Destroy a conduit */
    unregisterConduit(conduit) {
        const subject = this.getConduitSubject(conduit.subject);
        if (subject) {
            // remove the subject from the internal list of conduit subjects
            ConduitZone.subjects = ConduitZone.subjects.filter(_subject => _subject !== subject);
            // perform all unsubscriptions
            subject.destroy();
        }
    }
    /** Provide the zone with an ID */
    setZoneId(zoneId) {
        this._zoneId = zoneId;
    }
    /** Emit a value to all zones for checking */
    emit(event) {
        ConduitZone.events.next(event);
    }
    /** Retrieve a conduit subsject object from the rxjs subject */
    getConduitSubject(subject) {
        return ConduitZone.subjects.find(_subject => _subject.conduit.subject === subject);
    }
    /** Get all subjects from all zones */
    getSubjects() {
        return ConduitZone.subjects;
    }
    /** Alter the properties of a conduit dynamically */
    setConduitProperties(subject, properties) {
        // find the conduit with the matching subject
        const conduitSubject = this.getSubjects().find(_conduit => _conduit.conduit.subject === subject);
        // if a match was found update the properties
        if (conduitSubject) {
            // update each specified property
            for (const prop in properties) {
                conduitSubject.conduit[prop] = properties[prop];
            }
        }
    }
    /** Programmatically create a conduit at runtime */
    createConduit(subject, properties) {
        // register the conduit with the zone
        this.registerConduit({ ...properties, subject });
    }
    /** Register all conduits in a component */
    registerConduits(component) {
        if (Array.isArray(component._conduits)) {
            component._conduits.forEach((conduit) => this.registerConduit({ ...conduit, subject: component[conduit.propertyKey] }));
        }
    }
    /** Register all conduits in a component */
    unregisterConduits(component) {
        if (Array.isArray(component._conduits)) {
            component._conduits.forEach((conduit) => this.unregisterConduit(conduit));
        }
    }
    /** Return the global event stream */
    getEvents() {
        return ConduitZone.events;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitZone, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitZone }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitZone, decorators: [{
            type: Injectable
        }] });

class ConduitComponent {
    constructor() {
        this._zone = inject(ConduitZone, { optional: true });
    }
    /** We need to register the conduits with the zone when the component is initialised */
    ngOnInit() {
        // register the conduit in the zone and ensure it gets the correct instance of the target
        this._zone.registerConduits(this);
    }
    /** We need to unregister the conduits when the component is destroyed */
    ngOnDestroy() {
        this._zone.unregisterConduits(this);
    }
    /** Alter the properties of a conduit dynamically */
    setConduitProperties(subject, properties) {
        this._zone.setConduitProperties(subject, properties);
    }
    /** Programmatically create a conduit at runtime */
    createConduit(subject, properties) {
        this._zone.createConduit(subject, properties);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ConduitComponent, isStandalone: false, selector: "ux-conduit", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitComponent, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-conduit',
                    standalone: false,
                }]
        }] });

class ConduitZoneComponent extends ConduitComponent {
    ngOnInit() {
        this._zone.setZoneId(this.zoneId);
        super.ngOnInit();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitZoneComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ConduitZoneComponent, isStandalone: false, selector: "ux-conduit-zone", usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitZoneComponent, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-conduit-zone',
                    standalone: false,
                }]
        }] });

const defaultConduitProps = {
    acceptsInput: true,
    producesOutput: true,
};

/** Expose the property that conduits will be stored in */
const CONDUITS = '_conduits';
/** Create the conduit property decorator */
function Conduit(properties) {
    return (target, propertyKey) => {
        if (typeof properties === 'function') {
            properties = properties.call(null);
        }
        // if the target does not already have a conduit list then create one
        // eslint-disable-next-line no-prototype-builtins
        if (!target.hasOwnProperty(CONDUITS)) {
            Object.defineProperty(target, CONDUITS, { value: [] });
        }
        // add the conduit to the list ensuring all required properties are provided
        target[CONDUITS].push({
            ...defaultConduitProps,
            ...properties,
            target,
            propertyKey,
        });
    };
}

/**
 * This module is not required to be imported but is required
 * by the Angular compiler, otherwise it will complain that
 * ConduitZoneComponent is not part of an NgModule and will
 * fail to build
 */
class ConduitModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ConduitModule, declarations: [ConduitComponent, ConduitZoneComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConduitModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        ConduitComponent,
                        ConduitZoneComponent,
                    ],
                }]
        }] });

class DragService {
    constructor() {
        /** Emit when dragging begins */
        this.onDragStart = new Subject();
        /** Emit when dragging moves */
        this.onDrag = new Subject();
        /** Emit when dragging ends */
        this.onDragEnd = new Subject();
        /** Emit when the user is dragging over the drop area */
        this.onDropEnter = new Subject();
        /** Emit when the user is dragging out of the drop area */
        this.onDropLeave = new Subject();
        /** Emit when a drop occurs */
        this.onDrop = new Subject();
    }
    /** Destroy all observables */
    ngOnDestroy() {
        this.onDragStart.complete();
        this.onDrag.complete();
        this.onDragEnd.complete();
        this.onDrop.complete();
        this.onDropEnter.complete();
        this.onDropLeave.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class DragDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._ngZone = inject(NgZone);
        this._renderer = inject(Renderer2);
        this._scrollDispatcher = inject(ScrollDispatcher$1);
        this._drag = inject(DragService);
        /** Detemine if we should show a clone when dragging */
        this.clone = false;
        /** Allow the dragging to be enabled/disabled */
        this.draggable = true;
        /** Emit an event when dragging starts */
        this.onDragStart = new EventEmitter();
        /** Emit an event when the mouse moves while dragging */
        this.onDrag = new EventEmitter();
        /** Emit an event when the document scrolls while dragging */
        this.onDragScroll = new EventEmitter();
        /** Emit an event when the dragging finishes */
        this.onDragEnd = new EventEmitter();
        /** Emit when the user drops an item in a drop area */
        this.onDrop = new EventEmitter();
        /** Emit when the user drags over a drop area */
        this.onDropEnter = new EventEmitter();
        /** Emit when the user drags out of a drop area */
        this.onDropLeave = new EventEmitter();
        /** Store the dragging state */
        this._isDragging = false;
        /** The offset in pixels currently being scrolled while dragging */
        this._scrollOffset = { x: 0, y: 0 };
        /** Create an observable from the mouse down event */
        this._mousedown$ = fromEvent(this._elementRef.nativeElement, 'mousedown');
        /** Create an observable from the mouse move event */
        this._mousemove$ = fromEvent(document, 'mousemove');
        /** Create an observable from the mouse up event */
        this._mouseup$ = fromEvent(document, 'mouseup');
        /** Use an observable to unsubscribe from all subscriptions */
        this._onDestroy = new Subject();
        // ensure all mouse down events on the object are captured
        this._mousedown$
            .pipe(filter(() => this.draggable), takeUntil(this._onDestroy))
            .subscribe(this.dragStart.bind(this));
        // emit the outputs when drag events occur
        this._drag.onDragStart
            .pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
            .subscribe((dragEvent) => this.onDragStart.emit(dragEvent.event));
        this._drag.onDrag
            .pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
            .subscribe((dragEvent) => this.onDrag.emit(dragEvent.event));
        this._drag.onDragEnd
            .pipe(filter(event => this._isDragging || (this.model && this.model === event.data)), takeUntil(this._onDestroy))
            .subscribe(() => this.onDragEnd.emit());
        this._drag.onDrop
            .pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
            .subscribe((event) => this.onDrop.emit(event));
        this._drag.onDropEnter
            .pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
            .subscribe(() => this.onDropEnter.emit());
        this._drag.onDropLeave
            .pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
            .subscribe(() => this.onDropLeave.emit());
    }
    /** Emit events and create clone when drag starts */
    dragStart(event) {
        event.preventDefault();
        // start listening for scroll events on the nearest scrollable ancestor element
        this.createScrollEventListener();
        if (this.clone) {
            // clone the node
            this.cloneNode(event);
        }
        // apply a class to the element being dragged
        this._renderer.addClass(this._elementRef.nativeElement, 'ux-drag-dragging');
        // store the dragging state
        this._isDragging = true;
        // emit the drag start event
        this._ngZone.run(() => this._drag.onDragStart.next({ event, group: this.group, data: this.model }));
        this._mousemove$
            .pipe(takeUntil(this._mouseup$), takeUntil(this._onDestroy))
            .subscribe(this.dragMove.bind(this));
        // When dragging stops emit the drag end
        this._mouseup$.pipe(first()).subscribe(this.dragEnd.bind(this));
    }
    /** Emit event and update clone position when dragging moves */
    dragMove(event) {
        event.preventDefault();
        // scroll the viewport if needed
        this.updateScrolling(event);
        if (this._clone) {
            this.updateNodePosition(event);
        }
        // emit the drag start event
        this._ngZone.run(() => this._drag.onDrag.next({ event, group: this.group, data: this.model }));
    }
    /** Emit event and destroy clone when dragging ends */
    dragEnd() {
        // if the drag ended outside of the viewport, stop the scrolling interval
        this.stopScrolling();
        this.removeScrollEventListener();
        // if there was a clone, remove it
        if (this._clone) {
            this._renderer.removeChild(document.body, this._clone);
            this._clone = null;
        }
        // remove the dragging class
        this._renderer.removeClass(this._elementRef.nativeElement, 'ux-drag-dragging');
        // emit the on drag end output
        this._ngZone.run(() => this._drag.onDragEnd.next({ group: this.group, data: this.model }));
        // store the dragging state
        this._isDragging = false;
    }
    /** Emit the onDragScroll event */
    scroll() {
        const offsetX = this._scrollParent.scrollLeft - this._scrollPosition.left;
        const offsetY = this._scrollParent.scrollTop - this._scrollPosition.top;
        this.onDragScroll.emit({ offsetX, offsetY });
        this._scrollPosition = {
            top: this._scrollParent.scrollTop,
            left: this._scrollParent.scrollLeft,
        };
    }
    /** Create an exact clone of an element */
    cloneNode(event) {
        // duplicate the node
        this._clone = this._elementRef.nativeElement.cloneNode(true);
        // store the position within the draggable element
        const { top, left, width } = this._elementRef.nativeElement.getBoundingClientRect();
        this._offset = { x: event.clientX - left, y: event.clientY - top };
        // inline all styles so it looks identical regardless of its position in the DOM
        this.inlineStyles(this._elementRef.nativeElement, this._clone);
        // IE doesn't always calculate the correct width value using getComputedStyles... use bounding client value instead
        this._renderer.setStyle(this._clone, 'width', width + 'px');
        // ensure we can easily position the node an it is above all other elements
        this._renderer.setAttribute(this._clone, 'aria-hidden', 'true');
        this._renderer.setStyle(this._clone, 'position', 'absolute');
        this._renderer.setStyle(this._clone, 'z-index', '99999');
        // apply a class to allow custom styling
        this._renderer.addClass(this._clone, 'ux-drag-dragging-clone');
        // insert the cloned element
        this._renderer.appendChild(document.body, this._clone);
        // set the cloned element initial position
        this.updateNodePosition(event);
    }
    /** Position the clone relative to the mouse */
    updateNodePosition(event) {
        this._renderer.setStyle(this._clone, 'left', event.pageX - this._offset.x + 'px');
        this._renderer.setStyle(this._clone, 'top', event.pageY - this._offset.y + 'px');
    }
    /** Inline all styles to ensure styling is consistent regardless of its position in the dom */
    inlineStyles(source, target) {
        // get all the computed styles from the source element
        const styles = getComputedStyle(source);
        // inline every specified style
        for (let idx = 0; idx < styles.length; idx++) {
            const style = styles.item(idx);
            if (style !== undefined) {
                this._renderer.setStyle(target, styles[idx], styles[style]);
            }
        }
        // ensure we dont capture any move events
        this._renderer.setStyle(target, 'pointer-events', 'none');
        // do the same for all the child elements
        for (let idx = 0; idx < source.children.length; idx++) {
            this.inlineStyles(source.children[idx], target.children[idx]);
        }
    }
    /** Unsubscribe from all subscriptions */
    ngOnDestroy() {
        this.stopScrolling();
        this.removeScrollEventListener();
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    updateScrolling(event) {
        this._scrollOffset = this.getScrollOffsets(this._scrollParent, event);
        if (this._scrollOffset.x === 0 && this._scrollOffset.y === 0) {
            this.stopScrolling();
        }
        else {
            this.startScrolling(this._scrollParent);
        }
    }
    getScrollParent() {
        // Get the nearest ancestor element with the cdkScrollable directive applied
        const containers = this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);
        if (containers.length > 0) {
            return containers[containers.length - 1].getElementRef().nativeElement;
        }
        return document.documentElement;
    }
    createScrollEventListener() {
        // get the nearest scrollable ancestor
        this._scrollParent = this.getScrollParent();
        // save the current scroll position to allow calculation of the scroll delta
        this._scrollPosition = {
            top: this._scrollParent.scrollTop,
            left: this._scrollParent.scrollLeft,
        };
        // start listening for scroll events
        const target = this._scrollParent === document.documentElement ? 'document' : this._scrollParent;
        this._scrollListener = this._renderer.listen(target, 'scroll', this.scroll.bind(this));
    }
    removeScrollEventListener() {
        // remove the scroll event listener
        if (this._scrollListener) {
            this._scrollListener();
            this._scrollListener = null;
        }
    }
    getScrollOffsets(scrollElement, event) {
        let scrollX = 0;
        let scrollY = 0;
        // scroll by at least this much so that it still scrolls if the pointer is exactly at the edge of the scroll element
        const minScroll = 5;
        const isRoot = scrollElement === document.documentElement;
        const bounds = scrollElement.getBoundingClientRect();
        const pointerOffsetX = isRoot ? event.clientX : event.clientX - bounds.x;
        const pointerOffsetY = isRoot ? event.clientY : event.clientY - bounds.y;
        if (pointerOffsetX <= 0 && scrollElement.scrollLeft > 0) {
            scrollX = Math.min(pointerOffsetX, -minScroll);
        }
        else if (pointerOffsetX >= scrollElement.clientWidth &&
            scrollElement.scrollLeft + scrollElement.clientWidth < scrollElement.scrollWidth) {
            scrollX = Math.max(pointerOffsetX - scrollElement.clientWidth, minScroll);
        }
        if (pointerOffsetY <= 0 && scrollElement.scrollTop > 0) {
            scrollY = Math.min(pointerOffsetY, -minScroll);
        }
        else if (pointerOffsetY >= scrollElement.clientHeight &&
            scrollElement.scrollTop + scrollElement.clientHeight < scrollElement.scrollHeight) {
            scrollY = Math.max(pointerOffsetY - scrollElement.clientHeight, minScroll);
        }
        return { x: scrollX, y: scrollY };
    }
    startScrolling(scrollElement) {
        if (!this._scrollIntervalHandle) {
            this._scrollIntervalHandle = window.setInterval(() => this.performScroll(scrollElement), 100);
        }
    }
    stopScrolling() {
        if (this._scrollIntervalHandle) {
            clearInterval(this._scrollIntervalHandle);
            this._scrollIntervalHandle = 0;
        }
    }
    performScroll(scrollElement) {
        scrollElement.scrollLeft += this._scrollOffset.x;
        scrollElement.scrollTop += this._scrollOffset.y;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DragDirective, isStandalone: false, selector: "[uxDrag]", inputs: { clone: "clone", group: "group", model: "model", draggable: "draggable" }, outputs: { onDragStart: "onDragStart", onDrag: "onDrag", onDragScroll: "onDragScroll", onDragEnd: "onDragEnd", onDrop: "onDrop", onDropEnter: "onDropEnter", onDropLeave: "onDropLeave" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxDrag]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { clone: [{
                type: Input
            }], group: [{
                type: Input
            }], model: [{
                type: Input
            }], draggable: [{
                type: Input
            }], onDragStart: [{
                type: Output
            }], onDrag: [{
                type: Output
            }], onDragScroll: [{
                type: Output
            }], onDragEnd: [{
                type: Output
            }], onDrop: [{
                type: Output
            }], onDropEnter: [{
                type: Output
            }], onDropLeave: [{
                type: Output
            }] } });

class DropDirective {
    constructor() {
        this._dragService = inject(DragService);
        /** Define whether or not dropping is enabled */
        this.dropDisabled = false;
        /** Emit the model of the item dropped */
        this.onDrop = new EventEmitter();
        /** Determine whether or not the mouse is within the drop region */
        this.isMouseOver = false;
        /** Determine whether or not we are currently dragging an item */
        this.isDragging = false;
        /** Ensure we destroy all subscriptions */
        this._onDestroy = new Subject();
        // subscribe to drag events
        this._dragService.onDragStart
            .pipe(tap(event => (this._group = event.group)), filter(event => this.isDropAllowed(event.group)), takeUntil(this._onDestroy))
            .subscribe(this.onDragStart.bind(this));
        this._dragService.onDragEnd
            .pipe(filter(event => this.isDropAllowed(event.group)), takeUntil(this._onDestroy))
            .subscribe(this.onDragEnd.bind(this));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Update the mouse over state */
    onMouseOver() {
        if (this.isDropAllowed(this._group)) {
            this.isMouseOver = true;
            // emit that we are over a drop area
            this._dragService.onDropEnter.next();
        }
    }
    /** Update the mouse over state */
    onMouseLeave() {
        // always ensure this value is reset
        this.isMouseOver = false;
        // only emit the dropd leave event when appropriate
        if (this.isDropAllowed(this._group)) {
            this._dragService.onDropLeave.next();
        }
    }
    /** Update the dragging state */
    onDragStart() {
        this.isDragging = true;
    }
    /** Update the dragging state */
    onDragEnd(event) {
        // update the dragging state
        this.isDragging = false;
        // clear the cached group
        this._group = null;
        // if the mouse is over and it is in an allowed group emit the dop event
        if (this.isMouseOver && this.isDropAllowed(event.group)) {
            this.onDrop.emit(event.data);
            this._dragService.onDrop.next(event.data);
        }
    }
    /** Determine whether or not the event is part of the specified groups */
    isDropAllowed(group) {
        // if dropping is disabled then it is never allowed
        if (this.dropDisabled) {
            return false;
        }
        // if no group specified allow all groups
        if (!this.group) {
            return true;
        }
        // if it is an array then ensure it is allowed
        if (Array.isArray(this.group)) {
            return !!this.group.find(_group => _group === group);
        }
        return this.group === group;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DropDirective, isStandalone: false, selector: "[uxDrop]", inputs: { group: "group", dropDisabled: "dropDisabled" }, outputs: { onDrop: "onDrop" }, host: { listeners: { "mouseenter": "onMouseOver()", "mouseleave": "onMouseLeave()" }, properties: { "class.ux-drop-hover": "isMouseOver && isDragging && !dropDisabled" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DropDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxDrop]',
                    host: {
                        '[class.ux-drop-hover]': 'isMouseOver && isDragging && !dropDisabled',
                    },
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { group: [{
                type: Input
            }], dropDisabled: [{
                type: Input
            }], onDrop: [{
                type: Output
            }], onMouseOver: [{
                type: HostListener,
                args: ['mouseenter']
            }], onMouseLeave: [{
                type: HostListener,
                args: ['mouseleave']
            }] } });

class DragModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DragModule, declarations: [DragDirective, DropDirective], exports: [DragDirective, DropDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DragModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [DragDirective, DropDirective],
                    declarations: [DragDirective, DropDirective],
                }]
        }] });

var DashboardStackMode;
(function (DashboardStackMode) {
    DashboardStackMode[DashboardStackMode["Regular"] = 0] = "Regular";
    DashboardStackMode[DashboardStackMode["Stacked"] = 1] = "Stacked";
    /** Determine the mode automatically based on dashboard width. */
    DashboardStackMode[DashboardStackMode["Auto"] = 2] = "Auto";
})(DashboardStackMode || (DashboardStackMode = {}));

class DashboardService {
    get options() {
        return this.options$.getValue();
    }
    get widgets() {
        return this.widgets$.getValue();
    }
    get stacked() {
        return this.stacked$.getValue();
    }
    get dimensions() {
        return this.dimensions$.getValue();
    }
    get columnWidth() {
        return this.dimensions.width / this.options.columns;
    }
    constructor() {
        this._rowHeight = 0;
        this.initialized$ = new BehaviorSubject(false);
        this.widgets$ = new BehaviorSubject([]);
        this.options$ = new BehaviorSubject(defaultOptions);
        this.dimensions$ = new BehaviorSubject({});
        this.height$ = this.dimensions$.pipe(tick(), map(dimensions => dimensions.height), distinctUntilChanged());
        this.placeholder$ = new BehaviorSubject({
            visible: false,
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        });
        this.layout$ = new BehaviorSubject([]);
        this.stacked$ = new BehaviorSubject(false);
        this.isDragging$ = new BehaviorSubject(null);
        this.isGrabbing$ = new BehaviorSubject(null);
        this.userLayoutChange$ = new Subject();
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
        combineLatest(this.layout$, this.widgets$, this.initialized$)
            .pipe(tick(), filter(([layout, widgets, initialized]) => layout && widgets.length > 0 && initialized), takeUntil(this._onDestroy))
            .subscribe(([layout]) => {
            this.setLayoutData(layout);
            this.renderDashboard();
        });
        this.stacked$
            .pipe(distinctUntilChanged(), filter(stacked => stacked === true), takeUntil(this._onDestroy))
            .subscribe(this.updateWhenStacked.bind(this));
        this.widgets$.pipe(tick(), takeUntil(this._onDestroy)).subscribe(() => this.renderDashboard());
        this.dimensions$
            .pipe(tick(), takeUntil(this._onDestroy))
            .subscribe(() => this.renderDashboard());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Add a widget to the dashboard
     * @param widget The widget component to add to the dashboard
     */
    addWidget(widget) {
        this.widgets$.next([...this.widgets$.getValue(), widget]);
    }
    /**
     * Remove a widget from the dashboard
     * @param widget The widget to remove
     */
    removeWidget(widget) {
        this.widgets$.next(this.widgets$.getValue().filter(_widget => _widget !== widget));
    }
    /**
     * Indicate that the dashboard element has been resized
     * @param width The width of the dashboard element in px
     * @param height The height of the dashboard element in px
     */
    setDimensions(width = this.dimensions.width, height = this.dimensions.height) {
        if (this.dimensions.width !== width || this.dimensions.height !== height) {
            this.dimensions$.next({ width, height });
        }
    }
    /**
     * Produce an object containing all the required layout data.
     * This can be useful for exporting/saving a layout
     */
    getLayoutData() {
        return this.widgets.map(widget => {
            return {
                id: widget.id,
                col: widget.getColumn(),
                row: widget.getRow(),
                colSpan: widget.getColumnSpan(),
                rowSpan: widget.getRowSpan(),
                minColSpan: widget.minColSpan,
                minRowSpan: widget.minRowSpan,
            };
        });
    }
    /**
     * Position widgets programatically
     */
    setLayoutData(widgets) {
        // iterate through each widget data and find a match
        widgets.forEach(widget => {
            // find the matching widget
            const target = this.widgets.find(_widget => _widget.id === widget.id);
            if (target) {
                target.setColumn(widget.col);
                target.setRow(widget.row);
                target.setColumnSpan(widget.colSpan);
                target.setRowSpan(widget.rowSpan);
                target.minColSpan = widget.minColSpan ?? 1;
                target.minRowSpan = widget.minRowSpan ?? 1;
            }
        });
    }
    /**
     * Update the positions and sizes of the widgets
     */
    renderDashboard() {
        // get the dimensions of the dashboard
        this._rowHeight = this.options.rowHeight || this.columnWidth;
        // ensure the column width is not below the min widths
        this.stacked$.next(this.columnWidth < this.options.minWidth);
        this.setDashboardLayout();
        // iterate through each widget and set the size - except the one being resized
        this.widgets
            .filter(widget => !this._actionWidget || widget !== this._actionWidget.widget)
            .forEach(widget => widget.render());
    }
    /**
     * Determine where widgets should be positioned based on their positions, width and the size of the container
     */
    setDashboardLayout() {
        // find any widgets that do not currently have a position set
        this.widgets
            .filter(widget => widget.getColumn() === undefined || widget.getRow() === undefined)
            .forEach(widget => this.setWidgetPosition(widget));
        this.setDashboardHeight();
    }
    updateWhenStacked() {
        // iterate through each widget set it's stacked state and retain the rowSpan
        this.getWidgetsByOrder().forEach((widget, idx, widgets) => {
            const widgetsAbove = widgets.slice(0, idx);
            const row = widgetsAbove.reduce((currentRow, _widget) => currentRow + _widget.getRowSpan(), 0);
            widget.setColumn(0);
            widget.setRow(row);
        });
    }
    /** Get widgets in the order they visually appear as the widgets array order does not reflect this */
    getWidgetsByOrder() {
        return [...this.widgets].sort((w1, w2) => {
            const w1Position = w1.getColumn(DashboardStackMode.Regular) +
                w1.getRow(DashboardStackMode.Regular) * this.options.columns;
            const w2Position = w2.getColumn(DashboardStackMode.Regular) +
                w2.getRow(DashboardStackMode.Regular) * this.options.columns;
            if (w1Position < w2Position) {
                return -1;
            }
            if (w1Position > w2Position) {
                return 1;
            }
            return 0;
        });
    }
    /**
     * Find a position that a widget can fit in the dashboard
     * @param widget The widget to try and position
     */
    setWidgetPosition(widget) {
        // find a position for the widget
        let position = 0;
        let success = false;
        // repeat until a space is found
        while (!success) {
            // get a position to try
            const column = position % this.options.columns;
            const row = Math.floor(position / this.options.columns);
            // check the current position
            if (this.getPositionAvailable(column, row, widget.getColumnSpan(), widget.getRowSpan())) {
                success = true;
                widget.setColumn(column);
                widget.setRow(row);
                return;
            }
            if (column === 0 && widget.colSpan > this.options.columns) {
                throw new Error('Dashboard widgets have a colSpan greater than the max number of dashboard columns!');
            }
            position++;
        }
    }
    /**
     * Check if a position in the dashboard is vacant or not
     */
    getPositionAvailable(column, row, columnSpan, rowSpan, ignoreWidget) {
        // get a list of grid spaces that are populated
        const spaces = this.getOccupiedSpaces();
        // check if the block would still be in bounds
        if (column + columnSpan > this.options.columns) {
            return false;
        }
        // check each required position
        for (let x = column; x < column + columnSpan; x++) {
            for (let y = row; y < row + rowSpan; y++) {
                if (spaces.find(block => block.column === x && block.row === y && block.widget !== ignoreWidget)) {
                    return false;
                }
            }
        }
        return true;
    }
    getOccupiedSpaces() {
        // find all spaces that are currently occupied
        return this.widgets
            .filter(widget => widget.getColumn() !== undefined && widget.getRow() !== undefined)
            .reduce((value, widget) => {
            this.forEachBlock(widget, (column, row) => value.push({ widget, column, row }));
            return value;
        }, []);
    }
    /**
     * Begin resizing a widget
     * @param action The the widget to resize
     */
    onResizeStart(action) {
        this.cacheWidgets();
        // store the mouse event
        this._event = action.event;
        this._actionWidget = action;
        // bring the widget to the font
        this.bringToFront(action.widget);
    }
    onResizeDrag(action) {
        const mousePosX = this._event.pageX - pageXOffset;
        const mousePosY = this._event.pageY - pageYOffset;
        // if there was no movement then do nothing
        if (action.event.x === mousePosX && action.event.y === mousePosY) {
            return;
        }
        // update the stored mouse event
        this._event = action.event;
        // get handle for direction
        const { handle } = action;
        // get the bounds of the handle
        const bounds = handle.getBoundingClientRect();
        // get the center of the handle
        const centerX = bounds.left + bounds.width / 2;
        const centerY = bounds.top + bounds.height / 2;
        // get the current mouse position
        const mouseX = mousePosX - centerX;
        const mouseY = mousePosY - centerY;
        // store the new proposed dimensions for the widget
        const dimensions = {
            x: action.widget.x,
            y: action.widget.y,
            width: action.widget.width,
            height: action.widget.height,
        };
        // update widget based on the handle being dragged
        switch (action.direction) {
            case ActionDirection.Right:
                dimensions.width += mouseX;
                break;
            case ActionDirection.Left:
                dimensions.x += mouseX;
                dimensions.width -= mouseX;
                if (dimensions.width < this.options.minWidth) {
                    const difference = this.options.minWidth - dimensions.width;
                    dimensions.x -= difference;
                    dimensions.width += difference;
                }
                break;
            case ActionDirection.Bottom:
                dimensions.height += mouseY;
                break;
            case ActionDirection.Top:
                dimensions.y += mouseY;
                dimensions.height -= mouseY;
                if (dimensions.height < this.options.minHeight) {
                    const difference = this.options.minHeight - dimensions.height;
                    dimensions.y -= difference;
                    dimensions.height += difference;
                }
                break;
            // Support resizing on multiple axis simultaneously
            case ActionDirection.TopLeft:
                dimensions.x += mouseX;
                dimensions.width -= mouseX;
                if (dimensions.width < this.options.minWidth) {
                    const difference = this.options.minWidth - dimensions.width;
                    dimensions.x -= difference;
                    dimensions.width += difference;
                }
                dimensions.y += mouseY;
                dimensions.height -= mouseY;
                if (dimensions.height < this.options.minHeight) {
                    const difference = this.options.minHeight - dimensions.height;
                    dimensions.y -= difference;
                    dimensions.height += difference;
                }
                break;
            case ActionDirection.TopRight:
                dimensions.width += mouseX;
                dimensions.y += mouseY;
                dimensions.height -= mouseY;
                if (dimensions.height < this.options.minHeight) {
                    const difference = this.options.minHeight - dimensions.height;
                    dimensions.y -= difference;
                    dimensions.height += difference;
                }
                break;
            case ActionDirection.BottomLeft:
                dimensions.height += mouseY;
                dimensions.x += mouseX;
                dimensions.width -= mouseX;
                if (dimensions.width < this.options.minWidth) {
                    const difference = this.options.minWidth - dimensions.width;
                    dimensions.x -= difference;
                    dimensions.width += difference;
                }
                break;
            case ActionDirection.BottomRight:
                dimensions.height += mouseY;
                dimensions.width += mouseX;
                break;
        }
        const currentWidth = action.widget.x + action.widget.width;
        const currentHeight = action.widget.y + action.widget.height;
        // ensure values are within the dashboard bounds
        if (dimensions.x < 0) {
            dimensions.x = 0;
            dimensions.width = currentWidth;
        }
        if (dimensions.y < 0) {
            dimensions.y = 0;
            dimensions.height = currentHeight;
        }
        if (dimensions.x + dimensions.width > this.dimensions.width) {
            dimensions.width = this.dimensions.width - dimensions.x;
        }
        // if the proposed width is smaller than allowed then reset width to minimum and ignore x changes
        if (dimensions.width < this.options.minWidth) {
            dimensions.x = action.widget.x;
            dimensions.width = this.options.minWidth;
        }
        // if the proposed height is smaller than allowed then reset height to minimum and ignore y changes
        if (dimensions.height < this.options.minHeight) {
            dimensions.y = action.widget.y;
            dimensions.height = this.options.minHeight;
        }
        const minWidth = this.getColumnWidth() * action.widget.minColSpan - this.getColumnWidth() / 2 + 1;
        const isBelowMinWidth = dimensions.width < minWidth;
        if (isBelowMinWidth) {
            dimensions.x = action.widget.x;
            dimensions.width = minWidth;
        }
        const minHeight = this.options.rowHeight * action.widget.minRowSpan - this.options.rowHeight / 2 + 1;
        const isBelowMinHeight = dimensions.height < minHeight;
        if (isBelowMinHeight) {
            dimensions.y = action.widget.y;
            dimensions.height = minHeight;
        }
        // update the widget actual values
        action.widget.setBounds(dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // update placeholder position and value
        this.setPlaceholderBounds(true, dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // show the widget positions if the current positions and sizes were to persist
        this.updateWidgetPositions(action.widget);
    }
    onResizeEnd() {
        const placeholder = this.placeholder$.getValue();
        // commit resize changes
        this.commitWidgetChanges();
        // hide placeholder
        placeholder.visible = false;
        // update the placeholder
        this.placeholder$.next(placeholder);
        this._actionWidget = null;
        this._event = null;
        // ensure any vacant upper spaces are filled where required
        this.shiftWidgetsUp();
        // update dashboard height
        this.setDashboardHeight();
        // emit information about the layout
        this.layout$.next(this.getLayoutData());
        this.userLayoutChange$.next(this.getLayoutData());
    }
    onDragStart(action) {
        this.onResizeStart(action);
        // store the starting placeholder position
        this.setWidgetOrigin();
        this.cacheWidgets();
        // emit the widget we are dragging
        this.isDragging$.next(action.widget);
    }
    onDragEnd() {
        this.onResizeEnd();
        this._widgetOrigin = {};
        this.isDragging$.getValue().sendToBack();
        this.isDragging$.next(null);
        this.userLayoutChange$.next(this.getLayoutData());
    }
    onDrag(action) {
        // if there was no movement then do nothing
        if (action.event.clientX === this._event.clientX &&
            action.event.clientY === this._event.clientY) {
            return;
        }
        // get the current mouse position
        const mouseX = action.event.clientX - this._event.clientX;
        const mouseY = action.event.clientY - this._event.clientY;
        // store the latest event
        this._event = action.event;
        this.moveWidget(action.widget, mouseX, mouseY);
    }
    onDragScroll(widget, event) {
        this.moveWidget(widget, event.offsetX, event.offsetY);
    }
    getRowHeight() {
        return this._rowHeight;
    }
    cacheWidgets() {
        this._cache = this.widgets.map(widget => ({
            id: widget.id,
            column: widget.getColumn(),
            row: widget.getRow(),
            columnSpan: widget.getColumnSpan(),
            rowSpan: widget.getRowSpan(),
        }));
        // return a new array of the cache for custom caching
        return [...this._cache];
    }
    restoreWidgets(ignoreActionWidget = false, cache = this._cache, restoreSize = false) {
        cache
            .filter(widget => !ignoreActionWidget || widget.id !== this._actionWidget.widget.id)
            .forEach(widget => {
            const match = this.widgets.find(wgt => wgt.id === widget.id);
            if (match) {
                match.setColumn(widget.column);
                match.setRow(widget.row);
                if (restoreSize) {
                    match.setColumnSpan(widget.columnSpan);
                    match.setRowSpan(widget.rowSpan);
                }
            }
        });
    }
    /**
     * Return the set of widgets which overlap the given dashboard region.
     */
    getOverlappingWidgets(region, actionWidget) {
        const widgetsToMove = [];
        // check if there are any widgets overlapping widgets
        for (let row = region.row; row < region.row + region.rowSpan; row++) {
            for (let column = region.column; column < region.column + region.columnSpan; column++) {
                // store reference to any widgets that need moved
                this.getOccupiedSpaces()
                    .filter(space => space.column === column && space.row === row && space.widget !== actionWidget)
                    .forEach(space => widgetsToMove.push(space.widget));
            }
        }
        // remove any duplicates
        return widgetsToMove.filter((wgt, idx, array) => array.indexOf(wgt) === idx);
    }
    /**
     * Resolve any overlapping widgets after a widget changes rowSpan/colSpan.
     */
    resizeWidget(widget) {
        // make widget action and origin widget, direction is irrelevant to this function so set to 0
        this._actionWidget = { widget, direction: 0 };
        this._widgetOrigin = widget;
        const widgetRegion = {
            row: widget.row,
            column: widget.col,
            rowSpan: widget.rowSpan,
            columnSpan: widget.colSpan,
        };
        let done = false;
        const ITERATION_LIMIT = 100;
        for (let i = 0; i <= ITERATION_LIMIT; i++) {
            // Check for overlapping widgets and move them. This may need several iterations.
            this.shiftWidgetsFromRegion(widgetRegion, widget);
            done = this.getOverlappingWidgets(widgetRegion, widget).length === 0;
            if (done) {
                break;
            }
        }
        if (!done) {
            throw new Error('Unable to resolve overlapping widgets!');
        }
        this.shiftWidgetsUp();
        // clear the action and origin widget once we are done
        this._actionWidget = undefined;
        this._widgetOrigin = undefined;
    }
    /**
     * Move any widgets which intersect with the given dashboard region.
     */
    shiftWidgetsFromRegion(region, actionWidget, validatePosition) {
        const widgetsToMove = this.getOverlappingWidgets(region, actionWidget);
        // if no widgets need moved then we can stop here
        if (widgetsToMove.length === 0) {
            return;
        }
        // create a duplicate we can use to keep track of which have been moved
        const unmovedWidgets = widgetsToMove.slice();
        // attempt to move any widgets to the previous widget position
        widgetsToMove.forEach(widget => {
            // get a grid off all occupied spaces - taking into account the placeholder and ignoring widgets that need moved
            const grid = this.getOccupiedSpaces().filter(space => !unmovedWidgets.find(wgt => wgt === space.widget));
            // iterate each free block
            for (let row = this._widgetOrigin.row; row < this._widgetOrigin.row + this._widgetOrigin.rowSpan; row++) {
                for (let column = this._widgetOrigin.column; column < this._widgetOrigin.column + this._widgetOrigin.columnSpan; column++) {
                    // determine if the block can fit in this space
                    const requiredSpaces = this.getRequiredSpacesFromPoint(widget, column, row);
                    // check if widget would fit in space
                    const available = requiredSpaces.every(space => {
                        return (!grid.find(gridSpace => gridSpace.column === space.column && gridSpace.row === space.row) && space.column < this.getColumnCount());
                    });
                    if (available) {
                        widget.setColumn(column);
                        widget.setRow(row);
                        unmovedWidgets.splice(unmovedWidgets.findIndex(wgt => wgt === widget), 1);
                        return;
                    }
                }
            }
            // if we get to here then we can't simply swap the positions - next try moving right
            if (this.canWidgetMoveRight(widget, true)) {
                // after the shift check if placeholder position is still valid
                validatePosition?.(ActionDirection.Right);
                return;
            }
            // next try moving left
            if (this.canWidgetMoveLeft(widget, true)) {
                // after the shift check if placeholder position is still valid
                validatePosition?.(ActionDirection.Left);
                return;
            }
            // determine the distance that the widget needs to be moved down
            const distance = this._actionWidget.widget.getRow() -
                widget.getRow() +
                this._actionWidget.widget.getRowSpan();
            // as a last resort move the widget downwards
            this.moveWidgetDown(widget, distance);
        });
    }
    /**
     * When dragging any widgets that need to be moved should be moved to an appropriate position
     */
    shiftWidgets() {
        const placeholder = this.placeholder$.getValue();
        this.shiftWidgetsFromRegion(placeholder, this._actionWidget.widget, this.validatePlaceholderPosition.bind(this));
    }
    /**
     * After shifts have taken place we should verify the place holder position is still valid
     * @param shiftDirection - the position widgets were shifted
     */
    validatePlaceholderPosition(shiftDirection) {
        const placeholder = this.placeholder$.getValue();
        // check if the placeholder is over a widget
        if (this.getWidgetsAtPosition(placeholder.column, placeholder.row, true).length > 0) {
            // move the placeholder the opposite direction
            switch (shiftDirection) {
                case ActionDirection.Left:
                    this.setPlaceholderBounds(placeholder.visible, placeholder.x + this.getColumnWidth(), placeholder.y, placeholder.width, placeholder.height);
                    break;
                case ActionDirection.Right:
                    this.setPlaceholderBounds(placeholder.visible, placeholder.x - this.getColumnWidth(), placeholder.y, placeholder.width, placeholder.height);
                    break;
            }
            // validate this new position again
            this.validatePlaceholderPosition(shiftDirection);
        }
    }
    /**
     * Determine if a widget can be moved left - or if it can move the widgets to the right to make space for the widget
     */
    canWidgetMoveLeft(widget, performMove = false, shift = 0) {
        const actionWgt = this._widgetOrigin;
        let colShift = shift;
        // check if the widget is the action widget or occupies the first column
        if (widget === this._actionWidget.widget || widget.getColumn() === 0) {
            return false;
        }
        // if value has been provided skip this step
        if (colShift === 0) {
            // work out how far the widget is planning to move left
            if (actionWgt.row !== widget.getRow()) {
                // if the widgets aren't on the same row work out the difference
                if (actionWgt.column === widget.getColumn()) {
                    // if the widgets occupy the same column then shift the widget of the action widget
                    colShift = actionWgt.columnSpan;
                }
                else {
                    // else work out the exact number of spaces it will move left
                    const widgetDifference = actionWgt.column - widget.getColumn();
                    colShift = widget.getColumnSpan() - widgetDifference;
                }
            }
            else {
                // if they are on the same row then move one row
                colShift = 1;
            }
        }
        if (isNaN(colShift) || colShift === 0) {
            return;
        }
        // find the positions required
        const targetSpaces = this.getOccupiedSpaces()
            .filter(space => space.widget === widget)
            .map(space => {
            return { column: space.column - colShift, row: space.row, widget: space.widget };
        });
        // check if any of the target spaces are out of bounds
        if (targetSpaces.find(space => space.column < 0)) {
            return false;
        }
        // check if there are widget in the required positions and if so, can they move right?
        const moveable = targetSpaces.every(space => this.getWidgetsAtPosition(space.column, space.row)
            .filter(wgt => wgt !== space.widget)
            .every(wgt => this.canWidgetMoveLeft(wgt, false, colShift)));
        if (performMove && moveable) {
            // move all widgets to the left
            targetSpaces.forEach(space => this.getWidgetsAtPosition(space.column, space.row)
                .filter(wgt => wgt !== space.widget)
                .forEach(wgt => this.canWidgetMoveLeft(wgt, true, colShift)));
            // find the target column
            const column = targetSpaces.reduce((target, space) => Math.min(target, space.column), Infinity);
            // move current widget to the left
            if (column !== Infinity) {
                widget.setColumn(column);
            }
        }
        return moveable;
    }
    /**
     * Determine if a widget can be moved right - or if it can move the widgets to the right to make space for the widget
     */
    canWidgetMoveRight(widget, performMove = false, shift = 0) {
        const actionWgt = this._widgetOrigin;
        let colShift = shift;
        // check if the widget is the dragging widget or the widget occupies the final column
        if (widget === this._actionWidget.widget ||
            widget.getColumn() + widget.getColumnSpan() === this.options.columns) {
            return false;
        }
        // if value has been provided skip this step
        if (colShift === 0) {
            // work out how far the widget is planning to move right
            if (actionWgt.row !== widget.getRow()) {
                // if the widgets aren't on the same row work out the difference
                if (actionWgt.column === widget.getColumn()) {
                    // if the widgets occupy the same column then shift the widget of the action widget
                    colShift = actionWgt.columnSpan;
                }
                else {
                    // else work out the exact number of spaces it will move right
                    const widgetDifference = widget.getColumn() - actionWgt.column;
                    colShift = actionWgt.columnSpan - widgetDifference;
                }
            }
            else {
                // if they are on the same row then move one row
                colShift = 1;
            }
        }
        if (isNaN(colShift) || colShift === 0) {
            return;
        }
        // find the positions required
        const targetSpaces = this.getOccupiedSpaces()
            .filter(space => space.widget === widget)
            .map(space => {
            return { column: space.column + colShift, row: space.row, widget: space.widget };
        });
        // check if any of the target spaces are out of bounds
        if (targetSpaces.find(space => space.column >= this.getColumnCount())) {
            return false;
        }
        // check if there are widget in the required positions and if so, can they move right?
        const moveable = targetSpaces.every(space => this.getWidgetsAtPosition(space.column, space.row)
            .filter(wgt => wgt !== space.widget)
            .every(wgt => this.canWidgetMoveRight(wgt, false, colShift)));
        if (performMove && moveable) {
            // move all widgets to the right
            targetSpaces.forEach(space => this.getWidgetsAtPosition(space.column, space.row)
                .filter(wgt => wgt !== space.widget)
                .forEach(wgt => this.canWidgetMoveRight(wgt, true, colShift)));
            // move current widget to the right
            widget.setColumn(widget.getColumn() + colShift);
        }
        return moveable;
    }
    /**
     * Store the initial position of the widget being dragged
     */
    setWidgetOrigin() {
        this._widgetOrigin = {
            column: this._actionWidget.widget.getColumn(),
            row: this._actionWidget.widget.getRow(),
            columnSpan: this._actionWidget.widget.getColumnSpan(),
            rowSpan: this._actionWidget.widget.getRowSpan(),
        };
    }
    /**
     * Calculate all the required positions is a widget was to be positioned at a particular point
     */
    getRequiredSpacesFromPoint(widget, column, row) {
        const spaces = [];
        for (let y = row; y < row + widget.getRowSpan(); y++) {
            for (let x = column; x < column + widget.getColumnSpan(); x++) {
                spaces.push({ column: x, row: y, widget });
            }
        }
        return spaces;
    }
    /**
     * Position widgets based on the position of the placeholder - this is temporary until confirmed
     */
    updateWidgetPositions(widget) {
        const placeholder = this.placeholder$.getValue();
        // check all spaces the placeholder will occupy and move any widget currently in them down
        for (let column = placeholder.column; column < placeholder.column + placeholder.columnSpan; column++) {
            for (let row = placeholder.row; row < placeholder.row + placeholder.rowSpan; row++) {
                this.getWidgetsAtPosition(column, row, true)
                    .filter(wgt => wgt !== widget)
                    .forEach(wgt => this.moveWidgetDown(wgt));
            }
        }
        // update the height of the dashboard
        this.setDashboardHeight();
        // if we arent dragging the top handle then fill spaces
        if (this._actionWidget.direction !== ActionDirection.Top &&
            this._actionWidget.direction !== ActionDirection.TopLeft &&
            this._actionWidget.direction !== ActionDirection.TopRight) {
            this.shiftWidgetsUp();
        }
    }
    /**
     * Determine if a widget is occupying a specific row and column
     * @param column The columns to check if occupied
     * @param row The row to check if occupied
     * @param ignoreResizing Whether or not to ignore the widget currently being resized
     */
    getWidgetsAtPosition(column, row, ignoreResizing = false) {
        return this.getOccupiedSpaces()
            .filter(space => space.column === column && space.row === row)
            .filter(space => (this._actionWidget && space.widget !== this._actionWidget.widget) || !ignoreResizing)
            .map(space => space.widget);
    }
    /**
     * Update the placeholder visibility, position and size
     */
    setPlaceholderBounds(visible, x, y, width, height) {
        const placeholder = this.placeholder$.getValue();
        placeholder.visible = visible;
        placeholder.column = this.getPlaceholderColumn(x, width);
        placeholder.row = this.getPlaceholderRow(y, height);
        placeholder.columnSpan = this.getPlaceholderColumnSpan(width);
        placeholder.rowSpan = this.getPlaceholderRowSpan(height);
        // calculate the maximum number of rows
        const rowCount = this.widgets
            .filter(widget => widget !== this._actionWidget.widget)
            .reduce((previous, widget) => Math.max(widget.getRow() + widget.getRowSpan(), previous), 0);
        // constrain maximum placeholder row
        placeholder.row = Math.min(placeholder.row, rowCount);
        placeholder.x = placeholder.column * this.getColumnWidth() + this.options.padding;
        placeholder.y = placeholder.row * this._rowHeight + this.options.padding;
        placeholder.width = placeholder.columnSpan * this.getColumnWidth() - this.options.padding * 2;
        placeholder.height = placeholder.rowSpan * this._rowHeight - this.options.padding * 2;
        // set the values of the widget to match the values of the placeholder - however do not render the changes
        this._actionWidget.widget.setColumn(placeholder.column, false);
        this._actionWidget.widget.setRow(placeholder.row, false);
        this._actionWidget.widget.setColumnSpan(placeholder.columnSpan, false);
        this._actionWidget.widget.setRowSpan(placeholder.rowSpan, false);
        // update the placeholder
        this.placeholder$.next(placeholder);
    }
    /**
     * Get the placeholder column position
     */
    getPlaceholderColumn(x, width) {
        const column = this.getColumnFromPx(x, this._actionWidget.direction === ActionDirection.Move
            ? Rounding.RoundUpOverHalf
            : Rounding.RoundDown);
        const columnSpan = Math.floor(width / this.getColumnWidth());
        const upperLimit = this.getColumnCount() - columnSpan;
        // if we arent dragging left then just return the column
        if (this._actionWidget.direction !== ActionDirection.Left &&
            this._actionWidget.direction !== ActionDirection.TopLeft &&
            this._actionWidget.direction !== ActionDirection.BottomLeft) {
            return Math.max(Math.min(column, upperLimit), 0);
        }
        // get any overflow
        const overflow = width % this.getColumnWidth();
        return x <= 0 || overflow === 0 || columnSpan === 0 || overflow > this.getColumnWidth() / 2
            ? Math.max(Math.min(column, upperLimit), 0)
            : Math.max(Math.min(column + 1, upperLimit), 0);
    }
    /**
     * Get the column span of the placeholder
     */
    getPlaceholderColumnSpan(width) {
        const columnSpan = this.getColumnFromPx(width);
        // if we arent dragging right or left then just return the column span
        if (this._actionWidget.direction !== ActionDirection.Right &&
            this._actionWidget.direction !== ActionDirection.TopRight &&
            this._actionWidget.direction !== ActionDirection.BottomRight &&
            this._actionWidget.direction !== ActionDirection.Left &&
            this._actionWidget.direction !== ActionDirection.TopLeft &&
            this._actionWidget.direction !== ActionDirection.BottomLeft) {
            return this.getColumnFromPx(width, Rounding.RoundUpOverHalf);
        }
        // get the current column span and any overflow
        const overflow = width % this.getColumnWidth();
        return columnSpan > 0 && overflow > this.getColumnWidth() / 2
            ? Math.max(columnSpan + 1, 1)
            : Math.max(columnSpan, 1);
    }
    /**
     * Get the row position of the placeholder
     */
    getPlaceholderRow(y, height) {
        const row = this.getRowFromPx(y, this._actionWidget.direction === ActionDirection.Move
            ? Rounding.RoundUpOverHalf
            : Rounding.RoundDown);
        const rowSpan = Math.ceil(height / this._rowHeight);
        // if we arent dragging up then just return the row
        if (this._actionWidget.direction !== ActionDirection.Top &&
            this._actionWidget.direction !== ActionDirection.TopLeft &&
            this._actionWidget.direction !== ActionDirection.TopRight) {
            return Math.max(row, 0);
        }
        // get any overflow
        const overflow = height < this._rowHeight ? 0 : height % this._rowHeight;
        return y <= 0 || rowSpan === 0 || overflow === 0 || overflow > this._rowHeight / 2
            ? Math.max(row, 0)
            : Math.max(row + 1, 0);
    }
    /**
     * Get the row span of the placeholder
     */
    getPlaceholderRowSpan(height) {
        const rowSpan = this.getRowFromPx(height);
        // if we arent dragging up or down then just return the column span
        if (this._actionWidget.direction !== ActionDirection.Top &&
            this._actionWidget.direction !== ActionDirection.TopLeft &&
            this._actionWidget.direction !== ActionDirection.TopRight &&
            this._actionWidget.direction !== ActionDirection.Bottom &&
            this._actionWidget.direction !== ActionDirection.BottomLeft &&
            this._actionWidget.direction !== ActionDirection.BottomRight) {
            return Math.max(rowSpan, 1);
        }
        // get the current column span and any overflow
        const overflow = height % this._rowHeight;
        return overflow > this._rowHeight / 2 ? Math.max(rowSpan + 1, 1) : Math.max(rowSpan, 1);
    }
    getColumnFromPx(x, rounding = Rounding.RoundDown) {
        const column = Math.floor(x / Math.floor(this.getColumnWidth()));
        const overflow = x % Math.floor(this.getColumnWidth());
        const half = this.getColumnWidth() / 2;
        switch (rounding) {
            case Rounding.RoundDown:
                return column;
            case Rounding.RoundDownBelowHalf:
                return overflow < half ? column : column + 1;
            case Rounding.RoundUpOverHalf:
                return overflow > half ? column + 1 : column;
            case Rounding.RoundUp:
                return overflow > 0 ? column + 1 : column;
        }
    }
    getRowFromPx(y, rounding = Rounding.RoundDown) {
        const row = Math.floor(y / Math.floor(this._rowHeight));
        const overflow = y % Math.floor(this._rowHeight);
        const half = this._rowHeight / 2;
        switch (rounding) {
            case Rounding.RoundDown:
                return row;
            case Rounding.RoundDownBelowHalf:
                return overflow < half ? row : row + 1;
            case Rounding.RoundUpOverHalf:
                return overflow > half ? row + 1 : row;
            case Rounding.RoundUp:
                return overflow > 0 ? row + 1 : row;
        }
    }
    commitWidgetChanges() {
        const placeholder = this.placeholder$.getValue();
        // check that we have all the values we need
        if (placeholder.column === undefined ||
            placeholder.row === undefined ||
            placeholder.columnSpan === undefined ||
            placeholder.rowSpan === undefined) {
            return;
        }
        if (this._actionWidget) {
            this._actionWidget.widget.setColumn(placeholder.column);
            this._actionWidget.widget.setRow(placeholder.row);
            this._actionWidget.widget.setColumnSpan(placeholder.columnSpan);
            this._actionWidget.widget.setRowSpan(placeholder.rowSpan);
        }
        // reset all placeholder values
        placeholder.column = undefined;
        placeholder.row = undefined;
        placeholder.columnSpan = undefined;
        placeholder.rowSpan = undefined;
        // emit the new placeholder values
        this.placeholder$.next(placeholder);
    }
    /**
     * Get the current column width
     */
    getColumnWidth() {
        return Math.floor(this.columnWidth);
    }
    /**
     * Calculate the number of rows populated with widgets
     */
    getRowCount() {
        return this.widgets.reduce((previous, widget) => Math.max(widget.getRow() + widget.getRowSpan(), previous), 0);
    }
    /**
     * Set the height of the dashboard container element
     */
    setDashboardHeight() {
        // size the dashboard container to ensure all rows fit
        let rowCount = this.getRowCount();
        // if we should show an empty row increment the row count by 1
        if (this.options.emptyRow) {
            rowCount++;
        }
        this.setDimensions(undefined, rowCount * this._rowHeight);
    }
    /**
     * Orders the z-index of all widgets to move the active one to the front
     * @param widget The widget that should be brought to the front
     */
    bringToFront(widget) {
        this.widgets.forEach(_widget => _widget === widget ? _widget.bringToFront() : _widget.sendToBack());
    }
    /**
     * Move a widget down - if widgets are in the position below, then move them down further
     * @param widget The widget to move downwards
     */
    moveWidgetDown(widget, distance = 1) {
        // stop if a negative number is passed through
        if (distance < 0) {
            return;
        }
        // move the widget down one position
        widget.setRow(widget.getRow(DashboardStackMode.Auto) + distance);
        // check every space the widget occupies for collisions
        this.forEachBlock(widget, (column, row) => this.getWidgetsAtPosition(column, row, true)
            .filter(wgt => wgt !== widget)
            .forEach(wgt => this.moveWidgetDown(wgt, distance)));
    }
    /**
     * Widgets should not be allowed to have a vacant space above them - if there is one they should move upwards to fill it
     */
    shiftWidgetsUp() {
        // check whether or not changes have been made - if so we need to repeat until stable
        let stable = true;
        // iterate each widget and
        this.widgets.forEach(widget => {
            const widgetIsOnTopRow = widget.getRow() === 0;
            const widgetIsBeingResized = this._actionWidget?.widget === widget;
            const widgetShouldBeAutoPositioned = widget.autoPositioning || this.stacked;
            const widgetIsBeingMoved = !widgetShouldBeAutoPositioned && this.isDragging$.value?.id === widget.id;
            if (widgetIsOnTopRow ||
                widgetIsBeingResized ||
                widgetIsBeingMoved ||
                (!widgetShouldBeAutoPositioned && !this._cache)) {
                return;
            }
            if (!widgetShouldBeAutoPositioned) {
                const cachedVersionOfWidget = this._cache.find(cachedWidget => cachedWidget.id === widget.id);
                const isPreviousPositionAvailable = this.getPositionAvailable(cachedVersionOfWidget.column, cachedVersionOfWidget.row, cachedVersionOfWidget.columnSpan, cachedVersionOfWidget.rowSpan, widget);
                if (isPreviousPositionAvailable && widget.row !== cachedVersionOfWidget.row) {
                    widget.setRow(cachedVersionOfWidget.row);
                    stable = false;
                }
                return;
            }
            if (this.getPositionAvailable(widget.getColumn(), widget.getRow() - 1, widget.getColumnSpan(), 1)) {
                widget.setRow(widget.getRow() - 1);
                stable = false;
            }
        });
        // if changes occurred then we should repeat the process
        if (!stable) {
            this.shiftWidgetsUp();
            return true;
        }
        return false;
    }
    /**
     * Iterate over each space a widget occupied
     * @param widget The widget to determine spaces
     * @param callback The function to be called for each space, should expect a column and row argument witht he context being the widget
     */
    forEachBlock(widget, callback) {
        for (let row = widget.getRow(); row < widget.getRow() + widget.getRowSpan(); row++) {
            for (let column = widget.getColumn(); column < widget.getColumn() + widget.getColumnSpan(); column++) {
                callback.call(widget, column, row);
            }
        }
    }
    getWidgetBelow(widget) {
        const target = this.getWidgetsAtPosition(widget.getColumn(), widget.getRow() + widget.getRowSpan(), true);
        return target.length > 0 ? target[0] : null;
    }
    /**
     * Returns the number of columns available
     */
    getColumnCount() {
        return this.stacked ? 1 : this.options.columns;
    }
    onShiftStart(widget) {
        this.onDragStart({ direction: ActionDirection.Move, widget });
    }
    /** Programmatically move a widget in a given direction */
    onShift(widget, direction) {
        // get the current mouse position
        let deltaX = 0, deltaY = 0;
        // move based on the direction
        switch (direction) {
            case ActionDirection.Top:
                deltaY = -this.getRowHeight();
                break;
            case ActionDirection.Right:
                deltaX = this.getColumnWidth();
                break;
            case ActionDirection.Bottom: {
                deltaY = this.getRowHeight();
                break;
            }
            case ActionDirection.Left:
                deltaX = -this.getColumnWidth();
                break;
        }
        const dimensions = {
            x: widget.x + deltaX,
            y: widget.y + deltaY,
            width: widget.width,
            height: widget.height,
        };
        // update placeholder position and value
        this.setPlaceholderBounds(false, dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // update widget position
        const { x, y } = this.placeholder$.value;
        // move the widget to the placeholder position
        widget.setBounds(x - this.options.padding, y - this.options.padding, dimensions.width, dimensions.height);
        // update the height of the dashboard
        this.setDashboardHeight();
    }
    onShiftEnd() {
        // show the widget positions if the current positions and sizes were to persist
        this.shiftWidgets();
        // the height of the dashboard may have changed after moving widgets
        this.setDashboardHeight();
        // reset all properties
        this.onDragEnd();
    }
    /** Programmatically resize a widget in a given direction */
    onResize(widget, direction) {
        // perform the resizing
        let deltaX = 0, deltaY = 0;
        // move based on the direction
        switch (direction) {
            case ActionDirection.Top:
                deltaY = -this.getRowHeight();
                break;
            case ActionDirection.Right:
                deltaX = this.getColumnWidth();
                break;
            case ActionDirection.Bottom:
                deltaY = this.getRowHeight();
                break;
            case ActionDirection.Left:
                deltaX = -this.getColumnWidth();
                break;
        }
        const dimensions = {
            x: widget.x,
            y: widget.y,
            width: widget.width + deltaX,
            height: widget.height + deltaY,
        };
        const currentWidth = widget.x + widget.width;
        const currentHeight = widget.y + widget.height;
        // ensure values are within the dashboard bounds
        if (dimensions.x < 0) {
            dimensions.x = 0;
            dimensions.width = currentWidth;
        }
        if (dimensions.y < 0) {
            dimensions.y = 0;
            dimensions.height = currentHeight;
        }
        if (dimensions.x + dimensions.width > this.getColumnWidth() * this.getColumnCount()) {
            dimensions.width = widget.width;
        }
        // if the proposed width is smaller than allowed then reset width to minimum and ignore x changes
        if (dimensions.width < this.getColumnWidth()) {
            dimensions.x = widget.x;
            dimensions.width = this.getColumnWidth();
        }
        // if the proposed height is smaller than allowed then reset height to minimum and ignore y changes
        if (dimensions.height < this.getRowHeight()) {
            dimensions.y = widget.y;
            dimensions.height = this.getRowHeight();
        }
        const minWidth = this.getColumnWidth() * widget.minColSpan;
        const isBelowMinWidth = dimensions.width < minWidth;
        if (isBelowMinWidth) {
            dimensions.x = widget.x;
            dimensions.width = minWidth;
        }
        const minHeight = this.options.rowHeight * widget.minRowSpan;
        const isBelowMinHeight = dimensions.height < minHeight;
        if (isBelowMinHeight) {
            dimensions.y = widget.y;
            dimensions.height = minHeight;
        }
        // move the widget to the placeholder position
        widget.setBounds(dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // update placeholder position and value
        this.setPlaceholderBounds(false, dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // the height of the dashboard may have changed after moving widgets
        this.setDashboardHeight();
    }
    getSurroundingWidgets(widget, direction) {
        let widgets = [];
        for (let column = widget.getColumn(); column < widget.getColumn() + widget.getColumnSpan(); column++) {
            switch (direction) {
                case ActionDirection.Top:
                    widgets = [...widgets, ...this.getWidgetsAtPosition(column, widget.getRow() - 1)];
                    break;
                case ActionDirection.Bottom:
                    widgets = [
                        ...widgets,
                        ...this.getWidgetsAtPosition(column, widget.getRow() + widget.getRowSpan()),
                    ];
                    break;
            }
        }
        return widgets;
    }
    moveWidget(widget, offsetX, offsetY) {
        const dimensions = {
            x: widget.x + offsetX,
            y: widget.y + offsetY,
            width: widget.width,
            height: widget.height,
        };
        this.restoreWidgets(true);
        // update widget position
        widget.setBounds(dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // update placeholder position and value
        this.setPlaceholderBounds(true, dimensions.x, dimensions.y, dimensions.width, dimensions.height);
        // show the widget positions if the current positions and sizes were to persist
        this.shiftWidgets();
        this.setDashboardHeight();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });
const defaultOptions = {
    columns: 5,
    padding: 5,
    minWidth: 100,
    minHeight: 100,
    emptyRow: true,
};
var ActionDirection;
(function (ActionDirection) {
    ActionDirection[ActionDirection["Top"] = 0] = "Top";
    ActionDirection[ActionDirection["TopRight"] = 1] = "TopRight";
    ActionDirection[ActionDirection["Right"] = 2] = "Right";
    ActionDirection[ActionDirection["BottomRight"] = 3] = "BottomRight";
    ActionDirection[ActionDirection["Bottom"] = 4] = "Bottom";
    ActionDirection[ActionDirection["BottomLeft"] = 5] = "BottomLeft";
    ActionDirection[ActionDirection["Left"] = 6] = "Left";
    ActionDirection[ActionDirection["TopLeft"] = 7] = "TopLeft";
    ActionDirection[ActionDirection["Move"] = 8] = "Move";
})(ActionDirection || (ActionDirection = {}));
var Rounding;
(function (Rounding) {
    Rounding[Rounding["RoundDown"] = 0] = "RoundDown";
    Rounding[Rounding["RoundDownBelowHalf"] = 1] = "RoundDownBelowHalf";
    Rounding[Rounding["RoundUp"] = 2] = "RoundUp";
    Rounding[Rounding["RoundUpOverHalf"] = 3] = "RoundUpOverHalf";
})(Rounding || (Rounding = {}));

class DashboardWidgetComponent {
    /** Defines the column the widget is placed in */
    set col(col) {
        if (col !== null && col !== undefined) {
            this.setColumn(coerceNumberProperty(col));
            this.dashboardService.renderDashboard();
        }
    }
    get col() {
        return this.getColumn();
    }
    /** Defines the row the widget is placed in */
    set row(row) {
        if (row !== null && row !== undefined) {
            this.setRow(coerceNumberProperty(row));
            this.dashboardService.renderDashboard();
        }
    }
    get row() {
        return this.getRow();
    }
    /** Defines the number of columns this widget should occupy. */
    get colSpan() {
        return this.getColumnSpan();
    }
    set colSpan(colSpan) {
        if (colSpan !== null && colSpan !== undefined) {
            this.setColumnSpan(coerceNumberProperty(colSpan));
        }
    }
    /** Defines the number of rows this widget should occupy. */
    get rowSpan() {
        return this.getRowSpan();
    }
    set rowSpan(rowSpan) {
        if (rowSpan !== null && rowSpan !== undefined) {
            this.setRowSpan(coerceNumberProperty(rowSpan));
        }
    }
    /** Defines the minimum number of columns this widget should occupy. */
    get minColSpan() {
        return this._minColSpan;
    }
    set minColSpan(minColumns) {
        this._minColSpan = coerceNumberProperty(minColumns);
    }
    /** Defines the minimum number of rows this widget should occupy. */
    get minRowSpan() {
        return this._minRowSpan;
    }
    set minRowSpan(minRows) {
        this._minRowSpan = coerceNumberProperty(minRows);
    }
    /** Defines whether or not this widget will be automatically repositioned */
    set autoPositioning(autoPositioning) {
        this._autoPositioning = coerceBooleanProperty(autoPositioning);
    }
    get autoPositioning() {
        return this._autoPositioning;
    }
    constructor() {
        this.dashboardService = inject(DashboardService);
        /** Defines whether or not this widget can be resized. */
        this.resizable = false;
        /** Defines a function that returns an aria label for the widget */
        this.widgetAriaLabel = this.getDefaultAriaLabel;
        this.x = 0;
        this.y = 0;
        this.width = 100;
        this.height = 100;
        this.padding = 0;
        this.zIndex = null;
        this.role = 'group';
        this.isDragging = false;
        this.isGrabbing = false;
        this.isResizing = false;
        this.isDraggable = false;
        this._column = { regular: undefined, stacked: undefined };
        this._row = { regular: undefined, stacked: undefined };
        this._columnSpan = { regular: 1, stacked: 1 };
        this._rowSpan = { regular: 1, stacked: 1 };
        this._minColSpan = 1;
        this._minRowSpan = 1;
        this._autoPositioning = true;
        this._onDestroy = new Subject();
        // subscribe to option changes
        this.dashboardService.options$.pipe(takeUntil(this._onDestroy)).subscribe(() => this.update());
        // every time the layout changes we want to update the aria label
        this.dashboardService.layout$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this.ariaLabel = this.getAriaLabel()));
        // allow widget movements to be animated
        this.dashboardService.isDragging$
            .pipe(takeUntil(this._onDestroy), map(widget => widget === this))
            .subscribe(isDragging => (this.isDragging = isDragging));
        // allow widget movements to be animated
        this.dashboardService.isGrabbing$
            .pipe(takeUntil(this._onDestroy), map(widget => widget === this))
            .subscribe(isGrabbing => (this.isGrabbing = isGrabbing));
    }
    ngOnInit() {
        this._columnSpan.regular = this.colSpan;
        this._rowSpan.regular = this.rowSpan;
        this._rowSpan.stacked = this.rowSpan;
        this._row.regular = this.row;
        this._row.stacked = this.row;
        this._column.regular = this.col;
        this._column.stacked = this.col;
        if (!this.id) {
            console.warn('Dashboard Widget is missing an ID.');
            // set random id - keeps things working but prevents exporting of positions
            this.id = Math.floor(Math.random() * 100000).toString();
        }
    }
    ngAfterViewInit() {
        // add the widget to the dashboard
        this.dashboardService.addWidget(this);
        // apply the current options
        this.update();
    }
    ngOnChanges(changes) {
        if (this.hasPosition() && (changes.colSpan || changes.rowSpan)) {
            this.dashboardService.resizeWidget(this);
            this.dashboardService.renderDashboard();
        }
    }
    /**
     * If component is removed, then unregister it from the service
     */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this.dashboardService.removeWidget(this);
    }
    /**
     * Apply the current dashboard options
     */
    update() {
        // get the current options at the time
        const { padding, columns } = this.dashboardService.options;
        this.padding = padding;
        this._columnSpan.stacked = columns;
    }
    /**
     * Set the actual position and size values
     */
    render() {
        this.x = this.getColumn() * this.dashboardService.getColumnWidth();
        this.y = this.getRow() * this.dashboardService.getRowHeight();
        this.width = this.getColumnSpan() * this.dashboardService.getColumnWidth();
        this.height = this.getRowSpan() * this.dashboardService.getRowHeight();
    }
    getColumn(mode = DashboardStackMode.Auto) {
        switch (mode) {
            case DashboardStackMode.Auto:
                return this.getStackableValue(this._column);
            case DashboardStackMode.Regular:
                return this._column.regular;
            case DashboardStackMode.Stacked:
                return this._column.stacked;
        }
    }
    getRow(mode = DashboardStackMode.Auto) {
        switch (mode) {
            case DashboardStackMode.Auto:
                return this.getStackableValue(this._row);
            case DashboardStackMode.Regular:
                return this._row.regular;
            case DashboardStackMode.Stacked:
                return this._row.stacked;
        }
    }
    setColumn(column, render = true) {
        this.setStackableValue(this._column, column);
        if (render) {
            this.render();
        }
    }
    setRow(row, render = true) {
        this.setStackableValue(this._row, row);
        if (render) {
            this.render();
        }
    }
    getColumnSpan() {
        return this.getStackableValue(this._columnSpan);
    }
    getRowSpan() {
        return this.getStackableValue(this._rowSpan);
    }
    setColumnSpan(columnSpan, render = true) {
        if (columnSpan >= this.minColSpan) {
            this.setStackableValue(this._columnSpan, columnSpan);
            if (render) {
                this.render();
            }
        }
    }
    setRowSpan(rowSpan, render = true) {
        if (rowSpan >= this.minRowSpan) {
            this.setStackableValue(this._rowSpan, rowSpan);
            if (render) {
                this.render();
            }
        }
    }
    bringToFront() {
        this.zIndex = 1;
    }
    sendToBack() {
        this.zIndex = null;
    }
    setBounds(x, y, width, height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    dragstart(handle, event, direction) {
        this.isResizing = true;
        this.dashboardService.isGrabbing$.next(null);
        this.dashboardService.onResizeStart({ widget: this, direction, event, handle });
    }
    drag(handle, event, direction) {
        this.dashboardService.onResizeDrag({ widget: this, direction, event, handle });
    }
    dragend() {
        this.isResizing = false;
        this.dashboardService.onResizeEnd();
    }
    getAriaLabel() {
        if (this.widgetAriaLabel && typeof this.widgetAriaLabel === 'string') {
            return this.widgetAriaLabel;
        }
        else if (this.widgetAriaLabel && typeof this.widgetAriaLabel === 'function') {
            return this.widgetAriaLabel(this);
        }
        return this.ariaLabel;
    }
    getDefaultAriaLabel(widget) {
        let options = '';
        if (widget.resizable && widget.isDraggable) {
            options = 'It can be moved and resized.';
        }
        else if (widget.resizable) {
            options = 'It can be resized.';
        }
        else if (widget.isDraggable) {
            options = 'It can be moved.';
        }
        return `${widget.name} panel in row ${widget.getRow()}, column ${widget.getColumn()}, is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high. ${options}`;
    }
    /**
     * Allows automatic setting of stackable value
     * @param property The current StackableValue object
     * @param value The value to set in the appropriate field
     */
    setStackableValue(property, value) {
        if (this.dashboardService.stacked) {
            property.stacked = value;
        }
        else {
            property.regular = value;
        }
    }
    /**
     * Return the appropriate value from a stackable value
     * @param property The Stackable value object
     */
    getStackableValue(property) {
        return this.dashboardService.stacked ? property.stacked : property.regular;
    }
    /** Determine if the layout has been performed yet */
    hasPosition() {
        return this.getColumn() !== undefined && this.getRow() !== undefined;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DashboardWidgetComponent, isStandalone: false, selector: "ux-dashboard-widget", inputs: { id: "id", name: "name", col: "col", row: "row", colSpan: "colSpan", rowSpan: "rowSpan", minColSpan: "minColSpan", minRowSpan: "minRowSpan", resizable: "resizable", autoPositioning: "autoPositioning", widgetAriaLabel: "widgetAriaLabel", role: "role" }, host: { properties: { "style.left.px": "this.x", "style.top.px": "this.y", "style.width.px": "this.width", "style.height.px": "this.height", "style.padding.px": "this.padding", "style.z-index": "this.zIndex", "attr.aria-label": "this.ariaLabel", "attr.role": "this.role", "class.dragging": "this.isDragging", "class.grabbing": "this.isGrabbing", "class.resizing": "this.isResizing" } }, usesOnChanges: true, ngImport: i0, template: "<div\n  class=\"widget-content widget-col-span-{{ getColumnSpan() }} widget-row-span-{{ getRowSpan() }}\"\n>\n  <ng-content></ng-content>\n</div>\n\n<div\n  uxDrag\n  #handleTop\n  class=\"resizer-handle handle-top\"\n  (onDragStart)=\"dragstart(handleTop, $event, 0)\"\n  (onDrag)=\"drag(handleTop, $event, 0)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [hidden]=\"!resizable\"\n></div>\n\n<div\n  uxDrag\n  #handleTopRight\n  class=\"resizer-handle handle-top-right\"\n  (onDragStart)=\"dragstart(handleTopRight, $event, 1)\"\n  (onDrag)=\"drag(handleTopRight, $event, 1)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleRight\n  class=\"resizer-handle handle-right\"\n  (onDragStart)=\"dragstart(handleRight, $event, 2)\"\n  (onDrag)=\"drag(handleRight, $event, 2)\"\n  (onDragEnd)=\"dragend()\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleBottomRight\n  class=\"resizer-handle handle-bottom-right\"\n  (onDragStart)=\"dragstart(handleBottomRight, $event, 3)\"\n  (onDrag)=\"drag(handleBottomRight, $event, 3)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleBottom\n  class=\"resizer-handle handle-bottom\"\n  (onDragStart)=\"dragstart(handleBottom, $event, 4)\"\n  (onDrag)=\"drag(handleBottom, $event, 4)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [hidden]=\"!resizable\"\n></div>\n\n<div\n  uxDrag\n  #handleBottomLeft\n  class=\"resizer-handle handle-bottom-left\"\n  (onDragStart)=\"dragstart(handleBottomLeft, $event, 5)\"\n  (onDrag)=\"drag(handleBottomLeft, $event, 5)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleLeft\n  class=\"resizer-handle handle-left\"\n  (onDragStart)=\"dragstart(handleLeft, $event, 6)\"\n  (onDrag)=\"drag(handleLeft, $event, 6)\"\n  (onDragEnd)=\"dragend()\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleTopLeft\n  class=\"resizer-handle handle-top-left\"\n  (onDragStart)=\"dragstart(handleTopLeft, $event, 7)\"\n  (onDrag)=\"drag(handleTopLeft, $event, 7)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n", dependencies: [{ kind: "directive", type: DragDirective, selector: "[uxDrag]", inputs: ["clone", "group", "model", "draggable"], outputs: ["onDragStart", "onDrag", "onDragScroll", "onDragEnd", "onDrop", "onDropEnter", "onDropLeave"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardWidgetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-dashboard-widget', standalone: false, template: "<div\n  class=\"widget-content widget-col-span-{{ getColumnSpan() }} widget-row-span-{{ getRowSpan() }}\"\n>\n  <ng-content></ng-content>\n</div>\n\n<div\n  uxDrag\n  #handleTop\n  class=\"resizer-handle handle-top\"\n  (onDragStart)=\"dragstart(handleTop, $event, 0)\"\n  (onDrag)=\"drag(handleTop, $event, 0)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [hidden]=\"!resizable\"\n></div>\n\n<div\n  uxDrag\n  #handleTopRight\n  class=\"resizer-handle handle-top-right\"\n  (onDragStart)=\"dragstart(handleTopRight, $event, 1)\"\n  (onDrag)=\"drag(handleTopRight, $event, 1)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleRight\n  class=\"resizer-handle handle-right\"\n  (onDragStart)=\"dragstart(handleRight, $event, 2)\"\n  (onDrag)=\"drag(handleRight, $event, 2)\"\n  (onDragEnd)=\"dragend()\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleBottomRight\n  class=\"resizer-handle handle-bottom-right\"\n  (onDragStart)=\"dragstart(handleBottomRight, $event, 3)\"\n  (onDrag)=\"drag(handleBottomRight, $event, 3)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [style.right.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleBottom\n  class=\"resizer-handle handle-bottom\"\n  (onDragStart)=\"dragstart(handleBottom, $event, 4)\"\n  (onDrag)=\"drag(handleBottom, $event, 4)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [hidden]=\"!resizable\"\n></div>\n\n<div\n  uxDrag\n  #handleBottomLeft\n  class=\"resizer-handle handle-bottom-left\"\n  (onDragStart)=\"dragstart(handleBottomLeft, $event, 5)\"\n  (onDrag)=\"drag(handleBottomLeft, $event, 5)\"\n  (onDragEnd)=\"dragend()\"\n  [style.bottom.px]=\"padding\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleLeft\n  class=\"resizer-handle handle-left\"\n  (onDragStart)=\"dragstart(handleLeft, $event, 6)\"\n  (onDrag)=\"drag(handleLeft, $event, 6)\"\n  (onDragEnd)=\"dragend()\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n\n<div\n  uxDrag\n  #handleTopLeft\n  class=\"resizer-handle handle-top-left\"\n  (onDragStart)=\"dragstart(handleTopLeft, $event, 7)\"\n  (onDrag)=\"drag(handleTopLeft, $event, 7)\"\n  (onDragEnd)=\"dragend()\"\n  [style.top.px]=\"padding\"\n  [style.left.px]=\"padding\"\n  [hidden]=\"!resizable || (dashboardService.stacked$ | async)\"\n></div>\n" }]
        }], ctorParameters: () => [], propDecorators: { id: [{
                type: Input
            }], name: [{
                type: Input
            }], col: [{
                type: Input
            }], row: [{
                type: Input
            }], colSpan: [{
                type: Input
            }], rowSpan: [{
                type: Input
            }], minColSpan: [{
                type: Input
            }], minRowSpan: [{
                type: Input
            }], resizable: [{
                type: Input
            }], autoPositioning: [{
                type: Input
            }], widgetAriaLabel: [{
                type: Input
            }], x: [{
                type: HostBinding,
                args: ['style.left.px']
            }], y: [{
                type: HostBinding,
                args: ['style.top.px']
            }], width: [{
                type: HostBinding,
                args: ['style.width.px']
            }], height: [{
                type: HostBinding,
                args: ['style.height.px']
            }], padding: [{
                type: HostBinding,
                args: ['style.padding.px']
            }], zIndex: [{
                type: HostBinding,
                args: ['style.z-index']
            }], ariaLabel: [{
                type: HostBinding,
                args: ['attr.aria-label']
            }], role: [{
                type: HostBinding,
                args: ['attr.role']
            }, {
                type: Input
            }], isDragging: [{
                type: HostBinding,
                args: ['class.dragging']
            }], isGrabbing: [{
                type: HostBinding,
                args: ['class.grabbing']
            }], isResizing: [{
                type: HostBinding,
                args: ['class.resizing']
            }] } });

class DashboardGrabHandleService {
    constructor() {
        this._dashboard = inject(DashboardService);
        /** Self-registered drag handles in the dashboard. */
        this._handles = [];
        /** Automatically unsubscribe from all observables when destroyed */
        this._onDestroy = new Subject();
        // if a drag is performed by the mouse we should update the focusable item to be the first again
        this._dashboard.layout$
            .pipe(takeUntil(this._onDestroy), filter(() => !this._dashboard.isGrabbing$.value))
            .subscribe(() => this.setFirstItemFocusable());
    }
    /** Perform unsubscriptions */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Register a new grab handle. */
    addHandle(handle) {
        this._handles = this.getHandlesInOrder([...this._handles, handle]);
        // we want to make the first item focusable (raf to avoid expression changed error)
        requestAnimationFrame(() => this.ensureFocusable());
    }
    /** Unregister a removed grab handle. */
    removeHandle(handle) {
        this._handles = this._handles.filter(h => h !== handle);
        // Make sure there is still a focusable handle
        this.ensureFocusable();
    }
    /** Make the first visual item in the list focusable */
    setFirstItemFocusable() {
        this.setItemFocus(0, false);
    }
    /** Set an item at a given index focused */
    setItemFocus(index, focusElement = true) {
        // if the list is empty then do nothing
        if (!this._handles || this._handles.length === 0) {
            return;
        }
        // check if the index is out of bounds
        if (index < 0) {
            return this.setItemFocus(0);
        }
        if (index > this._handles.length - 1) {
            return this.setItemFocus(this._handles.length - 1);
        }
        // try focusing a specific index
        this.getHandlesInOrder().forEach((handle, idx) => idx === index ? handle.focus(focusElement) : handle.blur());
        // for safety we want to ensure one of the items is definitely still focusabled
        this.ensureFocusable();
    }
    /** Focus the previous grab handle */
    setPreviousItemFocus(handle) {
        this.setItemFocus(this.getHandleIndex(handle) - 1);
    }
    /** Focus the next grab handle */
    setNextItemFocus(handle) {
        this.setItemFocus(this.getHandleIndex(handle) + 1);
    }
    /** Focus the grab handle on the widget above */
    setSiblingItemFocus(widget, direction) {
        // find all widgets that are directly above and have grab handles
        const target = this._dashboard
            .getSurroundingWidgets(widget, direction)
            .map(_widget => this._handles.find(handle => handle.widget === _widget))
            .filter(handle => !!handle)
            .reduce((handle, current) => !handle || current.widget.getColumn() > handle.widget.getColumn() ? current : handle, null);
        // ensure we have a target before focusing
        if (!target) {
            return;
        }
        // get the index of the target handle
        const index = this.getHandleIndex(target);
        // focus the item
        this.setItemFocus(index);
    }
    /** Get handles in the order they appear rather than the order they are in the DOM */
    getHandlesInOrder(handles = this._handles) {
        const widgets = this._dashboard.getWidgetsByOrder();
        // sort the handles according to the position of the widget it belongs to
        return handles.sort((handleOne, handleTwo) => widgets.indexOf(handleOne.widget) - widgets.indexOf(handleTwo.widget));
    }
    getHandleIndex(handle) {
        return this.getHandlesInOrder().findIndex(_handle => _handle === handle);
    }
    /** If the current focusable handle is removed we need to make another one focusable */
    ensureFocusable() {
        if (!this._handles.find(handle => handle.tabIndex === 0)) {
            this.setFirstItemFocusable();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardGrabHandleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardGrabHandleService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardGrabHandleService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class DashboardGrabHandleDirective {
    constructor() {
        this.widget = inject(DashboardWidgetComponent);
        this._dashboard = inject(DashboardService);
        this._handle = inject(DashboardGrabHandleService);
        this._elementRef = inject(ElementRef);
        this._announcer = inject(LiveAnnouncer);
        /** Specify whether or not this handle can be used to perform moving */
        this.uxGrabAllowMove = true;
        /** Specify whether or not this handle can be used to perform resizing */
        this.uxGrabAllowResize = true;
        /** The aria label for the grab handle */
        this.uxGrabAriaLabel = this.getDefaultAriaLabel.bind(this);
        /** Customize the announcement that is made whenever an item has successfully been moved or resized */
        this.uxGrabChangeSuccessAnnouncement = this.getChangeSuccessAnnouncement.bind(this);
        /** Customize the announcement that is made whenever an item enters 'grab' mode */
        this.uxGrabStartAnnouncement = this.getStartAnnouncement.bind(this);
        /** Customize the announcement thqt is made whenever an item cannot be moved */
        this.uxGrabMoveFailAnnouncement = this.getMoveFailAnnouncement.bind(this);
        /** Customize the announcement thqt is made whenever an item cannot be resized */
        this.uxGrabResizeFailAnnouncement = this.getResizeFailAnnouncement.bind(this);
        /** Customize the announcement made whenever the moving/resizing is commited */
        this.uxGrabConfirmAnnouncement = this.getConfirmAnnouncement.bind(this);
        /** Customize the announcement made whenever the moving/resizing is cancelled */
        this.uxGrabCancelAnnouncement = this.getCancellationAnnouncement.bind(this);
        /** We must programmatically control the focus of the drag handles */
        this.tabIndex = -1;
        /** Store the current dragging state */
        this.isGrabbing = false;
        /** Emit when the directive is destroyed to unsubscribe from all observables */
        this._onDestroy = new Subject();
        if (!this.widget) {
            throw new Error('uxDashboardGrabHandle must be used within a dashboard widget');
        }
        this._handle.addHandle(this);
        // subscribe to changes to the current grab state
        this._dashboard.isGrabbing$
            .pipe(takeUntil(this._onDestroy), map(_widget => _widget === this.widget))
            .subscribe(isGrabbing => (this.isGrabbing = isGrabbing));
    }
    /** Set the initial aria label and subscribe to layout changes */
    ngOnInit() {
        if (!this.widget.name) {
            console.warn(`Dashboard widget ${this.widget.id} must have a valid 'name' to use uxDashboardGrabHandle`);
        }
        // set the initial aria label
        this.ariaLabel = this.getAnnouncement(this.uxGrabAriaLabel);
        // update the aria label when layout changes occur
        this._dashboard.layout$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this.ariaLabel = this.getAnnouncement(this.uxGrabAriaLabel)));
    }
    /** Unsubscribe from all observables */
    ngOnDestroy() {
        this._handle.removeHandle(this);
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Begin drag mode and cache initial state */
    enableDragMode() {
        if (!this.isGrabbing) {
            // cache the widgets so we can restore when escape is pressed
            this._cache = this._lastMovement = this._dashboard.cacheWidgets();
            // store the current widget being grabbed
            this._dashboard.isGrabbing$.next(this.widget);
            this._dashboard.onShiftStart(this.widget);
            // announce the grab start
            this._announcer.announce(this.getAnnouncement(this.uxGrabStartAnnouncement));
        }
    }
    /** Finish drag mode and commit the current state */
    disableDragMode() {
        if (this.isGrabbing) {
            this._dashboard.isGrabbing$.next(null);
            this._lastMovement = null;
            this._dashboard.onShiftEnd();
            // announce the confirmation
            this._announcer.announce(this.getAnnouncement(this.uxGrabConfirmAnnouncement));
        }
    }
    /** Finish the drag mode and restore the original state */
    cancelDragMode() {
        if (this.isGrabbing) {
            this._dashboard.onShiftEnd();
            this._dashboard.restoreWidgets(false, this._cache, true);
            this._dashboard.setDashboardHeight();
            this._dashboard.layout$.next(this._dashboard.getLayoutData());
            this._dashboard.isGrabbing$.next(null);
            // announce the cancellation
            this._announcer.announce(this.getAnnouncement(this.uxGrabCancelAnnouncement));
        }
    }
    /** Toggle the drag mode state */
    toggleDragMode() {
        this.isGrabbing ? this.disableDragMode() : this.enableDragMode();
    }
    /** Set the tab index and optionally focus the DOM element */
    focus(focusElement = true) {
        this.tabIndex = 0;
        if (focusElement) {
            this._elementRef.nativeElement.focus();
        }
    }
    /** Make this item non-tabbable */
    blur() {
        this.tabIndex = -1;
    }
    /** When the grab handle loses focus then exit 'grab' mode */
    onBlur() {
        this.disableDragMode();
    }
    /** Handle key events */
    onKeydown(event, key, ctrlKey) {
        switch (key) {
            case ESCAPE:
                this.cancelDragMode();
                break;
            case SPACE:
            case ENTER:
                this.toggleDragMode();
                event.preventDefault();
                event.stopPropagation();
                break;
            case UP_ARROW:
            case RIGHT_ARROW:
            case DOWN_ARROW:
            case LEFT_ARROW:
                if (this.isGrabbing) {
                    ctrlKey ? this.resizeWidget(event, key) : this.moveWidget(event, key);
                }
                else {
                    this.moveFocus(event, key);
                }
        }
    }
    /** Get an announcement from the inputs - they may be a string or a function so handle both */
    getAnnouncement(announcement, ...args) {
        return typeof announcement === 'function' ? announcement(this.widget, ...args) : announcement;
    }
    /** Move the widget in a given direction based on arrow keys */
    moveWidget(event, key) {
        // check if moving is allowed
        if (!this.widget.isDraggable || !this.uxGrabAllowMove) {
            return;
        }
        // attempt to perform the move
        this._dashboard.onShift(this.widget, this.getDirectionFromKey(key));
        // get the announcable diff
        const changes = this.getLayoutDiff();
        // if there were changes then announce them
        if (changes.length > 0) {
            this._announcer.announce(this.getAnnouncement(this.uxGrabChangeSuccessAnnouncement, changes));
        }
        else {
            this._announcer.announce(this.getAnnouncement(this.uxGrabMoveFailAnnouncement, this.getDirectionFromKey(key)));
        }
        this._lastMovement = this._dashboard.cacheWidgets();
        event.preventDefault();
        event.stopPropagation();
    }
    /** Resize the widgets accordingly based on the arrow keys */
    resizeWidget(event, key) {
        // check if resizing is allowed
        if (!this.widget.resizable || !this.uxGrabAllowResize) {
            return;
        }
        this._dashboard.onResize(this.widget, this.getDirectionFromKey(key));
        // get the announcable diff
        const changes = this.getLayoutDiff();
        // if there were changes then announce them
        if (changes.length > 0) {
            this._announcer.announce(this.getAnnouncement(this.uxGrabChangeSuccessAnnouncement, changes));
        }
        else {
            this._announcer.announce(this.getAnnouncement(this.uxGrabResizeFailAnnouncement, this.getDirectionFromKey(key)));
        }
        this._lastMovement = this._dashboard.cacheWidgets();
        event.preventDefault();
        event.stopPropagation();
    }
    /** Shift focus between the variour grab handles */
    moveFocus(event, key) {
        switch (key) {
            case UP_ARROW:
                this._handle.setSiblingItemFocus(this.widget, ActionDirection.Top);
                break;
            case RIGHT_ARROW:
                this._handle.setNextItemFocus(this);
                break;
            case DOWN_ARROW:
                this._handle.setSiblingItemFocus(this.widget, ActionDirection.Bottom);
                break;
            case LEFT_ARROW:
                this._handle.setPreviousItemFocus(this);
                break;
        }
        event.preventDefault();
        event.stopPropagation();
    }
    /** Convert an arrow key code into an ActionDirection enum */
    getDirectionFromKey(key) {
        switch (key) {
            case UP_ARROW:
                return ActionDirection.Top;
            case RIGHT_ARROW:
                return ActionDirection.Right;
            case DOWN_ARROW:
                return ActionDirection.Bottom;
            case LEFT_ARROW:
                return ActionDirection.Left;
        }
    }
    /** Supply the default grab handle aria label based on the provided constraints */
    getDefaultAriaLabel(widget) {
        if (widget.resizable && this.uxGrabAllowResize && widget.isDraggable && this.uxGrabAllowMove) {
            return `Press space to move and resize the ${widget.name} panel.`;
        }
        else if (widget.resizable && this.uxGrabAllowResize) {
            return `Press space to resize the ${widget.name} panel.`;
        }
        else if (widget.isDraggable && this.uxGrabAllowMove) {
            return `Press space to move the ${widget.name} panel.`;
        }
    }
    /** Get the default announcement whenever a movement or resize was successful */
    getChangeSuccessAnnouncement() {
        return `${this.getDiffAnnouncements().join(' ')} Use the cursor keys to continue moving and resizing, enter to commit, or escape to cancel.`;
    }
    getDiffAnnouncements() {
        // map the differences to strings
        return this.getLayoutDiff().map(diff => {
            const changes = [];
            // Handle movement strings
            if (diff.isMovedHorizontally && diff.isMovedVertically) {
                changes.push(`moved to row ${diff.currentRow}, column ${diff.currentColumn}`);
            }
            else if (diff.isMovedDown) {
                changes.push(`moved down to row ${diff.currentRow}, column ${diff.currentColumn}`);
            }
            else if (diff.isMovedUp) {
                changes.push(`moved up to row ${diff.currentRow}, column ${diff.currentColumn}`);
            }
            else if (diff.isMovedLeft) {
                changes.push(`moved left to row ${diff.currentRow}, column ${diff.currentColumn}`);
            }
            else if (diff.isMovedRight) {
                changes.push(`moved right to row ${diff.currentRow}, column ${diff.currentColumn}`);
            }
            // handle resize strings
            if (diff.isResized) {
                changes.push(`resized to ${diff.currentColumnSpan} columns wide and ${diff.currentRowSpan} rows high`);
            }
            return `${diff.widget.name} panel is ${changes.join(' and ')}.`;
        });
    }
    /** Get the default announcement whenever a movement is not possible due to dashboard boundaries */
    getMoveFailAnnouncement(widget, direction) {
        switch (direction) {
            case ActionDirection.Top:
                return `Cannot move the ${widget.name} panel up, because it is at the top edge of the dashboard`;
            case ActionDirection.Bottom:
                return `Cannot move the ${widget.name} panel down, because it is at the bottom edge of the dashboard`;
            case ActionDirection.Right:
                return `Cannot move the ${widget.name} panel right, because it is at the right edge of the dashboard`;
            case ActionDirection.Left:
                return `Cannot move the ${widget.name} panel left, because it is at the left edge of the dashboard`;
        }
    }
    /** Get the default announcement whenever a resize is not possible due to either widget constraints of dashboard bounds */
    getResizeFailAnnouncement(widget, direction) {
        switch (direction) {
            case ActionDirection.Top:
                return `Cannot make the ${widget.name} panel shorter, because it is currently at its minimum height.`;
            case ActionDirection.Bottom:
                return `Cannot make the ${widget.name} panel taller, because it is currently at its maximum height.`;
            case ActionDirection.Right:
                return `Cannot make the ${widget.name} panel wider, because it is at the right edge of the dashboard.`;
            case ActionDirection.Left:
                return `Cannot make the ${widget.name} panel narrower, because it is currently at its minimum width.`;
        }
    }
    /** Get the default announcement whenever we enter 'grab' mode */
    getStartAnnouncement(widget) {
        if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
            return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()} and is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high. Use the cursor keys to move the widget and the cursor keys with the control modifier to resize the widget. Press enter to commit changes and press escape to cancel changes.`;
        }
        else if (widget.isDraggable && this.uxGrabAllowMove) {
            return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()}. Use the cursor keys to move the widget. Press enter to commit changes and press escape to cancel changes.`;
        }
        else if (widget.resizable && this.uxGrabAllowResize) {
            return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()} and is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high. Use the cursor keys with the control modifier to resize the widget. Press enter to commit changes and press escape to cancel changes.`;
        }
    }
    /** Get the default announcement whenever grab mode is exited after a movement or resize */
    getConfirmAnnouncement(widget) {
        if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
            return `Moving and resizing complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
        else if (widget.isDraggable && this.uxGrabAllowMove) {
            return `Moving complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
        else if (widget.resizable && this.uxGrabAllowResize) {
            return `Resizing complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
    }
    /** Get the default announcement whenever grab mode is exited after being cancelled */
    getCancellationAnnouncement(widget) {
        if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
            return `Moving and resizing cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
        else if (widget.isDraggable && this.uxGrabAllowMove) {
            return `Moving cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
        else if (widget.resizable && this.uxGrabAllowResize) {
            return `Resizing cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
        }
    }
    /** Get a description of all dashboard widgets, their positions and sizes */
    getDashboardAriaLabel() {
        return `Dashboard with ${this._dashboard.options.columns} columns, containing ${this._dashboard.widgets.length} panels. ${this._dashboard.widgets.map(this.getWidgetAriaLabel).join(' ')}`;
    }
    /** Get a description of a given widget */
    getWidgetAriaLabel(widget) {
        return `${widget.name} panel in row ${widget.getRow()}, column ${widget.getColumn()}, is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high.`;
    }
    /** Get an object describing all the changes that have been made to all widgets since the last change */
    getLayoutDiff() {
        // find all changes
        const diffs = this._dashboard.getLayoutData().map(layout => {
            // get the most recent cache
            const cache = this._lastMovement || this._cache;
            // get the actual widget
            const widget = this._dashboard.widgets.find(_widget => _widget.id === layout.id);
            // get previous position
            const previousLayout = cache.find(_widget => _widget.id === layout.id);
            // ensure they are all numbers
            layout.row = Number(layout.row);
            layout.rowSpan = Number(layout.rowSpan);
            layout.col = Number(layout.col);
            layout.colSpan = Number(layout.colSpan);
            previousLayout.row = Number(previousLayout.row);
            previousLayout.rowSpan = Number(previousLayout.rowSpan);
            previousLayout.column = Number(previousLayout.column);
            previousLayout.columnSpan = Number(previousLayout.columnSpan);
            return {
                widget,
                currentRow: layout.row,
                currentColumn: layout.col,
                currentRowSpan: layout.rowSpan,
                currentColumnSpan: layout.colSpan,
                previousColumn: previousLayout.column,
                previousRow: previousLayout.row,
                previousColumnSpan: previousLayout.columnSpan,
                previousRowSpan: previousLayout.rowSpan,
                isMovedLeft: layout.col < previousLayout.column,
                isMovedRight: layout.col > previousLayout.column,
                isMovedUp: layout.row < previousLayout.row,
                isMovedDown: layout.row > previousLayout.row,
                isMovedHorizontally: layout.col !== previousLayout.column,
                isMovedVertically: layout.row !== previousLayout.row,
                isMoved: layout.col !== previousLayout.column || layout.row !== previousLayout.row,
                isResized: previousLayout.columnSpan !== layout.colSpan || previousLayout.rowSpan !== layout.rowSpan,
            };
        });
        // get the order the widgets appear visually
        const order = this._handle.getHandlesInOrder().map(handle => handle.widget);
        // only return items that have been repositioned or resized
        return diffs
            .filter(diff => diff.isMoved || diff.isResized)
            .sort((diffOne, diffTwo) => {
            // sort this so that the item that the user moved is first in the list, and the remainder are in their new order as seen in the dashboard
            if (diffOne.widget === this.widget) {
                return -1;
            }
            if (diffTwo.widget === this.widget) {
                return 1;
            }
            // otherwise sort based on their visual order
            return order.indexOf(diffOne.widget) < order.indexOf(diffTwo.widget) ? -1 : 1;
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardGrabHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DashboardGrabHandleDirective, isStandalone: false, selector: "[uxDashboardGrabHandle]", inputs: { uxGrabAllowMove: "uxGrabAllowMove", uxGrabAllowResize: "uxGrabAllowResize", uxGrabAriaLabel: "uxGrabAriaLabel", uxGrabChangeSuccessAnnouncement: "uxGrabChangeSuccessAnnouncement", uxGrabStartAnnouncement: "uxGrabStartAnnouncement", uxGrabMoveFailAnnouncement: "uxGrabMoveFailAnnouncement", uxGrabResizeFailAnnouncement: "uxGrabResizeFailAnnouncement", uxGrabConfirmAnnouncement: "uxGrabConfirmAnnouncement", uxGrabCancelAnnouncement: "uxGrabCancelAnnouncement" }, host: { listeners: { "blur": "onBlur()", "keydown": "onKeydown($event,$event.which,$event.ctrlKey)" }, properties: { "attr.aria-label": "this.ariaLabel", "tabIndex": "this.tabIndex" } }, exportAs: ["ux-dashboard-grab-handle"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardGrabHandleDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxDashboardGrabHandle]',
                    exportAs: 'ux-dashboard-grab-handle',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxGrabAllowMove: [{
                type: Input
            }], uxGrabAllowResize: [{
                type: Input
            }], uxGrabAriaLabel: [{
                type: Input
            }], uxGrabChangeSuccessAnnouncement: [{
                type: Input
            }], uxGrabStartAnnouncement: [{
                type: Input
            }], uxGrabMoveFailAnnouncement: [{
                type: Input
            }], uxGrabResizeFailAnnouncement: [{
                type: Input
            }], uxGrabConfirmAnnouncement: [{
                type: Input
            }], uxGrabCancelAnnouncement: [{
                type: Input
            }], ariaLabel: [{
                type: HostBinding,
                args: ['attr.aria-label']
            }], tabIndex: [{
                type: HostBinding,
                args: ['tabIndex']
            }], onBlur: [{
                type: HostListener,
                args: ['blur']
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event', '$event.which', '$event.ctrlKey']]
            }] } });

class DashboardComponent {
    /** If defined or changed this will set the positions of the widgets within the dashboard. This is a two way binding that will be updated with the current layout when it changes. */
    set layout(layout) {
        if (layout) {
            this.dashboardService.layout$.next(layout);
        }
    }
    /** Configures the options for the dashboard, if an option is not specified the default value will be used. */
    set options(options) {
        this.dashboardService.options$.next({ ...defaultOptions, ...options });
    }
    constructor() {
        this.dashboardService = inject(DashboardService);
        this._changeDetector = inject(ChangeDetectorRef);
        this.isGrabbing = false;
        this.customAriaLabel = this.getDefaultAriaLabel;
        /** Emits when layout has been changed. */
        this.layoutChange = new EventEmitter();
        this.role = 'region';
        /** Ensure we unsubscribe from all observables */
        this._onDestroy = new Subject();
        this.dashboardService.layout$
            .pipe(takeUntil(this._onDestroy), tap(() => (this.ariaLabel = this.getAriaLabel())))
            .subscribe(() => this._changeDetector.markForCheck());
        this.dashboardService.userLayoutChange$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(data => this.layoutChange.emit(data));
        // subscribe to changes to the grab mode
        this.dashboardService.isGrabbing$
            .pipe(takeUntil(this._onDestroy), map(widget => !!widget))
            .subscribe(isGrabbing => (this.isGrabbing = isGrabbing));
    }
    /**
     * Set the initial dimensions
     */
    ngAfterViewInit() {
        // set the initial dimensions
        this.dashboardService.setDimensions(this.dashboardElement.nativeElement.offsetWidth, this.dashboardElement.nativeElement.offsetHeight);
    }
    ngAfterContentInit() {
        this.dashboardService.initialized$.next(true);
    }
    ngOnChanges() {
        this.dashboardService.renderDashboard();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onResize(event) {
        this.dashboardService.setDimensions(event.width, event.height);
    }
    getAriaLabel() {
        if (this.customAriaLabel && typeof this.customAriaLabel === 'string') {
            return this.customAriaLabel;
        }
        else if (this.customAriaLabel && typeof this.customAriaLabel === 'function') {
            return this.customAriaLabel(this.dashboardService.widgets, this.dashboardService.options);
        }
        return this.ariaLabel;
    }
    /** Shift widgets up where possible to fill any available space to optimize the dashboard layout */
    refreshLayout() {
        const didChangeLayout = this.dashboardService.shiftWidgetsUp();
        if (didChangeLayout) {
            // if widgets have shifted up the dashboard may no longer occupy the same
            // height. We should remove any unneeded whitespace below widgets too.
            this.dashboardService.setDashboardHeight();
            // emit information about the layout
            this.dashboardService.layout$.next(this.dashboardService.getLayoutData());
            this.layoutChange.emit(this.dashboardService.layout$.value);
        }
    }
    getDefaultAriaLabel(widgets, options) {
        return `Dashboard with ${options.columns} columns, containing ${widgets.length} panels. ${widgets.map(this.getWidgetAriaLabel).join(' ')}`;
    }
    getWidgetAriaLabel(widget) {
        return `${widget.name} panel in row ${widget.getRow()}, column ${widget.getColumn()}, is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high.`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: DashboardComponent, isStandalone: false, selector: "ux-dashboard", inputs: { customAriaLabel: ["aria-label", "customAriaLabel"], layout: "layout", options: "options", role: "role" }, outputs: { layoutChange: "layoutChange" }, host: { properties: { "attr.aria-label": "this.ariaLabel", "attr.role": "this.role" } }, providers: [DashboardService, DashboardGrabHandleService], queries: [{ propertyName: "handles", predicate: DashboardGrabHandleDirective, descendants: true }], viewQueries: [{ propertyName: "dashboardElement", first: true, predicate: ["dashboard"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #dashboard class=\"dashboard-container\" [style.height.px]=\"dashboardService.height$ | async\">\n  <div (uxResize)=\"onResize($event)\" [throttle]=\"16\" class=\"dashboard\">\n    <ng-content></ng-content>\n  </div>\n\n  <!-- Wrap with ngIf so we only have one subscription rather than one for each property -->\n  @if (dashboardService.placeholder$ | async; as placeholder) {\n    @if (placeholder.visible) {\n      <div\n        class=\"position-indicator\"\n        [style.left.px]=\"placeholder.x\"\n        [style.top.px]=\"placeholder.y\"\n        [style.width.px]=\"placeholder.width\"\n        [style.height.px]=\"placeholder.height\"\n      ></div>\n    }\n  }\n</div>\n", dependencies: [{ kind: "directive", type: ResizeDirective, selector: "[uxResize]", inputs: ["throttle"], outputs: ["uxResize"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-dashboard', changeDetection: ChangeDetectionStrategy.OnPush, providers: [DashboardService, DashboardGrabHandleService], standalone: false, template: "<div #dashboard class=\"dashboard-container\" [style.height.px]=\"dashboardService.height$ | async\">\n  <div (uxResize)=\"onResize($event)\" [throttle]=\"16\" class=\"dashboard\">\n    <ng-content></ng-content>\n  </div>\n\n  <!-- Wrap with ngIf so we only have one subscription rather than one for each property -->\n  @if (dashboardService.placeholder$ | async; as placeholder) {\n    @if (placeholder.visible) {\n      <div\n        class=\"position-indicator\"\n        [style.left.px]=\"placeholder.x\"\n        [style.top.px]=\"placeholder.y\"\n        [style.width.px]=\"placeholder.width\"\n        [style.height.px]=\"placeholder.height\"\n      ></div>\n    }\n  }\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { customAriaLabel: [{
                type: Input,
                args: ['aria-label']
            }], layout: [{
                type: Input
            }], options: [{
                type: Input
            }], layoutChange: [{
                type: Output
            }], ariaLabel: [{
                type: HostBinding,
                args: ['attr.aria-label']
            }], role: [{
                type: HostBinding,
                args: ['attr.role']
            }, {
                type: Input
            }], dashboardElement: [{
                type: ViewChild,
                args: ['dashboard', { static: true }]
            }], handles: [{
                type: ContentChildren,
                args: [DashboardGrabHandleDirective, { descendants: true }]
            }] } });

class DashboardDragHandleDirective extends DragDirective {
    constructor() {
        super();
        this.widget = inject(DashboardWidgetComponent);
        this.dashboardService = inject(DashboardService);
        // inform the widget that it can be dragged
        this.widget.isDraggable = true;
        this.onDragStart
            .pipe(takeUntil(this._onDestroy), tap(() => this.dashboardService.isGrabbing$.next(null)))
            .subscribe((event) => this.dashboardService.onDragStart({
            widget: this.widget,
            direction: ActionDirection.Move,
            event,
        }));
        this.onDrag.pipe(takeUntil(this._onDestroy)).subscribe((event) => this.dashboardService.onDrag({
            widget: this.widget,
            direction: ActionDirection.Move,
            event,
        }));
        this.onDragScroll
            .pipe(takeUntil(this._onDestroy))
            .subscribe((event) => this.dashboardService.onDragScroll(this.widget, event));
        this.onDragEnd
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.dashboardService.onDragEnd());
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDragHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DashboardDragHandleDirective, isStandalone: false, selector: "[uxDashboardWidgetDragHandle], [ux-dashboard-widget-drag-handle]", usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardDragHandleDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxDashboardWidgetDragHandle], [ux-dashboard-widget-drag-handle]',
                    standalone: false,
                }]
        }], ctorParameters: () => [] });

const DECLARATIONS$9 = [
    DashboardComponent,
    DashboardWidgetComponent,
    DashboardDragHandleDirective,
    DashboardGrabHandleDirective,
];
class DashboardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DashboardModule, declarations: [DashboardComponent,
            DashboardWidgetComponent,
            DashboardDragHandleDirective,
            DashboardGrabHandleDirective], imports: [A11yModule, CommonModule, ResizeModule, DragModule], exports: [DashboardComponent,
            DashboardWidgetComponent,
            DashboardDragHandleDirective,
            DashboardGrabHandleDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardModule, providers: [DashboardService], imports: [A11yModule, CommonModule, ResizeModule, DragModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DashboardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [A11yModule, CommonModule, ResizeModule, DragModule],
                    exports: DECLARATIONS$9,
                    declarations: DECLARATIONS$9,
                    providers: [DashboardService],
                }]
        }] });

/**
 * Convert a single dimension array to a double dimension array
 * @param items the single dimension array to convert
 * @param columns the number of items each array should have
 */
function gridify(items, columns) {
    // create a copy of array so not to effect the original
    items = items.slice(0);
    const grid = [];
    while (items.length) {
        grid.push(items.splice(0, columns));
    }
    return grid;
}
/**
 * Create an array of numbers between two limits
 * @param start the lower limit
 * @param end the upper limit
 */
function range(start, end) {
    const list = [];
    for (let idx = start; idx <= end; idx++) {
        list.push(idx);
    }
    return list;
}
/**
 * Create an array of dates between two points
 * @param start the date to start the array
 * @param end the date to end the array
 */
function dateRange(start, end) {
    // don't alter the start date object
    start = new Date(start);
    const dates = [];
    // loop through all the days between the date range
    while (start <= end) {
        // add the date to the array
        dates.push(new Date(start));
        // move to the next day
        start.setDate(start.getDate() + 1);
    }
    return dates;
}
/**
 * Compare two dates to see if they are on the same day
 * @param day1 the first date to compare
 * @param day2 the second date to compare
 */
function compareDays(day1, day2) {
    return (day1.getDate() === day2.getDate() &&
        day1.getMonth() === day2.getMonth() &&
        day1.getFullYear() === day2.getFullYear());
}
/**
 * Date comparison for use primarily with distinctUntilChanged
 */
function dateComparator(dateOne, dateTwo) {
    if ((!dateOne && dateTwo) || (dateOne && !dateTwo)) {
        return false;
    }
    if (!dateOne && !dateTwo) {
        return true;
    }
    return dateOne.getTime() === dateTwo.getTime();
}
/**
 * Calculate the number of days between two dates
 * @param start The start date
 * @param end The end date
 * @param fullDay Whether or not we should take from 00:00 on the start date and 23:59 on the end date
 */
function differenceBetweenDates(start, end, fullDay = true) {
    if (!start || !end) {
        return null;
    }
    const millisecondsInDay = 86400000;
    const startDay = new Date(start.getTime() < end.getTime() ? start : end);
    const endDay = new Date(start.getTime() > end.getTime() ? start : end);
    // get the start of day
    if (fullDay) {
        startDay.setHours(0, 0, 0, 0);
        endDay.setHours(23, 59, 59, 0);
    }
    return Math.round((endDay.getTime() - startDay.getTime()) / millisecondsInDay);
}
/**
 * Timezone comparison for use primarily with distinctUntilChanged
 */
function timezoneComparator(zoneOne, zoneTwo) {
    return zoneOne.name === zoneTwo.name && zoneOne.offset === zoneTwo.offset;
}
/**
 * Get a date object with the time of the start of the given day
 * @param date The date to get the start of day
 */
function getStartOfDay(date) {
    const startOfDay = new Date(date);
    startOfDay.setHours(0, 0, 0, 0);
    return startOfDay;
}
function isDateAfter(date, after, isEqual = false) {
    return isEqual
        ? getStartOfDay(date).getTime() >= getStartOfDay(after).getTime()
        : getStartOfDay(date).getTime() > getStartOfDay(after).getTime();
}
function isDateBefore(date, before, isEqual = false) {
    return isEqual
        ? getStartOfDay(date).getTime() <= getStartOfDay(before).getTime()
        : getStartOfDay(date).getTime() < getStartOfDay(before).getTime();
}
/**
 * Export an array of all the available months
 */
const months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
];
const monthsShort = getShortMonthNames();
function getShortMonthNames() {
    return months.map(month => month.substring(0, 3));
}
/**
 * Export an array of all the available days of the week
 */
const weekdays = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
];
const weekdaysShort = getShortWeekdayNames();
const meridians = ['AM', 'PM'];
function getShortWeekdayNames() {
    return weekdays.map(weekday => weekday.substring(0, 3));
}
/** Export the default set of time zone */
const timezones = [
    { name: 'GMT-11', offset: 660 },
    { name: 'GMT-10', offset: 600 },
    { name: 'GMT-9', offset: 540 },
    { name: 'GMT-8', offset: 480 },
    { name: 'GMT-7', offset: 420 },
    { name: 'GMT-6', offset: 360 },
    { name: 'GMT-5', offset: 300 },
    { name: 'GMT-4', offset: 240 },
    { name: 'GMT-3', offset: 180 },
    { name: 'GMT-2', offset: 120 },
    { name: 'GMT-1', offset: 60 },
    { name: 'GMT', offset: 0 },
    { name: 'GMT+1', offset: -60 },
    { name: 'GMT+2', offset: -120 },
    { name: 'GMT+3', offset: -180 },
    { name: 'GMT+4', offset: -240 },
    { name: 'GMT+5', offset: -300 },
    { name: 'GMT+6', offset: -360 },
    { name: 'GMT+7', offset: -420 },
    { name: 'GMT+8', offset: -480 },
    { name: 'GMT+9', offset: -540 },
    { name: 'GMT+10', offset: -600 },
    { name: 'GMT+11', offset: -660 },
    { name: 'GMT+12', offset: -720 },
];

class DateRangeService {
    constructor() {
        /** Indicate whether we want to show a date range */
        this.isRange = false;
        /** Specify the direction of the selection */
        this.direction = DateRangePicker.Start;
        /** Emit whenever the start date changes */
        this.onStartChange = new Subject();
        /** Emit whenever the end date changes */
        this.onEndChange = new Subject();
        /** Emit whenever the range has changed */
        this.onRangeChange = new Subject();
        /** Emit whenever the hover date changes */
        this.onHoverChange = new Subject();
        /** Emit whenever the range is cleared */
        this.onClear = new Subject();
        /** Indicate if we should show time */
        this.showTime = false;
        /** Defines the aria label for the range start picker */
        this.startPickerAriaLabel = 'Selecting the start date';
        /** Defines the aria label for the range end picker */
        this.endPickerAriaLabel = 'Selecting the end date';
        /** Indicate if we are currently changing the time */
        this.isChangingTime = false;
        /** Store the current start time */
        this.startTime = { hours: 0, minutes: 0, seconds: 0 };
        /** Store the current end time */
        this.endTime = { hours: 23, minutes: 59, seconds: 59 };
    }
    setStartDate(date) {
        // if the start date is after the end date the clear the end date
        if (date && this.end && isDateAfter(date, this.end)) {
            this.clear();
        }
        this.start = date;
        this.onStartChange.next(this.start);
        this.onRangeChange.next();
    }
    setEndDate(date) {
        // if the end date is before the start date the clear the start date
        if (date && this.start && isDateBefore(date, this.start)) {
            this.clear();
        }
        this.end = date;
        this.onEndChange.next(this.end);
        this.onRangeChange.next();
    }
    clear() {
        this.setStartDate(null);
        this.setEndDate(null);
        this.onClear.next();
    }
    setDateMouseEnter(date) {
        this.hover = date;
        this.onHoverChange.next();
    }
    setDateMouseLeave(date) {
        if (date && this.hover && compareDays(date, this.hover)) {
            this.setDateMouseEnter(null);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangeService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangeService, decorators: [{
            type: Injectable
        }] });
var DateRangePicker;
(function (DateRangePicker) {
    DateRangePicker["Start"] = "start";
    DateRangePicker["End"] = "end";
})(DateRangePicker || (DateRangePicker = {}));

/**
 * This directive allows us to pass information down to a specific ux-date-time-picker
 * without having to expose additional inputs to the consuming application.
 *
 * For example, the day picker needs to know if it is the start or end picker
 * as the behavior will be different for each. However we don't want to expose an
 * input on the DateTimePickerComponent as this will appear in Editor Autocomplete Suggestions
 * options if the Angular Language Service is installed, and we don't want these to be public
 * options.
 */
class DateRangeOptions {
    constructor() {
        this.picker = DateRangePicker.Start;
    }
}
class DateRangePickerDirective {
    constructor() {
        this._options = inject(DateRangeOptions, { self: true });
    }
    /** Specify whether this is the start or end picker */
    set picker(picker) {
        this._options.picker = picker;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: DateRangePickerDirective, isStandalone: false, selector: "[uxDateRangePicker]", inputs: { picker: "picker" }, providers: [DateRangeOptions], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxDateRangePicker]',
                    providers: [DateRangeOptions],
                    standalone: false,
                }]
        }], propDecorators: { picker: [{
                type: Input
            }] } });

/**
 * Add a config service to allow an application
 * to customize the date time picker default settings
 * across the entire application
 */
class DateTimePickerConfig {
    constructor() {
        this.showDate = true;
        this.showTime = true;
        this.showTimezone = true;
        this.showSeconds = false;
        this.showMeridian = true;
        this.showSpinners = true;
        this.showNowBtn = true;
        this.weekdays = weekdaysShort;
        this.nowBtnText = 'Today';
        this.timezones = timezones;
        this.months = months;
        this.monthsShort = monthsShort;
        this.meridians = meridians;
        this.min = null;
        this.max = null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerConfig }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerConfig, decorators: [{
            type: Injectable
        }] });

class DateTimePickerService {
    constructor() {
        this._config = inject(DateTimePickerConfig, { optional: true });
        this.rangeService = inject(DateRangeService, { optional: true });
        this.rangeOptions = inject(DateRangeOptions, { optional: true });
        this.mode$ = new BehaviorSubject(DatePickerMode.Day);
        this.date$ = new BehaviorSubject(new Date());
        this.timezone$ = new BehaviorSubject(null);
        this.selected$ = new BehaviorSubject(new Date());
        // the month and year to display in the viewport
        this.month$ = new BehaviorSubject(new Date().getMonth());
        this.year$ = new BehaviorSubject(new Date().getFullYear());
        this.showDate$ = new BehaviorSubject(this._config ? this._config.showDate : true);
        this.showTime$ = new BehaviorSubject(this._config ? this._config.showTime : true);
        this.showTimezone$ = new BehaviorSubject(this._config ? this._config.showTimezone : true);
        this.showSeconds$ = new BehaviorSubject(this._config ? this._config.showSeconds : false);
        this.showMeridian$ = new BehaviorSubject(this._config ? this._config.showMeridian : true);
        this.showSpinners$ = new BehaviorSubject(this._config ? this._config.showSpinners : true);
        this.showNowBtn$ = new BehaviorSubject(this._config ? this._config.showNowBtn : true);
        this.weekdays$ = new BehaviorSubject(this._config ? this._config.weekdays : weekdaysShort);
        this.nowBtnText$ = new BehaviorSubject(this._config ? this._config.nowBtnText : 'Today');
        this.timezones$ = new BehaviorSubject(this._config ? this._config.timezones : timezones);
        this.min$ = new BehaviorSubject(this._config ? this._config.min : null);
        this.max$ = new BehaviorSubject(this._config ? this._config.max : null);
        this.header$ = new BehaviorSubject(null);
        this.headerEvent$ = new Subject();
        this.modeDirection = ModeDirection.None;
        this.startOfWeek$ = new BehaviorSubject(WeekDay.Sunday);
        this.months = this._config ? this._config.months : months;
        this.monthsShort = this._config ? this._config.monthsShort : monthsShort;
        this.meridians = this._config ? this._config.meridians : meridians;
        /**
         * Store whether or not the component has fully initialised or not. We use this to prevent initial
         * focus on the end date range picker when the popover is first opened
         */
        this.initialised = false;
        // when the active date changes set the currently selected date
        this._subscription = this.selected$.subscribe(date => {
            // the month and year displayed in the viewport should reflect the newly selected items
            if (date instanceof Date) {
                this.setViewportMonth(date.getMonth());
                this.setViewportYear(date.getFullYear());
            }
            // emit the new date to the component host but only if they are different
            if (!dateComparator(date, this.date$.value)) {
                if (this.rangeService) {
                    if (this.rangeOptions.picker === 'start') {
                        this.rangeService.setStartDate(date);
                    }
                    else {
                        this.rangeService.setEndDate(date);
                    }
                }
                else {
                    this.date$.next(date);
                }
            }
        });
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    setViewportMonth(month) {
        if (month < 0) {
            this.month$.next(11);
            this.year$.next(this.year$.value - 1);
        }
        else if (month > 11) {
            this.month$.next(0);
            this.year$.next(this.year$.value + 1);
        }
        else {
            this.month$.next(month);
        }
    }
    setViewportYear(year) {
        this.year$.next(year);
    }
    setDate(day, month, year, hours, minutes, seconds) {
        const date = new Date(this.selected$.value);
        date.setFullYear(year);
        date.setMonth(month, day);
        if (hours !== undefined) {
            date.setHours(hours);
        }
        if (minutes !== undefined) {
            date.setMinutes(minutes);
        }
        if (seconds !== undefined) {
            date.setSeconds(seconds);
        }
        if (this.isInRange(date)) {
            this.selected$.next(date);
        }
    }
    setDateToNow() {
        const now = new Date();
        if (this.isInRange(now)) {
            this.selected$.next(now);
        }
    }
    setViewportMode(mode) {
        this.mode$.next(mode);
    }
    goToChildMode() {
        this.modeDirection = ModeDirection.Descend;
        switch (this.mode$.value) {
            case DatePickerMode.Year:
                return this.setViewportMode(DatePickerMode.Month);
            case DatePickerMode.Month:
                return this.setViewportMode(DatePickerMode.Day);
        }
    }
    goToParentMode() {
        this.modeDirection = ModeDirection.Ascend;
        switch (this.mode$.value) {
            case DatePickerMode.Day:
                return this.setViewportMode(DatePickerMode.Month);
            case DatePickerMode.Month:
                return this.setViewportMode(DatePickerMode.Year);
        }
    }
    goToNext() {
        this.headerEvent$.next(DatePickerHeaderEvent.Next);
    }
    goToPrevious() {
        this.headerEvent$.next(DatePickerHeaderEvent.Previous);
    }
    setHeader(header) {
        this.header$.next(header);
    }
    isTimezoneAvailable(timezone) {
        if (!timezone || !this.timezones$.value) {
            return false;
        }
        return (this.timezones$.value.findIndex(_timezone => _timezone.offset === timezone.offset && _timezone.name === timezone.name) !== -1);
    }
    getDefaultTimezone() {
        const offset = new Date().getTimezoneOffset();
        const matchingZone = this.timezones$.value.find(_timezone => _timezone.offset === offset);
        return (matchingZone ||
            this.timezones$.value.find(_timezone => _timezone.offset === 0) || { name: 'GMT', offset: 0 });
    }
    isInRange(date) {
        return ((!this.min$.value || date >= this.min$.value) && (!this.max$.value || date <= this.max$.value));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });
var DatePickerMode;
(function (DatePickerMode) {
    DatePickerMode[DatePickerMode["Day"] = 0] = "Day";
    DatePickerMode[DatePickerMode["Month"] = 1] = "Month";
    DatePickerMode[DatePickerMode["Year"] = 2] = "Year";
})(DatePickerMode || (DatePickerMode = {}));
var ModeDirection;
(function (ModeDirection) {
    ModeDirection[ModeDirection["None"] = 0] = "None";
    ModeDirection[ModeDirection["Ascend"] = 1] = "Ascend";
    ModeDirection[ModeDirection["Descend"] = 2] = "Descend";
})(ModeDirection || (ModeDirection = {}));
var DatePickerHeaderEvent;
(function (DatePickerHeaderEvent) {
    DatePickerHeaderEvent[DatePickerHeaderEvent["Previous"] = 0] = "Previous";
    DatePickerHeaderEvent[DatePickerHeaderEvent["Next"] = 1] = "Next";
})(DatePickerHeaderEvent || (DatePickerHeaderEvent = {}));

class HeaderComponent {
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    get _rangeStart() {
        return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
    }
    get _rangeEnd() {
        return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
    }
    constructor() {
        this.datepicker = inject(DateTimePickerService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        this.canAscend$ = this.datepicker.mode$.pipe(map(mode => mode !== DatePickerMode.Year));
        this.mode$ = this.datepicker.mode$.pipe(map(mode => {
            switch (mode) {
                case DatePickerMode.Day:
                    return 'Day';
                case DatePickerMode.Month:
                    return 'Month';
                case DatePickerMode.Year:
                    return 'Year';
            }
        }));
        this.headerAria$ = this.datepicker.mode$.pipe(map(mode => {
            switch (mode) {
                case DatePickerMode.Day:
                    return 'Switch to show months in the year';
                case DatePickerMode.Month:
                    return 'Switch to show years in the decade';
                case DatePickerMode.Year:
                    return '';
            }
        }));
        this.previousAria$ = this.datepicker.mode$.pipe(map(mode => {
            switch (mode) {
                case DatePickerMode.Day:
                    return 'Previous month';
                case DatePickerMode.Month:
                    return 'Previous year';
                case DatePickerMode.Year:
                    return 'Previous decade';
            }
        }));
        this.nextAria$ = this.datepicker.mode$.pipe(map(mode => {
            switch (mode) {
                case DatePickerMode.Day:
                    return 'Next month';
                case DatePickerMode.Month:
                    return 'Next year';
                case DatePickerMode.Year:
                    return 'Next decade';
            }
        }));
        /** Unsubscribe from all observables */
        this._onDestroy = new Subject();
        if (this._rangeService) {
            // delay required to allow all ui to update elsewhere
            this._rangeService.onRangeChange
                .pipe(delay(100), takeUntil(this._onDestroy))
                .subscribe(() => this._changeDetector.detectChanges());
        }
    }
    ngAfterViewInit() {
        // update on min/max changes
        merge(this.datepicker.min$, this.datepicker.max$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._changeDetector.detectChanges());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Navigate to the previous day, month or year */
    previous() {
        this.datepicker.goToPrevious();
    }
    /** Navigate to the larger scale, eg. Days -> Months, Months -> Years */
    ascend() {
        this.datepicker.goToParentMode();
    }
    /** Navigate to the previous day, month or year */
    next() {
        this.datepicker.goToNext();
    }
    /** Determine if the previous button is enabled */
    isPreviousDisabled() {
        const min = this.datepicker.min$.value;
        if (min && this._isBeforeView(min)) {
            return true;
        }
        // if we are not in range mode or there are no disabled items then we can navigate back
        if (!this._isRangeMode ||
            (this._rangeStart && this._rangeEnd) ||
            (!this._rangeStart && !this._rangeEnd) ||
            this._isRangeStart ||
            (this._isRangeEnd && this._rangeEnd)) {
            return false;
        }
        if (this._isBeforeView(this._rangeStart)) {
            return true;
        }
        return false;
    }
    /** Determine if the previous button is enabled */
    isNextDisabled() {
        const max = this.datepicker.max$.value;
        if (max && this._isAfterView(max)) {
            return true;
        }
        // if we are not in range mode or there are no disabled items then we can navigate back
        if (!this._isRangeMode ||
            (this._rangeStart && this._rangeEnd) ||
            (!this._rangeStart && !this._rangeEnd) ||
            (this._isRangeStart && this._rangeStart) ||
            this._isRangeEnd) {
            return false;
        }
        if (this._isAfterView(this._rangeEnd)) {
            return true;
        }
        return false;
    }
    _isBeforeView(date) {
        const month = this.datepicker.month$.value;
        const year = this.datepicker.year$.value;
        const mode = this.datepicker.mode$.value;
        const yearRange = this.datepicker.yearRange;
        if (mode === DatePickerMode.Day) {
            return year <= date.getFullYear() && month <= date.getMonth();
        }
        if (mode === DatePickerMode.Month) {
            return year <= date.getFullYear();
        }
        if (mode === DatePickerMode.Year) {
            return yearRange.start <= date.getFullYear();
        }
    }
    _isAfterView(date) {
        const month = this.datepicker.month$.value;
        const year = this.datepicker.year$.value;
        const mode = this.datepicker.mode$.value;
        const yearRange = this.datepicker.yearRange;
        if (mode === DatePickerMode.Day) {
            return year >= date.getFullYear() && month >= date.getMonth();
        }
        if (mode === DatePickerMode.Month) {
            return year >= date.getFullYear();
        }
        if (mode === DatePickerMode.Year) {
            return yearRange.end >= date.getFullYear();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: HeaderComponent, isStandalone: false, selector: "ux-date-time-picker-header", ngImport: i0, template: "<header class=\"header\">\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    [disabled]=\"isPreviousDisabled()\"\n    class=\"header-navigation\"\n    (click)=\"previous(); $event.stopPropagation()\"\n    [attr.aria-label]=\"previousAria$ | async\"\n    tabindex=\"0\"\n  >\n    <ux-icon name=\"previous\" class=\"header-navigation-previous-icon\"></ux-icon>\n  </button>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"header-title\"\n    [attr.aria-label]=\"headerAria$ | async\"\n    [class.active]=\"canAscend$ | async\"\n    (click)=\"ascend(); $event.stopPropagation()\"\n    [tabindex]=\"(canAscend$ | async) ? 0 : -1\"\n  >\n    {{ datepicker.header$ | async }}\n  </button>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    [disabled]=\"isNextDisabled()\"\n    class=\"header-navigation\"\n    (click)=\"next(); $event.stopPropagation()\"\n    [attr.aria-label]=\"nextAria$ | async\"\n    tabindex=\"0\"\n  >\n    <ux-icon name=\"next\" class=\"header-navigation-next-icon\"></ux-icon>\n  </button>\n</header>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HeaderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker-header', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<header class=\"header\">\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    [disabled]=\"isPreviousDisabled()\"\n    class=\"header-navigation\"\n    (click)=\"previous(); $event.stopPropagation()\"\n    [attr.aria-label]=\"previousAria$ | async\"\n    tabindex=\"0\"\n  >\n    <ux-icon name=\"previous\" class=\"header-navigation-previous-icon\"></ux-icon>\n  </button>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"header-title\"\n    [attr.aria-label]=\"headerAria$ | async\"\n    [class.active]=\"canAscend$ | async\"\n    (click)=\"ascend(); $event.stopPropagation()\"\n    [tabindex]=\"(canAscend$ | async) ? 0 : -1\"\n  >\n    {{ datepicker.header$ | async }}\n  </button>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    [disabled]=\"isNextDisabled()\"\n    class=\"header-navigation\"\n    (click)=\"next(); $event.stopPropagation()\"\n    [attr.aria-label]=\"nextAria$ | async\"\n    tabindex=\"0\"\n  >\n    <ux-icon name=\"next\" class=\"header-navigation-next-icon\"></ux-icon>\n  </button>\n</header>\n" }]
        }], ctorParameters: () => [] });

class DayViewService {
    constructor() {
        this._datepicker = inject(DateTimePickerService);
        this.grid$ = new BehaviorSubject([[]]);
        this.focused$ = new BehaviorSubject(null);
        this._subscription = combineLatest(this._datepicker.month$, this._datepicker.year$, this._datepicker.startOfWeek$).subscribe(([month, year]) => this.createDayGrid(month, year));
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    setFocus(day, month, year) {
        this.focused$.next({ day, month, year });
        // update the date picker to show the required month and year
        this._datepicker.setViewportMonth(month);
        this._datepicker.setViewportYear(year);
    }
    createDayGrid(month, year) {
        // update the header
        this._datepicker.setHeader(this._datepicker.months[month] + ' ' + year);
        // find the lower and upper boundaries
        const start = new Date(year, month, 1);
        const end = new Date(year, month + 1, 0);
        // ensure the startOfWeek value is between 0-6 to prevent any infinite loop
        const startOfWeek = Math.min(WeekDay.Saturday, Math.max(WeekDay.Sunday, this._datepicker.startOfWeek$.value));
        // we always want to show from the specified start of week - this may include showing some dates from the previous month
        while (start.getDay() !== startOfWeek) {
            start.setDate(start.getDate() - 1);
        }
        // we also want to make sure that the range ends on a saturday
        end.setDate(end.getDate() + (6 - end.getDay()));
        // create an array of all the days to display
        const dates = dateRange(start, end).map(date => ({
            day: date.getDate(),
            month: date.getMonth(),
            year: date.getFullYear(),
            date,
            isToday: this.isToday(date),
            isActive: this.isActive(date),
            isCurrentMonth: date.getMonth() === month,
        }));
        // turn the dates into a grid
        const items = gridify(dates, 7);
        this.grid$.next(items);
        // if no item has yet been focused then focus the first day of the month
        if ((this._datepicker.modeDirection === ModeDirection.None ||
            this._datepicker.modeDirection === ModeDirection.Descend) &&
            this.focused$.value === null) {
            // check if the selected item is visible
            const selectedDay = dates.find(day => day.isCurrentMonth && day.isActive);
            if (selectedDay) {
                this.setFocus(selectedDay.day, selectedDay.month, selectedDay.year);
            }
            else {
                // find the first day of the month
                const first = dates.find(date => date.day === 1);
                // focus the date
                this.setFocus(first.day, first.month, first.year);
            }
        }
    }
    /**
     * Determine whether or not a specific date is today
     * @param date The date to check
     */
    isToday(date) {
        return compareDays(new Date(), date);
    }
    /**
     * Determines whether or not a specific date is the selected one
     * @param date the date to check
     */
    isActive(date) {
        return this._datepicker.selected$.value
            ? compareDays(this._datepicker.selected$.value, date)
            : false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DayViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DayViewService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DayViewService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class FocusIfDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        /** The delay that should ellapse before focussing the element */
        this.focusIfDelay = 0;
        /** Determine if we should scroll the element into view when focused */
        this.focusIfScroll = true;
        this._timeout = null;
    }
    /** Focus when the boolean value is true */
    set focusIf(focus) {
        // if a timeout is pending then cancel it
        if (!focus && this._timeout !== null) {
            clearTimeout(this._timeout);
            this._timeout = null;
        }
        if (focus && this._timeout === null) {
            this._timeout = window.setTimeout(() => {
                this._elementRef.nativeElement.focus({ preventScroll: !this.focusIfScroll });
                this._timeout = null;
            }, this.focusIfDelay);
        }
    }
    ngOnDestroy() {
        if (this._timeout !== null) {
            clearTimeout(this._timeout);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIfDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FocusIfDirective, isStandalone: false, selector: "[focusIf]", inputs: { focusIfDelay: "focusIfDelay", focusIfScroll: "focusIfScroll", focusIf: "focusIf" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIfDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[focusIf]',
                    standalone: false,
                }]
        }], propDecorators: { focusIfDelay: [{
                type: Input
            }], focusIfScroll: [{
                type: Input
            }], focusIf: [{
                type: Input
            }] } });

class WeekDaySortPipe {
    transform(value, startOfWeek) {
        // ensure start of week is in range
        startOfWeek = Math.max(WeekDay.Sunday, Math.min(WeekDay.Saturday, startOfWeek));
        // create a new array to avoid altering the original
        const weekdays = [...value];
        for (let idx = 0; idx < startOfWeek; idx++) {
            weekdays.push(weekdays.shift());
        }
        return weekdays;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WeekDaySortPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: WeekDaySortPipe, isStandalone: false, name: "weekDaySort" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WeekDaySortPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'weekDaySort',
                    standalone: false,
                }]
        }] });

class DayViewComponent {
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    get _rangeStart() {
        return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
    }
    get _rangeEnd() {
        return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
    }
    constructor() {
        this.datePicker = inject(DateTimePickerService);
        this.dayService = inject(DayViewService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._focusOrigin = inject(FocusIndicatorOriginService);
        this._liveAnnouncer = inject(LiveAnnouncer);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        this._onDestroy = new Subject();
        this.datePicker.headerEvent$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(event => (event === DatePickerHeaderEvent.Next ? this.next() : this.previous()));
        // if we are a range picker then we also want to subscribe to range changes
        if (this._rangeService) {
            merge(this._rangeService.onRangeChange, this._rangeService.onHoverChange)
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => this._changeDetector.detectChanges());
            // subscribe to changes to the start date
            this._rangeService.onStartChange
                .pipe(takeUntil(this._onDestroy), filter(date => !!date && this._isRangeEnd && this.datePicker.initialised), delay(0))
                .subscribe(date => this.onRangeChange(date));
            // subscribe to changes to the end date
            this._rangeService.onEndChange
                .pipe(takeUntil(this._onDestroy), filter(date => !!date && this._isRangeStart && this.datePicker.initialised), delay(0))
                .subscribe(date => this.onRangeChange(date));
            // when the range is cleared reset the selected date so we can click on the same date again if we want to
            this._rangeService.onClear
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => this.datePicker.selected$.next(null));
        }
    }
    ngAfterViewInit() {
        // update when there are changes to the min/max values
        merge(this.datePicker.min$, this.datePicker.max$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._changeDetector.detectChanges());
        // if we open and the range start is already selected, ensure that we move the end picker to a month with options
        if (!this.datePicker.initialised && this._rangeStart && !this._rangeEnd && this._isRangeEnd) {
            this.onRangeChange(this._rangeStart);
        }
        // if we open and the range end is already selected, ensure that we move the start picker to a month with options
        if (!this.datePicker.initialised && this._rangeEnd && !this._rangeStart && this._isRangeStart) {
            this.onRangeChange(this._rangeEnd);
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Navigate to the previous page of dates
     */
    previous() {
        this.datePicker.setViewportMonth(this.datePicker.month$.value - 1);
    }
    /**
     * Navigate to the next page of dates
     */
    next() {
        this.datePicker.setViewportMonth(this.datePicker.month$.value + 1);
    }
    /**
     * Select a particular date
     * @param date the date to select
     */
    select(date) {
        // if we are the start range picker and we click the already selected day deselect it
        if (this._isRangeMode &&
            this._isRangeStart &&
            this._rangeStart &&
            compareDays(this._rangeStart, date)) {
            this._rangeService.setStartDate(null);
            this.datePicker.selected$.next(null);
            return;
        }
        // if we are the end range picker and we click the already selected day deselect it
        if (this._isRangeMode &&
            this._isRangeEnd &&
            this._rangeEnd &&
            compareDays(this._rangeEnd, date)) {
            this._rangeService.setEndDate(null);
            this.datePicker.selected$.next(null);
            return;
        }
        // if we are in range mode ensure we include the time from the time picker
        if (this._isRangeMode) {
            // update the current date object
            if (this._isRangeStart && !this._rangeService.showTime) {
                this.datePicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), 0, 0, 0);
            }
            else if (this._isRangeEnd && !this._rangeService.showTime) {
                this.datePicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), 23, 59, 59);
            }
            else {
                this.datePicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), this.datePicker.hours, this.datePicker.minutes, this.datePicker.seconds);
            }
        }
        else {
            // update the current date object
            this.datePicker.setDate(date.getDate(), date.getMonth(), date.getFullYear());
        }
        // focus the newly selected date
        this.dayService.setFocus(date.getDate(), date.getMonth(), date.getFullYear());
        // if we select a start date that is after the end date then clear the end date
        if (this._isRangeMode && this._isRangeStart && this._rangeStart && this._rangeEnd) {
            if (this._rangeStart.getTime() > this._rangeEnd.getTime()) {
                this._rangeService.setEndDate(null);
            }
        }
        // if we select a end date that is before the start date then clear the start date
        if (this._isRangeMode && this._isRangeEnd && this._rangeStart && this._rangeEnd) {
            if (this._rangeEnd.getTime() < this._rangeStart.getTime()) {
                this._rangeService.setStartDate(null);
            }
        }
    }
    trackWeekByFn(index) {
        return index;
    }
    trackDayByFn(_index, item) {
        return `${item.day} ${item.month} ${item.year}`;
    }
    focusDate(item, dayOffset) {
        // determine the date of the day
        const target = new Date(item.date.setDate(item.date.getDate() + dayOffset));
        // we should force the origin to be keyboard
        this._focusOrigin.setOrigin('keyboard');
        // identify which date should be focused
        this.dayService.setFocus(target.getDate(), target.getMonth(), target.getFullYear());
    }
    getTabbable(item) {
        const focused = this.dayService.focused$.value;
        const grid = this.dayService.grid$.value;
        const month = this.datePicker.month$.value;
        // if there is a focused month check if this is it
        if (focused) {
            // check if the focused day is visible
            const isFocusedDayVisible = !!grid.find(row => !!row.find(_item => _item.day === focused.day &&
                _item.month === focused.month &&
                _item.year === focused.year &&
                _item.month === month));
            if (isFocusedDayVisible) {
                return (focused.day === item.day && focused.month === item.month && focused.year === item.year);
            }
        }
        // if there is no focusable day then check if there is a selected day
        const isSelectedDayVisible = !!grid.find(row => !!row.find(day => day.isActive));
        if (isSelectedDayVisible) {
            return item.isActive;
        }
        // find the first non disabled day that is part of the current month
        for (const row of grid) {
            for (const column of row) {
                if (column === item && column.month === month && !this.getDisabled(column.date)) {
                    return true;
                }
            }
        }
        return false;
    }
    getDisabled(date) {
        // if we are not in range mode then it will always be enabled
        if (this._isRangeMode) {
            // if we are range start and dates are after the range end then they should also be disabled
            if (this._isRangeStart &&
                !this._rangeStart &&
                this._rangeEnd &&
                isDateAfter(date, this._rangeEnd)) {
                return true;
            }
            // if we are range end and dates are before the range start then they should also be disabled
            if (this._isRangeEnd &&
                !this._rangeEnd &&
                this._rangeStart &&
                isDateBefore(date, this._rangeStart)) {
                return true;
            }
        }
        if (this.datePicker.min$.value && isDateBefore(date, this.datePicker.min$.value)) {
            return true;
        }
        if (this.datePicker.max$.value && isDateAfter(date, this.datePicker.max$.value)) {
            return true;
        }
        return false;
    }
    isRangeStartDate(date) {
        return this._isRangeMode && this._rangeStart && compareDays(date, this._rangeStart);
    }
    isRangeEndDate(date) {
        return this._isRangeMode && this._rangeEnd && compareDays(date, this._rangeEnd);
    }
    isWithinRange(date) {
        return (this._isRangeMode &&
            this._rangeStart &&
            isDateAfter(date, this._rangeStart) &&
            isDateBefore(date, this._rangeEnd));
    }
    isDateHovered(date) {
        // if we are not in range mode or both start and end dates are selected then dont show range hover
        if (!this._isRangeMode || !this._rangeService.hover || (this._rangeStart && this._rangeEnd)) {
            return;
        }
        return ((this._rangeStart &&
            isDateAfter(date, this._rangeStart) &&
            isDateBefore(date, this._rangeService.hover, true)) ||
            (this._rangeEnd &&
                isDateBefore(date, this._rangeEnd) &&
                isDateAfter(date, this._rangeService.hover, true)));
    }
    isItemActive(date, isActive) {
        if (!this._isRangeMode) {
            return isActive;
        }
        return ((this._isRangeStart && this._rangeStart && compareDays(this._rangeStart, date)) ||
            (this._isRangeEnd && this._rangeEnd && compareDays(this._rangeEnd, date)));
    }
    onRangeMouseEnter(date) {
        if (this._isRangeMode) {
            this._rangeService.setDateMouseEnter(date);
        }
    }
    onRangeMouseLeave(date) {
        if (this._isRangeMode) {
            this._rangeService.setDateMouseLeave(date);
        }
    }
    /** Announce the date when we focus on a date */
    announceRangeMode() {
        if (this._isRangeMode) {
            this._liveAnnouncer.announce(this._isRangeStart
                ? this._rangeService.startPickerAriaLabel
                : this._rangeService.endPickerAriaLabel);
        }
    }
    /** Determine if we should focus a date */
    shouldFocus(item) {
        // if we are opening the popover initially we never want to focus a date in the range end picker
        if ((!this.datePicker.initialised && this._isRangeEnd) ||
            (this._rangeService && this._rangeService.isChangingTime)) {
            return false;
        }
        // extract the current focused dates
        const { day, month, year } = this.dayService.focused$.value;
        // check if the current date is the focused date and it is in the viewport date
        return day === item.day && month === item.month && year === item.year && item.isCurrentMonth;
    }
    /** Update the viewport when the range changes to ensure focus is present on a valid item */
    onRangeChange(date) {
        if (!((this._isRangeStart && !this._rangeStart) || (this._isRangeEnd && !this._rangeEnd))) {
            return;
        }
        // get the month showing on the other date range picker
        const currentDate = new Date(this.datePicker.year$.value, this.datePicker.month$.value);
        // if we are the start date and we are after the end date - ONLY then should be change the visible month the match the end date
        const startShouldUpdate = this._isRangeStart &&
            !this._rangeStart &&
            (currentDate.getFullYear() > date.getFullYear() ||
                (currentDate.getFullYear() === date.getFullYear() &&
                    currentDate.getMonth() > date.getMonth()));
        // if we are the end date and we are before the start date - ONLY then should be change the visible month the match the start date
        const endShouldUpdate = this._isRangeEnd &&
            !this._rangeEnd &&
            (currentDate.getFullYear() < date.getFullYear() ||
                (currentDate.getFullYear() === date.getFullYear() &&
                    currentDate.getMonth() < date.getMonth()));
        if (startShouldUpdate || endShouldUpdate) {
            this.datePicker.setViewportMonth(date.getMonth());
            this.datePicker.setViewportYear(date.getFullYear());
            this.dayService.setFocus(date.getDate(), date.getMonth(), date.getFullYear());
            this._changeDetector.detectChanges();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DayViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: DayViewComponent, isStandalone: false, selector: "ux-date-time-picker-day-view", providers: [DayViewService], ngImport: i0, template: "<table class=\"calendar\">\n  <thead>\n    <tr>\n      @for (\n        day of datePicker.weekdays$ | async | weekDaySort: (datePicker.startOfWeek$ | async);\n        let index = $index;\n        track index\n      ) {\n        <th class=\"weekday\" [attr.aria-label]=\"day\">{{ day }}</th>\n      }\n    </tr>\n  </thead>\n\n  <tbody role=\"grid\">\n    @for (row of dayService.grid$ | async; track trackWeekByFn($index)) {\n      <tr role=\"row\">\n        @for (item of row; track trackDayByFn($index, item)) {\n          <td class=\"date-cell\">\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              uxFocusIndicatorOrigin\n              class=\"date-button\"\n              role=\"gridcell\"\n              [class.range-start]=\"isRangeStartDate(item.date)\"\n              [class.range-between]=\"isWithinRange(item.date) || isDateHovered(item.date)\"\n              [class.range-end]=\"isRangeEndDate(item.date)\"\n              [focusIf]=\"shouldFocus(item)\"\n              [attr.aria-label]=\"item.date | date\"\n              [attr.aria-selected]=\"isItemActive(item.date, item.isActive)\"\n              [attr.aria-hidden]=\"!item.isCurrentMonth\"\n              [class.current]=\"item.isToday\"\n              [class.active]=\"isItemActive(item.date, item.isActive) && !getDisabled(item.date)\"\n              [class.preview]=\"!item.isCurrentMonth\"\n              [tabindex]=\"getTabbable(item) ? 0 : -1\"\n              [disabled]=\"getDisabled(item.date)\"\n              (click)=\"select(item.date); $event.stopPropagation()\"\n              (mouseenter)=\"onRangeMouseEnter(item.date)\"\n              (mouseleave)=\"onRangeMouseLeave(item.date)\"\n              (keydown.ArrowLeft)=\"focusDate(item, -1); $event.preventDefault()\"\n              (keydown.ArrowRight)=\"focusDate(item, 1); $event.preventDefault()\"\n              (keydown.ArrowUp)=\"focusDate(item, -7); $event.preventDefault()\"\n              (keydown.ArrowDown)=\"focusDate(item, 7); $event.preventDefault()\"\n              (focus)=\"announceRangeMode()\"\n            >\n              {{ item.date.getDate() }}\n            </button>\n          </td>\n        }\n      </tr>\n    }\n  </tbody>\n</table>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: WeekDaySortPipe, name: "weekDaySort" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DayViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker-day-view', providers: [DayViewService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<table class=\"calendar\">\n  <thead>\n    <tr>\n      @for (\n        day of datePicker.weekdays$ | async | weekDaySort: (datePicker.startOfWeek$ | async);\n        let index = $index;\n        track index\n      ) {\n        <th class=\"weekday\" [attr.aria-label]=\"day\">{{ day }}</th>\n      }\n    </tr>\n  </thead>\n\n  <tbody role=\"grid\">\n    @for (row of dayService.grid$ | async; track trackWeekByFn($index)) {\n      <tr role=\"row\">\n        @for (item of row; track trackDayByFn($index, item)) {\n          <td class=\"date-cell\">\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              uxFocusIndicatorOrigin\n              class=\"date-button\"\n              role=\"gridcell\"\n              [class.range-start]=\"isRangeStartDate(item.date)\"\n              [class.range-between]=\"isWithinRange(item.date) || isDateHovered(item.date)\"\n              [class.range-end]=\"isRangeEndDate(item.date)\"\n              [focusIf]=\"shouldFocus(item)\"\n              [attr.aria-label]=\"item.date | date\"\n              [attr.aria-selected]=\"isItemActive(item.date, item.isActive)\"\n              [attr.aria-hidden]=\"!item.isCurrentMonth\"\n              [class.current]=\"item.isToday\"\n              [class.active]=\"isItemActive(item.date, item.isActive) && !getDisabled(item.date)\"\n              [class.preview]=\"!item.isCurrentMonth\"\n              [tabindex]=\"getTabbable(item) ? 0 : -1\"\n              [disabled]=\"getDisabled(item.date)\"\n              (click)=\"select(item.date); $event.stopPropagation()\"\n              (mouseenter)=\"onRangeMouseEnter(item.date)\"\n              (mouseleave)=\"onRangeMouseLeave(item.date)\"\n              (keydown.ArrowLeft)=\"focusDate(item, -1); $event.preventDefault()\"\n              (keydown.ArrowRight)=\"focusDate(item, 1); $event.preventDefault()\"\n              (keydown.ArrowUp)=\"focusDate(item, -7); $event.preventDefault()\"\n              (keydown.ArrowDown)=\"focusDate(item, 7); $event.preventDefault()\"\n              (focus)=\"announceRangeMode()\"\n            >\n              {{ item.date.getDate() }}\n            </button>\n          </td>\n        }\n      </tr>\n    }\n  </tbody>\n</table>\n" }]
        }], ctorParameters: () => [] });

class MonthViewService {
    constructor() {
        this._datepicker = inject(DateTimePickerService);
        this.grid$ = new BehaviorSubject([[]]);
        this.focused$ = new BehaviorSubject(null);
        this._subscription = this._datepicker.year$.subscribe(year => this.createMonthGrid(year));
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    setFocus(month, year) {
        this.focused$.next({ month, year });
        // update the viewport to ensure focused month is visible
        this._datepicker.setViewportYear(year);
    }
    createMonthGrid(year) {
        // update the header
        this._datepicker.setHeader(year.toString());
        // get the current year and month
        const currentMonth = new Date().getMonth();
        const currentYear = new Date().getFullYear();
        // get the currently selected month
        const activeMonth = this._datepicker.selected$.value
            ? this._datepicker.selected$.value.getMonth()
            : null;
        const activeYear = this._datepicker.selected$.value
            ? this._datepicker.selected$.value.getFullYear()
            : null;
        // create a 4x3 grid of month numbers
        const months = range(0, 11).map(month => {
            return {
                name: this._datepicker.monthsShort[month],
                month,
                year,
                isCurrentMonth: year === currentYear && month === currentMonth,
                isActiveMonth: year === activeYear && month === activeMonth,
            };
        });
        // map these to the appropriate format
        const items = gridify(months, 4);
        // update the grid
        this.grid$.next(items);
        // if there is no focused month select the first one
        if (this._datepicker.modeDirection === ModeDirection.Descend && this.focused$.value === null) {
            // check if the selected month is in view
            const selectedMonth = months.find(month => month.isActiveMonth);
            this.setFocus(selectedMonth ? selectedMonth.month : 0, year);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MonthViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MonthViewService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MonthViewService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class MonthViewComponent {
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    get _rangeStart() {
        return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
    }
    get _rangeEnd() {
        return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
    }
    get _minMonth() {
        return this._datePicker.min$.value
            ? new Date(this._datePicker.min$.value.getFullYear(), this._datePicker.min$.value.getMonth())
            : null;
    }
    get _maxMonth() {
        return this._datePicker.max$.value
            ? new Date(this._datePicker.max$.value.getFullYear(), this._datePicker.max$.value.getMonth())
            : null;
    }
    constructor() {
        this.monthService = inject(MonthViewService);
        this._datePicker = inject(DateTimePickerService);
        this._liveAnnouncer = inject(LiveAnnouncer);
        this._changeDetector = inject(ChangeDetectorRef);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        this._onDestroy = new Subject();
        this._datePicker.headerEvent$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(event => (event === DatePickerHeaderEvent.Next ? this.next() : this.previous()));
        if (this._rangeService) {
            this._rangeService.onRangeChange
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => this._changeDetector.detectChanges());
        }
        // if the currently focused item is disabled then choose a month that isn't disabled
        if (this.monthService.focused$.value) {
            if (this.getDisabled(this.monthService.focused$.value)) {
                for (const row of this.monthService.grid$.value) {
                    for (const column of row) {
                        if (!this.getDisabled(column)) {
                            this.monthService.setFocus(column.month, column.year);
                            return;
                        }
                    }
                }
            }
        }
    }
    ngAfterViewInit() {
        // update on min/max changes
        merge(this._datePicker.min$, this._datePicker.max$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._changeDetector.detectChanges());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Get the disabled state of a month */
    getDisabled(item) {
        const date = new Date(item.year, item.month);
        // if we are not in range mode then it will always be enabled
        if (this._isRangeMode) {
            // if we are range start and dates are after the range end then they should also be disabled
            if (this._isRangeStart &&
                !this._rangeStart &&
                this._rangeEnd &&
                isDateAfter(date, new Date(this._rangeEnd.getFullYear(), this._rangeEnd.getMonth()))) {
                return true;
            }
            // if we are range end and dates are before the range start then they should also be disabled
            if (this._isRangeEnd &&
                !this._rangeEnd &&
                this._rangeStart &&
                isDateBefore(date, new Date(this._rangeStart.getFullYear(), this._rangeStart.getMonth()))) {
                return true;
            }
        }
        if (this._minMonth && isDateBefore(date, this._minMonth)) {
            return true;
        }
        if (this._maxMonth && isDateAfter(date, this._maxMonth)) {
            return true;
        }
        return false;
    }
    /**
     * Go to the previous year
     */
    previous() {
        this._datePicker.setViewportYear(this._datePicker.year$.value - 1);
    }
    /**
     * Go to the next year
     */
    next() {
        this._datePicker.setViewportYear(this._datePicker.year$.value + 1);
    }
    /**
     * Select a month in the calendar
     * @param month the index of the month to select
     */
    select(month) {
        this._datePicker.setViewportMonth(month);
        // show the day picker
        this._datePicker.goToChildMode();
    }
    focusMonth(item, monthOffset) {
        let targetMonth = item.month + monthOffset;
        let targetYear = item.year;
        if (targetMonth < 0) {
            targetMonth += 12;
            targetYear -= 1;
        }
        if (targetMonth >= 12) {
            targetMonth -= 12;
            targetYear += 1;
        }
        this.monthService.setFocus(targetMonth, targetYear);
    }
    trackRowByFn(index) {
        return index;
    }
    trackMonthByFn(_index, item) {
        return `${item.month} ${item.year}`;
    }
    getTabbable(item) {
        const focused = this.monthService.focused$.value;
        const grid = this.monthService.grid$.value;
        // if there is a focused month check if this is it
        if (focused) {
            // check if the focused month is visible
            const isFocusedMonthVisible = !!grid.find(row => !!row.find(_item => _item.month === focused.month && _item.year === focused.year));
            if (isFocusedMonthVisible) {
                return focused.month === item.month && focused.year === item.year;
            }
        }
        // if there is no focusable month then check if there is a selected month
        const isSelectedMonthVisible = !!grid.find(row => !!row.find(month => month.isActiveMonth));
        if (isSelectedMonthVisible) {
            return item.isActiveMonth;
        }
        // otherwise find the first non-disabled month
        for (const row of grid) {
            for (const column of row) {
                if (!this.getDisabled(column)) {
                    return item === column;
                }
            }
        }
        return false;
    }
    /** Announce the date when we focus on a date */
    announceRangeMode() {
        if (this._isRangeMode) {
            this._liveAnnouncer.announce(this._isRangeStart
                ? this._rangeService.startPickerAriaLabel
                : this._rangeService.endPickerAriaLabel);
        }
    }
    shouldFocus(item) {
        const focused = this.monthService.focused$.value;
        if (focused) {
            return focused.month === item.month && focused.year === item.year;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MonthViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MonthViewComponent, isStandalone: false, selector: "ux-date-time-picker-month-view", providers: [MonthViewService], ngImport: i0, template: "<div class=\"calendar\" role=\"grid\">\n  @for (row of monthService.grid$ | async; track trackRowByFn($index)) {\n    <div class=\"calendar-row\" role=\"row\">\n      @for (item of row; track trackMonthByFn($index, item)) {\n        <button\n          type=\"button\"\n          uxFocusIndicator\n          uxFocusIndicatorOrigin\n          [programmaticFocusIndicator]=\"true\"\n          role=\"gridcell\"\n          class=\"calendar-item\"\n          [focusIf]=\"shouldFocus(item)\"\n          [disabled]=\"getDisabled(item)\"\n          [tabindex]=\"getTabbable(item) ? 0 : -1\"\n          [attr.aria-label]=\"item.name + ' ' + item.year\"\n          [attr.aria-selected]=\"item.isActiveMonth\"\n          [class.active]=\"item.isActiveMonth && !getDisabled(item)\"\n          [class.current]=\"item.isCurrentMonth\"\n          (click)=\"select(item.month); $event.stopPropagation()\"\n          (focus)=\"announceRangeMode()\"\n          (keydown.ArrowLeft)=\"focusMonth(item, -1); $event.preventDefault()\"\n          (keydown.ArrowRight)=\"focusMonth(item, 1); $event.preventDefault()\"\n          (keydown.ArrowUp)=\"focusMonth(item, -4); $event.preventDefault()\"\n          (keydown.ArrowDown)=\"focusMonth(item, 4); $event.preventDefault()\"\n        >\n          {{ item.name }}\n        </button>\n      }\n    </div>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MonthViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker-month-view', providers: [MonthViewService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"calendar\" role=\"grid\">\n  @for (row of monthService.grid$ | async; track trackRowByFn($index)) {\n    <div class=\"calendar-row\" role=\"row\">\n      @for (item of row; track trackMonthByFn($index, item)) {\n        <button\n          type=\"button\"\n          uxFocusIndicator\n          uxFocusIndicatorOrigin\n          [programmaticFocusIndicator]=\"true\"\n          role=\"gridcell\"\n          class=\"calendar-item\"\n          [focusIf]=\"shouldFocus(item)\"\n          [disabled]=\"getDisabled(item)\"\n          [tabindex]=\"getTabbable(item) ? 0 : -1\"\n          [attr.aria-label]=\"item.name + ' ' + item.year\"\n          [attr.aria-selected]=\"item.isActiveMonth\"\n          [class.active]=\"item.isActiveMonth && !getDisabled(item)\"\n          [class.current]=\"item.isCurrentMonth\"\n          (click)=\"select(item.month); $event.stopPropagation()\"\n          (focus)=\"announceRangeMode()\"\n          (keydown.ArrowLeft)=\"focusMonth(item, -1); $event.preventDefault()\"\n          (keydown.ArrowRight)=\"focusMonth(item, 1); $event.preventDefault()\"\n          (keydown.ArrowUp)=\"focusMonth(item, -4); $event.preventDefault()\"\n          (keydown.ArrowDown)=\"focusMonth(item, 4); $event.preventDefault()\"\n        >\n          {{ item.name }}\n        </button>\n      }\n    </div>\n  }\n</div>\n" }]
        }], ctorParameters: () => [] });

class YearViewService {
    constructor() {
        this._datepicker = inject(DateTimePickerService);
        this.grid$ = new BehaviorSubject([[]]);
        this.focused$ = new BehaviorSubject(null);
        this._year = new Date().getFullYear();
        this._subscription = new Subscription();
        const year = this._datepicker.year$.subscribe(_year => this.createYearGrid(_year));
        const event = this._datepicker.headerEvent$.subscribe(_event => _event === DatePickerHeaderEvent.Next ? this.goToNextDecade() : this.goToPreviousDecade());
        this._subscription.add(year);
        this._subscription.add(event);
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    setFocus(year) {
        this.focused$.next(year);
        this.createYearGrid(year);
    }
    goToPreviousDecade() {
        this.createYearGrid(this._year - 10);
    }
    goToNextDecade() {
        this.createYearGrid(this._year + 10);
    }
    createYearGrid(year = this._year) {
        this._year = year;
        // get the years to display
        const decade = this.getDecade(year);
        const currentYear = new Date().getFullYear();
        // produce items in the correct format
        const items = decade.range.map(_year => {
            return {
                year: _year,
                isCurrentYear: _year === currentYear,
                isActiveYear: _year === this._datepicker.year$.value,
            };
        });
        // update the header text
        this._datepicker.setHeader(decade.start + ' - ' + decade.end);
        // create the grid
        this.grid$.next(gridify(items, 4));
    }
    /**
     * Get the years in the current decade to display
     */
    getDecade(year) {
        // figure the start and end points
        const start = year - (year % 10);
        const end = start + 9;
        this._datepicker.yearRange = { start, end, range: range(start, end) };
        // create an array containing all the numbers between the start and end points
        return this._datepicker.yearRange;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YearViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YearViewService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YearViewService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class YearViewComponent {
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    get _rangeStart() {
        return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
    }
    get _rangeEnd() {
        return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
    }
    get _minYear() {
        return this._datePicker.min$.value
            ? new Date(this._datePicker.min$.value.getFullYear(), 0)
            : null;
    }
    get _maxYear() {
        return this._datePicker.max$.value
            ? new Date(this._datePicker.max$.value.getFullYear(), 0)
            : null;
    }
    constructor() {
        this.yearService = inject(YearViewService);
        this._datePicker = inject(DateTimePickerService);
        this._liveAnnouncer = inject(LiveAnnouncer);
        this._changeDetector = inject(ChangeDetectorRef);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        this._onDestroy = new Subject();
        if (this._rangeService) {
            this._rangeService.onRangeChange
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => this._changeDetector.detectChanges());
        }
    }
    ngAfterViewInit() {
        // update on min/max changes
        merge(this._datePicker.min$, this._datePicker.max$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._changeDetector.detectChanges());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    select(year) {
        this._datePicker.setViewportYear(year);
        // show the month picker
        this._datePicker.goToChildMode();
    }
    /** Get the disabled state of a month */
    getDisabled(item) {
        const date = new Date(item.year, 0);
        // if we are not in range mode then it will always be enabled
        if (this._isRangeMode) {
            // if we are range start and dates are after the range end then they should also be disabled
            if (this._isRangeStart &&
                !this._rangeStart &&
                this._rangeEnd &&
                isDateAfter(date, new Date(this._rangeEnd.getFullYear(), 0))) {
                return true;
            }
            // if we are range end and dates are before the range start then they should also be disabled
            if (this._isRangeEnd &&
                !this._rangeEnd &&
                this._rangeStart &&
                isDateBefore(date, new Date(this._rangeStart.getFullYear(), 0))) {
                return true;
            }
        }
        if (this._minYear && isDateBefore(date, this._minYear)) {
            return true;
        }
        if (this._maxYear && isDateAfter(date, this._maxYear)) {
            return true;
        }
        return false;
    }
    focusYear(item, yearOffset) {
        this.yearService.setFocus(item.year + yearOffset);
    }
    trackRowByFn(index) {
        return index;
    }
    trackYearByFn(_index, item) {
        return item.year;
    }
    getTabbable(item) {
        const focused = this.yearService.focused$.value;
        const grid = this.yearService.grid$.value;
        // if there is a focused year check if this is it
        if (focused) {
            // check if the focused year is visible
            const isFocusedYearVisible = !!grid.find(row => !!row.find(_item => _item.year === focused));
            if (isFocusedYearVisible) {
                return focused === item.year;
            }
        }
        // if there is no focusable year then check if there is a selected year
        const isSelectedYearVisible = !!grid.find(row => !!row.find(year => year.isActiveYear));
        if (isSelectedYearVisible) {
            return item.isActiveYear;
        }
        // otherwise find the first non-disabled month
        for (const row of this.yearService.grid$.value) {
            for (const column of row) {
                if (!this.getDisabled(column)) {
                    return item === column;
                }
            }
        }
        // otherwise make the first month tabbable
        return false;
    }
    /** Announce the date when we focus on a date */
    announceRangeMode() {
        if (this._isRangeMode) {
            this._liveAnnouncer.announce(this._isRangeStart
                ? this._rangeService.startPickerAriaLabel
                : this._rangeService.endPickerAriaLabel);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YearViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: YearViewComponent, isStandalone: false, selector: "ux-date-time-picker-year-view", providers: [YearViewService], ngImport: i0, template: "<div class=\"calendar\" role=\"grid\">\n  @for (row of yearService.grid$ | async; track trackRowByFn($index)) {\n    <div class=\"calendar-row\" role=\"row\">\n      @for (item of row; track trackYearByFn($index, item)) {\n        <button\n          uxFocusIndicator\n          uxFocusIndicatorOrigin\n          [programmaticFocusIndicator]=\"true\"\n          type=\"button\"\n          role=\"gridcell\"\n          class=\"calendar-item\"\n          [focusIf]=\"(yearService.focused$ | async) === item.year\"\n          [attr.aria-label]=\"item.year\"\n          [attr.aria-selected]=\"item.isActiveYear\"\n          [class.current]=\"item.isCurrentYear\"\n          [class.active]=\"item.isActiveYear && !getDisabled(item)\"\n          [disabled]=\"getDisabled(item)\"\n          (click)=\"select(item.year); $event.stopPropagation()\"\n          (focus)=\"announceRangeMode()\"\n          (keydown.ArrowLeft)=\"focusYear(item, -1); $event.preventDefault()\"\n          (keydown.ArrowRight)=\"focusYear(item, 1); $event.preventDefault()\"\n          (keydown.ArrowUp)=\"focusYear(item, -4); $event.preventDefault()\"\n          (keydown.ArrowDown)=\"focusYear(item, 4); $event.preventDefault()\"\n          [tabindex]=\"getTabbable(item) ? 0 : -1\"\n        >\n          {{ item.year }}\n        </button>\n      }\n    </div>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YearViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker-year-view', providers: [YearViewService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"calendar\" role=\"grid\">\n  @for (row of yearService.grid$ | async; track trackRowByFn($index)) {\n    <div class=\"calendar-row\" role=\"row\">\n      @for (item of row; track trackYearByFn($index, item)) {\n        <button\n          uxFocusIndicator\n          uxFocusIndicatorOrigin\n          [programmaticFocusIndicator]=\"true\"\n          type=\"button\"\n          role=\"gridcell\"\n          class=\"calendar-item\"\n          [focusIf]=\"(yearService.focused$ | async) === item.year\"\n          [attr.aria-label]=\"item.year\"\n          [attr.aria-selected]=\"item.isActiveYear\"\n          [class.current]=\"item.isCurrentYear\"\n          [class.active]=\"item.isActiveYear && !getDisabled(item)\"\n          [disabled]=\"getDisabled(item)\"\n          (click)=\"select(item.year); $event.stopPropagation()\"\n          (focus)=\"announceRangeMode()\"\n          (keydown.ArrowLeft)=\"focusYear(item, -1); $event.preventDefault()\"\n          (keydown.ArrowRight)=\"focusYear(item, 1); $event.preventDefault()\"\n          (keydown.ArrowUp)=\"focusYear(item, -4); $event.preventDefault()\"\n          (keydown.ArrowDown)=\"focusYear(item, 4); $event.preventDefault()\"\n          [tabindex]=\"getTabbable(item) ? 0 : -1\"\n        >\n          {{ item.year }}\n        </button>\n      }\n    </div>\n  }\n</div>\n" }]
        }], ctorParameters: () => [] });

const SPIN_BUTTON_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SpinButtonComponent),
    multi: true,
};
class SpinButtonComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        this.type = 'text';
        this.placeholder = '';
        this.disabled = false;
        this.spinners = true;
        this.readOnly = true;
        this.scrolling = true;
        this.arrowkeys = true;
        this.maxLength = Infinity;
        this.valueChange = new EventEmitter();
        this.increment = new EventEmitter();
        this.decrement = new EventEmitter();
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
        this._regexKeypress = RegExp(/^[0-9.,-]+$/);
        // eslint-disable-next-line no-useless-escape
        this._regexPaste = RegExp(/^\-?\d+(\.\d+)?$/);
    }
    set value(value) {
        this._value = value;
        this.onChangeCallback(value);
        this.onTouchedCallback();
    }
    get value() {
        return this._value;
    }
    scroll(event) {
        if (!this.scrolling) {
            return;
        }
        if (event.deltaY > 0) {
            this.triggerDecrement();
        }
        else {
            this.triggerIncrement();
        }
        event.preventDefault();
    }
    triggerIncrement() {
        if (!this.disabled) {
            this.increment.emit();
        }
    }
    triggerDecrement() {
        if (!this.disabled) {
            this.decrement.emit();
        }
    }
    writeValue(value) {
        this.value = value;
        this._changeDetector.markForCheck();
    }
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    onKeypress(event) {
        // we only need to perform checks if the type is number
        if (this.type !== 'number') {
            return;
        }
        if (!this._regexKeypress.test(event.key)) {
            return false;
        }
        return true;
    }
    onPaste(event) {
        // we only need to perform checks if the type is number
        if (this.type !== 'number') {
            return;
        }
        // get the value being pasted
        const value = event.clipboardData.getData('text');
        // check if it contains the character
        if (!this._regexPaste.test(value)) {
            // inset the numeric value only if there is one
            const numericValue = parseFloat(value);
            if (!isNaN(numericValue)) {
                this.value = numericValue;
            }
            event.stopPropagation();
            event.preventDefault();
        }
    }
    onValueChange(input, value) {
        // ensure the value is not longer than the maxLength (verify value is a string in case it is
        // null or undefined, before trying to check the length.
        if (typeof value === 'string' && value.length > this.maxLength) {
            // if the type specified is a number then it may begin with a 0
            // e.g. "02", in which case if we add a second digit we should drop
            // the leading "0" and allow the non-zero number to be added
            if (this.type === 'number') {
                value = parseFloat(value).toString();
            }
            // remove any characters over the max length
            value = value.substring(0, this.maxLength);
            // We must manually update the input value in this case rather than relying
            // on Angular, as if value was previously "11" and we add an additional digit
            // e.g. "112", after performing the substring, the outputted value would again
            // be "11" which Angular would not recognize as having changed so it will not
            // update the value displayed in the input.
            input.value = value;
        }
        // emit the value after all length checks
        this.valueChange.emit(value);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SpinButtonComponent, isStandalone: false, selector: "ux-spin-button", inputs: { value: "value", type: "type", min: "min", max: "max", placeholder: "placeholder", disabled: "disabled", spinners: "spinners", readOnly: "readOnly", scrolling: "scrolling", arrowkeys: "arrowkeys", maxLength: "maxLength", incrementAriaLabel: "incrementAriaLabel", inputAriaLabel: "inputAriaLabel", decrementAriaLabel: "decrementAriaLabel" }, outputs: { valueChange: "valueChange", increment: "increment", decrement: "decrement" }, providers: [SPIN_BUTTON_VALUE_ACCESSOR], ngImport: i0, template: "@if (spinners) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"spin-button\"\n    tabindex=\"-1\"\n    [disabled]=\"disabled\"\n    [attr.aria-label]=\"incrementAriaLabel\"\n    [attr.aria-disabled]=\"disabled\"\n    (click)=\"triggerIncrement()\"\n  >\n    <ux-icon name=\"up\" class=\"spin-button-up-icon\"></ux-icon>\n  </button>\n}\n\n<input\n  #input\n  type=\"text\"\n  role=\"spinbutton\"\n  [min]=\"min\"\n  [max]=\"max\"\n  [tabindex]=\"0\"\n  class=\"form-control\"\n  [placeholder]=\"placeholder\"\n  [readOnly]=\"readOnly\"\n  [disabled]=\"disabled\"\n  [attr.aria-label]=\"inputAriaLabel\"\n  [attr.aria-disabled]=\"disabled\"\n  [attr.aria-valuemin]=\"min\"\n  [attr.aria-valuenow]=\"value\"\n  [attr.aria-valuemax]=\"max\"\n  [attr.aria-readonly]=\"readOnly\"\n  [ngModel]=\"value\"\n  (ngModelChange)=\"onValueChange(input, $event)\"\n  (wheel)=\"scroll($event)\"\n  (keypress)=\"onKeypress($event)\"\n  (paste)=\"onPaste($event)\"\n  (keydown.arrowup)=\"arrowkeys ? triggerIncrement() : null; $event.preventDefault()\"\n  (keydown.arrowdown)=\"arrowkeys ? triggerDecrement() : null; $event.preventDefault()\"\n/>\n\n@if (spinners) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"spin-button\"\n    tabindex=\"-1\"\n    [disabled]=\"disabled\"\n    [attr.aria-label]=\"decrementAriaLabel\"\n    [attr.aria-disabled]=\"disabled\"\n    (click)=\"triggerDecrement()\"\n  >\n    <ux-icon name=\"down\" class=\"spin-button-down-icon\"></ux-icon>\n  </button>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-spin-button', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [SPIN_BUTTON_VALUE_ACCESSOR], standalone: false, template: "@if (spinners) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"spin-button\"\n    tabindex=\"-1\"\n    [disabled]=\"disabled\"\n    [attr.aria-label]=\"incrementAriaLabel\"\n    [attr.aria-disabled]=\"disabled\"\n    (click)=\"triggerIncrement()\"\n  >\n    <ux-icon name=\"up\" class=\"spin-button-up-icon\"></ux-icon>\n  </button>\n}\n\n<input\n  #input\n  type=\"text\"\n  role=\"spinbutton\"\n  [min]=\"min\"\n  [max]=\"max\"\n  [tabindex]=\"0\"\n  class=\"form-control\"\n  [placeholder]=\"placeholder\"\n  [readOnly]=\"readOnly\"\n  [disabled]=\"disabled\"\n  [attr.aria-label]=\"inputAriaLabel\"\n  [attr.aria-disabled]=\"disabled\"\n  [attr.aria-valuemin]=\"min\"\n  [attr.aria-valuenow]=\"value\"\n  [attr.aria-valuemax]=\"max\"\n  [attr.aria-readonly]=\"readOnly\"\n  [ngModel]=\"value\"\n  (ngModelChange)=\"onValueChange(input, $event)\"\n  (wheel)=\"scroll($event)\"\n  (keypress)=\"onKeypress($event)\"\n  (paste)=\"onPaste($event)\"\n  (keydown.arrowup)=\"arrowkeys ? triggerIncrement() : null; $event.preventDefault()\"\n  (keydown.arrowdown)=\"arrowkeys ? triggerDecrement() : null; $event.preventDefault()\"\n/>\n\n@if (spinners) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"spin-button\"\n    tabindex=\"-1\"\n    [disabled]=\"disabled\"\n    [attr.aria-label]=\"decrementAriaLabel\"\n    [attr.aria-disabled]=\"disabled\"\n    (click)=\"triggerDecrement()\"\n  >\n    <ux-icon name=\"down\" class=\"spin-button-down-icon\"></ux-icon>\n  </button>\n}\n" }]
        }], propDecorators: { value: [{
                type: Input
            }], type: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], disabled: [{
                type: Input
            }], spinners: [{
                type: Input
            }], readOnly: [{
                type: Input
            }], scrolling: [{
                type: Input
            }], arrowkeys: [{
                type: Input
            }], maxLength: [{
                type: Input
            }], incrementAriaLabel: [{
                type: Input
            }], inputAriaLabel: [{
                type: Input
            }], decrementAriaLabel: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], increment: [{
                type: Output
            }], decrement: [{
                type: Output
            }] } });

const TIME_PICKER_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => TimePickerComponent),
    multi: true,
};
class TimePickerComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Whether the arrow keys can be used to increment or decrement the selected time component. */
        this.arrowkeys = true;
        /** Whether the mouse scroll wheel can be used to increment or decrement the selected time component. */
        this.mousewheel = true;
        /** Whether the control is disabled. */
        this.disabled = false;
        /** Whether the control is readonly. */
        this.readOnly = false;
        /** Whether to show the meridian (AM/PM) selector. If this is false, the 24-hour clock will be used. */
        this.showMeridian = false;
        /** Whether to show the hour selector. */
        this.showHours = true;
        /** Whether to show the minute selector. */
        this.showMinutes = true;
        /** Whether to show the second selector. */
        this.showSeconds = false;
        /** Whether to show increment and decrement buttons in the time picker. */
        this.showSpinners = true;
        /** The number of hours to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */
        this.hourStep = 1;
        /** The number of minutes to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */
        this.minuteStep = 1;
        /** The number of seconds to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */
        this.secondStep = 1;
        /** An array containing the labels to show in the meridian selector. */
        this.meridians = ['AM', 'PM'];
        /** Emitted when the `value` changes. */
        this.valueChange = new EventEmitter();
        /** Emitted when the validity of the control changes. */
        this.isValid = new EventEmitter();
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
        this._value = new Date();
        this._isValid = true;
    }
    /** The value to display. */
    set value(value) {
        this._value = new Date(value);
        this.valueChange.emit(this._value);
        this.onChangeCallback(this._value);
        this.onTouchedCallback();
    }
    get value() {
        return new Date(this._value);
    }
    get _meridian() {
        return this._value.getHours() < 12 ? this.meridians[0] : this.meridians[1];
    }
    get _valid() {
        return this.checkValidity(this._value);
    }
    get isValidDate() {
        return this.value instanceof Date && !isNaN(this.value.getTime());
    }
    writeValue(value) {
        this.value = value;
        this._changeDetector.markForCheck();
    }
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    getMeridianTime(hour) {
        return hour > 12 ? hour - 12 : hour;
    }
    setHour(hour) {
        const date = this.value;
        date.setHours(hour ? hour : 0);
        this.value = date;
    }
    setMinute(minute) {
        const date = this.value;
        date.setMinutes(minute ? minute : 0);
        this.value = date;
    }
    setSeconds(seconds) {
        const date = this.value;
        date.setSeconds(seconds ? seconds : 0);
        this.value = date;
    }
    incrementHour(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setHour(this.value.getHours() + this.hourStep);
    }
    decrementHour(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setHour(this.value.getHours() - this.hourStep);
    }
    incrementMinute(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setMinute(this.value.getMinutes() + this.minuteStep);
    }
    decrementMinute(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setMinute(this.value.getMinutes() - this.minuteStep);
    }
    incrementSecond(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setSeconds(this.value.getSeconds() + this.secondStep);
    }
    decrementSecond(arrowkey = false) {
        if (this.disabled || (arrowkey && !this.arrowkeys)) {
            return;
        }
        this.setSeconds(this.value.getSeconds() - this.secondStep);
    }
    selectMeridian(meridian) {
        // get the current time
        const hour = this.value.getHours();
        // if we have selected AM
        if (meridian === this.meridians[0]) {
            if (hour >= 12) {
                this.setHour(hour - 12);
            }
        }
        // if we have selected PM
        if (meridian === this.meridians[1]) {
            if (hour < 12) {
                this.setHour(hour + 12);
            }
        }
    }
    checkValidity(date) {
        let valid = true;
        // Fix min and max date components in order to compare time only
        const min = this.normalizeDate(this.min, date);
        const max = this.normalizeDate(this.max, date);
        if ((min && date.getTime() < min.getTime()) || (max && date.getTime() > max.getTime())) {
            valid = false;
        }
        // if the valid state has changed then emit the isValid output
        if (valid !== this._isValid) {
            this._isValid = valid;
            this.isValid.emit(valid);
        }
        return valid;
    }
    hourChange(value) {
        // if the value is empty then emit nothing
        if (value && typeof value === 'string' && value.trim() === '') {
            return;
        }
        // convert the string to a number
        let hour = typeof value === 'number' ? value : parseInt(value);
        // ensure the hours is valid
        if (!isNaN(hour)) {
            if (hour < 0) {
                hour = 0;
            }
            if (hour > (this.showMeridian ? 12 : 23)) {
                hour = this.showMeridian ? 12 : 23;
            }
        }
        const currentHour = this.value.getHours();
        // if the value hasn't changed, do nothing
        if (hour === currentHour) {
            return;
        }
        hour = isNaN(hour) ? currentHour : hour;
        if (this.showMeridian) {
            // if the number is invalid then restore it to the previous value
            if (this._meridian === this.meridians[0]) {
                if (hour >= 12) {
                    hour -= 12;
                }
            }
            // if we have selected PM
            if (this._meridian === this.meridians[1]) {
                if (hour < 12) {
                    hour += 12;
                }
            }
        }
        this.setHour(hour);
    }
    minuteChange(value) {
        // convert the string to a number
        let minute = typeof value === 'number' ? value : parseInt(value);
        const currentMinute = this.value.getMinutes();
        // if the value hasn't changed, do nothing
        if (minute === currentMinute) {
            return;
        }
        // ensure the hours is valid
        if (!isNaN(minute)) {
            if (minute < 0) {
                minute = 59;
            }
            if (minute > 59) {
                minute = 0;
            }
        }
        // if the number is invalid then restore it to the previous value
        this.setMinute(isNaN(minute) ? currentMinute : minute);
    }
    secondChange(value) {
        // convert the string to a number
        let second = typeof value === 'number' ? value : parseInt(value);
        const currentSecond = this.value.getSeconds();
        // if the value hasn't changed, do nothing
        if (second === currentSecond) {
            return;
        }
        // ensure the hours is valid
        if (!isNaN(second)) {
            if (second < 0) {
                second = 0;
            }
            if (second > 59) {
                second = 59;
            }
        }
        // if the number is invalid then restore it to the previous value
        this.setSeconds(isNaN(second) ? currentSecond : second);
    }
    /** Normalise a date's year/month/date components. */
    normalizeDate(date, reference) {
        if (!date) {
            return null;
        }
        const normalized = new Date(date);
        normalized.setFullYear(reference.getFullYear());
        normalized.setMonth(reference.getMonth());
        normalized.setDate(reference.getDate());
        return normalized;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TimePickerComponent, isStandalone: false, selector: "ux-time-picker", inputs: { arrowkeys: "arrowkeys", mousewheel: "mousewheel", disabled: "disabled", readOnly: "readOnly", showMeridian: "showMeridian", showHours: "showHours", showMinutes: "showMinutes", showSeconds: "showSeconds", showSpinners: "showSpinners", hourStep: "hourStep", minuteStep: "minuteStep", secondStep: "secondStep", min: "min", max: "max", meridians: "meridians", value: "value" }, outputs: { valueChange: "valueChange", isValid: "isValid" }, providers: [TIME_PICKER_VALUE_ACCESSOR], ngImport: i0, template: "<div class=\"time-picker\">\n  @if (showHours) {\n    <div class=\"time-picker-column time-hours-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"HH\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"showMeridian ? 12 : 23\"\n        [value]=\"isValidDate ? (value | date: (showMeridian ? 'h' : 'HH')) : ''\"\n        (valueChange)=\"hourChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"hour\"\n        incrementAriaLabel=\"Increment the hour\"\n        decrementAriaLabel=\"Decrement the hour\"\n        (increment)=\"incrementHour()\"\n        (decrement)=\"decrementHour()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n\n  @if (showMinutes) {\n    <div class=\"time-picker-separator\">:</div>\n  }\n\n  @if (showMinutes) {\n    <div class=\"time-picker-column time-minutes-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"MM\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"59\"\n        [value]=\"isValidDate ? (value | date: 'mm') : ''\"\n        (valueChange)=\"minuteChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"minute\"\n        incrementAriaLabel=\"Increment the minute\"\n        decrementAriaLabel=\"Decrement the minute\"\n        (increment)=\"incrementMinute()\"\n        (decrement)=\"decrementMinute()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n\n  @if (showSeconds) {\n    <div class=\"time-picker-separator\">:</div>\n  }\n\n  @if (showSeconds) {\n    <div class=\"time-picker-column time-seconds-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"SS\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"59\"\n        [value]=\"isValidDate ? (value | date: 'ss') : ''\"\n        (valueChange)=\"secondChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"seconds\"\n        incrementAriaLabel=\"Increment the second\"\n        decrementAriaLabel=\"Decrement the second\"\n        (increment)=\"incrementSecond()\"\n        (decrement)=\"decrementSecond()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n</div>\n\n@if (showMeridian) {\n  <div class=\"time-picker-meridian\">\n    <div class=\"btn-group\" role=\"radiogroup\">\n      @for (meridian of meridians; track meridian) {\n        <button\n          type=\"button\"\n          class=\"btn button-toggle-accent\"\n          role=\"radio\"\n          tabindex=\"0\"\n          [disabled]=\"disabled\"\n          (click)=\"selectMeridian(meridian)\"\n          [class.active]=\"meridian === _meridian\"\n          [attr.aria-label]=\"meridian\"\n          [attr.aria-checked]=\"meridian === _meridian\"\n          [attr.aria-disabled]=\"disabled\"\n        >\n          {{ meridian }}\n        </button>\n      }\n    </div>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "component", type: SpinButtonComponent, selector: "ux-spin-button", inputs: ["value", "type", "min", "max", "placeholder", "disabled", "spinners", "readOnly", "scrolling", "arrowkeys", "maxLength", "incrementAriaLabel", "inputAriaLabel", "decrementAriaLabel"], outputs: ["valueChange", "increment", "decrement"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimePickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-time-picker', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [TIME_PICKER_VALUE_ACCESSOR], standalone: false, template: "<div class=\"time-picker\">\n  @if (showHours) {\n    <div class=\"time-picker-column time-hours-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"HH\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"showMeridian ? 12 : 23\"\n        [value]=\"isValidDate ? (value | date: (showMeridian ? 'h' : 'HH')) : ''\"\n        (valueChange)=\"hourChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"hour\"\n        incrementAriaLabel=\"Increment the hour\"\n        decrementAriaLabel=\"Decrement the hour\"\n        (increment)=\"incrementHour()\"\n        (decrement)=\"decrementHour()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n\n  @if (showMinutes) {\n    <div class=\"time-picker-separator\">:</div>\n  }\n\n  @if (showMinutes) {\n    <div class=\"time-picker-column time-minutes-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"MM\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"59\"\n        [value]=\"isValidDate ? (value | date: 'mm') : ''\"\n        (valueChange)=\"minuteChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"minute\"\n        incrementAriaLabel=\"Increment the minute\"\n        decrementAriaLabel=\"Decrement the minute\"\n        (increment)=\"incrementMinute()\"\n        (decrement)=\"decrementMinute()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n\n  @if (showSeconds) {\n    <div class=\"time-picker-separator\">:</div>\n  }\n\n  @if (showSeconds) {\n    <div class=\"time-picker-column time-seconds-picker\" [class.has-error]=\"!_valid\">\n      <ux-spin-button\n        type=\"number\"\n        class=\"time-spinner\"\n        placeholder=\"SS\"\n        [maxLength]=\"2\"\n        [min]=\"0\"\n        [max]=\"59\"\n        [value]=\"isValidDate ? (value | date: 'ss') : ''\"\n        (valueChange)=\"secondChange($event)\"\n        [spinners]=\"showSpinners\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        inputAriaLabel=\"seconds\"\n        incrementAriaLabel=\"Increment the second\"\n        decrementAriaLabel=\"Decrement the second\"\n        (increment)=\"incrementSecond()\"\n        (decrement)=\"decrementSecond()\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n</div>\n\n@if (showMeridian) {\n  <div class=\"time-picker-meridian\">\n    <div class=\"btn-group\" role=\"radiogroup\">\n      @for (meridian of meridians; track meridian) {\n        <button\n          type=\"button\"\n          class=\"btn button-toggle-accent\"\n          role=\"radio\"\n          tabindex=\"0\"\n          [disabled]=\"disabled\"\n          (click)=\"selectMeridian(meridian)\"\n          [class.active]=\"meridian === _meridian\"\n          [attr.aria-label]=\"meridian\"\n          [attr.aria-checked]=\"meridian === _meridian\"\n          [attr.aria-disabled]=\"disabled\"\n        >\n          {{ meridian }}\n        </button>\n      }\n    </div>\n  </div>\n}\n" }]
        }], propDecorators: { arrowkeys: [{
                type: Input
            }], mousewheel: [{
                type: Input
            }], disabled: [{
                type: Input
            }], readOnly: [{
                type: Input
            }], showMeridian: [{
                type: Input
            }], showHours: [{
                type: Input
            }], showMinutes: [{
                type: Input
            }], showSeconds: [{
                type: Input
            }], showSpinners: [{
                type: Input
            }], hourStep: [{
                type: Input
            }], minuteStep: [{
                type: Input
            }], secondStep: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }], meridians: [{
                type: Input
            }], value: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], isValid: [{
                type: Output
            }] } });

class TimeViewComponent {
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    get _rangeStart() {
        return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
    }
    get _rangeEnd() {
        return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
    }
    constructor() {
        this.datepicker = inject(DateTimePickerService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        /** Earliest time permitted on the time picker. */
        this.min = null;
        /** Latest time permitted on the time picker. */
        this.max = null;
        /** Emit when the timezone changes. */
        this.timezoneChange = new EventEmitter();
        this._onDestroy = new Subject();
        // when the date changes we should update the value
        this.datepicker.date$
            .pipe(filter(date => date && this.value instanceof Date), takeUntil(this._onDestroy))
            .subscribe(date => {
            this.value = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
            this._changeDetector.detectChanges();
        });
        if (!this._isRangeMode) {
            this.datepicker.selected$
                .pipe(filter(date => !!date), takeUntil(this._onDestroy))
                .subscribe(date => (this.value = new Date(date)));
        }
        if (this._isRangeMode && this._isRangeStart) {
            this.value = new Date();
            if (!this._rangeStart) {
                this.value.setHours(0, 0, 0, 0);
            }
            else {
                this.value.setHours(this._rangeStart.getHours(), this._rangeStart.getMinutes(), this._rangeStart.getSeconds());
            }
        }
        if (this._isRangeMode && this._isRangeEnd) {
            this.value = new Date();
            if (!this._rangeEnd) {
                this.value.setHours(23, 59, 59, 0);
            }
            else {
                this.value.setHours(this._rangeEnd.getHours(), this._rangeEnd.getMinutes(), this._rangeEnd.getSeconds());
            }
        }
    }
    ngOnInit() {
        // min should only apply if it's on the same day as the selected date
        combineLatest(this.datepicker.min$, this.datepicker.date$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(([min, date]) => {
            this.min = min && date && compareDays(date, min) ? min : null;
            this._changeDetector.detectChanges();
        });
        // max should only apply if it's on the same day as the selected date
        combineLatest(this.datepicker.max$, this.datepicker.date$)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(([max, date]) => {
            this.max = max && date && compareDays(date, max) ? max : null;
            this._changeDetector.detectChanges();
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onTimeChange(time) {
        if (this._isRangeMode) {
            this.datepicker.hours = time.getHours();
            this.datepicker.minutes = time.getMinutes();
            this.datepicker.seconds = time.getSeconds();
            // update the time in the range picker service
            if (this._isRangeStart) {
                this._rangeService.startTime = {
                    hours: time.getHours(),
                    minutes: time.getMinutes(),
                    seconds: time.getSeconds(),
                };
            }
            else {
                this._rangeService.endTime = {
                    hours: time.getHours(),
                    minutes: time.getMinutes(),
                    seconds: time.getSeconds(),
                };
            }
            // if a date is currently selected we should update it
            if (this._isRangeStart && this._rangeStart) {
                const start = new Date(this._rangeStart);
                start.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
                this._rangeService.setStartDate(start);
            }
            if (this._isRangeEnd && this._rangeEnd) {
                const end = new Date(this._rangeEnd);
                end.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
                this._rangeService.setEndDate(end);
            }
            return;
        }
        // if the selected time is null then do nothing
        if (!this.datepicker.selected$.value) {
            return;
        }
        // otherwise set the time
        const date = new Date(this.datepicker.selected$.value);
        // update the time
        date.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
        // emit the time
        this.datepicker.selected$.next(date);
    }
    selectTimezone(name) {
        const timezones = this.datepicker.timezones$.value;
        // find matching timezone
        const timezone = timezones.find(_timezone => _timezone.name === name);
        if (timezone) {
            this.timezoneChange.emit(timezone);
        }
    }
    incrementTimezone() {
        const timezone = this.datepicker.timezone$.value;
        const timezones = this.datepicker.timezones$.value;
        const currentZone = timezones.findIndex(zone => zone.name === timezone.name && zone.offset === timezone.offset);
        // try to get the previous zone
        this.timezoneChange.emit(timezones[currentZone + 1] ? timezones[currentZone + 1] : timezones[currentZone]);
    }
    decrementTimezone() {
        const timezone = this.datepicker.timezone$.value;
        const timezones = this.datepicker.timezones$.value;
        const currentZone = timezones.findIndex(zone => zone.name === timezone.name && zone.offset === timezone.offset);
        // try to get the previous zone
        this.timezoneChange.emit(timezones[currentZone - 1] ? timezones[currentZone - 1] : timezones[currentZone]);
    }
    onFocusWithin() {
        if (this._isRangeMode) {
            this._rangeService.isChangingTime = true;
        }
    }
    onFocusOut() {
        if (this._isRangeMode) {
            this._rangeService.isChangingTime = false;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TimeViewComponent, isStandalone: false, selector: "ux-date-time-picker-time-view", outputs: { timezoneChange: "timezoneChange" }, host: { listeners: { "focusin": "onFocusWithin()", "focusout": "onFocusOut()" } }, ngImport: i0, template: "@if (datepicker.showTime$ | async) {\n  <ux-time-picker\n    [value]=\"value\"\n    (valueChange)=\"onTimeChange($event)\"\n    [showSeconds]=\"datepicker.showSeconds$ | async\"\n    [showMeridian]=\"datepicker.showMeridian$ | async\"\n    [showSpinners]=\"datepicker.showSpinners$ | async\"\n    [meridians]=\"datepicker.meridians\"\n    [min]=\"min\"\n    [max]=\"max\"\n  >\n  </ux-time-picker>\n}\n\n@if (datepicker.showTimezone$ | async) {\n  @if (datepicker.showSpinners$ | async) {\n    <div class=\"time-zone-picker\">\n      <ux-spin-button\n        class=\"time-zone-spinner\"\n        [value]=\"(datepicker.timezone$ | async)?.name\"\n        [readOnly]=\"true\"\n        (increment)=\"incrementTimezone()\"\n        (decrement)=\"decrementTimezone()\"\n        inputAriaLabel=\"Time Zone\"\n        incrementAriaLabel=\"Switch to the next time zone\"\n        decrementAriaLabel=\"Switch to the previous time zone\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n  @if ((datepicker.showSpinners$ | async) === false) {\n    <div class=\"time-zone-picker\">\n      <select\n        class=\"form-control time-zone-select\"\n        tabindex=\"0\"\n        [ngModel]=\"(datepicker.timezone$ | async)?.name\"\n        (ngModelChange)=\"selectTimezone($event)\"\n        aria-label=\"Timezone\"\n        [attr.aria-valuetext]=\"(datepicker.timezone$ | async)?.name\"\n      >\n        @for (zone of datepicker.timezones$ | async; track zone) {\n          <option\n            [selected]=\"zone.name === (datepicker.timezone$ | async)?.name\"\n            [value]=\"zone.name\"\n          >\n            {{ zone?.name }}\n          </option>\n        }\n      </select>\n    </div>\n  }\n}\n", dependencies: [{ kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SpinButtonComponent, selector: "ux-spin-button", inputs: ["value", "type", "min", "max", "placeholder", "disabled", "spinners", "readOnly", "scrolling", "arrowkeys", "maxLength", "incrementAriaLabel", "inputAriaLabel", "decrementAriaLabel"], outputs: ["valueChange", "increment", "decrement"] }, { kind: "component", type: TimePickerComponent, selector: "ux-time-picker", inputs: ["arrowkeys", "mousewheel", "disabled", "readOnly", "showMeridian", "showHours", "showMinutes", "showSeconds", "showSpinners", "hourStep", "minuteStep", "secondStep", "min", "max", "meridians", "value"], outputs: ["valueChange", "isValid"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimeViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker-time-view', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@if (datepicker.showTime$ | async) {\n  <ux-time-picker\n    [value]=\"value\"\n    (valueChange)=\"onTimeChange($event)\"\n    [showSeconds]=\"datepicker.showSeconds$ | async\"\n    [showMeridian]=\"datepicker.showMeridian$ | async\"\n    [showSpinners]=\"datepicker.showSpinners$ | async\"\n    [meridians]=\"datepicker.meridians\"\n    [min]=\"min\"\n    [max]=\"max\"\n  >\n  </ux-time-picker>\n}\n\n@if (datepicker.showTimezone$ | async) {\n  @if (datepicker.showSpinners$ | async) {\n    <div class=\"time-zone-picker\">\n      <ux-spin-button\n        class=\"time-zone-spinner\"\n        [value]=\"(datepicker.timezone$ | async)?.name\"\n        [readOnly]=\"true\"\n        (increment)=\"incrementTimezone()\"\n        (decrement)=\"decrementTimezone()\"\n        inputAriaLabel=\"Time Zone\"\n        incrementAriaLabel=\"Switch to the next time zone\"\n        decrementAriaLabel=\"Switch to the previous time zone\"\n      >\n      </ux-spin-button>\n    </div>\n  }\n  @if ((datepicker.showSpinners$ | async) === false) {\n    <div class=\"time-zone-picker\">\n      <select\n        class=\"form-control time-zone-select\"\n        tabindex=\"0\"\n        [ngModel]=\"(datepicker.timezone$ | async)?.name\"\n        (ngModelChange)=\"selectTimezone($event)\"\n        aria-label=\"Timezone\"\n        [attr.aria-valuetext]=\"(datepicker.timezone$ | async)?.name\"\n      >\n        @for (zone of datepicker.timezones$ | async; track zone) {\n          <option\n            [selected]=\"zone.name === (datepicker.timezone$ | async)?.name\"\n            [value]=\"zone.name\"\n          >\n            {{ zone?.name }}\n          </option>\n        }\n      </select>\n    </div>\n  }\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { timezoneChange: [{
                type: Output
            }], onFocusWithin: [{
                type: HostListener,
                args: ['focusin']
            }], onFocusOut: [{
                type: HostListener,
                args: ['focusout']
            }] } });

class DateTimePickerComponent {
    /** Defines whether or not the date picker should be visible. */
    set showDate(value) {
        if (value !== undefined) {
            this.datepicker.showDate$.next(value);
        }
    }
    /** Defines whether or not the time picker should be visible. */
    set showTime(value) {
        if (value !== undefined) {
            this.datepicker.showTime$.next(value);
        }
    }
    /** Defines whether or not the time picker should allow the user to choose a timezone. */
    set showTimezone(value) {
        if (value !== undefined) {
            this.datepicker.showTimezone$.next(value);
        }
    }
    /** Defines whether or not the time picker should allow the user to specify seconds. */
    set showSeconds(value) {
        if (value !== undefined) {
            this.datepicker.showSeconds$.next(value);
        }
    }
    /** Defines whether or not the time picker should show an AM/PM button, or time should be represented in 24hr format instead. */
    set showMeridian(value) {
        if (value !== undefined) {
            this.datepicker.showMeridian$.next(value);
        }
    }
    /** Defines whether or not the time picker should allow the user to select the time using spinners. */
    set showSpinners(value) {
        if (value !== undefined) {
            this.datepicker.showSpinners$.next(value);
        }
    }
    /** If defined will override the weekday names displayed. */
    set weekdays(value) {
        if (value !== undefined) {
            this.datepicker.weekdays$.next(value);
        }
    }
    /** Defines the names of the months. */
    set months(months) {
        if (months !== undefined) {
            this.datepicker.months = months;
        }
    }
    /** Defines the short names of each month. */
    set monthsShort(months) {
        if (months !== undefined) {
            this.datepicker.monthsShort = months;
        }
    }
    /** Defines the labels to show in the meridian (AM/PM) selector. */
    set meridians(meridians) {
        if (meridians !== undefined) {
            this.datepicker.meridians = meridians;
        }
    }
    /** Defines the text to be displayed in the button used to set the selected time to the current time. */
    set nowBtnText(value) {
        if (value !== undefined) {
            this.datepicker.nowBtnText$.next(value);
        }
    }
    /** Specify whether or not to show the show now button */
    set showNowBtn(value) {
        if (value !== undefined) {
            this.datepicker.showNowBtn$.next(value);
        }
    }
    /**
     * Defines the list of available timezones. The `DateTimePickerTimezone` interface specifies that each timezone should
     * be an object with a `name` property that represents the timezone, eg. `GMT+2`, and an `offset` property that represents
     * the number of minutes relative to GMT the timezone is.
     */
    set timezones(value) {
        if (value !== undefined) {
            this.datepicker.timezones$.next(value);
        }
    }
    /** Defines the day of the week that should appear in the first column. `WeekDay` is an enumeration available in `@angular/common`. */
    set startOfWeek(startOfWeek) {
        if (startOfWeek !== undefined) {
            this.datepicker.startOfWeek$.next(startOfWeek);
        }
    }
    /** The selected date to be displayed in the component. */
    set date(value) {
        if (value && !dateComparator(value, this.datepicker.date$.value)) {
            if (this._isRangeMode) {
                this.datepicker.date$.next(new Date(value));
                this.datepicker.selected$.next(new Date(value));
            }
            else {
                this.datepicker.selected$.next(new Date(value));
            }
        }
        if (!value) {
            this.datepicker.date$.next(value);
            this.datepicker.selected$.next(value);
        }
    }
    /** Will set the selected timezone. */
    set timezone(value) {
        this.setTimezone(value);
    }
    /** The earliest selectable date. */
    set min(value) {
        this.datepicker.min$.next(value);
    }
    /** The latest selectable date. */
    set max(value) {
        this.datepicker.max$.next(value);
    }
    /** Determine if we are in range selection mode */
    get _isRangeMode() {
        return !!this._rangeOptions;
    }
    /** Determine if this picker is the start picker */
    get _isRangeStart() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
    }
    /** Determine if this picker is the end picker */
    get _isRangeEnd() {
        return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
    }
    /** Determine if the today button is disabled */
    get _isTodayDisabled() {
        const min = this.datepicker.min$.value;
        const max = this.datepicker.max$.value;
        if (!min && !max) {
            return false;
        }
        if (min && !max) {
            return isDateBefore(new Date(), min);
        }
        if (!min && max) {
            return isDateAfter(new Date(), max);
        }
        return isDateBefore(new Date(), min) || isDateAfter(new Date(), max);
    }
    constructor() {
        this.datepicker = inject(DateTimePickerService);
        this._rangeService = inject(DateRangeService, { optional: true });
        this._rangeOptions = inject(DateRangeOptions, { optional: true });
        /** Define the aria label for the now button */
        this.nowBtnAriaLabel = 'Set date to now';
        /** Emits an event when the date is changed using the component. */
        this.dateChange = new EventEmitter();
        /** If not defined the picker will try to use the user's timezone. If that is not available, it will revert to GMT. */
        this.timezoneChange = new EventEmitter();
        // expose enum to view
        this.DatePickerMode = DatePickerMode;
        this._onDestroy = new Subject();
        this.datepicker.selected$
            .pipe(distinctUntilChanged(dateComparator), takeUntil(this._onDestroy))
            .subscribe(date => this.dateChange.emit(date));
    }
    ngOnInit() {
        this.setTimezone(this.datepicker.timezone$.value);
    }
    ngAfterViewInit() {
        setTimeout(() => (this.datepicker.initialised = true));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Change the date to the current date and time
     */
    setToNow() {
        if (this._isRangeMode) {
            const date = new Date();
            if (this._isRangeStart && !this._rangeService.showTime) {
                this.datepicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), 0, 0, 0);
            }
            else if (this._isRangeEnd && !this._rangeService.showTime) {
                this.datepicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), 23, 59, 59);
            }
            else {
                this.datepicker.setDate(date.getDate(), date.getMonth(), date.getFullYear(), this.datepicker.hours, this.datepicker.minutes, this.datepicker.seconds);
            }
        }
        else {
            // set the date to the current moment
            this.datepicker.setDateToNow();
        }
    }
    _onTimezoneChange(timezone) {
        if (!timezoneComparator(this.datepicker.timezone$.value, timezone)) {
            this.timezoneChange.emit(timezone);
        }
    }
    /**
     * Update the service with the new timezone value, falling back on the default if it is undefined or
     * not present in `timezones`.
     */
    setTimezone(timezone) {
        // if the user does not provide a timezone, set it to the current timezone and emit the change
        if (!timezone) {
            this.datepicker.timezone$.next(this.datepicker.getDefaultTimezone());
            this.timezoneChange.emit(this.datepicker.timezone$.value);
            return;
        }
        // Check if the timezone is available in the timezones list; if not, get the default timezone
        if (this.datepicker.isTimezoneAvailable(timezone)) {
            this.datepicker.timezone$.next(timezone);
        }
        else {
            // This is probably an unintended state so emit a warning
            console.warn(`ux-date-time-picker: specified timezone ${JSON.stringify(timezone)} is not present in the timezones array.`);
            // Fall back on the default timezone
            const defaultTimezone = this.datepicker.getDefaultTimezone();
            if (!this.datepicker.timezone$.value ||
                !timezoneComparator(defaultTimezone, this.datepicker.timezone$.value)) {
                this.datepicker.timezone$.next(defaultTimezone);
                this.timezoneChange.emit(defaultTimezone);
            }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: DateTimePickerComponent, isStandalone: false, selector: "ux-date-time-picker", inputs: { showDate: "showDate", showTime: "showTime", showTimezone: "showTimezone", showSeconds: "showSeconds", showMeridian: "showMeridian", showSpinners: "showSpinners", weekdays: "weekdays", months: "months", monthsShort: "monthsShort", meridians: "meridians", nowBtnText: "nowBtnText", showNowBtn: "showNowBtn", timezones: "timezones", startOfWeek: "startOfWeek", nowBtnAriaLabel: "nowBtnAriaLabel", date: "date", timezone: "timezone", min: "min", max: "max" }, outputs: { dateChange: "dateChange", timezoneChange: "timezoneChange" }, providers: [DateTimePickerService], ngImport: i0, template: "<div class=\"calendar-container\">\n  <ux-date-time-picker-header></ux-date-time-picker-header>\n\n  @if (datepicker.showDate$ | async) {\n    @switch (datepicker.mode$ | async) {\n      <!-- Display days in the current month -->\n      @case (DatePickerMode.Day) {\n        <ux-date-time-picker-day-view></ux-date-time-picker-day-view>\n      }\n      <!-- Display the months in the current year -->\n      @case (DatePickerMode.Month) {\n        <ux-date-time-picker-month-view></ux-date-time-picker-month-view>\n      }\n      <!-- Display a decade -->\n      @case (DatePickerMode.Year) {\n        <ux-date-time-picker-year-view></ux-date-time-picker-year-view>\n      }\n    }\n  }\n\n  <!-- Display a Time Picker -->\n  @if (datepicker.showTime$ | async) {\n    <ux-date-time-picker-time-view\n      (timezoneChange)=\"_onTimezoneChange($event)\"\n    ></ux-date-time-picker-time-view>\n  }\n</div>\n\n@if (datepicker.showNowBtn$ | async) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"now-button\"\n    [attr.aria-label]=\"nowBtnAriaLabel\"\n    [disabled]=\"_isTodayDisabled\"\n    (click)=\"setToNow()\"\n  >\n    {{ datepicker.nowBtnText$ | async }}\n  </button>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: HeaderComponent, selector: "ux-date-time-picker-header" }, { kind: "component", type: DayViewComponent, selector: "ux-date-time-picker-day-view" }, { kind: "component", type: MonthViewComponent, selector: "ux-date-time-picker-month-view" }, { kind: "component", type: YearViewComponent, selector: "ux-date-time-picker-year-view" }, { kind: "component", type: TimeViewComponent, selector: "ux-date-time-picker-time-view", outputs: ["timezoneChange"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-time-picker', providers: [DateTimePickerService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"calendar-container\">\n  <ux-date-time-picker-header></ux-date-time-picker-header>\n\n  @if (datepicker.showDate$ | async) {\n    @switch (datepicker.mode$ | async) {\n      <!-- Display days in the current month -->\n      @case (DatePickerMode.Day) {\n        <ux-date-time-picker-day-view></ux-date-time-picker-day-view>\n      }\n      <!-- Display the months in the current year -->\n      @case (DatePickerMode.Month) {\n        <ux-date-time-picker-month-view></ux-date-time-picker-month-view>\n      }\n      <!-- Display a decade -->\n      @case (DatePickerMode.Year) {\n        <ux-date-time-picker-year-view></ux-date-time-picker-year-view>\n      }\n    }\n  }\n\n  <!-- Display a Time Picker -->\n  @if (datepicker.showTime$ | async) {\n    <ux-date-time-picker-time-view\n      (timezoneChange)=\"_onTimezoneChange($event)\"\n    ></ux-date-time-picker-time-view>\n  }\n</div>\n\n@if (datepicker.showNowBtn$ | async) {\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"now-button\"\n    [attr.aria-label]=\"nowBtnAriaLabel\"\n    [disabled]=\"_isTodayDisabled\"\n    (click)=\"setToNow()\"\n  >\n    {{ datepicker.nowBtnText$ | async }}\n  </button>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { showDate: [{
                type: Input
            }], showTime: [{
                type: Input
            }], showTimezone: [{
                type: Input
            }], showSeconds: [{
                type: Input
            }], showMeridian: [{
                type: Input
            }], showSpinners: [{
                type: Input
            }], weekdays: [{
                type: Input
            }], months: [{
                type: Input
            }], monthsShort: [{
                type: Input
            }], meridians: [{
                type: Input
            }], nowBtnText: [{
                type: Input
            }], showNowBtn: [{
                type: Input
            }], timezones: [{
                type: Input
            }], startOfWeek: [{
                type: Input
            }], nowBtnAriaLabel: [{
                type: Input
            }], dateChange: [{
                type: Output
            }], timezoneChange: [{
                type: Output
            }], date: [{
                type: Input
            }], timezone: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }] } });

class DateFormatterPipe {
    constructor() {
        this._locale = inject(LOCALE_ID);
    }
    transform(value, formatter) {
        // we may not initially have  a value
        if (!value) {
            return '';
        }
        return typeof formatter === 'function'
            ? formatter(value)
            : formatDate(value, formatter, this._locale);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipe, isStandalone: false, name: "formatDate" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'formatDate',
                    standalone: false,
                }]
        }] });

class DateRangePickerComponent {
    /** The selected start date to be displayed in the component. */
    set start(start) {
        this.rangeService.start = start;
    }
    /** The selected end date to be displayed in the component. */
    set end(end) {
        this.rangeService.end = end;
    }
    /** Defines the aria label for the range start picker */
    set startPickerAriaLabel(label) {
        this.rangeService.startPickerAriaLabel = label;
    }
    /** Defines the aria label for the range end picker */
    set endPickerAriaLabel(label) {
        this.rangeService.endPickerAriaLabel = label;
    }
    /** Defines whether or not the time picker should be visible. */
    set showTime(showTime) {
        this.rangeService.showTime = showTime;
    }
    get showTime() {
        return this.rangeService.showTime;
    }
    /** Calculate the number of days between the start and end date */
    get _duration() {
        if (this.rangeService.start && this.rangeService.end) {
            return differenceBetweenDates(this.rangeService.start, this.rangeService.end, false);
        }
        if (this.rangeService.start && !this.rangeService.end && this.rangeService.hover) {
            // apply the time from the time picker
            const hoverDate = new Date(this.rangeService.hover);
            hoverDate.setHours(this.rangeService.endTime.hours, this.rangeService.endTime.minutes, this.rangeService.endTime.seconds);
            return this.rangeService.start.getTime() <= hoverDate.getTime()
                ? differenceBetweenDates(this.rangeService.start, hoverDate, false)
                : null;
        }
        // if we only have one selected date and have a hover date
        if (this.rangeService.end && !this.rangeService.start && this.rangeService.hover) {
            // apply the time from the time picker
            const hoverDate = new Date(this.rangeService.hover);
            hoverDate.setHours(this.rangeService.startTime.hours, this.rangeService.startTime.minutes, this.rangeService.startTime.seconds);
            return this.rangeService.end.getTime() >= hoverDate.getTime()
                ? differenceBetweenDates(this.rangeService.end, hoverDate, false)
                : null;
        }
    }
    constructor() {
        this.rangeService = inject(DateRangeService);
        this._destroyRef = inject(DestroyRef);
        /** Expose enum to the view */
        this.DateRangePicker = DateRangePicker;
        /** Defines whether or not the time picker should allow the user to specify seconds. */
        this.showSeconds = false;
        /** Defines whether or not the time picker should show an AM/PM button, or time should be represented in 24hr format instead. */
        this.showMeridian = true;
        /** Defines whether or not the time picker should allow the user to select the time using spinners. */
        this.showSpinners = true;
        /** If defined will override the weekday names displayed. */
        this.weekdays = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
        /** Specify whether or not the show now button should be visible */
        this.showNowBtn = false;
        /** Defines the title to display above the start picker. */
        this.selectStartTitle = 'Select Start Date';
        /** Defines the title to display above the end picker. */
        this.selectEndTitle = 'Select End Date';
        /** Define the aria label for the now button */
        this.nowBtnAriaLabel = 'Set date to now';
        /**
         * Defines the list of available timezones. The `DateTimePickerTimezone` interface specifies that each timezone should
         * be an object with a `name` property that represents the timezone, eg. `GMT+2`, and an `offset` property that represents
         * the number of minutes relative to GMT the timezone is.
         */
        this.timezones = timezones;
        /** Will set the selected start timezone. */
        this.startTimezone = this.getCurrentTimezone();
        /** Will set the selected end timezone. */
        this.endTimezone = this.getCurrentTimezone();
        /** Defines the day of the week that should appear in the first column. `WeekDay` is an enumeration available in `@angular/common`. */
        this.startOfWeek = WeekDay.Sunday;
        /** Define a function to return the number of days within the selected range */
        this.durationTitle = this.getDurationTitle;
        /** Emit when the start date changes */
        this.startChange = new EventEmitter();
        /** Emit when the end date changes */
        this.endChange = new EventEmitter();
        /** Emit when the start timezone changes. */
        this.startTimezoneChange = new EventEmitter();
        /** Emit when the end timezone changes. */
        this.endTimezoneChange = new EventEmitter();
        /** Use an observable to debounce rapid start changes */
        this.startChange$ = new Subject();
        /** Use an observable to debounce rapid end changes */
        this.endChange$ = new Subject();
        /** Unsubscribe from all observables private  */
        this._onDestroy = new Subject();
        this.startChange$
            .pipe(debounceTime(0), filter(date => date !== undefined), takeUntilDestroyed(this._destroyRef))
            .subscribe(date => this.onStartChange(date));
        this.endChange$
            .pipe(debounceTime(0), filter(date => date !== undefined), takeUntilDestroyed(this._destroyRef))
            .subscribe(date => this.onEndChange(date));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Clear the selected date range */
    clear() {
        this.rangeService.clear();
    }
    /** Get the timezone based on the machine timezone */
    getCurrentTimezone() {
        return this.timezones.find(timezone => timezone.offset === new Date().getTimezoneOffset());
    }
    onStartChange(date) {
        this.rangeService.setStartDate(date);
        this.startChange.emit(date);
    }
    onEndChange(date) {
        this.rangeService.setEndDate(date);
        this.endChange.emit(date);
    }
    /** Get the text to display to indicate the duration */
    getDurationTitle(days) {
        return days + ' ' + (days > 1 ? 'days' : 'day');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: DateRangePickerComponent, isStandalone: false, selector: "ux-date-range-picker", inputs: { start: "start", end: "end", dateFormat: "dateFormat", timeFormat: "timeFormat", min: "min", max: "max", showTimezone: "showTimezone", showSeconds: "showSeconds", showMeridian: "showMeridian", showSpinners: "showSpinners", weekdays: "weekdays", months: "months", monthsShort: "monthsShort", meridians: "meridians", nowBtnText: "nowBtnText", showNowBtn: "showNowBtn", selectStartTitle: "selectStartTitle", selectEndTitle: "selectEndTitle", nowBtnAriaLabel: "nowBtnAriaLabel", startPickerAriaLabel: "startPickerAriaLabel", endPickerAriaLabel: "endPickerAriaLabel", showTime: "showTime", timezones: "timezones", startTimezone: "startTimezone", endTimezone: "endTimezone", startOfWeek: "startOfWeek", durationTitle: "durationTitle" }, outputs: { startChange: "startChange", endChange: "endChange", startTimezoneChange: "startTimezoneChange", endTimezoneChange: "endTimezoneChange" }, providers: [DateRangeService], ngImport: i0, template: "<div class=\"range-header\">\n  <div class=\"header-section\">\n    @if (!rangeService.start) {\n      <div class=\"select-header\">{{ selectStartTitle }}</div>\n    }\n    @if (rangeService.start) {\n      <div class=\"date-header\">\n        {{ rangeService.start | formatDate: dateFormat || 'd MMMM y' }}\n      </div>\n    }\n    <div\n      [style.visibility]=\"rangeService.start && showTime ? 'visible' : 'hidden'\"\n      class=\"time-header\"\n    >\n      {{ rangeService.start | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n    </div>\n  </div>\n\n  <div class=\"header-separator\">\n    <ux-icon name=\"link-next\"></ux-icon>\n    <p\n      class=\"duration\"\n      [style.visibility]=\"_duration !== null && _duration !== undefined ? 'visible' : 'hidden'\"\n    >\n      {{ durationTitle(_duration || 0) }}\n    </p>\n  </div>\n\n  <div class=\"header-section\">\n    @if (!rangeService.end) {\n      <div class=\"select-header\">{{ selectEndTitle }}</div>\n    }\n    @if (rangeService.end) {\n      <div class=\"date-header\">\n        {{ rangeService.end | formatDate: dateFormat || 'd MMMM y' }}\n      </div>\n    }\n    <div\n      [style.visibility]=\"rangeService.end && showTime ? 'visible' : 'hidden'\"\n      class=\"time-header\"\n    >\n      {{ rangeService.end | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n    </div>\n  </div>\n</div>\n\n<div class=\"content\">\n  <ux-date-time-picker\n    uxDateRangePicker\n    [picker]=\"DateRangePicker.Start\"\n    class=\"start-date-picker\"\n    [date]=\"rangeService.start\"\n    (dateChange)=\"startChange$.next($event)\"\n    [min]=\"min\"\n    [max]=\"max\"\n    [weekdays]=\"weekdays\"\n    [startOfWeek]=\"startOfWeek\"\n    [showTime]=\"showTime\"\n    [showTimezone]=\"showTimezone\"\n    [showSeconds]=\"showSeconds\"\n    [showMeridian]=\"showMeridian\"\n    [showSpinners]=\"showSpinners\"\n    [months]=\"months\"\n    [monthsShort]=\"monthsShort\"\n    [meridians]=\"meridians\"\n    [nowBtnText]=\"nowBtnText\"\n    [showNowBtn]=\"showNowBtn\"\n    [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n    [timezones]=\"timezones\"\n    [(timezone)]=\"startTimezone\"\n    (timezoneChange)=\"startTimezoneChange.emit($event)\"\n  >\n  </ux-date-time-picker>\n\n  <ux-date-time-picker\n    uxDateRangePicker\n    [picker]=\"DateRangePicker.End\"\n    class=\"end-date-picker\"\n    [date]=\"rangeService.end\"\n    (dateChange)=\"endChange$.next($event)\"\n    [min]=\"min\"\n    [max]=\"max\"\n    [weekdays]=\"weekdays\"\n    [startOfWeek]=\"startOfWeek\"\n    [showTime]=\"showTime\"\n    [showTimezone]=\"showTimezone\"\n    [showSeconds]=\"showSeconds\"\n    [showMeridian]=\"showMeridian\"\n    [showSpinners]=\"showSpinners\"\n    [months]=\"months\"\n    [monthsShort]=\"monthsShort\"\n    [meridians]=\"meridians\"\n    [nowBtnText]=\"nowBtnText\"\n    [showNowBtn]=\"showNowBtn\"\n    [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n    [timezones]=\"timezones\"\n    [(timezone)]=\"endTimezone\"\n    (timezoneChange)=\"endTimezoneChange.emit($event)\"\n  >\n  </ux-date-time-picker>\n</div>\n", dependencies: [{ kind: "component", type: DateTimePickerComponent, selector: "ux-date-time-picker", inputs: ["showDate", "showTime", "showTimezone", "showSeconds", "showMeridian", "showSpinners", "weekdays", "months", "monthsShort", "meridians", "nowBtnText", "showNowBtn", "timezones", "startOfWeek", "nowBtnAriaLabel", "date", "timezone", "min", "max"], outputs: ["dateChange", "timezoneChange"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: DateRangePickerDirective, selector: "[uxDateRangePicker]", inputs: ["picker"] }, { kind: "pipe", type: DateFormatterPipe, name: "formatDate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, providers: [DateRangeService], standalone: false, template: "<div class=\"range-header\">\n  <div class=\"header-section\">\n    @if (!rangeService.start) {\n      <div class=\"select-header\">{{ selectStartTitle }}</div>\n    }\n    @if (rangeService.start) {\n      <div class=\"date-header\">\n        {{ rangeService.start | formatDate: dateFormat || 'd MMMM y' }}\n      </div>\n    }\n    <div\n      [style.visibility]=\"rangeService.start && showTime ? 'visible' : 'hidden'\"\n      class=\"time-header\"\n    >\n      {{ rangeService.start | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n    </div>\n  </div>\n\n  <div class=\"header-separator\">\n    <ux-icon name=\"link-next\"></ux-icon>\n    <p\n      class=\"duration\"\n      [style.visibility]=\"_duration !== null && _duration !== undefined ? 'visible' : 'hidden'\"\n    >\n      {{ durationTitle(_duration || 0) }}\n    </p>\n  </div>\n\n  <div class=\"header-section\">\n    @if (!rangeService.end) {\n      <div class=\"select-header\">{{ selectEndTitle }}</div>\n    }\n    @if (rangeService.end) {\n      <div class=\"date-header\">\n        {{ rangeService.end | formatDate: dateFormat || 'd MMMM y' }}\n      </div>\n    }\n    <div\n      [style.visibility]=\"rangeService.end && showTime ? 'visible' : 'hidden'\"\n      class=\"time-header\"\n    >\n      {{ rangeService.end | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n    </div>\n  </div>\n</div>\n\n<div class=\"content\">\n  <ux-date-time-picker\n    uxDateRangePicker\n    [picker]=\"DateRangePicker.Start\"\n    class=\"start-date-picker\"\n    [date]=\"rangeService.start\"\n    (dateChange)=\"startChange$.next($event)\"\n    [min]=\"min\"\n    [max]=\"max\"\n    [weekdays]=\"weekdays\"\n    [startOfWeek]=\"startOfWeek\"\n    [showTime]=\"showTime\"\n    [showTimezone]=\"showTimezone\"\n    [showSeconds]=\"showSeconds\"\n    [showMeridian]=\"showMeridian\"\n    [showSpinners]=\"showSpinners\"\n    [months]=\"months\"\n    [monthsShort]=\"monthsShort\"\n    [meridians]=\"meridians\"\n    [nowBtnText]=\"nowBtnText\"\n    [showNowBtn]=\"showNowBtn\"\n    [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n    [timezones]=\"timezones\"\n    [(timezone)]=\"startTimezone\"\n    (timezoneChange)=\"startTimezoneChange.emit($event)\"\n  >\n  </ux-date-time-picker>\n\n  <ux-date-time-picker\n    uxDateRangePicker\n    [picker]=\"DateRangePicker.End\"\n    class=\"end-date-picker\"\n    [date]=\"rangeService.end\"\n    (dateChange)=\"endChange$.next($event)\"\n    [min]=\"min\"\n    [max]=\"max\"\n    [weekdays]=\"weekdays\"\n    [startOfWeek]=\"startOfWeek\"\n    [showTime]=\"showTime\"\n    [showTimezone]=\"showTimezone\"\n    [showSeconds]=\"showSeconds\"\n    [showMeridian]=\"showMeridian\"\n    [showSpinners]=\"showSpinners\"\n    [months]=\"months\"\n    [monthsShort]=\"monthsShort\"\n    [meridians]=\"meridians\"\n    [nowBtnText]=\"nowBtnText\"\n    [showNowBtn]=\"showNowBtn\"\n    [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n    [timezones]=\"timezones\"\n    [(timezone)]=\"endTimezone\"\n    (timezoneChange)=\"endTimezoneChange.emit($event)\"\n  >\n  </ux-date-time-picker>\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { start: [{
                type: Input
            }], end: [{
                type: Input
            }], dateFormat: [{
                type: Input
            }], timeFormat: [{
                type: Input
            }], min: [{
                type: Input
            }], max: [{
                type: Input
            }], showTimezone: [{
                type: Input
            }], showSeconds: [{
                type: Input
            }], showMeridian: [{
                type: Input
            }], showSpinners: [{
                type: Input
            }], weekdays: [{
                type: Input
            }], months: [{
                type: Input
            }], monthsShort: [{
                type: Input
            }], meridians: [{
                type: Input
            }], nowBtnText: [{
                type: Input
            }], showNowBtn: [{
                type: Input
            }], selectStartTitle: [{
                type: Input
            }], selectEndTitle: [{
                type: Input
            }], nowBtnAriaLabel: [{
                type: Input
            }], startPickerAriaLabel: [{
                type: Input
            }], endPickerAriaLabel: [{
                type: Input
            }], showTime: [{
                type: Input
            }], timezones: [{
                type: Input
            }], startTimezone: [{
                type: Input
            }], endTimezone: [{
                type: Input
            }], startOfWeek: [{
                type: Input
            }], durationTitle: [{
                type: Input
            }], startChange: [{
                type: Output
            }], endChange: [{
                type: Output
            }], startTimezoneChange: [{
                type: Output
            }], endTimezoneChange: [{
                type: Output
            }] } });

class DateFormatterPipeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipeModule, declarations: [DateFormatterPipe], exports: [DateFormatterPipe] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipeModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateFormatterPipeModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [DateFormatterPipe],
                    declarations: [DateFormatterPipe],
                }]
        }] });

class FocusIfModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIfModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FocusIfModule, declarations: [FocusIfDirective], exports: [FocusIfDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIfModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FocusIfModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [FocusIfDirective],
                    declarations: [FocusIfDirective],
                }]
        }] });

class SpinButtonModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonModule, declarations: [SpinButtonComponent], imports: [AccessibilityModule, CommonModule, FormsModule, IconModule], exports: [SpinButtonComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonModule, imports: [AccessibilityModule, CommonModule, FormsModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SpinButtonModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, FormsModule, IconModule],
                    exports: [SpinButtonComponent],
                    declarations: [SpinButtonComponent],
                }]
        }] });

class TimePickerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TimePickerModule, declarations: [TimePickerComponent], imports: [AccessibilityModule, CommonModule, FormsModule, SpinButtonModule], exports: [TimePickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimePickerModule, imports: [AccessibilityModule, CommonModule, FormsModule, SpinButtonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimePickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, FormsModule, SpinButtonModule],
                    exports: [TimePickerComponent],
                    declarations: [TimePickerComponent],
                }]
        }] });

class DateTimePickerModule {
    static forRoot() {
        return {
            ngModule: DateTimePickerModule,
            providers: [DateTimePickerConfig],
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerModule, declarations: [DateTimePickerComponent,
            HeaderComponent,
            DayViewComponent,
            MonthViewComponent,
            YearViewComponent,
            TimeViewComponent,
            WeekDaySortPipe], imports: [A11yModule,
            AccessibilityModule,
            CommonModule,
            FocusIfModule,
            FormsModule,
            IconModule,
            SpinButtonModule,
            TimePickerModule], exports: [DateTimePickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerModule, imports: [A11yModule,
            AccessibilityModule,
            CommonModule,
            FocusIfModule,
            FormsModule,
            IconModule,
            SpinButtonModule,
            TimePickerModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateTimePickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        CommonModule,
                        FocusIfModule,
                        FormsModule,
                        IconModule,
                        SpinButtonModule,
                        TimePickerModule,
                    ],
                    exports: [DateTimePickerComponent],
                    declarations: [
                        DateTimePickerComponent,
                        HeaderComponent,
                        DayViewComponent,
                        MonthViewComponent,
                        YearViewComponent,
                        TimeViewComponent,
                        WeekDaySortPipe,
                    ],
                }]
        }] });

class DateRangePickerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerModule, declarations: [DateRangePickerComponent, DateRangePickerDirective], imports: [CommonModule, DateTimePickerModule, IconModule, DateFormatterPipeModule], exports: [DateRangePickerComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerModule, imports: [CommonModule, DateTimePickerModule, IconModule, DateFormatterPipeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateRangePickerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, DateTimePickerModule, IconModule, DateFormatterPipeModule],
                    declarations: [DateRangePickerComponent, DateRangePickerDirective],
                    exports: [DateRangePickerComponent],
                }]
        }] });

class EboxComponent {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: EboxComponent, isStandalone: false, selector: "ux-ebox", ngImport: i0, template: "<div class=\"ux-ebox-header\">\n  <ng-content select=\"ux-ebox-header\"></ng-content>\n</div>\n\n<div class=\"ux-ebox-content\">\n  <ng-content select=\"ux-ebox-content\"></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-ebox', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ux-ebox-header\">\n  <ng-content select=\"ux-ebox-header\"></ng-content>\n</div>\n\n<div class=\"ux-ebox-content\">\n  <ng-content select=\"ux-ebox-content\"></ng-content>\n</div>\n" }]
        }] });
class EboxHeaderDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: EboxHeaderDirective, isStandalone: false, selector: "ux-ebox-header", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxHeaderDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-ebox-header',
                    standalone: false,
                }]
        }] });
class EboxContentDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: EboxContentDirective, isStandalone: false, selector: "ux-ebox-content", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxContentDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-ebox-content',
                    standalone: false,
                }]
        }] });

class EboxModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: EboxModule, declarations: [EboxComponent, EboxContentDirective, EboxHeaderDirective], exports: [EboxComponent, EboxContentDirective, EboxHeaderDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: EboxModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [EboxComponent, EboxContentDirective, EboxHeaderDirective],
                    declarations: [EboxComponent, EboxContentDirective, EboxHeaderDirective],
                }]
        }] });

class FacetHeaderComponent {
    constructor() {
        this.focusIndicatorService = inject(FocusIndicatorService);
        this.elementRef = inject(ElementRef);
        /** Defines whether or not clicking on the header will toggle the expanded state. */
        this.canExpand = true;
        /** Can be used to set the initial expanded state. */
        this.expanded = true;
        /** If two-way binding is used it will be updated when the expanded state changes. */
        this.expandedChange = new EventEmitter();
        this._focusIndicator = this.focusIndicatorService.monitor(this.elementRef.nativeElement);
    }
    ngOnDestroy() {
        this._focusIndicator.destroy();
    }
    toggleExpand() {
        // if not expandable then do nothing
        if (this.canExpand) {
            this.expanded = !this.expanded;
            this.expandedChange.emit(this.expanded);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetHeaderComponent, isStandalone: false, selector: "ux-facet-header", inputs: { header: "header", canExpand: "canExpand", expanded: "expanded" }, outputs: { expandedChange: "expandedChange" }, host: { attributes: { "role": "button", "tabindex": "0" }, listeners: { "click": "toggleExpand()", "keyup.enter": "toggleExpand()" }, properties: { "attr.aria-expanded": "expanded", "attr.aria-label": "header + ' Facet: Activate to ' + (expanded ? 'collapse' : 'expand')", "class.expanded": "this.expanded" } }, ngImport: i0, template: "<span class=\"facet-header-title\">{{ header }}</span>\n@if (canExpand) {\n  <ux-icon [name]=\"expanded ? 'down' : 'previous'\" class=\"facet-header-icon\"></ux-icon>\n}\n", dependencies: [{ kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetHeaderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-header', host: {
                        role: 'button',
                        tabindex: '0',
                        '(click)': 'toggleExpand()',
                        '(keyup.enter)': 'toggleExpand()',
                        '[attr.aria-expanded]': 'expanded',
                        '[attr.aria-label]': "header + ' Facet: Activate to ' + (expanded ? 'collapse' : 'expand')",
                    }, standalone: false, template: "<span class=\"facet-header-title\">{{ header }}</span>\n@if (canExpand) {\n  <ux-icon [name]=\"expanded ? 'down' : 'previous'\" class=\"facet-header-icon\"></ux-icon>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { header: [{
                type: Input
            }], canExpand: [{
                type: Input
            }], expanded: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.expanded']
            }], expandedChange: [{
                type: Output
            }] } });

class FacetSelect {
    constructor(facet) {
        this.facet = facet;
    }
}
class FacetDeselect {
    constructor(facet) {
        this.facet = facet;
    }
}
class FacetDeselectAll {
}

class FacetService {
    constructor() {
        /** The list of active facets */
        this.facets$ = new BehaviorSubject([]);
        /** Emit all the events when they occur */
        this.events$ = new Subject();
    }
    select(facet) {
        // if the facet is already selected or disabled then do nothing
        if (this.isSelected(facet) || facet.disabled) {
            return;
        }
        // update the list of active facets
        this.facets$.next([...this.facets$.value, facet]);
        // emit the event
        this.events$.next(new FacetSelect(facet));
    }
    deselect(facet) {
        // if the facet is not selected then do nothing
        if (!this.isSelected(facet)) {
            return;
        }
        // update the list of active facets
        this.facets$.next(this.facets$.value.filter(_selectedFacet => !this.isFacetMatch(_selectedFacet, facet)));
        // emit the event
        this.events$.next(new FacetDeselect(facet));
    }
    deselectAll() {
        // empty the list of active facets
        this.facets$.next([]);
        // emit the event
        this.events$.next(new FacetDeselectAll());
    }
    toggle(facet) {
        this.isSelected(facet) ? this.deselect(facet) : this.select(facet);
    }
    isSelected(facet) {
        return !!this.facets$.value.find(_selectedFacet => this.isFacetMatch(_selectedFacet, facet));
    }
    isFacetMatch(facet1, facet2) {
        if (facet1.id === undefined || facet2.id === undefined) {
            return facet1 === facet2;
        }
        return facet1.id === facet2.id;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetService, decorators: [{
            type: Injectable
        }] });

class FacetCheckListItemComponent {
    constructor() {
        this.facet = null;
        this.selected = false;
        this.tabbable = false;
        this.simplified = false;
        this.selectedChange = new EventEmitter();
        this.itemFocus = new EventEmitter();
        this.itemBlur = new EventEmitter();
    }
    get disabled() {
        return this.facet && this.facet.disabled;
    }
    getLabel() {
        return this.facet ? this.facet.title : '';
    }
    focus() {
        this.option.nativeElement.focus();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetCheckListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetCheckListItemComponent, isStandalone: false, selector: "ux-facet-check-list-item", inputs: { id: "id", facet: "facet", selected: "selected", tabbable: "tabbable", simplified: "simplified" }, outputs: { selectedChange: "selectedChange", itemFocus: "itemFocus", itemBlur: "itemBlur" }, host: { properties: { "id": "this.id" } }, viewQueries: [{ propertyName: "option", first: true, predicate: ["option"], descendants: true, static: true }], ngImport: i0, template: "<div\n  #option\n  uxFocusIndicator\n  class=\"facet-check-list-item\"\n  [class.facet-active]=\"selected\"\n  [attr.aria-checked]=\"selected\"\n  role=\"option\"\n  [tabindex]=\"tabbable ? 0 : -1\"\n  (focus)=\"itemFocus.emit()\"\n  (blur)=\"itemBlur.emit()\"\n  (click)=\"selectedChange.emit(facet)\"\n  (keydown.enter)=\"selectedChange.emit(facet)\"\n  (keydown.space)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  (keydown.spacebar)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  [class.disabled]=\"facet?.disabled\"\n>\n  <!-- Show check icon to indicate the state -->\n  <ux-checkbox\n    [clickable]=\"false\"\n    [value]=\"selected\"\n    [simplified]=\"simplified\"\n    [tabindex]=\"-1\"\n    [disabled]=\"disabled\"\n    [id]=\"id + '-checkbox'\"\n  >\n    <span class=\"facet-check-list-item-title\">{{ facet?.title }}</span>\n    @if (facet?.count !== undefined) {\n      <span class=\"facet-check-list-item-count\">({{ facet?.count }})</span>\n    }\n  </ux-checkbox>\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: CheckboxComponent, selector: "ux-checkbox", inputs: ["id", "name", "value", "required", "tabindex", "clickable", "simplified", "indeterminateValue", "disabled", "aria-label", "aria-labelledby"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetCheckListItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-check-list-item', changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, standalone: false, template: "<div\n  #option\n  uxFocusIndicator\n  class=\"facet-check-list-item\"\n  [class.facet-active]=\"selected\"\n  [attr.aria-checked]=\"selected\"\n  role=\"option\"\n  [tabindex]=\"tabbable ? 0 : -1\"\n  (focus)=\"itemFocus.emit()\"\n  (blur)=\"itemBlur.emit()\"\n  (click)=\"selectedChange.emit(facet)\"\n  (keydown.enter)=\"selectedChange.emit(facet)\"\n  (keydown.space)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  (keydown.spacebar)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  [class.disabled]=\"facet?.disabled\"\n>\n  <!-- Show check icon to indicate the state -->\n  <ux-checkbox\n    [clickable]=\"false\"\n    [value]=\"selected\"\n    [simplified]=\"simplified\"\n    [tabindex]=\"-1\"\n    [disabled]=\"disabled\"\n    [id]=\"id + '-checkbox'\"\n  >\n    <span class=\"facet-check-list-item-title\">{{ facet?.title }}</span>\n    @if (facet?.count !== undefined) {\n      <span class=\"facet-check-list-item-count\">({{ facet?.count }})</span>\n    }\n  </ux-checkbox>\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding
            }], facet: [{
                type: Input
            }], selected: [{
                type: Input
            }], tabbable: [{
                type: Input
            }], simplified: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], itemFocus: [{
                type: Output
            }], itemBlur: [{
                type: Output
            }], option: [{
                type: ViewChild,
                args: ['option', { static: true }]
            }] } });

let uniqueId$a = 0;
class FacetCheckListComponent {
    /** This will allow you to define an initial set of selected facets. */
    set selected(selection) {
        if (Array.isArray(selection)) {
            selection.forEach(facet => this.facetService.select(facet));
        }
    }
    constructor() {
        this.facetService = inject(FacetService);
        this.id = `ux-facet-check-list-${uniqueId$a++}`;
        /** Defines the complete list of facets that can be selected. */
        this.facets = [];
        /** If `false` the list will grow to display all possible facets. If `true` a scrollbar will appear to prevent the list from growing too large. */
        this.scrollbar = true;
        /** Defines whether or not the checkboxes will appear in simplified form. */
        this.simplified = false;
        /** Defines whether or not the Facet Check List should be initially expanded or not. */
        this.expanded = true;
        /**
         * This will be triggered when a facet is selected, deselected or all facets are deselected.
         * The event will be an instance of either `FacetSelect`, `FacetDeselect` or `FacetDeselectAll` and
         * will contain the facet being selected or deselected in a `facet` property (deselect all will not contain affected facets).
         */
        this.events = new Subject();
        /** If two-way binding is used this array will get updated any time the selected facets change. */
        this.selectedChange = new EventEmitter();
        this.isFocused = false;
        this.activeIndex = 0;
        this._onDestroy = new Subject();
        this.facetService.events$.pipe(takeUntil(this._onDestroy)).subscribe(event => {
            // deselect all events should always be emitted
            if (event instanceof FacetDeselectAll) {
                this.events.next(event);
                this.selectedChange.next([]);
            }
            // selection and deselection events should only be emitted when the facet belongs to this component
            if ((event instanceof FacetSelect || event instanceof FacetDeselect) &&
                this.isOwnFacet(event.facet)) {
                this.events.next(event);
                this.selectedChange.next(this.getSelectedFacets());
            }
        });
    }
    ngAfterViewInit() {
        this._focusKeyManager = new FocusKeyManager(this.options).withVerticalOrientation();
        this._focusKeyManager.change
            .pipe(takeUntil(this._onDestroy))
            .subscribe(index => (this.activeIndex = index));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onFocus(index) {
        if (this._focusKeyManager.activeItemIndex === -1) {
            this._focusKeyManager.setActiveItem(index);
        }
    }
    onKeydown(event) {
        this._focusKeyManager.onKeydown(event);
    }
    toggleFacet(index, facet) {
        this.facetService.toggle(facet);
        this._focusKeyManager.setActiveItem(index);
    }
    getSelectedFacets() {
        return this.facetService.facets$.value.filter(facet => this.isOwnFacet(facet));
    }
    isOwnFacet(facet) {
        return this.facets.indexOf(facet) !== -1;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetCheckListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetCheckListComponent, isStandalone: false, selector: "ux-facet-check-list", inputs: { id: "id", selected: "selected", facets: "facets", header: "header", scrollbar: "scrollbar", simplified: "simplified", expanded: "expanded" }, outputs: { events: "events", selectedChange: "selectedChange" }, host: { properties: { "id": "this.id" } }, viewQueries: [{ propertyName: "options", predicate: FacetCheckListItemComponent, descendants: true }], ngImport: i0, template: "<ux-facet-header [header]=\"header\" [(expanded)]=\"expanded\"></ux-facet-header>\n\n<!-- Create a container which will show when section is expanded -->\n@if (expanded) {\n  <div\n    class=\"facet-check-list-container\"\n    tabindex=\"-1\"\n    role=\"listbox\"\n    [class.facet-check-list-scrollbar]=\"scrollbar\"\n    [class.facet-check-list-scrollbar-focused]=\"isFocused\"\n  >\n    <!-- Iterate through each possible facet -->\n    @for (facet of facets; track facet; let index = $index) {\n      <ux-facet-check-list-item\n        [id]=\"id + '-check-list-item-' + index\"\n        [facet]=\"facet\"\n        [simplified]=\"simplified\"\n        [tabbable]=\"activeIndex === index\"\n        [selected]=\"facetService.isSelected(facet)\"\n        (selectedChange)=\"toggleFacet(index, facet)\"\n        (keydown)=\"onKeydown($event)\"\n        (itemFocus)=\"isFocused = true; onFocus(index)\"\n        (itemBlur)=\"isFocused = false\"\n      >\n      </ux-facet-check-list-item>\n    }\n  </div>\n}\n", dependencies: [{ kind: "component", type: FacetHeaderComponent, selector: "ux-facet-header", inputs: ["header", "canExpand", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: FacetCheckListItemComponent, selector: "ux-facet-check-list-item", inputs: ["id", "facet", "selected", "tabbable", "simplified"], outputs: ["selectedChange", "itemFocus", "itemBlur"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetCheckListComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-check-list', standalone: false, template: "<ux-facet-header [header]=\"header\" [(expanded)]=\"expanded\"></ux-facet-header>\n\n<!-- Create a container which will show when section is expanded -->\n@if (expanded) {\n  <div\n    class=\"facet-check-list-container\"\n    tabindex=\"-1\"\n    role=\"listbox\"\n    [class.facet-check-list-scrollbar]=\"scrollbar\"\n    [class.facet-check-list-scrollbar-focused]=\"isFocused\"\n  >\n    <!-- Iterate through each possible facet -->\n    @for (facet of facets; track facet; let index = $index) {\n      <ux-facet-check-list-item\n        [id]=\"id + '-check-list-item-' + index\"\n        [facet]=\"facet\"\n        [simplified]=\"simplified\"\n        [tabbable]=\"activeIndex === index\"\n        [selected]=\"facetService.isSelected(facet)\"\n        (selectedChange)=\"toggleFacet(index, facet)\"\n        (keydown)=\"onKeydown($event)\"\n        (itemFocus)=\"isFocused = true; onFocus(index)\"\n        (itemBlur)=\"isFocused = false\"\n      >\n      </ux-facet-check-list-item>\n    }\n  </div>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding
            }], selected: [{
                type: Input
            }], facets: [{
                type: Input
            }], header: [{
                type: Input
            }], scrollbar: [{
                type: Input
            }], simplified: [{
                type: Input
            }], expanded: [{
                type: Input
            }], events: [{
                type: Output
            }], selectedChange: [{
                type: Output
            }], options: [{
                type: ViewChildren,
                args: [FacetCheckListItemComponent]
            }] } });

class FacetClearButtonDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetClearButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FacetClearButtonDirective, isStandalone: false, selector: "[uxFacetClearButton]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetClearButtonDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFacetClearButton]',
                    standalone: false,
                }]
        }] });

class FacetContainerComponent {
    /** Allows a predefined set of Facets to be displayed. */
    set facets(facets) {
        this.facetService.facets$.next(facets);
    }
    get facets() {
        return this.facetService.facets$.value;
    }
    constructor() {
        this.facetService = inject(FacetService);
        this._announcer = inject(LiveAnnouncer);
        /** Defines the text displayed at the top of the Facet Container. */
        this.header = 'Selected';
        /** Defines the text to display in the tooltip when hovering over the clear all button. */
        this.clearTooltip = 'Clear All';
        /** Defines the text to display when there are no selected facets. */
        this.emptyText = 'No Items';
        /** Determines if the facets can be reordered. */
        this.facetsReorderable = false;
        /** Defines the aria-label for the clear all button. */
        this.clearAriaLabel = 'Clear All';
        /** Defines the aria-label for the deselect facet button.. */
        this.deselectFacetAriaLabel = 'Deselect Facet';
        /** If using two-way binding this array will update when the selected facets change. */
        this.facetsChange = new EventEmitter();
        /**
         * This will be triggered when a facet is selected, deselected or all facets are deselected.
         * The event will be an instance of either `FacetSelect`, `FacetDeselect` or `FacetDeselectAll` and
         * will contain the facet being selected or deselected in a `facet` property
         * (deselect all will not contain affected facets). */
        this.events = new EventEmitter();
        this.target = null;
        this.source = null;
        this.dragRef = null;
        this._onDestroy = new Subject();
        this.facetService.facets$.subscribe(facets => this.facetsChange.next(facets));
        this.facetService.events$.subscribe(event => this.triggerEvent(event));
        // announce deselection
        this.facetService.events$
            .pipe(filter(event => event instanceof FacetDeselect))
            .subscribe(event => this._announcer.announce(`Option ${event.facet.title} deselected.`, 'assertive'));
    }
    ngAfterViewInit() {
        const placeholderElement = this.placeholder.element.nativeElement;
        placeholderElement.style.display = 'none';
        placeholderElement.parentNode.removeChild(placeholderElement);
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onDropListDropped() {
        if (!this.target) {
            return;
        }
        const placeholderElement = this.placeholder.element.nativeElement;
        const placeholderParentElement = placeholderElement.parentElement;
        placeholderElement.style.display = 'none';
        placeholderParentElement.removeChild(placeholderElement);
        placeholderParentElement.appendChild(placeholderElement);
        placeholderParentElement.insertBefore(this.source.element.nativeElement, placeholderParentElement.children[this.sourceIndex]);
        if (this.placeholder._dropListRef.isDragging()) {
            this.placeholder._dropListRef.exit(this.dragRef);
        }
        this.target = null;
        this.source = null;
        this.dragRef = null;
        if (this.sourceIndex !== this.targetIndex) {
            moveItemInArray(this.facets, this.sourceIndex, this.targetIndex);
        }
    }
    onDropListEntered({ item, container }) {
        if (container == this.placeholder) {
            return;
        }
        const placeholderElement = this.placeholder.element.nativeElement;
        const sourceElement = item.dropContainer.element.nativeElement;
        const dropElement = container.element.nativeElement;
        const dragIndex = Array.prototype.indexOf.call(dropElement.parentElement.children, this.source ? placeholderElement : sourceElement);
        const dropIndex = Array.prototype.indexOf.call(dropElement.parentElement.children, dropElement);
        if (!this.source) {
            this.sourceIndex = dragIndex;
            this.source = item.dropContainer;
            sourceElement.parentElement.removeChild(sourceElement);
        }
        this.targetIndex = dropIndex;
        this.target = container;
        this.dragRef = item._dragRef;
        placeholderElement.style.display = '';
        dropElement.parentElement.insertBefore(placeholderElement, dropIndex > dragIndex ? dropElement.nextSibling : dropElement);
        this.placeholder._dropListRef.enter(item._dragRef, item.element.nativeElement.offsetLeft, item.element.nativeElement.offsetTop);
    }
    selectFacet(facet) {
        this.facetService.select(facet);
    }
    deselectFacet(facet, tag) {
        // find the index of the item in the selected array
        const idx = this.facets.findIndex(selectedFacet => facet === selectedFacet);
        // if match there was no match then finish
        if (idx === -1) {
            return;
        }
        // remove the last item
        this.facetService.deselect(facet);
        // announce the facet removal
        this._announcer.announce(`Option ${facet.title} deselected.`, 'assertive');
        // focus another tag if there is one
        if (tag) {
            const sibling = tag.previousElementSibling || tag.nextElementSibling;
            // if there is a sibling then focus it
            if (sibling) {
                sibling.focus();
            }
        }
    }
    deselectAllFacets() {
        // empty the selected array
        this.facetService.deselectAll();
        // announce the facet removal
        this._announcer.announce('All options deselected.', 'assertive');
    }
    trackBy(_index, facet) {
        return facet.id || facet.title;
    }
    shiftRight(facet, element) {
        // only move the item if reordering is allowed
        if (this.facetsReorderable === false) {
            return;
        }
        // perform the movement
        this.shiftFacet(facet, 1);
        // the item may become unfocused during the reorder so we should refocus it
        requestAnimationFrame(() => element.focus());
        // announce the move
        this._announcer.announce(`Option ${facet.title} moved down.`);
    }
    shiftLeft(facet, element) {
        // only move the item if reordering is allowed
        if (this.facetsReorderable === false) {
            return;
        }
        // perform the movement
        this.shiftFacet(facet, -1);
        // the item may become unfocused during the reorder so we should refocus it
        requestAnimationFrame(() => element.focus());
        // announce the move
        this._announcer.announce(`Option ${facet.title} moved up.`);
    }
    shiftFacet(facet, distance) {
        const index = this.facets.indexOf(facet);
        const target = index + distance;
        // Ensure the move is valid
        if (target < 0 || target === this.facets.length) {
            return;
        }
        // Perform the move
        this.facets.splice(index, 1);
        this.facets.splice(target, 0, facet);
    }
    triggerEvent(event) {
        this.events.next(event);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetContainerComponent, isStandalone: false, selector: "ux-facet-container", inputs: { header: "header", clearTooltip: "clearTooltip", emptyText: "emptyText", facetsReorderable: "facetsReorderable", facets: "facets", clearAriaLabel: "clearAriaLabel", deselectFacetAriaLabel: "deselectFacetAriaLabel" }, outputs: { facetsChange: "facetsChange", events: "events" }, providers: [FacetService], queries: [{ propertyName: "clearButton", first: true, predicate: FacetClearButtonDirective, descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "placeholder", first: true, predicate: CdkDropList, descendants: true }], ngImport: i0, template: "<!-- Display Any Selected Facets -->\n<div class=\"facets-selected-container\">\n  <!-- Display Title an Clear Button -->\n  <div class=\"facets-selected-header-container\">\n    <!-- Show The Selected Text -->\n    <span class=\"facets-selected-header-label\">{{ header }}</span>\n\n    <!-- Add a Clear Button -->\n    @if ((facetService.facets$ | async).length > 0) {\n      <ng-container [ngTemplateOutlet]=\"clearButton || clearButtonDefault\"></ng-container>\n    }\n  </div>\n\n  <!-- Display Tags For Selected Items -->\n  <div class=\"facets-selected-list\" cdkDropListGroup role=\"list\">\n    <div\n      cdkDropList\n      (cdkDropListEntered)=\"onDropListEntered($event)\"\n      (cdkDropListDropped)=\"onDropListDropped()\"\n    ></div>\n\n    <!-- Show Selected Tags -->\n    @for (facet of facetService.facets$ | async; track trackBy($index, facet)) {\n      <div\n        #tag\n        class=\"facet-selected-tag\"\n        role=\"listitem\"\n        tabindex=\"0\"\n        cdkDropList\n        (cdkDropListEntered)=\"onDropListEntered($event)\"\n        (cdkDropListDropped)=\"onDropListDropped()\"\n        [attr.aria-label]=\"facet.title\"\n        (mousedown)=\"tag.focus()\"\n        (touchmove)=\"$event.preventDefault()\"\n        (keydown.ArrowRight)=\"shiftRight(facet, tag)\"\n        (keydown.ArrowLeft)=\"shiftLeft(facet, tag)\"\n      >\n        @if (facetsReorderable) {\n          <div class=\"facet-drag-item\" cdkDrag>\n            <!-- Display Label -->\n            <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n            <!-- Display Remove Icon -->\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              class=\"facet-selected-remove-btn\"\n              [attr.aria-label]=\"deselectFacetAriaLabel\"\n              (click)=\"deselectFacet(facet, tag)\"\n            >\n              <ux-icon name=\"close\"></ux-icon>\n            </button>\n          </div>\n        } @else {\n          <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n          <button\n            type=\"button\"\n            uxFocusIndicator\n            class=\"facet-selected-remove-btn\"\n            [attr.aria-label]=\"deselectFacetAriaLabel\"\n            (click)=\"deselectFacet(facet, tag)\"\n          >\n            <ux-icon name=\"close\"></ux-icon>\n          </button>\n        }\n      </div>\n    }\n  </div>\n\n  <!-- Show Message Here if No Facets Selected -->\n  @if (emptyText && (facetService.facets$ | async).length === 0) {\n    <p class=\"facets-selected-none-label\">{{ emptyText }}</p>\n  }\n</div>\n\n<!-- Any Facet Elements Should be Added Here By User -->\n<div class=\"facets-region\">\n  <ng-content></ng-content>\n</div>\n\n<ng-template #clearButtonDefault>\n  <button\n    type=\"button\"\n    class=\"btn btn-link btn-icon button-secondary\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"clearAriaLabel\"\n    [uxTooltip]=\"clearTooltip\"\n    placement=\"left\"\n    (click)=\"deselectAllFacets()\"\n  >\n    <svg\n      class=\"facets-selected-clear-graphic\"\n      focusable=\"false\"\n      viewBox=\"0 0 19 12\"\n      shape-rendering=\"geometricPrecision\"\n    >\n      <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n      <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n      <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n      <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n      <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n    </svg>\n  </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "directive", type: i6.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i6.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i6.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetContainerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-container', providers: [FacetService], preserveWhitespaces: false, standalone: false, template: "<!-- Display Any Selected Facets -->\n<div class=\"facets-selected-container\">\n  <!-- Display Title an Clear Button -->\n  <div class=\"facets-selected-header-container\">\n    <!-- Show The Selected Text -->\n    <span class=\"facets-selected-header-label\">{{ header }}</span>\n\n    <!-- Add a Clear Button -->\n    @if ((facetService.facets$ | async).length > 0) {\n      <ng-container [ngTemplateOutlet]=\"clearButton || clearButtonDefault\"></ng-container>\n    }\n  </div>\n\n  <!-- Display Tags For Selected Items -->\n  <div class=\"facets-selected-list\" cdkDropListGroup role=\"list\">\n    <div\n      cdkDropList\n      (cdkDropListEntered)=\"onDropListEntered($event)\"\n      (cdkDropListDropped)=\"onDropListDropped()\"\n    ></div>\n\n    <!-- Show Selected Tags -->\n    @for (facet of facetService.facets$ | async; track trackBy($index, facet)) {\n      <div\n        #tag\n        class=\"facet-selected-tag\"\n        role=\"listitem\"\n        tabindex=\"0\"\n        cdkDropList\n        (cdkDropListEntered)=\"onDropListEntered($event)\"\n        (cdkDropListDropped)=\"onDropListDropped()\"\n        [attr.aria-label]=\"facet.title\"\n        (mousedown)=\"tag.focus()\"\n        (touchmove)=\"$event.preventDefault()\"\n        (keydown.ArrowRight)=\"shiftRight(facet, tag)\"\n        (keydown.ArrowLeft)=\"shiftLeft(facet, tag)\"\n      >\n        @if (facetsReorderable) {\n          <div class=\"facet-drag-item\" cdkDrag>\n            <!-- Display Label -->\n            <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n            <!-- Display Remove Icon -->\n            <button\n              type=\"button\"\n              uxFocusIndicator\n              class=\"facet-selected-remove-btn\"\n              [attr.aria-label]=\"deselectFacetAriaLabel\"\n              (click)=\"deselectFacet(facet, tag)\"\n            >\n              <ux-icon name=\"close\"></ux-icon>\n            </button>\n          </div>\n        } @else {\n          <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n          <button\n            type=\"button\"\n            uxFocusIndicator\n            class=\"facet-selected-remove-btn\"\n            [attr.aria-label]=\"deselectFacetAriaLabel\"\n            (click)=\"deselectFacet(facet, tag)\"\n          >\n            <ux-icon name=\"close\"></ux-icon>\n          </button>\n        }\n      </div>\n    }\n  </div>\n\n  <!-- Show Message Here if No Facets Selected -->\n  @if (emptyText && (facetService.facets$ | async).length === 0) {\n    <p class=\"facets-selected-none-label\">{{ emptyText }}</p>\n  }\n</div>\n\n<!-- Any Facet Elements Should be Added Here By User -->\n<div class=\"facets-region\">\n  <ng-content></ng-content>\n</div>\n\n<ng-template #clearButtonDefault>\n  <button\n    type=\"button\"\n    class=\"btn btn-link btn-icon button-secondary\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"clearAriaLabel\"\n    [uxTooltip]=\"clearTooltip\"\n    placement=\"left\"\n    (click)=\"deselectAllFacets()\"\n  >\n    <svg\n      class=\"facets-selected-clear-graphic\"\n      focusable=\"false\"\n      viewBox=\"0 0 19 12\"\n      shape-rendering=\"geometricPrecision\"\n    >\n      <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n      <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n      <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n      <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n      <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n    </svg>\n  </button>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { header: [{
                type: Input
            }], clearTooltip: [{
                type: Input
            }], emptyText: [{
                type: Input
            }], facetsReorderable: [{
                type: Input
            }], facets: [{
                type: Input
            }], clearAriaLabel: [{
                type: Input
            }], deselectFacetAriaLabel: [{
                type: Input
            }], facetsChange: [{
                type: Output
            }], events: [{
                type: Output
            }], clearButton: [{
                type: ContentChild,
                args: [FacetClearButtonDirective, { read: TemplateRef, static: false }]
            }], placeholder: [{
                type: ViewChild,
                args: [CdkDropList]
            }] } });

class TypeaheadOptionEvent {
    constructor(option, origin) {
        this.option = option;
        this.origin = origin;
    }
}

class TypeaheadKeyService {
    handleKey(event, typeahead) {
        if (!typeahead) {
            return;
        }
        switch (event.keyCode) {
            case UP_ARROW:
                if (!typeahead.open) {
                    typeahead.open = true;
                }
                else {
                    typeahead.moveHighlight(-1);
                }
                event.preventDefault();
                break;
            case DOWN_ARROW:
                if (!typeahead.open) {
                    typeahead.open = true;
                }
                else {
                    typeahead.moveHighlight(1);
                }
                event.preventDefault();
                break;
            case ESCAPE:
                typeahead.open = false;
                break;
            case ENTER:
                if (typeahead.selectOnEnter) {
                    typeahead.selectHighlighted();
                }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadKeyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadKeyService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadKeyService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class InfiniteScrollLoadButtonDirective {
    get visible() {
        return this._visible;
    }
    set visible(value) {
        if (value !== this._visible) {
            if (value) {
                const viewRef = this._viewContainer.createEmbeddedView(this._template);
                this._renderer.listen(viewRef.rootNodes[0], 'click', this.onClick.bind(this));
            }
            else {
                this._viewContainer.clear();
            }
        }
        this._visible = value;
    }
    constructor() {
        this._template = inject(TemplateRef);
        this._viewContainer = inject(ViewContainerRef);
        this._renderer = inject(Renderer2);
        this._visible = false;
        this._load = new Subject();
        this.loading = this._load.asObservable();
    }
    onClick(event) {
        this._load.next(event);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollLoadButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: InfiniteScrollLoadButtonDirective, isStandalone: false, selector: "[uxInfiniteScrollLoadButton]", inputs: { visible: ["uxInfiniteScrollLoadButton", "visible"] }, outputs: { loading: "loading" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollLoadButtonDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxInfiniteScrollLoadButton]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { visible: [{
                type: Input,
                args: ['uxInfiniteScrollLoadButton']
            }], loading: [{
                type: Output
            }] } });

class InfiniteScrollLoadingDirective {
    constructor() {
        this._templateRef = inject(TemplateRef);
        this._viewContainer = inject(ViewContainerRef);
        this._visible = false;
    }
    get visible() {
        return this._visible;
    }
    set visible(value) {
        value = coerceBooleanProperty(value);
        if (value !== this._visible) {
            if (value) {
                this._viewContainer.createEmbeddedView(this._templateRef);
            }
            else {
                this._viewContainer.clear();
            }
        }
        this._visible = value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollLoadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: InfiniteScrollLoadingDirective, isStandalone: false, selector: "[uxInfiniteScrollLoading]", inputs: { visible: ["uxInfiniteScrollLoading", "visible"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollLoadingDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxInfiniteScrollLoading]',
                    standalone: false,
                }]
        }], propDecorators: { visible: [{
                type: Input,
                args: ['uxInfiniteScrollLoading']
            }] } });

class InfiniteScrollDirective {
    get collection() {
        return this._collection;
    }
    set collection(value) {
        this.collectionChange.emit(value);
        this._collection = value;
    }
    set scrollElement(element) {
        this._scrollElement = element instanceof ElementRef ? element : new ElementRef(element);
    }
    constructor() {
        this._element = inject(ElementRef);
        this._collection = [];
        this.enabled = true;
        this.loadOnInit = true;
        this.loadOnScroll = true;
        this.pageSize = 20;
        this.collectionChange = new EventEmitter();
        this.loadingEvent = new EventEmitter();
        this.loadedEvent = new EventEmitter();
        this.loadErrorEvent = new EventEmitter();
        this._nextPageNum = 0;
        this._updateRequests = new Subject();
        this._isLoading = new BehaviorSubject(false);
        this._isExhausted = new BehaviorSubject(false);
        this._loadButtonEnabled = new BehaviorSubject(false);
        this._subscriptions = [];
        this._loadButtonSubscriptions = [];
        this._onDestroy = new Subject();
        this._canLoadManually = this._isLoading.pipe(combineLatest$1(this._isExhausted, this._loadButtonEnabled, (isLoading, isExhausted, loadButtonEnabled) => {
            return !isLoading && !isExhausted && loadButtonEnabled;
        }));
    }
    ngOnInit() {
        if (!this._scrollElement) {
            this._scrollElement = this._element;
        }
        this._loadButtonEnabled.next(!this.loadOnScroll);
    }
    ngAfterContentInit() {
        // There are two kinds of update requests: check and load.
        // Check requests are throttled and will only cause an update if more data is required
        // to fill the scrolling view, and it isn't already loading some.
        // Load requests are not throttled and always request a page of data.
        this._updateRequests
            .pipe(filter(request => request.check), auditTime(200), takeUntil(this._onDestroy))
            .subscribe(this.doRequest.bind(this));
        this._updateRequests
            .pipe(filter(request => !request.check), takeUntil(this._onDestroy))
            .subscribe(this.doRequest.bind(this));
        if (this.enabled) {
            // Subscribe to scroll events and DOM changes.
            this.attachEventHandlers();
        }
        // Connect the Load More button visible state.
        this._canLoadManually.pipe(takeUntil(this._onDestroy)).subscribe(canLoad => {
            this._loadButtonQuery.forEach(loadButton => {
                loadButton.visible = canLoad;
            });
        });
        // Connect the loading indicator visible state.
        this._isLoading.pipe(takeUntil(this._onDestroy)).subscribe(isLoading => {
            this._loadingIndicatorQuery.forEach(loading => {
                loading.visible = isLoading;
            });
        });
        // Link the Load More button click event to trigger an update.
        this.attachLoadButtonEvents();
        this._loadButtonQuery.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            this.attachLoadButtonEvents();
        });
        // Initial update.
        if (this.loadOnInit) {
            this.loadNextPage();
        }
    }
    ngOnChanges(changes) {
        let check = true;
        if (changes.enabled && changes.enabled.currentValue !== changes.enabled.previousValue) {
            if (changes.enabled.currentValue) {
                this.attachEventHandlers();
                this.reset();
                check = false;
            }
            else {
                this.detachEventHandlers();
            }
        }
        if (this.enabled) {
            if (changes.filter &&
                this.coerceFilter(changes.filter.currentValue) !==
                    this.coerceFilter(changes.filter.previousValue)) {
                this.reset();
                check = false;
            }
            if (changes.loadOnScroll) {
                this._loadButtonEnabled.next(!changes.loadOnScroll.currentValue);
            }
            if (changes.pageSize && changes.pageSize.currentValue !== changes.pageSize.previousValue) {
                this.reset();
                check = false;
            }
            this._updateRequests.next({
                check,
                pageNumber: this._nextPageNum,
                pageSize: this.pageSize,
                filter: this.coerceFilter(this.filter),
            });
        }
    }
    ngOnDestroy() {
        this.detachEventHandlers();
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Request an additional page of data.
     */
    loadNextPage() {
        if (!this.enabled) {
            return;
        }
        this._updateRequests.next({
            check: false,
            pageNumber: this._nextPageNum,
            pageSize: this.pageSize,
            filter: this.coerceFilter(this.filter),
        });
    }
    /**
     * Request a check for whether an additional page of data is required. This is throttled.
     */
    check() {
        if (!this.enabled) {
            return;
        }
        this._updateRequests.next({
            check: true,
            pageNumber: this._nextPageNum,
            pageSize: this.pageSize,
            filter: this.coerceFilter(this.filter),
        });
    }
    /**
     * Clear the collection. Future requests will load from page 0.
     */
    reset(clearSubscriptions = true) {
        if (!this.enabled) {
            return;
        }
        // Reset the page counter.
        this._nextPageNum = 0;
        // Clear the collection (without changing the reference).
        if (this.collection) {
            this.collection.length = 0;
        }
        if (this._subscriptions && clearSubscriptions) {
            this._pages = [];
            // Reset the exhausted flag, allowing the Load More button to appear.
            this._isExhausted.next(false);
            // reset the loading state
            this._isLoading.next(false);
            // Cancel any pending requests
            this._subscriptions.forEach(request => request.unsubscribe());
        }
    }
    /**
     * Reload the data without clearing the view.
     */
    reload() {
        this._pages?.forEach((page, i) => this.reloadPage(i));
    }
    /**
     * Reload the data in a specific page without clearing the view.
     * @param pageNum Page number
     */
    reloadPage(pageNum) {
        if (!this.enabled) {
            return;
        }
        this._updateRequests.next({
            check: false,
            pageNumber: pageNum,
            pageSize: this.pageSize,
            filter: this.coerceFilter(this.filter),
            reload: true,
        });
    }
    /** A filter value of null or undefined should be considered the same as an empty string */
    coerceFilter(value) {
        return value === undefined || value === null ? '' : value;
    }
    /**
     * Attach scroll event handler and DOM observer.
     */
    attachEventHandlers() {
        // if the scrollElement is documentElement we must watch for a scroll event on the document
        const target = this._scrollElement.nativeElement instanceof HTMLHtmlElement
            ? document
            : this._scrollElement.nativeElement;
        // Subscribe to the scroll event on the target element.
        this._scrollEventSub = fromEvent(target, 'scroll').subscribe(this.check.bind(this));
        // Subscribe to child DOM changes. The main effect of this is to check whether even more data is
        // required after the initial load.
        this._domObserver = new MutationObserver(this.check.bind(this));
        this._domObserver.observe(this._scrollElement.nativeElement, {
            childList: true,
            subtree: true,
        });
    }
    /**
     * Detach scroll event handler and DOM observer.
     */
    detachEventHandlers() {
        if (this._scrollEventSub) {
            this._scrollEventSub.unsubscribe();
            this._scrollEventSub = null;
        }
        if (this._domObserver) {
            this._domObserver.disconnect();
            this._domObserver = null;
        }
    }
    /**
     * Remove any existing event subscriptions for the load button `loading` event, then attach
     * subscriptions
     * for any in the query.
     */
    attachLoadButtonEvents() {
        this._loadButtonSubscriptions.forEach(s => s.unsubscribe());
        this._loadButtonSubscriptions = this._loadButtonQuery.map(loadButton => loadButton.loading.subscribe(this.loadNextPage.bind(this)));
    }
    /**
     * Conditionally loads a page into the collection based on directive state and request parameters.
     */
    doRequest(request) {
        // Load a new page if the scroll position is beyond the threshhold and if the client code did not
        // cancel.
        if (this.needsData(request) && this.beginLoading(request)) {
            // Invoke the callback load function, which returns a promose or plain data.
            const loadResult = this.load(request.pageNumber, request.pageSize, request.filter);
            if (loadResult === null) {
                return;
            }
            const observable = Array.isArray(loadResult) ? of(loadResult) : from(loadResult);
            let completed = false;
            // subscription needs to be a let here if subscription completes right away the complete function can be called
            // before the assignment. While browsers will ignore this currently as we transpile
            // all const/let statements to var, when this is no longer the case (or we are running in a test environment)
            // const will be used and this can throw an error if we try to access a const variable before it is assigned
            const subscription = observable.pipe(first()).subscribe(items => {
                // Make sure that the parameters have not changed since the load started;
                // otherwise discard the results.
                if (request.filter === this.coerceFilter(this.filter) &&
                    request.pageSize === this.pageSize) {
                    if (items && items.length) {
                        this.setPageItems(request.pageNumber, items);
                    }
                    // Emit the loaded event
                    this.endLoading(request, items);
                }
            }, (reason) => {
                // Emit the loadError event
                this.endLoadingWithError(request, reason);
            }, () => {
                // remove this request from the list
                this._subscriptions = this._subscriptions.filter(s => s !== subscription);
                completed = true;
            });
            // only add the subscription to the list of requests if it isnt complete.
            if (!completed) {
                this._subscriptions.push(subscription);
            }
        }
    }
    /**
     * Returns true if the request should be fulfilled.
     */
    needsData(request) {
        if (!this.enabled) {
            return false;
        }
        // Always load for a load request
        if (!request.check) {
            return true;
        }
        // Ignore a check request when the end of data has been detected, or if data is currently loading.
        if (this._isExhausted.getValue() || this._isLoading.getValue()) {
            return false;
        }
        // Load if the remaining scroll area is <= the element height.
        if (this._scrollElement && this.loadOnScroll) {
            const element = this._scrollElement.nativeElement;
            const remainingScroll = element.scrollHeight - (element.scrollTop + element.clientHeight);
            const isVisible = element.scrollHeight > 0;
            return isVisible && remainingScroll <= element.clientHeight;
        }
        return false;
    }
    /**
     * Updates state for the beginning of a load. Returns false if the `loading` event was cancelled.
     */
    beginLoading(request) {
        const event = new InfiniteScrollLoadingEvent(request.pageNumber, request.pageSize, request.filter);
        this.loadingEvent.emit(event);
        this._isLoading.next(!event.defaultPrevented());
        return !event.defaultPrevented();
    }
    setPageItems(pageNum, items) {
        this._pages[pageNum] = items;
        this.collection = this._pages.reduce((previous, current) => previous.concat(current), []);
    }
    /**
     * Updates state from a successful load. Raises the `loaded` event.
     */
    endLoading(request, data) {
        this._isLoading.next(false);
        const isExhausted = !!(data && data.length < this.pageSize);
        this._isExhausted.next(isExhausted);
        this.loadedEvent.emit(new InfiniteScrollLoadedEvent(request.pageNumber, request.pageSize, request.filter, data, isExhausted));
        if (!request.reload) {
            this._nextPageNum += 1;
        }
    }
    /**
     * Updates state from a failed load. Raises the `loadError` event.
     */
    endLoadingWithError(request, error) {
        this._isLoading.next(false);
        this.loadErrorEvent.emit(new InfiniteScrollLoadErrorEvent(request.pageNumber, request.pageSize, request.filter, error));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: InfiniteScrollDirective, isStandalone: false, selector: "[uxInfiniteScroll]", inputs: { load: ["uxInfiniteScroll", "load"], _collection: ["collection", "_collection"], scrollElement: "scrollElement", enabled: "enabled", filter: "filter", loadOnInit: "loadOnInit", loadOnScroll: "loadOnScroll", pageSize: "pageSize" }, outputs: { collectionChange: "collectionChange", loadingEvent: "loading", loadedEvent: "loaded", loadErrorEvent: "loadError" }, queries: [{ propertyName: "_loadButtonQuery", predicate: InfiniteScrollLoadButtonDirective }, { propertyName: "_loadingIndicatorQuery", predicate: InfiniteScrollLoadingDirective }], exportAs: ["uxInfiniteScroll"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxInfiniteScroll]',
                    exportAs: 'uxInfiniteScroll',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { load: [{
                type: Input,
                args: ['uxInfiniteScroll']
            }], _collection: [{
                type: Input,
                args: ['collection']
            }], scrollElement: [{
                type: Input
            }], enabled: [{
                type: Input
            }], filter: [{
                type: Input
            }], loadOnInit: [{
                type: Input
            }], loadOnScroll: [{
                type: Input
            }], pageSize: [{
                type: Input
            }], collectionChange: [{
                type: Output
            }], loadingEvent: [{
                type: Output,
                args: ['loading']
            }], loadedEvent: [{
                type: Output,
                args: ['loaded']
            }], loadErrorEvent: [{
                type: Output,
                args: ['loadError']
            }], _loadButtonQuery: [{
                type: ContentChildren,
                args: [InfiniteScrollLoadButtonDirective]
            }], _loadingIndicatorQuery: [{
                type: ContentChildren,
                args: [InfiniteScrollLoadingDirective]
            }] } });
/**
 * The internal data associated with a load/check request.
 */
class InfiniteScrollRequest {
}
/**
 * Event raised before the `loading` function is called.
 */
class InfiniteScrollLoadingEvent {
    constructor(
    /**
     * The index of the requested page, starting from 0.
     */
    pageNumber, 
    /**
     * The number of items requested.
     */
    pageSize, 
    /**
     * The filter details as provided via the `filter` binding.
     */
    filter) {
        this.pageNumber = pageNumber;
        this.pageSize = pageSize;
        this.filter = filter;
        this._defaultPrevented = false;
    }
    /**
     * Prevents the default behaviour of the `loading` event (loading function will not be called).
     */
    preventDefault() {
        this._defaultPrevented = true;
    }
    defaultPrevented() {
        return this._defaultPrevented;
    }
}
/**
 * Event raised when the loading function result has been resolved and added to the collection.
 */
class InfiniteScrollLoadedEvent {
    constructor(
    /**
     * The index of the requested page, starting from 0.
     */
    pageNumber, 
    /**
     * The number of items requested.
     */
    pageSize, 
    /**
     * The filter details as provided via the `filter` binding.
     */
    filter, 
    /**
     * The result of the promise returned from the loading function.
     */
    data, 
    /**
     * True if the data is considered exhausted (number of items returned less than `pageSize`).
     */
    exhausted) {
        this.pageNumber = pageNumber;
        this.pageSize = pageSize;
        this.filter = filter;
        this.data = data;
        this.exhausted = exhausted;
    }
}
/**
 * Event raised if the loading function returns a rejected promise.
 */
class InfiniteScrollLoadErrorEvent {
    constructor(
    /**
     * The index of the requested page, starting from 0.
     */
    pageNumber, 
    /**
     * The number of items requested.
     */
    pageSize, 
    /**
     * The filter details as provided via the `filter` binding.
     */
    filter, 
    /**
     * The object provided when rejecting the promise.
     */
    error) {
        this.pageNumber = pageNumber;
        this.pageSize = pageSize;
        this.filter = filter;
        this.error = error;
    }
}

class InfiniteScrollModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollModule, declarations: [InfiniteScrollDirective,
            InfiniteScrollLoadButtonDirective,
            InfiniteScrollLoadingDirective], exports: [InfiniteScrollDirective,
            InfiniteScrollLoadButtonDirective,
            InfiniteScrollLoadingDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InfiniteScrollModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [],
                    exports: [
                        InfiniteScrollDirective,
                        InfiniteScrollLoadButtonDirective,
                        InfiniteScrollLoadingDirective,
                    ],
                    declarations: [
                        InfiniteScrollDirective,
                        InfiniteScrollLoadButtonDirective,
                        InfiniteScrollLoadingDirective,
                    ],
                    providers: [],
                }]
        }] });

class PopoverOrientationService {
    constructor() {
        this._resizeService = inject(ResizeService);
        this._viewportRuler = inject(ViewportRuler);
    }
    createPopoverOrientationListener(element, parentElement) {
        const nativeElement = element instanceof ElementRef ? element.nativeElement : element;
        const nativeElementParent = parentElement instanceof ElementRef ? parentElement.nativeElement : element;
        return new PopoverOrientationListener(nativeElement, nativeElementParent, this._resizeService, this._viewportRuler);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverOrientationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverOrientationService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverOrientationService, decorators: [{
            type: Injectable
        }] });
class PopoverOrientationListener {
    constructor(_element, _elementParent, _resizeService, _viewportRuler) {
        this._element = _element;
        this._elementParent = _elementParent;
        this._resizeService = _resizeService;
        this._viewportRuler = _viewportRuler;
        /** Allow subscribing to state changes */
        this.orientation$ = new BehaviorSubject(1);
        /** Max value the height of the dropdown can be */
        this.maxHeight = 250;
        this._onDestroy = new Subject();
        // watch for changes to the typeahead size
        this._resizeService
            .addResizeListener(this._element)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => {
            this.onScrollOrResize();
        });
        // watch for changes to the typeahead position when scrolling or resizing
        fromEvent(window, 'scroll', { passive: true })
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.onScrollOrResize());
        fromEvent(window, 'resize', { passive: true })
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.onScrollOrResize());
    }
    destroy() {
        this.orientation$.complete();
        this._onDestroy.next();
        this._onDestroy.complete();
        this._resizeService.removeResizeListener(this._element);
    }
    onScrollOrResize() {
        this._rect = this._elementParent
            ? this._elementParent.parentElement.getBoundingClientRect()
            : this._element.parentElement.getBoundingClientRect();
        // use the maxHeight input value if the element does not exist yet to prevent the direction from immediately changing when opened
        const itemHeight = this._element.offsetHeight || this.maxHeight;
        const viewportSize = this._viewportRuler.getViewportSize();
        const bottomSpaceAvailable = viewportSize.height - this._rect.bottom - itemHeight;
        this.orientation$.next(bottomSpaceAvailable <= 0 ? 0 /* PopoverOrientation.Up */ : 1 /* PopoverOrientation.Down */);
    }
}

class TypeaheadService {
    constructor() {
        this.open$ = new BehaviorSubject(false);
        this.highlightedElement$ = new BehaviorSubject(null);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadService, decorators: [{
            type: Injectable
        }] });

class SafeInnerHtmlDirective {
    constructor() {
        this._sanitizer = inject(DomSanitizer);
    }
    set safeInnerHtml(value) {
        // Angular's DomSanitizer allows anchor tags, however it does remove any dangerous attributes. That being said
        // we still want to escape any anchor tags regardless.
        value = value.replace(/<a/g, '&lt;a').replace(/<\/a>/g, '&lt;/a&gt;');
        this.safeHtml = this._sanitizer.sanitize(SecurityContext.HTML, value);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SafeInnerHtmlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SafeInnerHtmlDirective, isStandalone: true, selector: "[uxSafeInnerHtml]", inputs: { safeInnerHtml: ["uxSafeInnerHtml", "safeInnerHtml"] }, host: { properties: { "innerHtml": "this.safeHtml" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SafeInnerHtmlDirective, decorators: [{
            type: Directive,
            args: [{
                    standalone: true,
                    selector: '[uxSafeInnerHtml]',
                }]
        }], propDecorators: { safeHtml: [{
                type: HostBinding,
                args: ['innerHtml']
            }], safeInnerHtml: [{
                type: Input,
                args: ['uxSafeInnerHtml']
            }] } });

class ScrollIntoViewService {
    scrollIntoView(elem, scrollParent) {
        const offsetTop = elem.getBoundingClientRect().top +
            scrollParent.scrollTop -
            scrollParent.getBoundingClientRect().top;
        if (offsetTop < scrollParent.scrollTop) {
            scrollParent.scrollTop = offsetTop;
        }
        else {
            const offsetBottom = offsetTop + elem.offsetHeight;
            if (offsetBottom > scrollParent.scrollTop + scrollParent.clientHeight) {
                scrollParent.scrollTop = offsetBottom - scrollParent.clientHeight;
            }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewService, decorators: [{
            type: Injectable
        }] });

class ScrollIntoViewIfDirective {
    constructor() {
        this._element = inject(ElementRef);
        this._scrollIntoViewService = inject(ScrollIntoViewService);
        this.condition = false;
    }
    ngOnChanges() {
        if (this.condition) {
            setTimeout(() => this._scrollIntoViewService.scrollIntoView(this._element.nativeElement, this.scrollParent));
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewIfDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ScrollIntoViewIfDirective, isStandalone: false, selector: "[uxScrollIntoViewIf]", inputs: { condition: ["uxScrollIntoViewIf", "condition"], scrollParent: "scrollParent" }, providers: [ScrollIntoViewService], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewIfDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxScrollIntoViewIf]',
                    providers: [ScrollIntoViewService],
                    standalone: false,
                }]
        }], propDecorators: { condition: [{
                type: Input,
                args: ['uxScrollIntoViewIf']
            }], scrollParent: [{
                type: Input
            }] } });

class TypeaheadHighlightDirective {
    constructor() {
        this._service = inject(TypeaheadService);
        this._elementRef = inject(ElementRef);
    }
    set highlight(value) {
        if (value) {
            this._service.highlightedElement$.next(this._elementRef.nativeElement);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadHighlightDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TypeaheadHighlightDirective, isStandalone: false, selector: "[uxTypeaheadHighlight]", inputs: { highlight: ["uxTypeaheadHighlight", "highlight"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadHighlightDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTypeaheadHighlight]',
                    standalone: false,
                }]
        }], propDecorators: { highlight: [{
                type: Input,
                args: ['uxTypeaheadHighlight']
            }] } });

class TypeaheadOptionsListComponent {
    constructor() {
        this.startIndex = 0;
        this.isMultiselectable = false;
        this.optionMouseover = new EventEmitter();
        this.optionMousedown = new EventEmitter();
        this.optionClick = new EventEmitter();
    }
    trackByFn(_, option) {
        return option.key;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadOptionsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TypeaheadOptionsListComponent, isStandalone: false, selector: "ux-typeahead-options-list", inputs: { id: "id", startIndex: "startIndex", options: "options", highlighted: "highlighted", activeKey: "activeKey", disabledOptions: "disabledOptions", isMultiselectable: "isMultiselectable", optionTemplate: "optionTemplate", optionApi: "optionApi", typeaheadElement: "typeaheadElement", ariaLabel: "ariaLabel" }, outputs: { optionMouseover: "optionMouseover", optionMousedown: "optionMousedown", optionClick: "optionClick" }, ngImport: i0, template: "<ol role=\"listbox\" [attr.aria-label]=\"ariaLabel\" [attr.aria-multiselectable]=\"isMultiselectable\">\n  @for (option of options; track trackByFn(i, option); let i = $index) {\n    <li\n      [attr.id]=\"id + '-option-' + (i + startIndex)\"\n      [class.disabled]=\"optionApi.getDisabled(option.value)\"\n      [class.highlighted]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      [class.active]=\"activeKey === option.key && !option.isRecentOption\"\n      [uxTypeaheadHighlight]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      [uxScrollIntoViewIf]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      role=\"option\"\n      [attr.aria-selected]=\"\n        isMultiselectable\n          ? optionApi.getDisabled(option.value)\n          : activeKey === option.key\n            ? true\n            : null\n      \"\n      [scrollParent]=\"typeaheadElement.nativeElement\"\n      (mouseover)=\"optionMouseover.emit({ option: option, event: $event })\"\n      (mousedown)=\"optionMousedown.emit({ option: option, event: $event })\"\n      (click)=\"optionClick.emit({ option: option, event: $event })\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"optionTemplate\"\n        [ngTemplateOutletContext]=\"{ option: option.value, api: optionApi }\"\n      >\n      </ng-container>\n    </li>\n  }\n</ol>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ScrollIntoViewIfDirective, selector: "[uxScrollIntoViewIf]", inputs: ["uxScrollIntoViewIf", "scrollParent"] }, { kind: "directive", type: TypeaheadHighlightDirective, selector: "[uxTypeaheadHighlight]", inputs: ["uxTypeaheadHighlight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadOptionsListComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-typeahead-options-list', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ol role=\"listbox\" [attr.aria-label]=\"ariaLabel\" [attr.aria-multiselectable]=\"isMultiselectable\">\n  @for (option of options; track trackByFn(i, option); let i = $index) {\n    <li\n      [attr.id]=\"id + '-option-' + (i + startIndex)\"\n      [class.disabled]=\"optionApi.getDisabled(option.value)\"\n      [class.highlighted]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      [class.active]=\"activeKey === option.key && !option.isRecentOption\"\n      [uxTypeaheadHighlight]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      [uxScrollIntoViewIf]=\"\n        highlighted?.key === option.key && highlighted?.isRecentOption === option.isRecentOption\n      \"\n      role=\"option\"\n      [attr.aria-selected]=\"\n        isMultiselectable\n          ? optionApi.getDisabled(option.value)\n          : activeKey === option.key\n            ? true\n            : null\n      \"\n      [scrollParent]=\"typeaheadElement.nativeElement\"\n      (mouseover)=\"optionMouseover.emit({ option: option, event: $event })\"\n      (mousedown)=\"optionMousedown.emit({ option: option, event: $event })\"\n      (click)=\"optionClick.emit({ option: option, event: $event })\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"optionTemplate\"\n        [ngTemplateOutletContext]=\"{ option: option.value, api: optionApi }\"\n      >\n      </ng-container>\n    </li>\n  }\n</ol>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], startIndex: [{
                type: Input
            }], options: [{
                type: Input
            }], highlighted: [{
                type: Input
            }], activeKey: [{
                type: Input
            }], disabledOptions: [{
                type: Input
            }], isMultiselectable: [{
                type: Input
            }], optionTemplate: [{
                type: Input
            }], optionApi: [{
                type: Input
            }], typeaheadElement: [{
                type: Input
            }], ariaLabel: [{
                type: Input
            }], optionMouseover: [{
                type: Output
            }], optionMousedown: [{
                type: Output
            }], optionClick: [{
                type: Output
            }] } });

let uniqueId$9 = 0;
class TypeaheadComponent {
    /** Specify if the typeahead is open */
    get open() {
        return this._service.open$.getValue();
    }
    set open(value) {
        this._service.open$.next(value);
    }
    /** Specify which options are disabled */
    set disabledOptions(options) {
        this._disabledOptions = coerceArray(options);
    }
    get disabledOptions() {
        return this._disabledOptions;
    }
    /** Specify the max height of the dropdown */
    get maxHeight() {
        return this._maxHeight;
    }
    set maxHeight(maxHeight) {
        this._maxHeight = maxHeight;
        if (this.maxHeight.endsWith('px')) {
            this._popoverOrientationListener.maxHeight = Number(this.maxHeight.slice(0, -2));
        }
    }
    /** Specify the currently active item */
    set active(item) {
        this.activeKey = this.getKey(item);
    }
    get highlighted() {
        const value = this.highlighted$.getValue();
        return value ? value.value : null;
    }
    constructor() {
        this.typeaheadElement = inject(ElementRef);
        this.popoverOrientation = inject(PopoverOrientationService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._service = inject(TypeaheadService);
        /** Define a unique id for the typeahead */
        this.id = `ux-typeahead-${++uniqueId$9}`;
        this._disabledOptions = [];
        /** Specify the drop direction */
        this.dropDirection = 'down';
        /** Specify the aria multi selectable attribute value */
        this.multiselectable = false;
        /** Specify if the dropdown should appear when the filter appears */
        this.openOnFilterChange = true;
        /** Specify the page size */
        this.pageSize = 20;
        /** Specify if we should select the first item by default */
        this.selectFirst = true;
        /** Specify if we should select an item on enter key press */
        this.selectOnEnter = false;
        /** Specify the loading state */
        this.loading = false;
        /** Maximum number of displayed recently selected options. */
        this.recentOptionsMaxCount = 5;
        /** Emit when the open state changes */
        this.openChange = new EventEmitter();
        /** Emit when an option is selected */
        this.optionSelected = new EventEmitter();
        /** Emit whenever a highlighted item changes */
        this.highlightedChange = new EventEmitter();
        /** Emit the highlighted element when it changes */
        this.highlightedElementChange = new EventEmitter();
        /** Emits when recently selected options change.*/
        this.recentOptionsChange = new EventEmitter();
        this.activeKey = null;
        this.clicking = false;
        this.hasBeenOpened = false;
        this.highlighted$ = new BehaviorSubject(null);
        this.visibleOptions$ = new BehaviorSubject([]);
        this.visibleRecentOptions$ = new BehaviorSubject([]);
        this.allVisibleOptions = [];
        this._onDestroy = new Subject();
        this._maxHeight = '250px';
        this.optionApi = {
            getKey: this.getKey.bind(this),
            getDisplay: this.getDisplay.bind(this),
            getDisplayHtml: this.getDisplayHtml.bind(this),
            getDisabled: this.isDisabled.bind(this),
        };
        this.loadOptionsCallback = (pageNum, pageSize, filter) => {
            if (typeof this.options === 'function') {
                // Invoke the callback which may return an array or a promise.
                const arrayOrPromise = this.options(pageNum, pageSize, filter);
                // Map the results to an array of TypeaheadVisibleOption.
                return Promise.resolve(arrayOrPromise).then(newOptions => {
                    if (!Array.isArray(newOptions)) {
                        return newOptions;
                    }
                    return this.getVisibleOptions(newOptions, '');
                });
            }
            return null;
        };
        this._service.open$
            .pipe(distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(isOpen => {
            this.openChange.emit(isOpen);
            if (isOpen) {
                this.hasBeenOpened = true;
                this.initOptions();
            }
        });
        this._popoverOrientationListener = this.popoverOrientation.createPopoverOrientationListener(this.typeaheadElement.nativeElement, this.typeaheadElement.nativeElement.parentElement);
        this._popoverOrientationListener.orientation$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(direction => {
            if (this.dropDirection === 'auto') {
                this.dropUp = direction === 0 /* PopoverOrientation.Up */;
            }
        });
        this.highlighted$.pipe(takeUntil(this._onDestroy)).subscribe(next => {
            this.highlightedChange.emit(next ? next.value : null);
        });
        combineLatest([this.visibleOptions$, this.visibleRecentOptions$])
            .pipe(takeUntil(this._onDestroy))
            .subscribe(([visibleOptions, visibleRecentOptions]) => {
            this.allVisibleOptions = [...visibleRecentOptions, ...visibleOptions];
        });
        combineLatest([this._service.open$, this._service.highlightedElement$, this.visibleOptions$])
            .pipe(takeUntil(this._onDestroy))
            .subscribe(([open, highlightedElement, visibleOptions]) => {
            this.highlightedElementChange.emit(open && visibleOptions.length > 0 ? highlightedElement : null);
        });
    }
    ngOnChanges(changes) {
        // Open the dropdown if the filter value updates
        if (changes.filter) {
            if (this.openOnFilterChange &&
                changes.filter.currentValue &&
                changes.filter.currentValue.length > 0) {
                // if the dropdown item was just selected, and we set the filter value to match the
                // selected value then open will have also just been set to `false`, in which case we do
                // not want to set open to `true`
                if (changes.open &&
                    changes.open.previousValue === true &&
                    changes.open.currentValue === false) {
                    return;
                }
                // show the dropdown
                this.open = true;
            }
        }
        // Cut off recentOptions at recentOptionsMaxCount
        if (changes.recentOptions || changes.recentOptionsMaxCount) {
            this._recentOptions = this.recentOptions
                ? this.recentOptions.slice(0, this.recentOptionsMaxCount)
                : undefined;
            if (changes.recentOptionsMaxCount) {
                // Avoid ExpressionChangedAfterChecked error
                setTimeout(() => {
                    this.recentOptionsChange.emit(this._recentOptions);
                });
            }
        }
        if (changes.dropDirection) {
            if (changes.dropDirection.currentValue === 'auto') {
                this.dropUp =
                    this._popoverOrientationListener.orientation$.getValue() === 0 /* PopoverOrientation.Up */;
            }
            else {
                this.dropUp = changes.dropDirection.currentValue === 'up';
            }
        }
        if (changes.options && changes.options.firstChange === false) {
            this.infiniteScroll.reset(false);
            this.infiniteScroll.reload();
        }
        // Re-filter visibleOptions
        this.updateOptions();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this._popoverOrientationListener.destroy();
    }
    mousedownHandler(event, target) {
        this.clicking = true;
        /**
         * Chrome Bug Workaround: (https://bugs.chromium.org/p/chromium/issues/detail?id=6759)
         * In Chrome, if the typeahead is within a tabbable element, e.g. it has a parent with
         * `tabindex="0"` like our side panel has, then clicking on a scrollbar will remove
         * focus from the input element.
         *
         * To avoid this we can determine if the mousedown event occurred within the scrollbar
         * region of the typeahead and if so preventDefault which will stop the input
         * from losing focus
         */
        if (event.clientX >= target.clientWidth || event.clientY >= target.clientHeight) {
            event.preventDefault();
        }
    }
    mouseupHandler() {
        this.clicking = false;
    }
    optionMousedownHandler(event) {
        // Workaround to prevent focus changing when an option is clicked
        event.preventDefault();
    }
    optionClickHandler(_event, option) {
        this.select(option, 'mouse');
    }
    /**
     * Returns the unique key value of the given option.
     */
    getKey(option) {
        if (typeof this.key === 'function') {
            return this.key(option);
        }
        // eslint-disable-next-line no-prototype-builtins
        if (typeof this.key === 'string' && option && option.hasOwnProperty(this.key)) {
            return option[this.key];
        }
        return this.getDisplay(option);
    }
    /**
     * Returns the display value of the given option.
     */
    getDisplay(option) {
        if (typeof this.display === 'function') {
            return this.display(option);
        }
        // eslint-disable-next-line no-prototype-builtins
        if (typeof this.display === 'string' && option && option.hasOwnProperty(this.display)) {
            return option[this.display];
        }
        if (typeof option === 'string') {
            return option;
        }
    }
    /**
     * Returns the display value of the given option with HTML markup added to highlight the part which matches the current filter value.
     * @param option
     */
    getDisplayHtml(option) {
        const displayText = this.getDisplay(option);
        // getDisplay may return undefined in certain cases
        if (!displayText) {
            return '';
        }
        let displayHtml = displayText
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
        if (this.filter) {
            const length = this.filter.length;
            const matchIndex = displayText.toLowerCase().indexOf(this.filter.toLowerCase());
            if (matchIndex >= 0) {
                const highlight = `<span class="ux-filter-match">${displayText.substr(matchIndex, length)}</span>`;
                displayHtml =
                    displayText.substr(0, matchIndex) + highlight + displayText.substr(matchIndex + length);
            }
        }
        return displayHtml;
    }
    /**
     * Returns true if the infinite scroll component should load
     */
    isInfiniteScroll() {
        return typeof this.options === 'function';
    }
    /**
     * Selects the given option, emitting the optionSelected event and closing the dropdown.
     */
    select(option, origin) {
        if (!this.isDisabled(option.value)) {
            this.optionSelected.emit(new TypeaheadOptionEvent(option.value, origin));
            this.highlighted$.next(null);
            this.open = false;
            this.addToRecentOptions(option.value);
        }
    }
    addToRecentOptions(value) {
        if (this._recentOptions) {
            this._recentOptions = [
                value,
                ...this._recentOptions.filter(recentOption => recentOption !== value),
            ].slice(0, this.recentOptionsMaxCount);
            this.recentOptionsChange.emit(this._recentOptions);
        }
    }
    /**
     * Returns true if the given option is part of the disabledOptions array.
     */
    isDisabled(option) {
        return Array.isArray(this.disabledOptions)
            ? this.disabledOptions.some(selectedOption => this.getKey(selectedOption) === this.getKey(option))
            : false;
    }
    /**
     * Set the given option as the current highlighted option, available in the highlightedOption parameter.
     */
    highlight(option) {
        if (!this.isDisabled(option.value)) {
            this.highlighted$.next(option);
            this._changeDetector.detectChanges();
        }
    }
    /**
     * Increment or decrement the highlighted option in the list. Disabled options are skipped.
     * @param d Value to be added to the index of the highlighted option, i.e. -1 to move backwards, +1 to move forwards.
     */
    moveHighlight(d) {
        const highlightIndex = this.indexOfVisibleOption(this.highlighted$.getValue());
        let newIndex = highlightIndex;
        let disabled = true;
        let inBounds = true;
        do {
            newIndex = newIndex + d;
            inBounds = newIndex >= 0 && newIndex < this.allVisibleOptions.length;
            disabled = inBounds && this.isDisabled(this.allVisibleOptions[newIndex].value);
        } while (inBounds && disabled);
        if (!disabled && inBounds) {
            this.highlight(this.allVisibleOptions[newIndex]);
        }
        return this.highlighted;
    }
    selectHighlighted() {
        if (this.highlighted$.getValue()) {
            this.select(this.highlighted$.getValue(), 'keyboard');
        }
    }
    /**
     * Set up the options before the dropdown is displayed.
     */
    initOptions() {
        // Clear previous highlight
        this.highlighted$.next(null);
        if (this.selectFirst) {
            // This will highlight the first non-disabled option.
            this.moveHighlight(1);
        }
    }
    /**
     * Display the first item as highlighted when there are several pages
     */
    onLoadedHighlight(event) {
        if (this.selectFirst && this.options && event.pageNumber === 0) {
            // This will highlight the first non-disabled option.
            this.moveHighlight(1);
        }
    }
    /**
     * Update the visibleOptions and visibleRecentOptions arrays with the current filter.
     */
    updateOptions() {
        const normalisedInput = (this.filter || '').toLowerCase();
        // Create new visibleOptions only if `options` is not a function
        if (typeof this.options === 'object') {
            this.visibleOptions$.next(this.getVisibleOptions(this.options, normalisedInput));
        }
        this.visibleRecentOptions$.next(this.getVisibleOptions(this._recentOptions, normalisedInput, true));
        this.initOptions();
        this._changeDetector.detectChanges();
    }
    /**
     * Convert a set of raw options into a filtered list of `TypeaheadVisibleOption` objects.
     * @param options Set of raw options
     * @param filter The filter expression
     * @param isRecentOptions Whether `options` is a set of recent options
     */
    getVisibleOptions(options, filter = '', isRecentOptions = false) {
        if (options) {
            return options
                .filter(option => this.getDisplay(option).toLowerCase().indexOf(filter) >= 0)
                .map(value => ({
                value,
                key: this.getKey(value),
                isRecentOption: isRecentOptions,
            }));
        }
        return [];
    }
    /**
     * Return the index of the given option in the allVisibleOptions array.
     * Returns -1 if the option is not currently visible.
     */
    indexOfVisibleOption(option) {
        if (option) {
            return this.allVisibleOptions.findIndex(el => {
                return el.key === option.key && el.isRecentOption === option.isRecentOption;
            });
        }
        return -1;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TypeaheadComponent, isStandalone: false, selector: "ux-typeahead", inputs: { id: "id", options: "options", filter: "filter", open: "open", display: "display", key: "key", disabledOptions: "disabledOptions", dropDirection: "dropDirection", maxHeight: "maxHeight", multiselectable: "multiselectable", ariaLabel: "ariaLabel", openOnFilterChange: "openOnFilterChange", pageSize: "pageSize", selectFirst: "selectFirst", selectOnEnter: "selectOnEnter", loading: "loading", loadingTemplate: "loadingTemplate", optionTemplate: "optionTemplate", noOptionsTemplate: "noOptionsTemplate", active: "active", recentOptions: "recentOptions", recentOptionsMaxCount: "recentOptionsMaxCount", recentOptionsHeadingTemplate: "recentOptionsHeadingTemplate", optionsHeadingTemplate: "optionsHeadingTemplate" }, outputs: { openChange: "openChange", optionSelected: "optionSelected", highlightedChange: "highlightedChange", highlightedElementChange: "highlightedElementChange", recentOptionsChange: "recentOptionsChange" }, host: { listeners: { "mousedown": "mousedownHandler($event,$event.target)", "mouseup": "mouseupHandler()" }, properties: { "class.open": "open", "style.maxHeight": "maxHeight", "attr.id": "this.id", "class.drop-up": "this.dropUp" } }, providers: [TypeaheadService, PopoverOrientationService], viewQueries: [{ propertyName: "infiniteScroll", first: true, predicate: InfiniteScrollDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n  class=\"ux-typeahead-options\"\n  [uxInfiniteScroll]=\"loadOptionsCallback\"\n  [collection]=\"visibleOptions$ | async\"\n  (collectionChange)=\"visibleOptions$.next($event)\"\n  [enabled]=\"hasBeenOpened && isInfiniteScroll()\"\n  [filter]=\"filter\"\n  [loadOnScroll]=\"true\"\n  [pageSize]=\"pageSize\"\n  [scrollElement]=\"typeaheadElement\"\n  (loading)=\"loading = true\"\n  (loaded)=\"loading = false; onLoadedHighlight($event)\"\n>\n  @if ((visibleRecentOptions$ | async).length > 0) {\n    <div>\n      <ng-container [ngTemplateOutlet]=\"recentOptionsHeadingTemplate\"></ng-container>\n    </div>\n  }\n\n  <!-- Recent options -->\n  @if ((visibleRecentOptions$ | async).length > 0) {\n    <ux-typeahead-options-list\n      class=\"ux-typeahead-recent-options\"\n      [id]=\"id\"\n      [options]=\"visibleRecentOptions$ | async\"\n      [highlighted]=\"highlighted$ | async\"\n      [activeKey]=\"activeKey\"\n      [disabledOptions]=\"_disabledOptions\"\n      [isMultiselectable]=\"multiselectable\"\n      [optionTemplate]=\"optionTemplate || defaultOptionTemplate\"\n      [optionApi]=\"optionApi\"\n      [typeaheadElement]=\"typeaheadElement\"\n      (optionMouseover)=\"highlight($event.option)\"\n      (optionMousedown)=\"optionMousedownHandler($event.event)\"\n      (optionClick)=\"optionClickHandler($event.event, $event.option)\"\n    >\n    </ux-typeahead-options-list>\n  }\n\n  <ng-container [ngTemplateOutlet]=\"optionsHeadingTemplate\"></ng-container>\n\n  <!-- All options -->\n  @if ((visibleOptions$ | async).length > 0) {\n    <ux-typeahead-options-list\n      class=\"ux-typeahead-all-options\"\n      [id]=\"id\"\n      [ariaLabel]=\"ariaLabel\"\n      [startIndex]=\"(visibleRecentOptions$ | async).length\"\n      [options]=\"visibleOptions$ | async\"\n      [highlighted]=\"highlighted$ | async\"\n      [activeKey]=\"activeKey\"\n      [disabledOptions]=\"_disabledOptions\"\n      [isMultiselectable]=\"multiselectable\"\n      [optionTemplate]=\"optionTemplate || defaultOptionTemplate\"\n      [optionApi]=\"optionApi\"\n      [typeaheadElement]=\"typeaheadElement\"\n      (optionMouseover)=\"highlight($event.option)\"\n      (optionMousedown)=\"optionMousedownHandler($event.event)\"\n      (optionClick)=\"optionClickHandler($event.event, $event.option)\"\n    >\n    </ux-typeahead-options-list>\n  }\n\n  <div *uxInfiniteScrollLoading>\n    <ng-container [ngTemplateOutlet]=\"loadingTemplate || defaultLoadingTemplate\"></ng-container>\n  </div>\n\n  @if (isInfiniteScroll() === false && (visibleOptions$ | async).length === 0 && loading) {\n    <div>\n      <ng-container [ngTemplateOutlet]=\"loadingTemplate || defaultLoadingTemplate\"></ng-container>\n    </div>\n  }\n</div>\n@if ((visibleOptions$ | async).length === 0 && !loading) {\n  <div>\n    <ng-container [ngTemplateOutlet]=\"noOptionsTemplate || defaultNoOptionsTemplate\">\n    </ng-container>\n  </div>\n}\n\n<ng-template #defaultLoadingTemplate>\n  <div class=\"ux-typeahead-loading\">\n    <div class=\"spinner spinner-accent spinner-bounce-middle\"></div>\n    <div>Loading...</div>\n  </div>\n</ng-template>\n\n<ng-template #defaultOptionTemplate let-option=\"option\" let-api=\"api\">\n  <span class=\"ux-typeahead-option\" [uxSafeInnerHtml]=\"api.getDisplayHtml(option)\"></span>\n</ng-template>\n\n<ng-template #defaultNoOptionsTemplate>\n  <span class=\"ux-typeahead-no-options\">No results</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: InfiniteScrollDirective, selector: "[uxInfiniteScroll]", inputs: ["uxInfiniteScroll", "collection", "scrollElement", "enabled", "filter", "loadOnInit", "loadOnScroll", "pageSize"], outputs: ["collectionChange", "loading", "loaded", "loadError"], exportAs: ["uxInfiniteScroll"] }, { kind: "directive", type: InfiniteScrollLoadingDirective, selector: "[uxInfiniteScrollLoading]", inputs: ["uxInfiniteScrollLoading"] }, { kind: "directive", type: SafeInnerHtmlDirective, selector: "[uxSafeInnerHtml]", inputs: ["uxSafeInnerHtml"] }, { kind: "component", type: TypeaheadOptionsListComponent, selector: "ux-typeahead-options-list", inputs: ["id", "startIndex", "options", "highlighted", "activeKey", "disabledOptions", "isMultiselectable", "optionTemplate", "optionApi", "typeaheadElement", "ariaLabel"], outputs: ["optionMouseover", "optionMousedown", "optionClick"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-typeahead', providers: [TypeaheadService, PopoverOrientationService], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.open]': 'open',
                        '[style.maxHeight]': 'maxHeight',
                    }, standalone: false, template: "<div\n  class=\"ux-typeahead-options\"\n  [uxInfiniteScroll]=\"loadOptionsCallback\"\n  [collection]=\"visibleOptions$ | async\"\n  (collectionChange)=\"visibleOptions$.next($event)\"\n  [enabled]=\"hasBeenOpened && isInfiniteScroll()\"\n  [filter]=\"filter\"\n  [loadOnScroll]=\"true\"\n  [pageSize]=\"pageSize\"\n  [scrollElement]=\"typeaheadElement\"\n  (loading)=\"loading = true\"\n  (loaded)=\"loading = false; onLoadedHighlight($event)\"\n>\n  @if ((visibleRecentOptions$ | async).length > 0) {\n    <div>\n      <ng-container [ngTemplateOutlet]=\"recentOptionsHeadingTemplate\"></ng-container>\n    </div>\n  }\n\n  <!-- Recent options -->\n  @if ((visibleRecentOptions$ | async).length > 0) {\n    <ux-typeahead-options-list\n      class=\"ux-typeahead-recent-options\"\n      [id]=\"id\"\n      [options]=\"visibleRecentOptions$ | async\"\n      [highlighted]=\"highlighted$ | async\"\n      [activeKey]=\"activeKey\"\n      [disabledOptions]=\"_disabledOptions\"\n      [isMultiselectable]=\"multiselectable\"\n      [optionTemplate]=\"optionTemplate || defaultOptionTemplate\"\n      [optionApi]=\"optionApi\"\n      [typeaheadElement]=\"typeaheadElement\"\n      (optionMouseover)=\"highlight($event.option)\"\n      (optionMousedown)=\"optionMousedownHandler($event.event)\"\n      (optionClick)=\"optionClickHandler($event.event, $event.option)\"\n    >\n    </ux-typeahead-options-list>\n  }\n\n  <ng-container [ngTemplateOutlet]=\"optionsHeadingTemplate\"></ng-container>\n\n  <!-- All options -->\n  @if ((visibleOptions$ | async).length > 0) {\n    <ux-typeahead-options-list\n      class=\"ux-typeahead-all-options\"\n      [id]=\"id\"\n      [ariaLabel]=\"ariaLabel\"\n      [startIndex]=\"(visibleRecentOptions$ | async).length\"\n      [options]=\"visibleOptions$ | async\"\n      [highlighted]=\"highlighted$ | async\"\n      [activeKey]=\"activeKey\"\n      [disabledOptions]=\"_disabledOptions\"\n      [isMultiselectable]=\"multiselectable\"\n      [optionTemplate]=\"optionTemplate || defaultOptionTemplate\"\n      [optionApi]=\"optionApi\"\n      [typeaheadElement]=\"typeaheadElement\"\n      (optionMouseover)=\"highlight($event.option)\"\n      (optionMousedown)=\"optionMousedownHandler($event.event)\"\n      (optionClick)=\"optionClickHandler($event.event, $event.option)\"\n    >\n    </ux-typeahead-options-list>\n  }\n\n  <div *uxInfiniteScrollLoading>\n    <ng-container [ngTemplateOutlet]=\"loadingTemplate || defaultLoadingTemplate\"></ng-container>\n  </div>\n\n  @if (isInfiniteScroll() === false && (visibleOptions$ | async).length === 0 && loading) {\n    <div>\n      <ng-container [ngTemplateOutlet]=\"loadingTemplate || defaultLoadingTemplate\"></ng-container>\n    </div>\n  }\n</div>\n@if ((visibleOptions$ | async).length === 0 && !loading) {\n  <div>\n    <ng-container [ngTemplateOutlet]=\"noOptionsTemplate || defaultNoOptionsTemplate\">\n    </ng-container>\n  </div>\n}\n\n<ng-template #defaultLoadingTemplate>\n  <div class=\"ux-typeahead-loading\">\n    <div class=\"spinner spinner-accent spinner-bounce-middle\"></div>\n    <div>Loading...</div>\n  </div>\n</ng-template>\n\n<ng-template #defaultOptionTemplate let-option=\"option\" let-api=\"api\">\n  <span class=\"ux-typeahead-option\" [uxSafeInnerHtml]=\"api.getDisplayHtml(option)\"></span>\n</ng-template>\n\n<ng-template #defaultNoOptionsTemplate>\n  <span class=\"ux-typeahead-no-options\">No results</span>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { infiniteScroll: [{
                type: ViewChild,
                args: [InfiniteScrollDirective]
            }], id: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.id']
            }], options: [{
                type: Input
            }], filter: [{
                type: Input
            }], open: [{
                type: Input
            }], display: [{
                type: Input
            }], key: [{
                type: Input
            }], disabledOptions: [{
                type: Input
            }], dropDirection: [{
                type: Input
            }], maxHeight: [{
                type: Input
            }], multiselectable: [{
                type: Input
            }], ariaLabel: [{
                type: Input
            }], openOnFilterChange: [{
                type: Input
            }], pageSize: [{
                type: Input
            }], selectFirst: [{
                type: Input
            }], selectOnEnter: [{
                type: Input
            }], loading: [{
                type: Input
            }], loadingTemplate: [{
                type: Input
            }], optionTemplate: [{
                type: Input
            }], noOptionsTemplate: [{
                type: Input
            }], active: [{
                type: Input
            }], recentOptions: [{
                type: Input
            }], recentOptionsMaxCount: [{
                type: Input
            }], recentOptionsHeadingTemplate: [{
                type: Input
            }], optionsHeadingTemplate: [{
                type: Input
            }], openChange: [{
                type: Output
            }], optionSelected: [{
                type: Output
            }], highlightedChange: [{
                type: Output
            }], highlightedElementChange: [{
                type: Output
            }], recentOptionsChange: [{
                type: Output
            }], dropUp: [{
                type: HostBinding,
                args: ['class.drop-up']
            }], mousedownHandler: [{
                type: HostListener,
                args: ['mousedown', ['$event', '$event.target']]
            }], mouseupHandler: [{
                type: HostListener,
                args: ['mouseup']
            }] } });

class ScrollIntoViewDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        /** Allow a condition around whether or not this should scroll into view */
        this.uxScrollIntoView = true;
        /** Allow user to provide the browser supported options */
        this.scrollIntoViewOptions = true;
    }
    ngAfterViewInit() {
        if (this.uxScrollIntoView) {
            this._elementRef.nativeElement.scrollIntoView(this.scrollIntoViewOptions);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ScrollIntoViewDirective, isStandalone: false, selector: "[uxScrollIntoView]", inputs: { uxScrollIntoView: "uxScrollIntoView", scrollIntoViewOptions: "scrollIntoViewOptions" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollIntoViewDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxScrollIntoView]',
                    standalone: false,
                }]
        }], propDecorators: { uxScrollIntoView: [{
                type: Input
            }], scrollIntoViewOptions: [{
                type: Input
            }] } });

class ScrollModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ScrollModule, declarations: [ScrollIntoViewIfDirective, ScrollIntoViewDirective], exports: [ScrollIntoViewIfDirective, ScrollIntoViewDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ScrollModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [ScrollIntoViewIfDirective, ScrollIntoViewDirective],
                    declarations: [ScrollIntoViewIfDirective, ScrollIntoViewDirective],
                }]
        }] });

class TypeaheadModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadModule, declarations: [TypeaheadComponent, TypeaheadHighlightDirective, TypeaheadOptionsListComponent], imports: [CommonModule, InfiniteScrollModule, ResizeModule, ScrollModule, SafeInnerHtmlDirective], exports: [TypeaheadComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadModule, providers: [PopoverOrientationService], imports: [CommonModule, InfiniteScrollModule, ResizeModule, ScrollModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TypeaheadModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, InfiniteScrollModule, ResizeModule, ScrollModule, SafeInnerHtmlDirective],
                    exports: [TypeaheadComponent],
                    declarations: [TypeaheadComponent, TypeaheadHighlightDirective, TypeaheadOptionsListComponent],
                    providers: [PopoverOrientationService],
                }]
        }] });

class FacetTypeaheadListItemComponent {
    constructor() {
        this.selected = false;
        this.simplified = false;
        this.tabbable = false;
        this.itemFocus = new EventEmitter();
        this.selectedChange = new EventEmitter();
    }
    get disabled() {
        return this.facet && this.facet.disabled;
    }
    getLabel() {
        return this.facet ? this.facet.title : null;
    }
    focus() {
        this.option.nativeElement.focus();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetTypeaheadListItemComponent, isStandalone: false, selector: "ux-facet-typeahead-list-item", inputs: { facet: "facet", selected: "selected", simplified: "simplified", tabbable: "tabbable" }, outputs: { itemFocus: "itemFocus", selectedChange: "selectedChange" }, viewQueries: [{ propertyName: "option", first: true, predicate: ["option"], descendants: true, static: true }], ngImport: i0, template: "<div\n  #option\n  uxFocusIndicator\n  role=\"option\"\n  class=\"facet-typeahead-list-selected-option\"\n  [attr.aria-checked]=\"selected\"\n  [tabindex]=\"tabbable ? 0 : -1\"\n  (focus)=\"itemFocus.emit()\"\n  (click)=\"selectedChange.emit(facet)\"\n  (keydown.enter)=\"selectedChange.emit(facet)\"\n  (keydown.space)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  (keydown.spacebar)=\"selectedChange.emit(facet); $event.preventDefault()\"\n>\n  <ux-checkbox\n    [clickable]=\"false\"\n    [value]=\"selected\"\n    [simplified]=\"simplified\"\n    [tabindex]=\"-1\"\n    [disabled]=\"disabled\"\n  >\n    <span class=\"facet-typeahead-list-selected-option-title\">{{ facet?.title }}</span>\n    @if (facet?.count !== undefined) {\n      <span class=\"facet-typeahead-list-selected-option-count\">({{ facet?.count }})</span>\n    }\n  </ux-checkbox>\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: CheckboxComponent, selector: "ux-checkbox", inputs: ["id", "name", "value", "required", "tabindex", "clickable", "simplified", "indeterminateValue", "disabled", "aria-label", "aria-labelledby"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadListItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-typeahead-list-item', changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, standalone: false, template: "<div\n  #option\n  uxFocusIndicator\n  role=\"option\"\n  class=\"facet-typeahead-list-selected-option\"\n  [attr.aria-checked]=\"selected\"\n  [tabindex]=\"tabbable ? 0 : -1\"\n  (focus)=\"itemFocus.emit()\"\n  (click)=\"selectedChange.emit(facet)\"\n  (keydown.enter)=\"selectedChange.emit(facet)\"\n  (keydown.space)=\"selectedChange.emit(facet); $event.preventDefault()\"\n  (keydown.spacebar)=\"selectedChange.emit(facet); $event.preventDefault()\"\n>\n  <ux-checkbox\n    [clickable]=\"false\"\n    [value]=\"selected\"\n    [simplified]=\"simplified\"\n    [tabindex]=\"-1\"\n    [disabled]=\"disabled\"\n  >\n    <span class=\"facet-typeahead-list-selected-option-title\">{{ facet?.title }}</span>\n    @if (facet?.count !== undefined) {\n      <span class=\"facet-typeahead-list-selected-option-count\">({{ facet?.count }})</span>\n    }\n  </ux-checkbox>\n</div>\n" }]
        }], propDecorators: { facet: [{
                type: Input
            }], selected: [{
                type: Input
            }], simplified: [{
                type: Input
            }], tabbable: [{
                type: Input
            }], itemFocus: [{
                type: Output
            }], selectedChange: [{
                type: Output
            }], option: [{
                type: ViewChild,
                args: ['option', { static: true }]
            }] } });

let uniqueId$8 = 1;
class FacetTypeaheadListComponent {
    constructor() {
        this.typeaheadKeyService = inject(TypeaheadKeyService);
        this.facetService = inject(FacetService);
        this._announcer = inject(LiveAnnouncer);
        /** Defines whether or not the Facet Typeahead List should be initially expanded or not. */
        this.expanded = true;
        /** Defines a list of facets which will be displayed above the typeahead to allow the user to quickly select some facets. */
        this.suggestions = [];
        /** Defines whether or not the checkboxes displayed alongside suggestions will appear in simplified form. */
        this.simplified = true;
        /** Emits the current query when the value of the input field changes. */
        this.queryChange = new EventEmitter();
        /**
         * This will be triggered when a facet is selected, deselected or all facets are deselected.
         * The event will be an instance of either `FacetSelect`, `FacetDeselect` or `FacetDeselectAll` and
         * will contain the facet being selected or deselected in a `facet` property (deselect all will not contain affected facets).
         */
        this.events = new Subject();
        /** If two-way binding is used this array will get updated any time the selected facets change. */
        this.selectedChange = new EventEmitter();
        this.query$ = new BehaviorSubject('');
        this.loading = false;
        this.activeIndex = 0;
        this.typeaheadId = `ux-facet-typeahead-${uniqueId$8++}`;
        this.typeaheadOpen = false;
        this.typeaheadOptions = [];
        this._facets = [];
        this._selected = [];
        this._onDestroy = new Subject();
        this._config = { placeholder: '', maxResults: 50, minCharacters: 1 };
    }
    /** This will allow you to define an initial set of selected facets. */
    set selected(selection) {
        if (Array.isArray(selection)) {
            selection.forEach(facet => this.facetService.select(facet));
        }
    }
    /** Defines the query displayed in the input field. */
    set query(query) {
        if (query !== this.query$.value) {
            this.query$.next(query);
        }
    }
    /**
     * Allows configuration of the typeahead control. The possible values are:
     * - `placeholder` - **string** - Sets the placeholder of the typeahead.
     * - `minCharacters` - **number** - Defines the minimum number of characters that are required before results will be shown. **Default**: `1`.
     * - `maxResults` - **number** - Sets the maximum number of results to display. **Default**: `50`.
     * - `delay` - **number** - Defines the number of milliseconds to wait before the results are filtered. **Default**: `0`.
     */
    set typeaheadConfig(config) {
        this._config = { placeholder: '', maxResults: 50, minCharacters: 1, ...config };
    }
    get typeaheadConfig() {
        return this._config;
    }
    ngOnInit() {
        this.facetService.events$.pipe(takeUntil(this._onDestroy)).subscribe(event => {
            // deselect all events should always be emitted
            if (event instanceof FacetDeselectAll) {
                this.events.next(event);
                this._selected = [];
                this.selectedChange.next(this._selected);
            }
            // if deselected remove the facet from our internal list of selected facets
            if (event instanceof FacetDeselect && this.isOwnFacet(event.facet)) {
                if (this._selected.indexOf(event.facet) !== -1) {
                    this.events.next(event);
                    this._selected = this._selected.filter(_facet => _facet !== event.facet);
                    this.selectedChange.next(this._selected);
                }
            }
            // selection and deselection events should only be emitted when the facet belongs to this component
            if (event instanceof FacetSelect && this.isOwnFacet(event.facet)) {
                if (this._selected.indexOf(event.facet) === -1) {
                    this.events.next(event);
                    this._selected = [...this._selected, event.facet];
                    this.selectedChange.next(this._selected);
                }
            }
        });
        // store the original list of all possible facets
        this.getFacetObservable()
            .pipe(first())
            .subscribe(facets => (this._facets = facets));
    }
    ngAfterViewInit() {
        // emit the latest query whenever it changes
        this.query$
            .pipe(takeUntil(this._onDestroy), distinctUntilChanged())
            .subscribe(query => this.queryChange.emit(query));
        // set up search query subscription
        this.query$
            .pipe(takeUntil(this._onDestroy), tick(), tap(() => {
            this.loading = true;
            this.typeaheadOptions = [];
        }), mergeMap(() => this.getFacetObservable().pipe(map(facets => {
            return facets
                .filter(facet => !facet.disabled &&
                !this.facetService.facets$.value.find(selectedFacet => selectedFacet === facet))
                .slice(0, this._config.maxResults);
        }))))
            .subscribe(facets => {
            this.loading = false;
            this.typeaheadOptions = facets;
        });
        this._focusKeyManager = new FocusKeyManager(this.options).withVerticalOrientation();
        this._focusKeyManager.change
            .pipe(takeUntil(this._onDestroy))
            .subscribe(index => (this.activeIndex = index));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onKeydown(event) {
        this._focusKeyManager.onKeydown(event);
    }
    onFocus(index) {
        if (this._focusKeyManager.activeItemIndex === -1) {
            this._focusKeyManager.setActiveItem(index);
        }
    }
    toggleFacet(index, facet) {
        // toggle selection
        this.facetService.isSelected(facet)
            ? this.facetService.deselect(facet)
            : this.facetService.select(facet);
        // focus the correct item
        this._focusKeyManager.setActiveItem(index);
    }
    /** Only show typeahead if we have enough characters */
    updateTypeahead(query = '') {
        this.typeaheadOpen = query.length >= this._config.minCharacters;
    }
    getFacetObservable() {
        return isObservable(this.facets) ? this.facets : of(this.facets);
    }
    select(event) {
        // check to make sure that the item is not currently selected
        if (this.facetService.isSelected(event.option)) {
            return;
        }
        // select the facet
        this.facetService.select(event.option);
        // clear the typeahead
        this.query$.next('');
        // announce the selected facet
        this._announcer.announce(`${event.option.title} selected.`);
    }
    isOwnFacet(facet) {
        return this._facets ? this._facets.indexOf(facet) !== -1 : false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FacetTypeaheadListComponent, isStandalone: false, selector: "ux-facet-typeahead-list", inputs: { selected: "selected", facets: "facets", header: "header", expanded: "expanded", suggestions: "suggestions", simplified: "simplified", query: "query", typeaheadConfig: "typeaheadConfig" }, outputs: { queryChange: "queryChange", events: "events", selectedChange: "selectedChange" }, viewQueries: [{ propertyName: "options", predicate: FacetTypeaheadListItemComponent, descendants: true }], ngImport: i0, template: "<ux-facet-header [header]=\"header\" [(expanded)]=\"expanded\"></ux-facet-header>\n\n@if (expanded) {\n  <div class=\"facet-typeahead-list-container\" role=\"listbox\">\n    @if (suggestions?.length > 0) {\n      <div class=\"facet-typeahead-list-selected-container\" tabindex=\"-1\">\n        @for (facet of suggestions; track facet; let index = $index) {\n          <ux-facet-typeahead-list-item\n            [facet]=\"facet\"\n            [tabbable]=\"activeIndex === index\"\n            [selected]=\"facetService.isSelected(facet)\"\n            (selectedChange)=\"toggleFacet(index, facet)\"\n            (keydown)=\"onKeydown($event)\"\n            (itemFocus)=\"onFocus(index)\"\n          >\n          </ux-facet-typeahead-list-item>\n        }\n      </div>\n    }\n    <div class=\"facet-typeahead-list-control\">\n      <!-- Create Typeahead Control -->\n      <input\n        type=\"text\"\n        class=\"form-control\"\n        [placeholder]=\"typeaheadConfig?.placeholder\"\n        [attr.aria-activedescendant]=\"highlightedElement?.id\"\n        aria-autocomplete=\"list\"\n        aria-multiline=\"false\"\n        [attr.aria-controls]=\"typeaheadId\"\n        [ngModel]=\"query$ | async\"\n        (ngModelChange)=\"query$.next($event); updateTypeahead($event)\"\n        (keydown)=\"typeaheadKeyService.handleKey($event, typeahead)\"\n        (blur)=\"typeaheadOpen = false\"\n      />\n      <ux-typeahead\n        #typeahead\n        [id]=\"typeaheadId\"\n        [(open)]=\"typeaheadOpen\"\n        [loading]=\"loading\"\n        display=\"title\"\n        [options]=\"typeaheadOptions\"\n        [optionTemplate]=\"facetOptionTemplate\"\n        [selectOnEnter]=\"true\"\n        (optionSelected)=\"select($event)\"\n        (highlightedElementChange)=\"highlightedElement = $event\"\n      >\n      </ux-typeahead>\n    </div>\n  </div>\n}\n\n<ng-template #facetOptionTemplate let-option=\"option\" let-api=\"api\">\n  <p class=\"facet-typeahead-list-option\" [attr.aria-label]=\"option.title\">\n    <span [uxSafeInnerHtml]=\"option.title | facetTypeaheadHighlight: (query$ | async)\"></span>\n    @if (option.count) {\n      <span class=\"facet-typeahead-list-option-count\"> ({{ option.count }}) </span>\n    }\n  </p>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i0.forwardRef(() => i2$2.DefaultValueAccessor), selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i0.forwardRef(() => i2$2.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i2$2.NgModel), selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i0.forwardRef(() => TypeaheadComponent), selector: "ux-typeahead", inputs: ["id", "options", "filter", "open", "display", "key", "disabledOptions", "dropDirection", "maxHeight", "multiselectable", "ariaLabel", "openOnFilterChange", "pageSize", "selectFirst", "selectOnEnter", "loading", "loadingTemplate", "optionTemplate", "noOptionsTemplate", "active", "recentOptions", "recentOptionsMaxCount", "recentOptionsHeadingTemplate", "optionsHeadingTemplate"], outputs: ["openChange", "optionSelected", "highlightedChange", "highlightedElementChange", "recentOptionsChange"] }, { kind: "directive", type: i0.forwardRef(() => SafeInnerHtmlDirective), selector: "[uxSafeInnerHtml]", inputs: ["uxSafeInnerHtml"] }, { kind: "component", type: i0.forwardRef(() => FacetHeaderComponent), selector: "ux-facet-header", inputs: ["header", "canExpand", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: i0.forwardRef(() => FacetTypeaheadListItemComponent), selector: "ux-facet-typeahead-list-item", inputs: ["facet", "selected", "simplified", "tabbable"], outputs: ["itemFocus", "selectedChange"] }, { kind: "pipe", type: i0.forwardRef(() => i1.AsyncPipe), name: "async" }, { kind: "pipe", type: i0.forwardRef(() => FacetTypeaheadHighlight), name: "facetTypeaheadHighlight" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadListComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-facet-typeahead-list', standalone: false, template: "<ux-facet-header [header]=\"header\" [(expanded)]=\"expanded\"></ux-facet-header>\n\n@if (expanded) {\n  <div class=\"facet-typeahead-list-container\" role=\"listbox\">\n    @if (suggestions?.length > 0) {\n      <div class=\"facet-typeahead-list-selected-container\" tabindex=\"-1\">\n        @for (facet of suggestions; track facet; let index = $index) {\n          <ux-facet-typeahead-list-item\n            [facet]=\"facet\"\n            [tabbable]=\"activeIndex === index\"\n            [selected]=\"facetService.isSelected(facet)\"\n            (selectedChange)=\"toggleFacet(index, facet)\"\n            (keydown)=\"onKeydown($event)\"\n            (itemFocus)=\"onFocus(index)\"\n          >\n          </ux-facet-typeahead-list-item>\n        }\n      </div>\n    }\n    <div class=\"facet-typeahead-list-control\">\n      <!-- Create Typeahead Control -->\n      <input\n        type=\"text\"\n        class=\"form-control\"\n        [placeholder]=\"typeaheadConfig?.placeholder\"\n        [attr.aria-activedescendant]=\"highlightedElement?.id\"\n        aria-autocomplete=\"list\"\n        aria-multiline=\"false\"\n        [attr.aria-controls]=\"typeaheadId\"\n        [ngModel]=\"query$ | async\"\n        (ngModelChange)=\"query$.next($event); updateTypeahead($event)\"\n        (keydown)=\"typeaheadKeyService.handleKey($event, typeahead)\"\n        (blur)=\"typeaheadOpen = false\"\n      />\n      <ux-typeahead\n        #typeahead\n        [id]=\"typeaheadId\"\n        [(open)]=\"typeaheadOpen\"\n        [loading]=\"loading\"\n        display=\"title\"\n        [options]=\"typeaheadOptions\"\n        [optionTemplate]=\"facetOptionTemplate\"\n        [selectOnEnter]=\"true\"\n        (optionSelected)=\"select($event)\"\n        (highlightedElementChange)=\"highlightedElement = $event\"\n      >\n      </ux-typeahead>\n    </div>\n  </div>\n}\n\n<ng-template #facetOptionTemplate let-option=\"option\" let-api=\"api\">\n  <p class=\"facet-typeahead-list-option\" [attr.aria-label]=\"option.title\">\n    <span [uxSafeInnerHtml]=\"option.title | facetTypeaheadHighlight: (query$ | async)\"></span>\n    @if (option.count) {\n      <span class=\"facet-typeahead-list-option-count\"> ({{ option.count }}) </span>\n    }\n  </p>\n</ng-template>\n" }]
        }], propDecorators: { selected: [{
                type: Input
            }], facets: [{
                type: Input
            }], header: [{
                type: Input
            }], expanded: [{
                type: Input
            }], suggestions: [{
                type: Input
            }], simplified: [{
                type: Input
            }], query: [{
                type: Input
            }], queryChange: [{
                type: Output
            }], events: [{
                type: Output
            }], selectedChange: [{
                type: Output
            }], typeaheadConfig: [{
                type: Input
            }], options: [{
                type: ViewChildren,
                args: [FacetTypeaheadListItemComponent]
            }] } });
class FacetTypeaheadHighlight {
    transform(value, searchQuery) {
        const regex = new RegExp(searchQuery, 'i');
        return value.replace(regex, `<b class="facet-typeahead-highlighted">${value.match(regex)}</b>`);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadHighlight, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadHighlight, isStandalone: false, name: "facetTypeaheadHighlight" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetTypeaheadHighlight, decorators: [{
            type: Pipe,
            args: [{
                    name: 'facetTypeaheadHighlight',
                    standalone: false,
                }]
        }] });

class ReorderableHandleDirective extends CdkDragHandle {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableHandleDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ReorderableHandleDirective, isStandalone: false, selector: "[uxReorderableHandle]", providers: [{ provide: CDK_DRAG_HANDLE, useExisting: ReorderableHandleDirective }], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableHandleDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxReorderableHandle]',
                    providers: [{ provide: CDK_DRAG_HANDLE, useExisting: ReorderableHandleDirective }],
                    standalone: false,
                }]
        }] });

class ReorderableModelDirective extends CdkDrag {
    constructor() {
        super(...arguments);
        /** Apply the dragula preview class to avoid backwards compatibility issues */
        this.previewClass = 'gu-mirror';
        /** Preserve the column widths */
        this._widths = new Map();
        /** Unsubscribe on destroy */
        this._destroy$ = new Subject();
    }
    // allow the user to specify a model for the item - allows use with ngFor
    set uxReorderableModel(model) {
        this.data = model;
    }
    ngOnInit() {
        // cast the drop container as we have replaced it with our directive
        const dropContainer = this.dropContainer;
        this._dragRef.beforeStarted
            .pipe(takeUntil(this._destroy$))
            .subscribe(() => this.captureTableCellStyles());
        this.started.pipe(takeUntil(this._destroy$)).subscribe(() => {
            dropContainer.reorderStart.emit({
                element: this.element.nativeElement,
                model: this.data,
            });
            this.setTableCellWidths();
        });
        this.dropped.pipe(takeUntil(this._destroy$)).subscribe((dragEvent) => {
            if (dragEvent.container === dragEvent.previousContainer &&
                dragEvent.currentIndex === dragEvent.previousIndex) {
                dropContainer.reorderCancel.emit({
                    element: this.element.nativeElement,
                    model: this.data,
                });
            }
            else {
                dropContainer.reorderEnd.emit({
                    element: this.element.nativeElement,
                    model: this.data,
                });
            }
        });
    }
    ngOnDestroy() {
        super.ngOnDestroy();
        this._destroy$.next();
        this._destroy$.complete();
    }
    /**
     * When table elements are being dragged they are hoisted to the document level
     * when dragged they lose the sizing and spacing provided when they are used inside a table.
     *
     * This function will capture the styles so we can inline them to preseve the styling.
     */
    captureTableCellStyles() {
        // if it is not a table row then skip this
        if (this.element.nativeElement.tagName !== 'TR') {
            return;
        }
        // iterate each cell and store the styles and enforce the width by using a minWidth
        Array.from(this.element.nativeElement.children).forEach((cell, index) => {
            this._widths.set(index, getComputedStyle(cell).getPropertyValue('width'));
        });
    }
    setTableCellWidths() {
        // if it is not a table row then skip this
        if (this.element.nativeElement.tagName !== 'TR') {
            return;
        }
        // access the preview element, this is private but there is no public way to access
        // it and the UI is incorrect when draggingtable rows without this.
        const previewElement = this._dragRef._preview;
        // re-apply all the stored styles
        Array.from(previewElement.children).forEach((cell, index) => (cell.style.minWidth = this._widths.get(index)));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModelDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ReorderableModelDirective, isStandalone: false, selector: "[uxReorderableModel]", inputs: { uxReorderableModel: "uxReorderableModel" }, host: { properties: { "class.ux-reorderable-moving": "_dragRef.isDragging()" } }, providers: [{ provide: CDK_DRAG_PARENT, useExisting: ReorderableModelDirective }], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModelDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxReorderableModel]',
                    providers: [{ provide: CDK_DRAG_PARENT, useExisting: ReorderableModelDirective }],
                    host: {
                        '[class.ux-reorderable-moving]': '_dragRef.isDragging()',
                    },
                    standalone: false,
                }]
        }], propDecorators: { uxReorderableModel: [{
                type: Input
            }] } });

class ReorderableDirective extends CdkDropList {
    constructor() {
        super(...arguments);
        /**
         * This event will be triggered when the order changes and will contain an updated dataset containing the items
         * in their current order. This should be used when the list of items is generated using ngFor to ensure the
         * data remains in the same order for both the `uxReorderable` and `ngFor` directives.
         */
        this.reorderableModelChange = new EventEmitter();
        /** This event is triggered when a user begins dragging an item. The event will contain the element being moved. */
        this.reorderStart = new EventEmitter();
        /** This event is triggered when the item being dragged is returned to the same location as it began. The event will contain the element that was being moved. */
        this.reorderCancel = new EventEmitter();
        /** This event is triggered when a user has relocated an item. The event will contain the element that was moved. */
        this.reorderEnd = new EventEmitter();
        this._destroy$ = new Subject();
    }
    /**
     * The name of the reorderable group which this container belongs to `uxReorderable` elements which belong to
     * the same group can have items dragged between them. Only required if multiple drop containers are being created.
     */
    set reorderableGroup(group) {
        const groups = ReorderableDirective._groups$.value;
        this._reorderableGroup = group;
        ReorderableDirective._groups$.next({
            ...groups,
            [group]: [...(groups[group] ?? []), this.id],
        });
    }
    get reorderableGroup() {
        return this._reorderableGroup;
    }
    /** Determines if reordering is disabled. */
    set reorderingDisabled(isDisabled) {
        this.disabled = isDisabled;
    }
    /** Store all the group ids so we can identify which lists can interact */
    static { this._groups$ = new BehaviorSubject({}); }
    ngOnInit() {
        this.dropped.pipe(takeUntil(this._destroy$)).subscribe((dropEvent) => {
            if (dropEvent.previousContainer === dropEvent.container) {
                moveItemInArray(this.reorderableModel, dropEvent.previousIndex, dropEvent.currentIndex);
            }
            else {
                const previousContainer = dropEvent.previousContainer;
                const currentContainer = dropEvent.container;
                transferArrayItem(previousContainer.reorderableModel, currentContainer.reorderableModel, dropEvent.previousIndex, dropEvent.currentIndex);
            }
            this.reorderableModelChange.emit(this.reorderableModel);
        });
        // if the available groups are updated we need to update the lists we can drag to
        ReorderableDirective._groups$
            .pipe(takeUntil(this._destroy$))
            .subscribe(groups => (this.connectedTo = (groups[this.reorderableGroup] ?? []).filter(group => group !== this.id)));
    }
    ngOnDestroy() {
        this._destroy$.next();
        this._destroy$.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ReorderableDirective, isStandalone: false, selector: "[uxReorderable]", inputs: { reorderableModel: "reorderableModel", reorderableGroup: "reorderableGroup", reorderingDisabled: "reorderingDisabled" }, outputs: { reorderableModelChange: "reorderableModelChange", reorderStart: "reorderStart", reorderCancel: "reorderCancel", reorderEnd: "reorderEnd" }, providers: [{ provide: CDK_DROP_LIST, useExisting: ReorderableDirective }], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxReorderable]',
                    providers: [{ provide: CDK_DROP_LIST, useExisting: ReorderableDirective }],
                    standalone: false,
                }]
        }], propDecorators: { reorderableModel: [{
                type: Input
            }], reorderableGroup: [{
                type: Input
            }], reorderingDisabled: [{
                type: Input
            }], reorderableModelChange: [{
                type: Output
            }], reorderStart: [{
                type: Output
            }], reorderCancel: [{
                type: Output
            }], reorderEnd: [{
                type: Output
            }] } });

class ReorderableModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModule, declarations: [ReorderableDirective, ReorderableHandleDirective, ReorderableModelDirective], imports: [CommonModule, DragDropModule], exports: [ReorderableDirective, ReorderableHandleDirective, ReorderableModelDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModule, imports: [CommonModule, DragDropModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ReorderableModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, DragDropModule],
                    declarations: [ReorderableDirective, ReorderableHandleDirective, ReorderableModelDirective],
                    exports: [ReorderableDirective, ReorderableHandleDirective, ReorderableModelDirective],
                }]
        }] });

const DECLARATIONS$8 = [
    FacetContainerComponent,
    FacetHeaderComponent,
    FacetCheckListComponent,
    FacetCheckListItemComponent,
    FacetTypeaheadListComponent,
    FacetTypeaheadListItemComponent,
    FacetTypeaheadHighlight,
    FacetClearButtonDirective,
];
class FacetsModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FacetsModule, declarations: [FacetContainerComponent,
            FacetHeaderComponent,
            FacetCheckListComponent,
            FacetCheckListItemComponent,
            FacetTypeaheadListComponent,
            FacetTypeaheadListItemComponent,
            FacetTypeaheadHighlight,
            FacetClearButtonDirective], imports: [A11yModule,
            AccessibilityModule,
            CheckboxModule,
            CommonModule,
            FormsModule,
            IconModule,
            ReorderableModule,
            TooltipModule,
            TypeaheadModule,
            DragDropModule,
            SafeInnerHtmlDirective], exports: [FacetContainerComponent,
            FacetHeaderComponent,
            FacetCheckListComponent,
            FacetCheckListItemComponent,
            FacetTypeaheadListComponent,
            FacetTypeaheadListItemComponent,
            FacetTypeaheadHighlight,
            FacetClearButtonDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetsModule, imports: [A11yModule,
            AccessibilityModule,
            CheckboxModule,
            CommonModule,
            FormsModule,
            IconModule,
            ReorderableModule,
            TooltipModule,
            TypeaheadModule,
            DragDropModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FacetsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        CheckboxModule,
                        CommonModule,
                        FormsModule,
                        IconModule,
                        ReorderableModule,
                        TooltipModule,
                        TypeaheadModule,
                        DragDropModule,
                        SafeInnerHtmlDirective,
                    ],
                    exports: DECLARATIONS$8,
                    declarations: DECLARATIONS$8,
                }]
        }] });

class Facet {
    constructor(title, data = {}, count, disabled = false, id) {
        this.title = title;
        this.data = data;
        this.count = count;
        this.disabled = disabled;
        this.id = id;
    }
}

class FilterAddEvent {
    constructor(filter) {
        this.filter = filter;
    }
}

class FilterRemoveAllEvent {
}

class FilterRemoveEvent {
    constructor(filter) {
        this.filter = filter;
    }
}

class FilterService {
    constructor() {
        /** The list of active filters */
        this.filters$ = new BehaviorSubject([]);
        /** Emit all the events when they occur */
        this.events$ = new Subject();
    }
    add(filter) {
        // if the filter is already selected or it is the intial filter then do nothing
        if (this.isSelected(filter) || filter.initial) {
            return;
        }
        // update the list of active filters
        this.filters$.next([...this.filters$.value, filter]);
        // emit the event
        this.events$.next(new FilterAddEvent(filter));
    }
    remove(filter) {
        // if the filter is not selected then do nothing
        if (!this.isSelected(filter)) {
            return;
        }
        // update the list of active filters
        this.filters$.next(this.filters$.value.filter(_filter => _filter !== filter));
        // emit the event
        this.events$.next(new FilterRemoveEvent(filter));
    }
    removeAll() {
        // empty the list of active filters
        this.filters$.next([]);
        // emit the event
        this.events$.next(new FilterRemoveAllEvent());
    }
    isSelected(filter) {
        return this.filters$.value.indexOf(filter) > -1;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterService, decorators: [{
            type: Injectable
        }] });

class FilterContainerComponent {
    /** Allow filters to set from outside the component */
    set filters(filters) {
        this.filterService.filters$.next(filters);
    }
    constructor() {
        this.filterService = inject(FilterService);
        /** Defines the aria-label for the clear all button */
        this.clearAriaLabel = 'Clear All Filters';
        /** Emit when the active filters chance */
        this.filtersChange = new EventEmitter();
        /** Emit when a specific event occurs */
        this.events = new EventEmitter();
        /** Unsubscribe from the subscriptions on destroy */
        this._onDestroy = new Subject();
        // subscribe to changes to the active filters
        this.filterService.filters$
            .pipe(distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(filters => this.filtersChange.emit(filters));
        // relay any events to the event emitter
        this.filterService.events$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(event => this.events.emit(event));
    }
    /** Destroy all subscriptions */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FilterContainerComponent, isStandalone: false, selector: "ux-filter-container", inputs: { filters: "filters", clearTooltip: "clearTooltip", clearAriaLabel: "clearAriaLabel" }, outputs: { filtersChange: "filtersChange", events: "events" }, providers: [FilterService], queries: [{ propertyName: "clearAllTemplate", first: true, predicate: ["clearAllTemplate"], descendants: true }], ngImport: i0, template: "<ng-content></ng-content>\n\n<!-- Add a Clear Button -->\n@if ((filterService.filters$ | async).length > 0) {\n  <button\n    type=\"button\"\n    class=\"btn btn-link btn-secondary m-l-xs\"\n    [class.table-filter-clear]=\"clearAllTemplate\"\n    [class.btn-icon]=\"!clearAllTemplate\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"clearAriaLabel\"\n    [uxTooltip]=\"clearTooltip || 'Clear All'\"\n    (click)=\"filterService.removeAll()\"\n  >\n    <ng-container [ngTemplateOutlet]=\"clearAllTemplate || defaultClearAllTemplate\"> </ng-container>\n  </button>\n}\n\n<ng-template #defaultClearAllTemplate>\n  <svg\n    class=\"filter-selected-clear-graphic\"\n    width=\"100%\"\n    viewBox=\"0 0 19 12\"\n    shape-rendering=\"geometricPrecision\"\n  >\n    <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n    <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n    <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n    <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n    <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n  </svg>\n</ng-template>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterContainerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-filter-container', providers: [FilterService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-content></ng-content>\n\n<!-- Add a Clear Button -->\n@if ((filterService.filters$ | async).length > 0) {\n  <button\n    type=\"button\"\n    class=\"btn btn-link btn-secondary m-l-xs\"\n    [class.table-filter-clear]=\"clearAllTemplate\"\n    [class.btn-icon]=\"!clearAllTemplate\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"clearAriaLabel\"\n    [uxTooltip]=\"clearTooltip || 'Clear All'\"\n    (click)=\"filterService.removeAll()\"\n  >\n    <ng-container [ngTemplateOutlet]=\"clearAllTemplate || defaultClearAllTemplate\"> </ng-container>\n  </button>\n}\n\n<ng-template #defaultClearAllTemplate>\n  <svg\n    class=\"filter-selected-clear-graphic\"\n    width=\"100%\"\n    viewBox=\"0 0 19 12\"\n    shape-rendering=\"geometricPrecision\"\n  >\n    <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n    <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n    <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n    <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n    <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n  </svg>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { filters: [{
                type: Input
            }], clearTooltip: [{
                type: Input
            }], clearAriaLabel: [{
                type: Input
            }], filtersChange: [{
                type: Output
            }], events: [{
                type: Output
            }], clearAllTemplate: [{
                type: ContentChild,
                args: ['clearAllTemplate', { static: false }]
            }] } });

let uniqueId$7 = 0;
class FilterDropdownComponent {
    /** Defined the closeOnBlur state for the ux-menu trigger */
    set closeOnBlur(value) {
        this._closeOnBlur = coerceBooleanProperty(value);
    }
    get closeOnBlur() {
        return this._closeOnBlur;
    }
    get filterId() {
        return this.id ?? `ux-filter-dropdown-${this._uniqueId}`;
    }
    constructor() {
        this._filterService = inject(FilterService);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Store the unique id so we only increment the counter once per instance */
        this._uniqueId = uniqueId$7++;
        /** The list of items to display in the dropdown */
        this.filters = [];
        /** Emit when the filter menu is closed */
        this.closed = new EventEmitter();
        this._onDestroy = new Subject();
        this._closeOnBlur = false;
        this._filterService.events$
            .pipe(filter(event => event instanceof FilterRemoveAllEvent), takeUntil(this._onDestroy))
            .subscribe(() => this.removeFilter());
        // ensure that the current selected filter is still selected when the active filters change
        this._filterService.filters$.pipe(takeUntil(this._onDestroy)).subscribe(filters => {
            if (this.selected && filters.indexOf(this.selected) === -1) {
                this.removeFilter();
            }
        });
    }
    ngOnInit() {
        this.selected = this.initial;
        // check to see if any of the filters have been preselected or changes to selected filters
        this._filterService.filters$.pipe(takeUntil(this._onDestroy)).subscribe(filters => {
            filters.forEach(filter => {
                if (this.filters.indexOf(filter) !== -1) {
                    this.selected = filter;
                }
            });
            this._changeDetector.markForCheck();
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    selectFilter(filter, event) {
        this.removeFilter();
        this.selected = filter;
        this._filterService.add(this.selected);
        event.stopPropagation();
        event.preventDefault();
    }
    removeFilter() {
        this._filterService.remove(this.selected);
        this.selected = this.initial;
        this._changeDetector.markForCheck();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FilterDropdownComponent, isStandalone: false, selector: "ux-filter-dropdown", inputs: { id: "id", filters: "filters", initial: "initial", closeOnBlur: "closeOnBlur" }, outputs: { closed: "closed" }, ngImport: i0, template: "<div class=\"btn-group\">\n  <button\n    type=\"button\"\n    class=\"filter-dropdown btn dropdown-toggle\"\n    [id]=\"filterId + '-trigger'\"\n    [class.active]=\"selected !== initial\"\n    [uxMenuTriggerFor]=\"menu\"\n    [closeOnBlur]=\"closeOnBlur\"\n    (closed)=\"closed.emit()\"\n  >\n    {{ selected?.group }}\n    @if (selected !== initial) {\n      <span class=\"filter-header\"> ({{ selected?.name }}) </span>\n    }\n    <ux-icon name=\"down\"></ux-icon>\n  </button>\n\n  <ux-menu #menu menuClass=\"ux-filter-menu\">\n    @for (filter of filters; track filter; let index = $index) {\n      <button\n        type=\"button\"\n        uxMenuItem\n        [id]=\"filter.id || filterId + '-item-' + index\"\n        [attr.aria-selected]=\"filter === selected\"\n        (click)=\"selectFilter(filter, $event)\"\n        (keydown.enter)=\"selectFilter(filter, $event)\"\n      >\n        <ux-icon name=\"checkmark\" [style.visibility]=\"filter === selected ? 'visible' : 'hidden'\">\n        </ux-icon>\n        <span class=\"filter-dropdown-title\">{{ filter.name }}</span>\n      </button>\n    }\n  </ux-menu>\n</div>\n", dependencies: [{ kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "component", type: MenuItemComponent, selector: "[uxMenuItem]", inputs: ["disabled", "closeOnSelect", "role"], outputs: ["activate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDropdownComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-filter-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"btn-group\">\n  <button\n    type=\"button\"\n    class=\"filter-dropdown btn dropdown-toggle\"\n    [id]=\"filterId + '-trigger'\"\n    [class.active]=\"selected !== initial\"\n    [uxMenuTriggerFor]=\"menu\"\n    [closeOnBlur]=\"closeOnBlur\"\n    (closed)=\"closed.emit()\"\n  >\n    {{ selected?.group }}\n    @if (selected !== initial) {\n      <span class=\"filter-header\"> ({{ selected?.name }}) </span>\n    }\n    <ux-icon name=\"down\"></ux-icon>\n  </button>\n\n  <ux-menu #menu menuClass=\"ux-filter-menu\">\n    @for (filter of filters; track filter; let index = $index) {\n      <button\n        type=\"button\"\n        uxMenuItem\n        [id]=\"filter.id || filterId + '-item-' + index\"\n        [attr.aria-selected]=\"filter === selected\"\n        (click)=\"selectFilter(filter, $event)\"\n        (keydown.enter)=\"selectFilter(filter, $event)\"\n      >\n        <ux-icon name=\"checkmark\" [style.visibility]=\"filter === selected ? 'visible' : 'hidden'\">\n        </ux-icon>\n        <span class=\"filter-dropdown-title\">{{ filter.name }}</span>\n      </button>\n    }\n  </ux-menu>\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { id: [{
                type: Input
            }], filters: [{
                type: Input
            }], initial: [{
                type: Input
            }], closeOnBlur: [{
                type: Input
            }], closed: [{
                type: Output
            }] } });

class FilterTypeaheadHighlight {
    transform(value, searchQuery) {
        const regex = new RegExp(searchQuery, 'i');
        return value.replace(regex, `<b class="filter-typeahead-highlighted">${value.match(regex)}</b>`);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterTypeaheadHighlight, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FilterTypeaheadHighlight, isStandalone: false, name: "filterTypeaheadHighlight" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterTypeaheadHighlight, decorators: [{
            type: Pipe,
            args: [{
                    name: 'filterTypeaheadHighlight',
                    standalone: false,
                }]
        }] });

let uniqueId$6 = 0;
class FilterDynamicComponent {
    /** Defined the closeOnBlur state for the ux-menu trigger */
    set closeOnBlur(value) {
        this._closeOnBlur = coerceBooleanProperty(value);
    }
    get closeOnBlur() {
        return this._closeOnBlur;
    }
    /** Specify the typeahead options */
    set options(options) {
        this._options = options;
    }
    /** Get the options with the defaults for any missing options */
    get options() {
        return { ...this._defaultOptions, ...this._options };
    }
    /** Get the user provided id or fallback to a default ID */
    get filterId() {
        return this.id ?? `ux-filter-dynamic-${this._uniqueId}`;
    }
    constructor() {
        this.typeaheadKeyService = inject(TypeaheadKeyService);
        this._filterService = inject(FilterService);
        this._changeDetector = inject(ChangeDetectorRef);
        /** The unique id is used multiple times - this is to ensure we only increment it once per instance */
        this._uniqueId = uniqueId$6++;
        /** The list of possible filter options */
        this.filters = [];
        /** Emit when the filter menu is closed */
        this.closed = new EventEmitter();
        /** Generate a unique id for the typeahead */
        this.typeaheadId = `ux-filter-dynamic-typeahead-${this._uniqueId}`;
        /** Store the current search query */
        this.query$ = new BehaviorSubject('');
        /** Indicate whether or not the typeahead should be shown */
        this.showTypeahead = true;
        /** Store the items that should be displayed in the typeahead */
        this.typeaheadItems = [];
        /** Store the open state of the typeahead */
        this.typeaheadOpen = false;
        /** The default options */
        this._defaultOptions = {
            placeholder: '',
            minCharacters: 3,
            maxResults: Infinity,
        };
        /** Store the user specified typeahead options */
        this._options = { ...this._defaultOptions };
        /** Unsubscribe from all subscriptions */
        this._onDestroy = new Subject();
        this._closeOnBlur = false;
        // listen for remove all events in which case we should deselect event initial filters
        this._filterService.events$
            .pipe(filter(event => event instanceof FilterRemoveAllEvent), takeUntil(this._onDestroy))
            .subscribe(() => this.removeFilter());
        // ensure that the current selected filter is still selected when the active filters change
        this._filterService.filters$.pipe(takeUntil(this._onDestroy)).subscribe(filters => {
            if (this.selected && filters.indexOf(this.selected) === -1) {
                this.removeFilter();
            }
        });
    }
    /** Set up the initial conditions */
    ngOnInit() {
        // The initially selected item should be set the the specified initial item
        this.selected = this.initial;
        // watch for changes to the selected filters
        this._filterService.filters$.pipe(takeUntil(this._onDestroy)).subscribe(filters => {
            filters.forEach(filter => {
                if (this.filters.indexOf(filter) !== -1) {
                    this.selected = filter;
                }
            });
            this._changeDetector.markForCheck();
        });
        // get the items to be displayed in the typeahead
        this.typeaheadItems = this.getItems();
        // hide the typeahead if the number of filters always visible equals or exceeds the
        // total number of filters as there would be no additional filters to display in the typeahead
        const shouldHideTypeahead = this.options?.maxIndividualItems + 1 >= this.filters.length;
        if (shouldHideTypeahead) {
            this.showTypeahead = false;
        }
    }
    /** Cleanup all subscriptions */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Get the items to display in the typeahead based on the search query */
    getItems() {
        const query = this.query$.value.toLowerCase();
        return this.filters
            .filter(item => item !== this.initial && item.name.toLowerCase().indexOf(query) !== -1)
            .map(item => item.name)
            .slice(0, this._options.maxResults);
    }
    /** When the dropdown is closed clear the query */
    onClose() {
        this.query$.next('');
    }
    /** If a filter needs removed, and is not the initial filter then remove it */
    removeFilter() {
        // check if the filter we want to remove is the initial filter
        if (this.selected !== this.initial) {
            this._filterService.remove(this.selected);
            this.selected = this.initial;
        }
        // clear the search query
        this.query$.next('');
        this._changeDetector.markForCheck();
    }
    /** Select a specific filter */
    selectFilter(filter) {
        // clear any current filters
        this.removeFilter();
        // store the newly selected filter
        this.selected = filter;
        // store the filter in the service
        this._filterService.add(this.selected);
    }
    /** Update typeahead items and visibility */
    updateTypeahead(query) {
        this.typeaheadOpen = query.length >= this._options.minCharacters;
        this.typeaheadItems = this.getItems();
    }
    /** Select a filter from a typeahead item */
    select(event) {
        // find the filter with the matching name
        const filter = this.filters.find(_filter => _filter.name === event.option);
        if (filter) {
            this.selectFilter(filter);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDynamicComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FilterDynamicComponent, isStandalone: false, selector: "ux-filter-dynamic", inputs: { id: "id", filters: "filters", initial: "initial", closeOnBlur: "closeOnBlur", options: "options" }, outputs: { closed: "closed" }, ngImport: i0, template: "<div class=\"btn-group ux-dynamic-filter\">\n  <button\n    type=\"button\"\n    [class.active]=\"selected !== initial\"\n    class=\"filter-dropdown btn dropdown-toggle\"\n    [id]=\"filterId + '-trigger'\"\n    [uxMenuTriggerFor]=\"menu\"\n    [closeOnBlur]=\"closeOnBlur\"\n    (closed)=\"closed.emit()\"\n    #trigger=\"ux-menu-trigger\"\n  >\n    {{ selected?.group }}\n    @if (selected !== initial) {\n      <span class=\"filter-header\"> ({{ selected?.name }}) </span>\n    }\n    <ux-icon name=\"down\"></ux-icon>\n  </button>\n\n  <ux-menu #menu menuClass=\"ux-dynamic-filter-menu\" (closed)=\"onClose()\">\n    <!-- Initial Option -->\n    @if (showTypeahead) {\n      <button\n        uxMenuItem\n        [id]=\"initial.id || filterId + '-initial-option'\"\n        (click)=\"removeFilter()\"\n        (keydown.enter)=\"removeFilter()\"\n      >\n        <ux-icon name=\"checkmark\" [style.visibility]=\"initial === selected ? 'visible' : 'hidden'\">\n        </ux-icon>\n        <span class=\"filter-dropdown-title\">\n          {{ initial.name }}\n        </span>\n      </button>\n    }\n\n    <!-- Selected Options -->\n    @if (selected !== initial && showTypeahead) {\n      <button uxMenuItem [id]=\"selected.id || filterId + '-selection'\">\n        <ux-icon name=\"checkmark\"></ux-icon>\n        <span class=\"filter-dropdown-title\">{{ selected.name }}</span>\n      </button>\n    }\n\n    @if (showTypeahead) {\n      <ux-menu-divider></ux-menu-divider>\n    }\n\n    @if (showTypeahead) {\n      <div class=\"typeahead-box\" role=\"none\">\n        <input\n          type=\"text\"\n          class=\"form-control\"\n          [placeholder]=\"options?.placeholder\"\n          [attr.aria-activedescendant]=\"highlightedElement?.id\"\n          [attr.aria-controls]=\"typeaheadId\"\n          aria-autocomplete=\"list\"\n          aria-multiline=\"false\"\n          [ngModel]=\"query$ | async\"\n          (ngModelChange)=\"query$.next($event); updateTypeahead($event)\"\n          (keydown)=\"typeaheadKeyService.handleKey($event, typeahead); $event.stopPropagation()\"\n          (keydown.enter)=\"$event.preventDefault()\"\n          (blur)=\"typeaheadOpen = false\"\n          (click)=\"$event.stopPropagation()\"\n        />\n        <ux-typeahead\n          #typeahead\n          [id]=\"typeaheadId\"\n          [(open)]=\"typeaheadOpen\"\n          display=\"title\"\n          [selectOnEnter]=\"true\"\n          [options]=\"typeaheadItems\"\n          [optionTemplate]=\"filterOptionTemplate\"\n          (optionSelected)=\"select($event); trigger.closeMenu($event.origin)\"\n          (highlightedElementChange)=\"highlightedElement = $event\"\n        >\n        </ux-typeahead>\n      </div>\n    }\n\n    @if (!showTypeahead) {\n      @for (filter of filters; track filter; let index = $index) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          [id]=\"filter.id || filterId + '-item-' + index\"\n          (click)=\"selectFilter(filter); trigger.closeMenu('mouse')\"\n          (keydown.enter)=\"selectFilter(filter); trigger.closeMenu('keyboard')\"\n        >\n          <ux-icon name=\"checkmark\" [style.visibility]=\"filter === selected ? 'visible' : 'hidden'\">\n          </ux-icon>\n          <span class=\"filter-dropdown-title\">{{ filter.name }}</span>\n        </button>\n      }\n    }\n  </ux-menu>\n</div>\n\n<ng-template #filterOptionTemplate let-option=\"option\" let-api=\"api\">\n  <span\n    [attr.aria-label]=\"option\"\n    [uxSafeInnerHtml]=\"option | filterTypeaheadHighlight: (query$ | async)\"\n  ></span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "component", type: MenuItemComponent, selector: "[uxMenuItem]", inputs: ["disabled", "closeOnSelect", "role"], outputs: ["activate"] }, { kind: "component", type: MenuDividerComponent, selector: "ux-menu-divider" }, { kind: "component", type: TypeaheadComponent, selector: "ux-typeahead", inputs: ["id", "options", "filter", "open", "display", "key", "disabledOptions", "dropDirection", "maxHeight", "multiselectable", "ariaLabel", "openOnFilterChange", "pageSize", "selectFirst", "selectOnEnter", "loading", "loadingTemplate", "optionTemplate", "noOptionsTemplate", "active", "recentOptions", "recentOptionsMaxCount", "recentOptionsHeadingTemplate", "optionsHeadingTemplate"], outputs: ["openChange", "optionSelected", "highlightedChange", "highlightedElementChange", "recentOptionsChange"] }, { kind: "directive", type: SafeInnerHtmlDirective, selector: "[uxSafeInnerHtml]", inputs: ["uxSafeInnerHtml"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: FilterTypeaheadHighlight, name: "filterTypeaheadHighlight" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDynamicComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-filter-dynamic', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"btn-group ux-dynamic-filter\">\n  <button\n    type=\"button\"\n    [class.active]=\"selected !== initial\"\n    class=\"filter-dropdown btn dropdown-toggle\"\n    [id]=\"filterId + '-trigger'\"\n    [uxMenuTriggerFor]=\"menu\"\n    [closeOnBlur]=\"closeOnBlur\"\n    (closed)=\"closed.emit()\"\n    #trigger=\"ux-menu-trigger\"\n  >\n    {{ selected?.group }}\n    @if (selected !== initial) {\n      <span class=\"filter-header\"> ({{ selected?.name }}) </span>\n    }\n    <ux-icon name=\"down\"></ux-icon>\n  </button>\n\n  <ux-menu #menu menuClass=\"ux-dynamic-filter-menu\" (closed)=\"onClose()\">\n    <!-- Initial Option -->\n    @if (showTypeahead) {\n      <button\n        uxMenuItem\n        [id]=\"initial.id || filterId + '-initial-option'\"\n        (click)=\"removeFilter()\"\n        (keydown.enter)=\"removeFilter()\"\n      >\n        <ux-icon name=\"checkmark\" [style.visibility]=\"initial === selected ? 'visible' : 'hidden'\">\n        </ux-icon>\n        <span class=\"filter-dropdown-title\">\n          {{ initial.name }}\n        </span>\n      </button>\n    }\n\n    <!-- Selected Options -->\n    @if (selected !== initial && showTypeahead) {\n      <button uxMenuItem [id]=\"selected.id || filterId + '-selection'\">\n        <ux-icon name=\"checkmark\"></ux-icon>\n        <span class=\"filter-dropdown-title\">{{ selected.name }}</span>\n      </button>\n    }\n\n    @if (showTypeahead) {\n      <ux-menu-divider></ux-menu-divider>\n    }\n\n    @if (showTypeahead) {\n      <div class=\"typeahead-box\" role=\"none\">\n        <input\n          type=\"text\"\n          class=\"form-control\"\n          [placeholder]=\"options?.placeholder\"\n          [attr.aria-activedescendant]=\"highlightedElement?.id\"\n          [attr.aria-controls]=\"typeaheadId\"\n          aria-autocomplete=\"list\"\n          aria-multiline=\"false\"\n          [ngModel]=\"query$ | async\"\n          (ngModelChange)=\"query$.next($event); updateTypeahead($event)\"\n          (keydown)=\"typeaheadKeyService.handleKey($event, typeahead); $event.stopPropagation()\"\n          (keydown.enter)=\"$event.preventDefault()\"\n          (blur)=\"typeaheadOpen = false\"\n          (click)=\"$event.stopPropagation()\"\n        />\n        <ux-typeahead\n          #typeahead\n          [id]=\"typeaheadId\"\n          [(open)]=\"typeaheadOpen\"\n          display=\"title\"\n          [selectOnEnter]=\"true\"\n          [options]=\"typeaheadItems\"\n          [optionTemplate]=\"filterOptionTemplate\"\n          (optionSelected)=\"select($event); trigger.closeMenu($event.origin)\"\n          (highlightedElementChange)=\"highlightedElement = $event\"\n        >\n        </ux-typeahead>\n      </div>\n    }\n\n    @if (!showTypeahead) {\n      @for (filter of filters; track filter; let index = $index) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          [id]=\"filter.id || filterId + '-item-' + index\"\n          (click)=\"selectFilter(filter); trigger.closeMenu('mouse')\"\n          (keydown.enter)=\"selectFilter(filter); trigger.closeMenu('keyboard')\"\n        >\n          <ux-icon name=\"checkmark\" [style.visibility]=\"filter === selected ? 'visible' : 'hidden'\">\n          </ux-icon>\n          <span class=\"filter-dropdown-title\">{{ filter.name }}</span>\n        </button>\n      }\n    }\n  </ux-menu>\n</div>\n\n<ng-template #filterOptionTemplate let-option=\"option\" let-api=\"api\">\n  <span\n    [attr.aria-label]=\"option\"\n    [uxSafeInnerHtml]=\"option | filterTypeaheadHighlight: (query$ | async)\"\n  ></span>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { id: [{
                type: Input
            }], filters: [{
                type: Input
            }], initial: [{
                type: Input
            }], closeOnBlur: [{
                type: Input
            }], options: [{
                type: Input
            }], closed: [{
                type: Output
            }] } });

const DECLARATIONS$7 = [
    FilterContainerComponent,
    FilterDropdownComponent,
    FilterDynamicComponent,
    FilterTypeaheadHighlight,
];
class FilterModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FilterModule, declarations: [FilterContainerComponent,
            FilterDropdownComponent,
            FilterDynamicComponent,
            FilterTypeaheadHighlight], imports: [A11yModule,
            AccessibilityModule,
            CommonModule,
            FormsModule,
            IconModule,
            MenuModule,
            TooltipModule,
            TypeaheadModule,
            SafeInnerHtmlDirective], exports: [FilterContainerComponent,
            FilterDropdownComponent,
            FilterDynamicComponent,
            FilterTypeaheadHighlight] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterModule, imports: [A11yModule,
            AccessibilityModule,
            CommonModule,
            FormsModule,
            IconModule,
            MenuModule,
            TooltipModule,
            TypeaheadModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        CommonModule,
                        FormsModule,
                        IconModule,
                        MenuModule,
                        TooltipModule,
                        TypeaheadModule,
                        SafeInnerHtmlDirective,
                    ],
                    exports: DECLARATIONS$7,
                    declarations: DECLARATIONS$7,
                }]
        }] });

class FlippableCardComponent {
    constructor() {
        this.focusIndicatorService = inject(FocusIndicatorService);
        this.elementRef = inject(ElementRef);
        /** Determines whether the card should flip horizontally or vertically. */
        this.direction = 'horizontal';
        /**
         * Determines when the card should flip. Possible options are `click`, `hover` and `manual`.
         * The manual option should be used if you want complete control over when the card should flip.
         */
        this.trigger = 'hover';
        /** Sets the width (in pixels) of the card. */
        this.width = 280;
        /** Sets the height (in pixels) of the card. */
        this.height = 200;
        /** Determines whether or not the card is flipped. */
        this.flipped = false;
        /** If two way binding is used this value will be updated when the state of the card changes. */
        this.flippedChange = new EventEmitter();
        this._focusIndicator = this.focusIndicatorService.monitor(this.elementRef.nativeElement);
    }
    ngOnDestroy() {
        this._focusIndicator.destroy();
    }
    setFlipped(state) {
        this.flipped = state;
        this.flippedChange.emit(this.flipped);
    }
    toggleFlipped() {
        this.setFlipped(!this.flipped);
    }
    clickTrigger() {
        // add or remove the class depending on whether or not the card has been flipped
        if (this.trigger === 'click') {
            this.toggleFlipped();
        }
    }
    hoverEnter() {
        // if the trigger is hover then begin to flip
        if (this.trigger === 'hover') {
            this.setFlipped(true);
        }
    }
    hoverExit() {
        if (this.trigger === 'hover') {
            this.setFlipped(false);
        }
    }
    onKeyDown(event) {
        if (this.trigger !== 'manual') {
            this.toggleFlipped();
            event.preventDefault();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FlippableCardComponent, isStandalone: false, selector: "ux-flippable-card", inputs: { direction: "direction", trigger: "trigger", width: "width", height: "height", flipped: "flipped" }, outputs: { flippedChange: "flippedChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "click": "clickTrigger()", "mouseenter": "hoverEnter()", "mouseleave": "hoverExit()", "keydown.enter": "onKeyDown($event)", "keydown.space": "onKeyDown($event)", "keydown.spacebar": "onKeyDown($event)" }, properties: { "class.horizontal": "direction === \"horizontal\"", "class.vertical": "direction === \"vertical\"" } }, exportAs: ["ux-flippable-card"], ngImport: i0, template: "<div\n  class=\"ux-flipper\"\n  [class.ux-flip-card]=\"flipped\"\n  [style.width.px]=\"width\"\n  [style.height.px]=\"height\"\n>\n  <div\n    class=\"ux-flippable-card-front\"\n    [style.width.px]=\"width\"\n    [style.height.px]=\"height\"\n    [attr.aria-hidden]=\"flipped\"\n  >\n    <ng-content select=\"ux-flippable-card-front\"></ng-content>\n  </div>\n\n  <div\n    class=\"ux-flippable-card-back\"\n    [style.width.px]=\"width\"\n    [style.height.px]=\"height\"\n    [attr.aria-hidden]=\"!flipped\"\n  >\n    <ng-content select=\"ux-flippable-card-back\"></ng-content>\n  </div>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-flippable-card', host: {
                        tabindex: '0',
                        '[class.horizontal]': 'direction === "horizontal"',
                        '[class.vertical]': 'direction === "vertical"',
                    }, exportAs: 'ux-flippable-card', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div\n  class=\"ux-flipper\"\n  [class.ux-flip-card]=\"flipped\"\n  [style.width.px]=\"width\"\n  [style.height.px]=\"height\"\n>\n  <div\n    class=\"ux-flippable-card-front\"\n    [style.width.px]=\"width\"\n    [style.height.px]=\"height\"\n    [attr.aria-hidden]=\"flipped\"\n  >\n    <ng-content select=\"ux-flippable-card-front\"></ng-content>\n  </div>\n\n  <div\n    class=\"ux-flippable-card-back\"\n    [style.width.px]=\"width\"\n    [style.height.px]=\"height\"\n    [attr.aria-hidden]=\"!flipped\"\n  >\n    <ng-content select=\"ux-flippable-card-back\"></ng-content>\n  </div>\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { direction: [{
                type: Input
            }], trigger: [{
                type: Input
            }], width: [{
                type: Input
            }], height: [{
                type: Input
            }], flipped: [{
                type: Input
            }], flippedChange: [{
                type: Output
            }], clickTrigger: [{
                type: HostListener,
                args: ['click']
            }], hoverEnter: [{
                type: HostListener,
                args: ['mouseenter']
            }], hoverExit: [{
                type: HostListener,
                args: ['mouseleave']
            }], onKeyDown: [{
                type: HostListener,
                args: ['keydown.enter', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.space', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.spacebar', ['$event']]
            }] } });
class FlippableCardFrontDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardFrontDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FlippableCardFrontDirective, isStandalone: false, selector: "ux-flippable-card-front", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardFrontDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-flippable-card-front',
                    standalone: false,
                }]
        }] });
class FlippableCardBackDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardBackDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FlippableCardBackDirective, isStandalone: false, selector: "ux-flippable-card-back", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardBackDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-flippable-card-back',
                    standalone: false,
                }]
        }] });

class FlippableCardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardModule, declarations: [FlippableCardComponent, FlippableCardBackDirective, FlippableCardFrontDirective], imports: [AccessibilityModule], exports: [FlippableCardComponent, FlippableCardBackDirective, FlippableCardFrontDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardModule, imports: [AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FlippableCardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule],
                    exports: [FlippableCardComponent, FlippableCardBackDirective, FlippableCardFrontDirective],
                    declarations: [FlippableCardComponent, FlippableCardBackDirective, FlippableCardFrontDirective],
                }]
        }] });

class FloatingActionButtonsService {
    constructor() {
        this.open$ = new BehaviorSubject(false);
        this.direction$ = new BehaviorSubject('top');
    }
    open() {
        this.open$.next(true);
    }
    toggle() {
        this.open$.next(!this.open$.getValue());
    }
    close() {
        this.open$.next(false);
        // make the first button tabbable again
        this.setPrimaryButtonFocusable();
    }
    isHorizontal() {
        return this.direction$.value === 'left' || this.direction$.value === 'right';
    }
    isVertical() {
        return this.direction$.value === 'top' || this.direction$.value === 'bottom';
    }
    setButtons(buttons) {
        this._buttons = buttons;
        // make the first button tabbable (after a delay to prevent expression changed error)
        requestAnimationFrame(() => this.setPrimaryButtonFocusable());
    }
    /** Make only the first button tabbable */
    setPrimaryButtonFocusable() {
        this._buttons.forEach(btn => btn.tabindex$.next(btn.primary ? 0 : -1));
    }
    focusPrimaryButton() {
        this.focus(this._buttons.find(btn => btn.primary));
    }
    focus(button) {
        // if the button is not defined then do nothing
        if (!button) {
            return;
        }
        // set the button tab index
        this._buttons.forEach(btn => btn.tabindex$.next(button === btn ? 0 : -1));
        // apply the focus
        button.focus();
    }
    focusSibling(next) {
        // if the buttons are not visible then do nothing
        if (this.open$.value === false) {
            return;
        }
        // get the current focused item
        const button = this.getFocusedButton();
        if (next && button === this._buttons.last) {
            return this.focus(this._buttons.first);
        }
        else if (!next && button === this._buttons.first) {
            return this.focus(this._buttons.last);
        }
        // find the sibling button
        const sibling = this._buttons.toArray()[this.getButtonIndex(button) + (next ? 1 : -1)];
        // focus the next button
        this.focus(sibling);
    }
    getFocusedButton() {
        return this._buttons.find(btn => btn.tabindex$.value === 0);
    }
    getButtonIndex(button) {
        return this._buttons.toArray().findIndex(btn => btn === button);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsService, decorators: [{
            type: Injectable
        }] });

class FloatingActionButtonComponent {
    constructor(primary) {
        this.fab = inject(FloatingActionButtonsService);
        this._tooltip = inject(TooltipDirective, { optional: true });
        /** Determine if this is the primary button in the set */
        this.primary = false;
        /** Store the tabindex */
        this.tabindex$ = new BehaviorSubject(-1);
        /** Unsubscribe from all observables on component destroy */
        this._onDestroy = new Subject();
        this.primary = primary !== null;
    }
    ngAfterViewInit() {
        if (this._tooltip) {
            // ensure the tooltip gets hidden when the button is hidden
            this.fab.open$
                .pipe(takeUntil(this._onDestroy), filter(isOpen => !isOpen && !this.primary))
                .subscribe(() => this._tooltip.hide());
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    focus() {
        this.button.nativeElement.focus();
    }
    onFocus() {
        // ensure the tooltip gets shown
        if (this._tooltip) {
            this._tooltip.show();
        }
    }
    onBlur() {
        // ensure the tooltip gets hidden
        if (this._tooltip) {
            this._tooltip.hide();
        }
    }
    close() {
        this.fab.close();
    }
    onKeydown(event) {
        switch (event.which) {
            case UP_ARROW:
                if (this.fab.isVertical()) {
                    this.fab.focusSibling(this.fab.direction$.value !== 'bottom');
                    event.preventDefault();
                }
                break;
            case DOWN_ARROW:
                if (this.fab.isVertical()) {
                    this.fab.focusSibling(this.fab.direction$.value === 'bottom');
                    event.preventDefault();
                }
                break;
            case LEFT_ARROW:
                if (this.fab.isHorizontal()) {
                    this.fab.focusSibling(this.fab.direction$.value !== 'right');
                    event.preventDefault();
                }
                break;
            case RIGHT_ARROW:
                if (this.fab.isHorizontal()) {
                    this.fab.focusSibling(this.fab.direction$.value === 'right');
                    event.preventDefault();
                }
                break;
            case ENTER:
                this.fab.focusPrimaryButton();
                break;
            case ESCAPE:
                this.fab.focusPrimaryButton();
                this.fab.close();
                break;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonComponent, deps: [{ token: 'fab-primary', attribute: true }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FloatingActionButtonComponent, isStandalone: false, selector: "ux-floating-action-button", inputs: { ariaLabel: ["aria-label", "ariaLabel"] }, host: { listeners: { "keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "button", first: true, predicate: ["button"], descendants: true, static: true }], ngImport: i0, template: "<button\n  #button\n  uxFocusIndicator\n  [programmaticFocusIndicator]=\"true\"\n  type=\"button\"\n  class=\"btn floating-action-button\"\n  [class.button-primary]=\"primary\"\n  [class.button-secondary]=\"!primary\"\n  [attr.aria-label]=\"ariaLabel\"\n  [tabIndex]=\"tabindex$ | async\"\n  (focus)=\"onFocus()\"\n  (blur)=\"onBlur()\"\n  (click)=\"primary ? fab.toggle() : close()\"\n>\n  <ng-content></ng-content>\n</button>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-floating-action-button', changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, standalone: false, template: "<button\n  #button\n  uxFocusIndicator\n  [programmaticFocusIndicator]=\"true\"\n  type=\"button\"\n  class=\"btn floating-action-button\"\n  [class.button-primary]=\"primary\"\n  [class.button-secondary]=\"!primary\"\n  [attr.aria-label]=\"ariaLabel\"\n  [tabIndex]=\"tabindex$ | async\"\n  (focus)=\"onFocus()\"\n  (blur)=\"onBlur()\"\n  (click)=\"primary ? fab.toggle() : close()\"\n>\n  <ng-content></ng-content>\n</button>\n" }]
        }], ctorParameters: () => [{ type: undefined, decorators: [{
                    type: Attribute,
                    args: ['fab-primary']
                }] }], propDecorators: { ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], button: [{
                type: ViewChild,
                args: ['button', { static: true }]
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class FloatingActionButtonsComponent {
    /** Specify the direction that the FAB should display */
    set direction(direction) {
        this.fab.direction$.next(direction);
    }
    constructor() {
        this.fab = inject(FloatingActionButtonsService);
        this._elementRef = inject(ElementRef);
        /** Emit whenever the open state changes */
        this.openChange = new EventEmitter();
        this._subscription = new Subscription();
        this._subscription.add(this.fab.open$.subscribe(value => this.openChange.emit(value)));
    }
    ngAfterViewInit() {
        this.fab.setButtons(this.buttons);
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
    }
    /*
     * Detect any clicks to trigger close of the menu
     */
    close(target) {
        if (!this._elementRef.nativeElement.contains(target)) {
            this.fab.close();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FloatingActionButtonsComponent, isStandalone: false, selector: "ux-floating-action-buttons", inputs: { direction: "direction" }, outputs: { openChange: "openChange" }, host: { listeners: { "document:click": "close($event.target)" } }, providers: [FloatingActionButtonsService], queries: [{ propertyName: "buttons", predicate: FloatingActionButtonComponent }], ngImport: i0, template: "<ng-content select=\"[fab-primary]\"></ng-content>\n\n@if (fab.open$ | async) {\n  <div\n    class=\"floating-action-button-list\"\n    [@fabAnimation]=\"fab.open$ | async\"\n    [ngClass]=\"fab.direction$ | async\"\n  >\n    <ng-content></ng-content>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], animations: [
            trigger('fabAnimation', [
                transition('void => true', [
                    query('ux-floating-action-button', style({ opacity: 0 })),
                    query('ux-floating-action-button', stagger(50, animate(250, style({ opacity: 1 })))),
                ]),
                transition('true => void', [
                    query('ux-floating-action-button', stagger(-50, animate(250, style({ opacity: 0 })))),
                ]),
            ]),
        ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-floating-action-buttons', providers: [FloatingActionButtonsService], changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, animations: [
                        trigger('fabAnimation', [
                            transition('void => true', [
                                query('ux-floating-action-button', style({ opacity: 0 })),
                                query('ux-floating-action-button', stagger(50, animate(250, style({ opacity: 1 })))),
                            ]),
                            transition('true => void', [
                                query('ux-floating-action-button', stagger(-50, animate(250, style({ opacity: 0 })))),
                            ]),
                        ]),
                    ], standalone: false, template: "<ng-content select=\"[fab-primary]\"></ng-content>\n\n@if (fab.open$ | async) {\n  <div\n    class=\"floating-action-button-list\"\n    [@fabAnimation]=\"fab.open$ | async\"\n    [ngClass]=\"fab.direction$ | async\"\n  >\n    <ng-content></ng-content>\n  </div>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { direction: [{
                type: Input
            }], openChange: [{
                type: Output
            }], buttons: [{
                type: ContentChildren,
                args: [FloatingActionButtonComponent]
            }], close: [{
                type: HostListener,
                args: ['document:click', ['$event.target']]
            }] } });

class FloatingActionButtonsModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsModule, declarations: [FloatingActionButtonsComponent, FloatingActionButtonComponent], imports: [AccessibilityModule, CommonModule, IconModule], exports: [FloatingActionButtonsComponent, FloatingActionButtonComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsModule, imports: [AccessibilityModule, CommonModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatingActionButtonsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, IconModule],
                    exports: [FloatingActionButtonsComponent, FloatingActionButtonComponent],
                    declarations: [FloatingActionButtonsComponent, FloatingActionButtonComponent],
                }]
        }] });

class HierarchyBarNodeIconDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarNodeIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: HierarchyBarNodeIconDirective, isStandalone: false, selector: "[uxHierarchyBarNodeIcon]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarNodeIconDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxHierarchyBarNodeIcon]',
                    standalone: false,
                }]
        }] });

class HierarchyBarService {
    constructor() {
        /** Define the list of selected nodes */
        this.nodes$ = new BehaviorSubject([]);
        /** Define the events that show the popover when interacting with the arrows */
        this.popoverShowTriggers = ['click'];
        /** Define the events that hide the popover when interacting with the arrows */
        this.popoverHideTriggers = ['click', 'clickoutside', 'escape'];
        /** Emit the selected node when it changes */
        this.selection$ = new Subject();
        /** Define the aria label for the show siblings popover button */
        this.showSiblingsAriaLabel = 'Show Siblings';
        /** Store nodes as a flattened list */
        this._nodes = [];
    }
    /**
     * Store the root node of the hierarchy tree
     */
    setRootNode(root) {
        // if the node is null or undefined then do nothing
        if (!root) {
            return;
        }
        // store the root node
        this._root = root;
        // create a flat structure of nodes
        this._nodes = this.getNodeList(root);
        // flatten the array - based on the selected node
        this.nodes$.next(this.getSelectedChildren(root));
    }
    /**
     * Select a node. This causes all nodes to be
     * deselected and the path to the selected node
     * to be selected
     */
    selectNode(node) {
        // deselect all nodes
        this.deselectAll();
        // if the node is undefined then do nothing
        if (!node) {
            return;
        }
        // ensure the current node is selected and its parents
        this.select(node);
        // emit a new node list to trigger change detection
        this.nodes$.next(this.getSelectedChildren(this._root));
        // emit the new selection
        this.selection$.next(node);
    }
    /**
     * Handles getting children with support for both arrays and observables
     */
    getChildren(node) {
        if (Array.isArray(node.children)) {
            return of({ loading: false, children: node.children });
        }
        const children$ = node.children;
        // if it is an observable then handle loading
        return Observable.create((observer) => {
            // emit initial value
            observer.next({ loading: true, children: [] });
            // now wait until the children observable completes
            children$.pipe(first()).subscribe(children => {
                // replace the observable with an array for future loading
                node.children = children;
                // rebuild the node tree
                this.setRootNode(this._root);
                // emit the latest value
                observer.next({ loading: false, children });
                // close the observable stream
                observer.complete();
            });
        });
    }
    /**
     * Utility function to get the sibling nodes, taking into account that
     * a node may be a root node and may not have a parent.
     */
    getSiblings(node) {
        return node.parent ? this.getChildren(node.parent) : of({ loading: false, children: [] });
    }
    /**
     * Traverses all the parents to ensure they are selected
     */
    select(node) {
        node.selected = true;
        if (node.parent) {
            this.select(node.parent);
        }
    }
    /**
     * Deselects all nodes
     */
    deselectAll() {
        this._nodes.forEach(node => (node.selected = false));
    }
    /**
     * Gets all the nodes in the tree as a flat array.
     * It also stores the parent node in a parent property
     * on the node for easy traversal in both directions
     */
    getNodeList(node) {
        // if there are no children then return only itself
        if (!node.children || isObservable(node.children) || node.children.length === 0) {
            return [node];
        }
        // store the parent property
        node.children.forEach(child => (child.parent = node));
        // get all descendants of this node
        const descendants = node.children.reduce((nodes, current) => [...nodes, ...this.getNodeList(current)], []);
        return [node, ...descendants];
    }
    /**
     * Gets all selected nodes from the parent node.
     */
    getSelectedChildren(node) {
        if (isObservable(node.children)) {
            return [node];
        }
        // get the children - and account for when there is none
        const children = node.children || [];
        // check if any child is selected
        const child = children.find(_child => _child.selected);
        // return the remaining chain of selected items
        return child ? [node, ...this.getSelectedChildren(child)] : [node];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarService, decorators: [{
            type: Injectable
        }] });

class ClickOutsideDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this.uxClickOutside = new EventEmitter();
        /** Often a click event makes the element appear - if so we can end up closing it immediately */
        this._initialised = false;
        setTimeout(() => (this._initialised = true));
    }
    click(event) {
        if (this._initialised &&
            this._elementRef.nativeElement !== event.target &&
            !this._elementRef.nativeElement.contains(event.target)) {
            this.uxClickOutside.emit(event);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ClickOutsideDirective, isStandalone: false, selector: "[uxClickOutside]", outputs: { uxClickOutside: "uxClickOutside" }, host: { listeners: { "document:click": "click($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxClickOutside]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxClickOutside: [{
                type: Output
            }], click: [{
                type: HostListener,
                args: ['document:click', ['$event']]
            }] } });

let uniquePopoverId = 0;
class PopoverComponent extends TooltipComponent {
    constructor() {
        super();
        /** Define a unique id for each popover */
        this.id = `ux-popover-${++uniquePopoverId}`;
        /** This will emit an event any time the user clicks outside the popover */
        this.clickOutside$ = new Subject();
    }
    /** This will update the title of the popover and trigger change detection */
    setTitle(title) {
        this.title = title;
        this._changeDetectorRef.markForCheck();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PopoverComponent, isStandalone: false, selector: "ux-popover", usesInheritance: true, ngImport: i0, template: "<div\n  class=\"popover show\"\n  [ngClass]=\"[positionClass, 'popover-align-' + alignment, customClass]\"\n  [id]=\"id\"\n  [attr.role]=\"role\"\n  (uxClickOutside)=\"clickOutside$.next($event)\"\n>\n  <div class=\"arrow\"></div>\n  @if (title) {\n    <h3 class=\"popover-title\">{{ title }}</h3>\n  }\n  <div class=\"popover-content\" (uxResize)=\"reposition()\">\n    @if (!isTemplateRef) {\n      {{ content }}\n    }\n    @if (isTemplateRef) {\n      <ng-container\n        [ngTemplateOutlet]=\"$any(content)\"\n        [ngTemplateOutletContext]=\"context\"\n      ></ng-container>\n    }\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[uxClickOutside]", outputs: ["uxClickOutside"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-popover', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div\n  class=\"popover show\"\n  [ngClass]=\"[positionClass, 'popover-align-' + alignment, customClass]\"\n  [id]=\"id\"\n  [attr.role]=\"role\"\n  (uxClickOutside)=\"clickOutside$.next($event)\"\n>\n  <div class=\"arrow\"></div>\n  @if (title) {\n    <h3 class=\"popover-title\">{{ title }}</h3>\n  }\n  <div class=\"popover-content\" (uxResize)=\"reposition()\">\n    @if (!isTemplateRef) {\n      {{ content }}\n    }\n    @if (isTemplateRef) {\n      <ng-container\n        [ngTemplateOutlet]=\"$any(content)\"\n        [ngTemplateOutletContext]=\"context\"\n      ></ng-container>\n    }\n  </div>\n</div>\n" }]
        }], ctorParameters: () => [] });

class PopoverDirective extends TooltipDirective {
    constructor() {
        super();
        /** All the user to add a custom class to the popover */
        this.customClass = '';
        /** All the user to add a role to the popover - default is tooltip */
        this.role = 'tooltip';
        /** Provide the TemplateRef a context object */
        this.context = {};
        /** Delay the showing of the popover by a number of miliseconds */
        this.delay = 0;
        /** Specify which events should show the popover */
        this.showTriggers = ['click'];
        /** Specify which events should hide the popover */
        this.hideTriggers = ['click', 'clickoutside', 'escape'];
        /** Keep track of the tooltip visibility and update aria-expanded attribute */
        this.isVisible = false;
        /** Define the overlay class */
        this._overlayClass = 'ux-overlay-pane';
        /** Internally store the type of this component - usual for distinctions when extending the tooltip class */
        this._type = 'popover';
    }
    /** Set up the triggers and bind to the show/hide events to keep visibility in sync */
    ngOnInit() {
        // set up the event triggers
        fromEvent(document, 'keydown')
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onKeyDown.bind(this));
        // check if there is an aria-described by attribute
        this._ariaDescribedBy = this._elementRef.nativeElement.hasAttribute('aria-describedby');
        // set up the default event triggers
        super.ngOnInit();
    }
    /**
     * We need to send input changes to the popover component
     * We can't use setters as they may trigger before popover initialised and can't resend once initialised
     **/
    ngOnChanges(changes) {
        super.ngOnChanges(changes);
        if (this._instance && changes.title) {
            this._instance.setTitle(changes.title.currentValue);
        }
    }
    createInstance(overlayRef) {
        const instance = overlayRef.attach(this._portal).instance;
        // supply the tooltip with the correct properties
        instance.setTitle(this.title);
        instance.setContent(this.content);
        instance.setPlacement(this.placement);
        instance.setAlignment(this.alignment);
        instance.setClass(this.customClass);
        instance.setContext(this.context);
        instance.setRole(this.role);
        // Update the aria-describedby attribute
        this.setAriaDescribedBy(instance.id);
        // subscribe to the outside click event
        instance.clickOutside$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onClickOutside.bind(this));
        return instance;
    }
    createPortal() {
        return this._portal || new ComponentPortal(PopoverComponent, this._viewContainerRef);
    }
    onKeyDown(event) {
        // if visible and the escape key is pressed and it is one of the hide triggers
        if (this.isVisible && event.keyCode === ESCAPE && this.includes(this.hideTriggers, 'escape')) {
            this.hide();
        }
    }
    onClickOutside() {
        // if visible and it is one of the hide triggers
        if (this.isVisible && this.includes(this.hideTriggers, 'clickoutside')) {
            this.hide();
        }
    }
    /** Programmatically update the aria-describedby property */
    setAriaDescribedBy(id) {
        // we only want to set the aria-describedby attr when the content is a string and there was no user defined attribute already
        if (this._ariaDescribedBy === false && typeof this.content === 'string') {
            super.setAriaDescribedBy(id);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PopoverDirective, isStandalone: false, selector: "[uxPopover]", inputs: { content: ["uxPopover", "content"], title: ["popoverTitle", "title"], disabled: ["popoverDisabled", "disabled"], customClass: ["popoverClass", "customClass"], role: ["popoverRole", "role"], context: ["popoverContext", "context"], delay: ["popoverDelay", "delay"], showTriggers: "showTriggers", hideTriggers: "hideTriggers" }, host: { properties: { "attr.aria-expanded": "this.isVisible" } }, exportAs: ["ux-popover"], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxPopover]',
                    exportAs: 'ux-popover',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { content: [{
                type: Input,
                args: ['uxPopover']
            }], title: [{
                type: Input,
                args: ['popoverTitle']
            }], disabled: [{
                type: Input,
                args: ['popoverDisabled']
            }], customClass: [{
                type: Input,
                args: ['popoverClass']
            }], role: [{
                type: Input,
                args: ['popoverRole']
            }], context: [{
                type: Input,
                args: ['popoverContext']
            }], delay: [{
                type: Input,
                args: ['popoverDelay']
            }], showTriggers: [{
                type: Input
            }], hideTriggers: [{
                type: Input
            }], isVisible: [{
                type: HostBinding,
                args: ['attr.aria-expanded']
            }] } });

class HierarchyBarNodeComponent {
    constructor() {
        this.hierarchyBar = inject(HierarchyBarService);
        this._elementRef = inject(ElementRef);
        /** Optionally define the horizontal offset */
        this.offset = 0;
        /** Emit when the node is selected */
        this.selected = new EventEmitter();
        /** Determine if this node should be hidden due to overflow */
        this.visible = true;
    }
    /** Get the width of the element */
    get width() {
        return this._elementRef.nativeElement.offsetWidth;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarNodeComponent, isStandalone: false, selector: "ux-hierarchy-bar-node", inputs: { node: "node", popoverTemplate: "popoverTemplate", mode: "mode", readonly: "readonly", offset: "offset" }, outputs: { selected: "selected" }, host: { properties: { "style.visibility": "visible ? \"visible\" : \"hidden\"" } }, ngImport: i0, template: "<div\n  class=\"hierarchy-bar-node\"\n  [class.hierarchy-bar-node-readonly]=\"readonly\"\n  [class.hierarchy-bar-node-child-indicator]=\"node.children\"\n  [style.transform]=\"offset ? 'translateX(' + offset + 'px)' : null\"\n>\n  @if (mode === 'dropdown') {\n    <button\n      type=\"button\"\n      uxFocusIndicator\n      uxFocusIndicatorOrigin\n      #popover=\"ux-popover\"\n      aria-label=\"Show children\"\n      class=\"hierarchy-bar-node-content\"\n      [disabled]=\"readonly\"\n      [uxPopover]=\"popoverTemplate\"\n      [popoverContext]=\"{ node: node, popover: popover }\"\n      placement=\"bottom\"\n      popoverClass=\"hierarchy-bar-popover\"\n      [showTriggers]=\"node.children ? hierarchyBar.popoverShowTriggers : []\"\n      [hideTriggers]=\"node.children ? hierarchyBar.popoverHideTriggers : []\"\n      tabindex=\"0\"\n      [attr.aria-label]=\"node.title\"\n    >\n      <!-- Show a custom icon if specified -->\n      @if (hierarchyBar.icon) {\n        <div class=\"hierarchy-bar-node-icon\">\n          <ng-container\n            [ngTemplateOutlet]=\"hierarchyBar.icon\"\n            [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n          ></ng-container>\n        </div>\n      }\n      <!-- Show an icon if specified -->\n      @if (node.icon && !hierarchyBar.icon) {\n        <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n      }\n      <!-- Show the name of the current node -->\n      <span class=\"hierarchy-bar-node-title\">{{ node.title }}</span>\n      <!-- Show a dropdown arrow if there are children -->\n      <div [class.readonly-arrow]=\"readonly\" class=\"hierarchy-bar-node-arrow-icon-dropdown\">\n        @if (node.children) {\n          <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"> </ux-icon>\n        }\n      </div>\n    </button>\n  } @else {\n    <button\n      type=\"button\"\n      uxFocusIndicator\n      class=\"hierarchy-bar-node-content\"\n      [disabled]=\"readonly\"\n      [attr.aria-label]=\"node.title\"\n      (click)=\"selected.emit(node)\"\n    >\n      <!-- Show a custom icon if specified -->\n      @if (hierarchyBar.icon) {\n        <div class=\"hierarchy-bar-node-icon\">\n          <ng-container\n            [ngTemplateOutlet]=\"hierarchyBar.icon\"\n            [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n          ></ng-container>\n        </div>\n      }\n      <!-- Show an icon if specified -->\n      @if (node.icon && !hierarchyBar.icon) {\n        <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n      }\n      <!-- Show the name of the current node -->\n      <span class=\"hierarchy-bar-node-title\">{{ node.title }}</span>\n    </button>\n    <!-- Show a dropdown arrow if there are children -->\n    @if (node.children) {\n      <button\n        type=\"button\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        #popover=\"ux-popover\"\n        aria-label=\"Show children\"\n        role=\"button\"\n        class=\"hierarchy-bar-node-arrow\"\n        [disabled]=\"readonly\"\n        [uxPopover]=\"popoverTemplate\"\n        [popoverContext]=\"{ node: node, popover: popover }\"\n        placement=\"bottom\"\n        popoverClass=\"hierarchy-bar-popover\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        tabindex=\"0\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"> </ux-icon>\n      </button>\n    }\n  }\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarNodeComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar-node', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[style.visibility]': 'visible ? "visible" : "hidden"',
                    }, standalone: false, template: "<div\n  class=\"hierarchy-bar-node\"\n  [class.hierarchy-bar-node-readonly]=\"readonly\"\n  [class.hierarchy-bar-node-child-indicator]=\"node.children\"\n  [style.transform]=\"offset ? 'translateX(' + offset + 'px)' : null\"\n>\n  @if (mode === 'dropdown') {\n    <button\n      type=\"button\"\n      uxFocusIndicator\n      uxFocusIndicatorOrigin\n      #popover=\"ux-popover\"\n      aria-label=\"Show children\"\n      class=\"hierarchy-bar-node-content\"\n      [disabled]=\"readonly\"\n      [uxPopover]=\"popoverTemplate\"\n      [popoverContext]=\"{ node: node, popover: popover }\"\n      placement=\"bottom\"\n      popoverClass=\"hierarchy-bar-popover\"\n      [showTriggers]=\"node.children ? hierarchyBar.popoverShowTriggers : []\"\n      [hideTriggers]=\"node.children ? hierarchyBar.popoverHideTriggers : []\"\n      tabindex=\"0\"\n      [attr.aria-label]=\"node.title\"\n    >\n      <!-- Show a custom icon if specified -->\n      @if (hierarchyBar.icon) {\n        <div class=\"hierarchy-bar-node-icon\">\n          <ng-container\n            [ngTemplateOutlet]=\"hierarchyBar.icon\"\n            [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n          ></ng-container>\n        </div>\n      }\n      <!-- Show an icon if specified -->\n      @if (node.icon && !hierarchyBar.icon) {\n        <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n      }\n      <!-- Show the name of the current node -->\n      <span class=\"hierarchy-bar-node-title\">{{ node.title }}</span>\n      <!-- Show a dropdown arrow if there are children -->\n      <div [class.readonly-arrow]=\"readonly\" class=\"hierarchy-bar-node-arrow-icon-dropdown\">\n        @if (node.children) {\n          <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"> </ux-icon>\n        }\n      </div>\n    </button>\n  } @else {\n    <button\n      type=\"button\"\n      uxFocusIndicator\n      class=\"hierarchy-bar-node-content\"\n      [disabled]=\"readonly\"\n      [attr.aria-label]=\"node.title\"\n      (click)=\"selected.emit(node)\"\n    >\n      <!-- Show a custom icon if specified -->\n      @if (hierarchyBar.icon) {\n        <div class=\"hierarchy-bar-node-icon\">\n          <ng-container\n            [ngTemplateOutlet]=\"hierarchyBar.icon\"\n            [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n          ></ng-container>\n        </div>\n      }\n      <!-- Show an icon if specified -->\n      @if (node.icon && !hierarchyBar.icon) {\n        <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n      }\n      <!-- Show the name of the current node -->\n      <span class=\"hierarchy-bar-node-title\">{{ node.title }}</span>\n    </button>\n    <!-- Show a dropdown arrow if there are children -->\n    @if (node.children) {\n      <button\n        type=\"button\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        #popover=\"ux-popover\"\n        aria-label=\"Show children\"\n        role=\"button\"\n        class=\"hierarchy-bar-node-arrow\"\n        [disabled]=\"readonly\"\n        [uxPopover]=\"popoverTemplate\"\n        [popoverContext]=\"{ node: node, popover: popover }\"\n        placement=\"bottom\"\n        popoverClass=\"hierarchy-bar-popover\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        tabindex=\"0\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"> </ux-icon>\n      </button>\n    }\n  }\n</div>\n" }]
        }], propDecorators: { node: [{
                type: Input
            }], popoverTemplate: [{
                type: Input
            }], mode: [{
                type: Input
            }], readonly: [{
                type: Input
            }], offset: [{
                type: Input
            }], selected: [{
                type: Output
            }] } });

class HierarchyBarPopoverItemComponent {
    constructor() {
        this.focusOriginService = inject(FocusIndicatorOriginService);
        this.elementRef = inject(ElementRef);
        this.renderer = inject(Renderer2);
        this.hierarchyBar = inject(HierarchyBarService);
        /**
         * Emit when a click or enter key press occurs.
         * Note this is an `async` EventEmitter to ensure that
         * the event handlers in the `FocusIndicatorOrigin` set
         * the origin before we emit the select event, otherwise
         * the item may not get a focus ring when the keyboard is used.
         */
        this.selected = new EventEmitter(true);
        this._focusOrigin = new FocusIndicatorOrigin(this.focusOriginService, this.elementRef, this.renderer);
    }
    ngOnDestroy() {
        this._focusOrigin.destroy();
    }
    onSelect() {
        this.selected.emit(this.node);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarPopoverItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarPopoverItemComponent, isStandalone: false, selector: "ux-hierarchy-bar-popover-item", inputs: { node: "node" }, outputs: { selected: "selected" }, host: { listeners: { "click": "onSelect()", "keydown.enter": "onSelect()" } }, ngImport: i0, template: "<!-- Show an icon if specified -->\n@if (!hierarchyBar.icon && node?.icon) {\n  <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n}\n\n<!-- Show a custom icon if specified -->\n@if (hierarchyBar.icon) {\n  <div class=\"hierarchy-bar-node-icon\">\n    <ng-container\n      [ngTemplateOutlet]=\"hierarchyBar.icon\"\n      [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n    >\n    </ng-container>\n  </div>\n}\n\n<!-- Show the name of the current node -->\n<span class=\"hierarchy-bar-node-title\">{{ node?.title }}</span>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarPopoverItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar-popover-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Show an icon if specified -->\n@if (!hierarchyBar.icon && node?.icon) {\n  <img class=\"hierarchy-bar-node-icon\" [src]=\"node.icon\" alt=\"Hierarchy Bar Icon\" />\n}\n\n<!-- Show a custom icon if specified -->\n@if (hierarchyBar.icon) {\n  <div class=\"hierarchy-bar-node-icon\">\n    <ng-container\n      [ngTemplateOutlet]=\"hierarchyBar.icon\"\n      [ngTemplateOutletContext]=\"{ node: node, $implicit: node }\"\n    >\n    </ng-container>\n  </div>\n}\n\n<!-- Show the name of the current node -->\n<span class=\"hierarchy-bar-node-title\">{{ node?.title }}</span>\n" }]
        }], ctorParameters: () => [], propDecorators: { node: [{
                type: Input
            }], selected: [{
                type: Output
            }], onSelect: [{
                type: HostListener,
                args: ['click']
            }, {
                type: HostListener,
                args: ['keydown.enter']
            }] } });

class HierarchyBarPopoverComponent {
    constructor() {
        this.hierarchyBar = inject(HierarchyBarService);
        /** Define the nodes to display */
        this.nodes = [];
        /** Defines if dropdown items should have separators between them to distinguish if nodes are siblings or ancestors */
        this.separator = false;
        /** Emit a select event when an item ahs been clicked or enter key pressed */
        this.selected = new EventEmitter();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarPopoverComponent, isStandalone: false, selector: "ux-hierarchy-bar-popover", inputs: { nodes: "nodes", loading: "loading", separator: "separator" }, outputs: { selected: "selected" }, ngImport: i0, template: "<!-- Loading Indicator -->\n@if (loading) {\n  <ul class=\"hierarchy-bar-node-list\">\n    <li class=\"hierarchy-bar-loading-indicator\">\n      <ng-container\n        [ngTemplateOutlet]=\"hierarchyBar.loadingIndicator || defaultLoadingIndicator\"\n      ></ng-container>\n    </li>\n  </ul>\n}\n\n<!-- List of children -->\n@if (!loading) {\n  <div\n    class=\"hierarchy-bar-node-list\"\n    [class.hierarchy-bar-node-list-separator]=\"separator\"\n    uxTabbableList\n    [returnFocus]=\"true\"\n  >\n    @for (node of nodes; track node; let first = $first) {\n      <ux-hierarchy-bar-popover-item\n        uxFocusIndicator\n        [node]=\"node\"\n        [focusIf]=\"first\"\n        uxTabbableListItem\n        (selected)=\"selected.emit($event)\"\n      >\n      </ux-hierarchy-bar-popover-item>\n    }\n  </div>\n}\n\n<!-- Loading Indicator Template -->\n<ng-template #defaultLoadingIndicator>\n  <div class=\"hierarchy-bar-loading-icon\" alt=\"Hierarchy Bar Loading Indicator\">\n    <div class=\"spinner spinner-accent spinner-bounce-middle\"></div>\n  </div>\n\n  <!-- Show the name of the current node -->\n  <span class=\"hierarchy-bar-loading-title\">Loading...</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: HierarchyBarPopoverItemComponent, selector: "ux-hierarchy-bar-popover-item", inputs: ["node"], outputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarPopoverComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar-popover', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Loading Indicator -->\n@if (loading) {\n  <ul class=\"hierarchy-bar-node-list\">\n    <li class=\"hierarchy-bar-loading-indicator\">\n      <ng-container\n        [ngTemplateOutlet]=\"hierarchyBar.loadingIndicator || defaultLoadingIndicator\"\n      ></ng-container>\n    </li>\n  </ul>\n}\n\n<!-- List of children -->\n@if (!loading) {\n  <div\n    class=\"hierarchy-bar-node-list\"\n    [class.hierarchy-bar-node-list-separator]=\"separator\"\n    uxTabbableList\n    [returnFocus]=\"true\"\n  >\n    @for (node of nodes; track node; let first = $first) {\n      <ux-hierarchy-bar-popover-item\n        uxFocusIndicator\n        [node]=\"node\"\n        [focusIf]=\"first\"\n        uxTabbableListItem\n        (selected)=\"selected.emit($event)\"\n      >\n      </ux-hierarchy-bar-popover-item>\n    }\n  </div>\n}\n\n<!-- Loading Indicator Template -->\n<ng-template #defaultLoadingIndicator>\n  <div class=\"hierarchy-bar-loading-icon\" alt=\"Hierarchy Bar Loading Indicator\">\n    <div class=\"spinner spinner-accent spinner-bounce-middle\"></div>\n  </div>\n\n  <!-- Show the name of the current node -->\n  <span class=\"hierarchy-bar-loading-title\">Loading...</span>\n</ng-template>\n" }]
        }], propDecorators: { nodes: [{
                type: Input
            }], loading: [{
                type: Input
            }], separator: [{
                type: Input
            }], selected: [{
                type: Output
            }] } });

class HierarchyBarStandardComponent {
    constructor() {
        this.hierarchyBar = inject(HierarchyBarService);
        /** Value in pixels to translate the visible nodes by to fill the empty space occupied by hidden nodes */
        this.overflowTranslateOffset = 0;
        /** Identify which nodes are overflowing */
        this.overflow$ = new BehaviorSubject([]);
        /** Determine if there is any overflow */
        this.isOverflowing$ = new BehaviorSubject(false);
        /** Unsubscribe from all subscriptions when component is destroyed */
        this._onDestroy = new Subject();
        // subscribe to changes in the selected node - update the UI after the render
        this.hierarchyBar.nodes$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => requestAnimationFrame(this.scrollIntoView.bind(this)));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * When there is overflow ensure that the rightmost
     * node remains in view at all times. The nodes no longer
     * visible should be displayed in a popover available on the
     * overflow indicator
     */
    scrollIntoView() {
        if (!this.nodelist) {
            return;
        }
        // get the native element
        const { nativeElement } = this.nodelist;
        const isOverflowing = nativeElement.scrollWidth > nativeElement.offsetWidth;
        // emit whether we are overflowing or not
        this.isOverflowing$.next(isOverflowing);
        // we don't need to do anything else if there is no overflow
        if (!isOverflowing) {
            this.nodeInstances.forEach(node => (node.visible = true));
            this.overflowTranslateOffset = 0;
            return;
        }
        let isFull = false;
        // find the nodes that should be visible (we start at the rightmost node)
        const nodes = this.nodeInstances
            .toArray()
            .reduceRight((visibleNodes, node) => {
            // there must always be one visible node
            if (visibleNodes.length === 0) {
                return [node];
            }
            // if the hierarchy bar is already occupying the available space then we can skip calculations
            if (isFull) {
                // hide the node
                node.visible = false;
                return visibleNodes;
            }
            // get the cumulative width of all the visible nodes
            const consumedWidth = visibleNodes.reduce((totalWidth, visibleNode) => totalWidth + visibleNode.width, 0);
            // get the width that would be consumed if this node was included
            const width = node.width + consumedWidth;
            isFull = width > nativeElement.offsetWidth;
            node.visible = !isFull;
            if (isFull) {
                this.overflowTranslateOffset = this.nodelist.nativeElement.clientWidth - consumedWidth;
            }
            return isFull ? visibleNodes : [...visibleNodes, node];
        }, []);
        if (nodes.length === 1 && this.nodelist.nativeElement.offsetWidth < nodes[0].width) {
            this.overflowTranslateOffset = 0;
        }
        // move the scroll position to always show the last item
        this.nodelist.nativeElement.scrollLeft = nativeElement.scrollWidth - nativeElement.offsetWidth;
        // determine which nodes should be hidden
        const nodesHidden = this.nodeInstances.filter(node => {
            return !node.visible;
        });
        this.overflow$.next(nodesHidden.map(node => node.node));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarStandardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarStandardComponent, isStandalone: false, selector: "ux-hierarchy-bar-standard", inputs: { mode: "mode", readonly: "readonly" }, viewQueries: [{ propertyName: "nodelist", first: true, predicate: ["nodelist"], descendants: true, static: true }, { propertyName: "nodes", predicate: HierarchyBarNodeComponent, descendants: true, read: ElementRef }, { propertyName: "nodeInstances", predicate: HierarchyBarNodeComponent, descendants: true }, { propertyName: "barNodes", predicate: ["barNodes"], descendants: true, read: ElementRef }], ngImport: i0, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n  @if (isOverflowing$ | async) {\n    <div\n      #popover=\"ux-popover\"\n      class=\"hierarchy-bar-overflow-indicator\"\n      [uxPopover]=\"overflow\"\n      [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n      [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n      [popoverContext]=\"{ popover: popover }\"\n      placement=\"bottom\"\n      popoverClass=\"hierarchy-bar-popover\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n        [ngTemplateOutletContext]=\"{ $implicit: overflow$ | async }\"\n      >\n      </ng-container>\n      <button\n        [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        class=\"hierarchy-bar-node-arrow\"\n        placement=\"bottom\"\n        [uxPopover]=\"nodeList\"\n        popoverClass=\"hierarchy-bar-popover\"\n        role=\"button\"\n        tabindex=\"0\"\n        [disabled]=\"readonly\"\n        #popover=\"ux-popover\"\n        [popoverContext]=\"{ popover: popover }\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        type=\"button\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n      </button>\n    </div>\n  }\n  <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodelist class=\"hierarchy-bar-nodes\" (uxResize)=\"scrollIntoView()\">\n  @for (node of hierarchyBar.nodes$ | async; track node; let i = $index; let last = $last) {\n    <ux-hierarchy-bar-node\n      #barNodes\n      [mode]=\"mode\"\n      [readonly]=\"readonly\"\n      [node]=\"node\"\n      [popoverTemplate]=\"content\"\n      (selected)=\"hierarchyBar.selectNode(node)\"\n      [offset]=\"-overflowTranslateOffset\"\n      [style.max-width.px]=\"(isOverflowing$ | async) && last ? nodelist.offsetWidth : null\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  <!-- Allow content to be placed after the last node -->\n  <div class=\"hierarchy-bar-addons\">\n    <ng-content select=\"trailing-addons\"></ng-content>\n  </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n    [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the popover list -->\n<ng-template #nodeList let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"node\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the overflow popover list -->\n<ng-template #overflow let-popover=\"popover\">\n  <div uxTabbableList [returnFocus]=\"true\">\n    @for (child of overflow$ | async; track child; let first = $first) {\n      <ux-hierarchy-bar-popover-item\n        uxFocusIndicator\n        [node]=\"child\"\n        [focusIf]=\"first\"\n        uxTabbableListItem\n        (selected)=\"hierarchyBar.selectNode(child); popover.hide()\"\n      >\n      </ux-hierarchy-bar-popover-item>\n    }\n  </div>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n  <button\n    uxFocusIndicator\n    uxFocusIndicatorOrigin\n    [disabled]=\"readonly\"\n    class=\"overflow-button\"\n    aria-label=\"Show parents\"\n    role=\"button\"\n    type=\"button\"\n  >\n    <ux-icon name=\"more\"></ux-icon>\n  </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }, { kind: "directive", type: ResizeDirective, selector: "[uxResize]", inputs: ["throttle"], outputs: ["uxResize"] }, { kind: "component", type: HierarchyBarNodeComponent, selector: "ux-hierarchy-bar-node", inputs: ["node", "popoverTemplate", "mode", "readonly", "offset"], outputs: ["selected"] }, { kind: "component", type: HierarchyBarPopoverComponent, selector: "ux-hierarchy-bar-popover", inputs: ["nodes", "loading", "separator"], outputs: ["selected"] }, { kind: "component", type: HierarchyBarPopoverItemComponent, selector: "ux-hierarchy-bar-popover-item", inputs: ["node"], outputs: ["selected"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarStandardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar-standard', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n  @if (isOverflowing$ | async) {\n    <div\n      #popover=\"ux-popover\"\n      class=\"hierarchy-bar-overflow-indicator\"\n      [uxPopover]=\"overflow\"\n      [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n      [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n      [popoverContext]=\"{ popover: popover }\"\n      placement=\"bottom\"\n      popoverClass=\"hierarchy-bar-popover\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n        [ngTemplateOutletContext]=\"{ $implicit: overflow$ | async }\"\n      >\n      </ng-container>\n      <button\n        [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        class=\"hierarchy-bar-node-arrow\"\n        placement=\"bottom\"\n        [uxPopover]=\"nodeList\"\n        popoverClass=\"hierarchy-bar-popover\"\n        role=\"button\"\n        tabindex=\"0\"\n        [disabled]=\"readonly\"\n        #popover=\"ux-popover\"\n        [popoverContext]=\"{ popover: popover }\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        type=\"button\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n      </button>\n    </div>\n  }\n  <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodelist class=\"hierarchy-bar-nodes\" (uxResize)=\"scrollIntoView()\">\n  @for (node of hierarchyBar.nodes$ | async; track node; let i = $index; let last = $last) {\n    <ux-hierarchy-bar-node\n      #barNodes\n      [mode]=\"mode\"\n      [readonly]=\"readonly\"\n      [node]=\"node\"\n      [popoverTemplate]=\"content\"\n      (selected)=\"hierarchyBar.selectNode(node)\"\n      [offset]=\"-overflowTranslateOffset\"\n      [style.max-width.px]=\"(isOverflowing$ | async) && last ? nodelist.offsetWidth : null\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  <!-- Allow content to be placed after the last node -->\n  <div class=\"hierarchy-bar-addons\">\n    <ng-content select=\"trailing-addons\"></ng-content>\n  </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n    [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the popover list -->\n<ng-template #nodeList let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"node\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the overflow popover list -->\n<ng-template #overflow let-popover=\"popover\">\n  <div uxTabbableList [returnFocus]=\"true\">\n    @for (child of overflow$ | async; track child; let first = $first) {\n      <ux-hierarchy-bar-popover-item\n        uxFocusIndicator\n        [node]=\"child\"\n        [focusIf]=\"first\"\n        uxTabbableListItem\n        (selected)=\"hierarchyBar.selectNode(child); popover.hide()\"\n      >\n      </ux-hierarchy-bar-popover-item>\n    }\n  </div>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n  <button\n    uxFocusIndicator\n    uxFocusIndicatorOrigin\n    [disabled]=\"readonly\"\n    class=\"overflow-button\"\n    aria-label=\"Show parents\"\n    role=\"button\"\n    type=\"button\"\n  >\n    <ux-icon name=\"more\"></ux-icon>\n  </button>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { mode: [{
                type: Input
            }], readonly: [{
                type: Input
            }], nodelist: [{
                type: ViewChild,
                args: ['nodelist', { static: true }]
            }], nodes: [{
                type: ViewChildren,
                args: [HierarchyBarNodeComponent, { read: ElementRef }]
            }], nodeInstances: [{
                type: ViewChildren,
                args: [HierarchyBarNodeComponent]
            }], barNodes: [{
                type: ViewChildren,
                args: ['barNodes', { read: ElementRef }]
            }] } });

class HierarchyBarCollapsedComponent {
    constructor() {
        this.hierarchyBar = inject(HierarchyBarService);
        this._renderer = inject(Renderer2);
        this._resizeService = inject(ResizeService);
        this._elementRef = inject(ElementRef);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
    }
    /** Get all the sibling nodes */
    get _siblings() {
        return this.hierarchyBar.getSiblings(this._last);
    }
    /** Get all the nodes between the first and last nodes */
    get _parents() {
        return this._nodes.filter(node => node !== this._first && node !== this._last);
    }
    /** Get the nodes as an array */
    get _nodes() {
        return this.hierarchyBar.nodes$.value;
    }
    ngAfterViewInit() {
        // Update the UI when the selected nodes change
        this.hierarchyBar.nodes$.pipe(takeUntil(this._onDestroy)).subscribe(this.update.bind(this));
        // watch for the host element size changing
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.updateOverflow());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        // remove the resize event listener
        this._resizeService.removeResizeListener(this._elementRef.nativeElement);
    }
    update(nodes) {
        this._first = nodes[0];
        this._last = nodes.length > 1 ? nodes[nodes.length - 1] : null;
        this.updateOverflow();
        this._changeDetector.detectChanges();
    }
    updateOverflow() {
        // remove the class if it is present
        this._renderer.removeClass(this.nodeContainer.nativeElement, 'hierarchy-bar-nodes-overflow');
        // check if there is overflow
        if (this.nodeContainer.nativeElement.scrollWidth > this.nodeContainer.nativeElement.offsetWidth) {
            this._renderer.addClass(this.nodeContainer.nativeElement, 'hierarchy-bar-nodes-overflow');
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarCollapsedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarCollapsedComponent, isStandalone: false, selector: "ux-hierarchy-bar-collapsed", inputs: { readonly: "readonly" }, viewQueries: [{ propertyName: "nodeContainer", first: true, predicate: ["nodes"], descendants: true, static: true }], ngImport: i0, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodes class=\"hierarchy-bar-nodes\">\n  @if (_first) {\n    <ux-hierarchy-bar-node\n      [readonly]=\"readonly\"\n      [popoverTemplate]=\"content\"\n      [node]=\"_first\"\n      (selected)=\"hierarchyBar.selectNode($event)\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  @if (_parents.length > 0) {\n    <div class=\"hierarchy-bar-overflow\">\n      <div class=\"hierarchy-bar-overflow-container\">\n        <ng-container\n          [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n          [ngTemplateOutletContext]=\"{ $implicit: _parents }\"\n        >\n        </ng-container>\n      </div>\n      <button\n        [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        class=\"hierarchy-bar-node-arrow\"\n        placement=\"bottom\"\n        [uxPopover]=\"siblingsTemplate\"\n        popoverClass=\"hierarchy-bar-popover\"\n        role=\"button\"\n        tabindex=\"0\"\n        [disabled]=\"readonly\"\n        #popover=\"ux-popover\"\n        [popoverContext]=\"{ popover: popover }\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        type=\"button\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n      </button>\n    </div>\n  }\n\n  @if (_last) {\n    <ux-hierarchy-bar-node\n      [readonly]=\"readonly\"\n      [popoverTemplate]=\"content\"\n      [node]=\"_last\"\n      (selected)=\"hierarchyBar.selectNode($event)\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  <!-- Allow content to be placed after the last node -->\n  <div class=\"hierarchy-bar-addons\">\n    <ng-content select=\"trailing-addons\"></ng-content>\n  </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n    [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the siblings popover list -->\n<ng-template #siblingsTemplate let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"(_siblings | async)?.children\"\n    [loading]=\"(_siblings | async)?.loading\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the parents popover list -->\n<ng-template #parentsTemplate let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"_parents\"\n    [separator]=\"true\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n  <button\n    uxFocusIndicator\n    uxFocusIndicatorOrigin\n    [disabled]=\"readonly\"\n    class=\"overflow-button\"\n    aria-label=\"Show parents\"\n    [uxPopover]=\"parentsTemplate\"\n    popoverClass=\"hierarchy-bar-popover\"\n    role=\"button\"\n    tabindex=\"0\"\n    #popover=\"ux-popover\"\n    [popoverContext]=\"{ popover: popover }\"\n    [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n    [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n    placement=\"bottom\"\n    type=\"button\"\n  >\n    <ux-icon name=\"more\"></ux-icon>\n  </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }, { kind: "component", type: HierarchyBarNodeComponent, selector: "ux-hierarchy-bar-node", inputs: ["node", "popoverTemplate", "mode", "readonly", "offset"], outputs: ["selected"] }, { kind: "component", type: HierarchyBarPopoverComponent, selector: "ux-hierarchy-bar-popover", inputs: ["nodes", "loading", "separator"], outputs: ["selected"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarCollapsedComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar-collapsed', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Allow content to be placed on the left of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"left-addons\"></ng-content>\n</div>\n\n<div #nodes class=\"hierarchy-bar-nodes\">\n  @if (_first) {\n    <ux-hierarchy-bar-node\n      [readonly]=\"readonly\"\n      [popoverTemplate]=\"content\"\n      [node]=\"_first\"\n      (selected)=\"hierarchyBar.selectNode($event)\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  @if (_parents.length > 0) {\n    <div class=\"hierarchy-bar-overflow\">\n      <div class=\"hierarchy-bar-overflow-container\">\n        <ng-container\n          [ngTemplateOutlet]=\"hierarchyBar.overflowTemplate || defaultOverflowTemplate\"\n          [ngTemplateOutletContext]=\"{ $implicit: _parents }\"\n        >\n        </ng-container>\n      </div>\n      <button\n        [attr.aria-label]=\"hierarchyBar.showSiblingsAriaLabel\"\n        uxFocusIndicator\n        uxFocusIndicatorOrigin\n        class=\"hierarchy-bar-node-arrow\"\n        placement=\"bottom\"\n        [uxPopover]=\"siblingsTemplate\"\n        popoverClass=\"hierarchy-bar-popover\"\n        role=\"button\"\n        tabindex=\"0\"\n        [disabled]=\"readonly\"\n        #popover=\"ux-popover\"\n        [popoverContext]=\"{ popover: popover }\"\n        [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n        [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n        type=\"button\"\n      >\n        <ux-icon name=\"next\" class=\"hierarchy-bar-node-arrow-icon\"></ux-icon>\n      </button>\n    </div>\n  }\n\n  @if (_last) {\n    <ux-hierarchy-bar-node\n      [readonly]=\"readonly\"\n      [popoverTemplate]=\"content\"\n      [node]=\"_last\"\n      (selected)=\"hierarchyBar.selectNode($event)\"\n    >\n    </ux-hierarchy-bar-node>\n  }\n\n  <!-- Allow content to be placed after the last node -->\n  <div class=\"hierarchy-bar-addons\">\n    <ng-content select=\"trailing-addons\"></ng-content>\n  </div>\n</div>\n\n<!-- Allow content to be placed on the right of the items -->\n<div class=\"hierarchy-bar-addons\">\n  <ng-content select=\"right-addons\"></ng-content>\n</div>\n\n<!-- Template for the popover list -->\n<ng-template #content let-node=\"node\" let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [loading]=\"(hierarchyBar.getChildren(node) | async)?.loading\"\n    [nodes]=\"(hierarchyBar.getChildren(node) | async)?.children\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the siblings popover list -->\n<ng-template #siblingsTemplate let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"(_siblings | async)?.children\"\n    [loading]=\"(_siblings | async)?.loading\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Template for the parents popover list -->\n<ng-template #parentsTemplate let-popover=\"popover\">\n  <ux-hierarchy-bar-popover\n    [nodes]=\"_parents\"\n    [separator]=\"true\"\n    (selected)=\"hierarchyBar.selectNode($event); popover.hide()\"\n  >\n  </ux-hierarchy-bar-popover>\n</ng-template>\n\n<!-- Default Overflow Template -->\n<ng-template #defaultOverflowTemplate>\n  <button\n    uxFocusIndicator\n    uxFocusIndicatorOrigin\n    [disabled]=\"readonly\"\n    class=\"overflow-button\"\n    aria-label=\"Show parents\"\n    [uxPopover]=\"parentsTemplate\"\n    popoverClass=\"hierarchy-bar-popover\"\n    role=\"button\"\n    tabindex=\"0\"\n    #popover=\"ux-popover\"\n    [popoverContext]=\"{ popover: popover }\"\n    [showTriggers]=\"hierarchyBar.popoverShowTriggers\"\n    [hideTriggers]=\"hierarchyBar.popoverHideTriggers\"\n    placement=\"bottom\"\n    type=\"button\"\n  >\n    <ux-icon name=\"more\"></ux-icon>\n  </button>\n</ng-template>\n" }]
        }], propDecorators: { readonly: [{
                type: Input
            }], nodeContainer: [{
                type: ViewChild,
                args: ['nodes', { static: true }]
            }] } });

class HierarchyBarComponent {
    /** Define the root node of the hierarchy bar */
    set root(node) {
        this._hierarchyBar.setRootNode(node);
    }
    /** Define the selected node in the hierarchy bar */
    set selected(node) {
        this._hierarchyBar.selectNode(node);
    }
    /** Provide a custom loading indicator */
    set loadingIndicator(loadingIndicator) {
        this._hierarchyBar.loadingIndicator = loadingIndicator;
    }
    /** Provide a custom overflow template */
    set overflowTemplate(overflowTemplate) {
        this._hierarchyBar.overflowTemplate = overflowTemplate;
    }
    /** Define the events that show the popover when interacting with the arrows */
    set popoverShowTriggers(popoverShowTriggers) {
        this._hierarchyBar.popoverShowTriggers = popoverShowTriggers;
    }
    /** Define the events that hide the popover when interacting with the arrows */
    set popoverHideTriggers(popoverHideTriggers) {
        this._hierarchyBar.popoverHideTriggers = popoverHideTriggers;
    }
    /** Define the aria label for the show siblings popover button */
    set showSiblingsAriaLabel(label) {
        this._hierarchyBar.showSiblingsAriaLabel = label;
    }
    /** Allow a custom icon to be specified */
    set icon(icon) {
        this._hierarchyBar.icon = icon;
    }
    constructor() {
        this._hierarchyBar = inject(HierarchyBarService);
        /** Define which presentational mode we should display */
        this.mode = 'standard';
        /** hierarchy bar as being readonly - default false */
        this.readonly = false;
        /** Emit when the selected node changes */
        this.selectedChange = new EventEmitter();
        /** Unsubscribe from all subscriptions when component is destroyed */
        this._onDestroy = new Subject();
        // emit the latest selection value
        this._hierarchyBar.selection$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(selection => this.selectedChange.next(selection));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: HierarchyBarComponent, isStandalone: false, selector: "ux-hierarchy-bar", inputs: { mode: "mode", readonly: "readonly", root: "root", selected: "selected", loadingIndicator: "loadingIndicator", overflowTemplate: "overflowTemplate", popoverShowTriggers: "popoverShowTriggers", popoverHideTriggers: "popoverHideTriggers", showSiblingsAriaLabel: "showSiblingsAriaLabel" }, outputs: { selectedChange: "selectedChange" }, queries: [{ propertyName: "icon", first: true, predicate: HierarchyBarNodeIconDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: "<!-- Hierarchy Bar - Standard Layout -->\n@if (mode !== 'collapsed') {\n  <ux-hierarchy-bar-standard [readonly]=\"readonly\" [mode]=\"mode\">\n    <!-- Forward the content to the correct layout -->\n    <ng-container ngProjectAs=\"left-addons\" [ngTemplateOutlet]=\"leftAddons\"></ng-container>\n    <ng-container ngProjectAs=\"trailing-addons\" [ngTemplateOutlet]=\"trailingAddons\"></ng-container>\n    <ng-container ngProjectAs=\"right-addons\" [ngTemplateOutlet]=\"rightAddons\"></ng-container>\n  </ux-hierarchy-bar-standard>\n}\n\n<!-- Hierarchy Bar - Collapsed Layout -->\n@if (mode === 'collapsed') {\n  <ux-hierarchy-bar-collapsed [readonly]=\"readonly\">\n    <!-- Forward the content to the correct layout -->\n    <ng-container ngProjectAs=\"left-addons\" [ngTemplateOutlet]=\"leftAddons\"></ng-container>\n    <ng-container ngProjectAs=\"trailing-addons\" [ngTemplateOutlet]=\"trailingAddons\"></ng-container>\n    <ng-container ngProjectAs=\"right-addons\" [ngTemplateOutlet]=\"rightAddons\"></ng-container>\n  </ux-hierarchy-bar-collapsed>\n}\n\n<!-- We can only have one ng-content so this allows us to use it more than once -->\n<ng-template #leftAddons>\n  <ng-content select=\"[uxHierarchyBarLeftAddon]\"></ng-content>\n</ng-template>\n\n<ng-template #trailingAddons>\n  <ng-content select=\"[uxHierarchyBarTrailingAddon]\"></ng-content>\n</ng-template>\n\n<ng-template #rightAddons>\n  <ng-content select=\"[uxHierarchyBarRightAddon]\"></ng-content>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HierarchyBarStandardComponent, selector: "ux-hierarchy-bar-standard", inputs: ["mode", "readonly"] }, { kind: "component", type: HierarchyBarCollapsedComponent, selector: "ux-hierarchy-bar-collapsed", inputs: ["readonly"] }], viewProviders: [HierarchyBarService], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-hierarchy-bar', changeDetection: ChangeDetectionStrategy.OnPush, viewProviders: [HierarchyBarService], standalone: false, template: "<!-- Hierarchy Bar - Standard Layout -->\n@if (mode !== 'collapsed') {\n  <ux-hierarchy-bar-standard [readonly]=\"readonly\" [mode]=\"mode\">\n    <!-- Forward the content to the correct layout -->\n    <ng-container ngProjectAs=\"left-addons\" [ngTemplateOutlet]=\"leftAddons\"></ng-container>\n    <ng-container ngProjectAs=\"trailing-addons\" [ngTemplateOutlet]=\"trailingAddons\"></ng-container>\n    <ng-container ngProjectAs=\"right-addons\" [ngTemplateOutlet]=\"rightAddons\"></ng-container>\n  </ux-hierarchy-bar-standard>\n}\n\n<!-- Hierarchy Bar - Collapsed Layout -->\n@if (mode === 'collapsed') {\n  <ux-hierarchy-bar-collapsed [readonly]=\"readonly\">\n    <!-- Forward the content to the correct layout -->\n    <ng-container ngProjectAs=\"left-addons\" [ngTemplateOutlet]=\"leftAddons\"></ng-container>\n    <ng-container ngProjectAs=\"trailing-addons\" [ngTemplateOutlet]=\"trailingAddons\"></ng-container>\n    <ng-container ngProjectAs=\"right-addons\" [ngTemplateOutlet]=\"rightAddons\"></ng-container>\n  </ux-hierarchy-bar-collapsed>\n}\n\n<!-- We can only have one ng-content so this allows us to use it more than once -->\n<ng-template #leftAddons>\n  <ng-content select=\"[uxHierarchyBarLeftAddon]\"></ng-content>\n</ng-template>\n\n<ng-template #trailingAddons>\n  <ng-content select=\"[uxHierarchyBarTrailingAddon]\"></ng-content>\n</ng-template>\n\n<ng-template #rightAddons>\n  <ng-content select=\"[uxHierarchyBarRightAddon]\"></ng-content>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { mode: [{
                type: Input
            }], readonly: [{
                type: Input
            }], root: [{
                type: Input
            }], selected: [{
                type: Input
            }], loadingIndicator: [{
                type: Input
            }], overflowTemplate: [{
                type: Input
            }], popoverShowTriggers: [{
                type: Input
            }], popoverHideTriggers: [{
                type: Input
            }], showSiblingsAriaLabel: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], icon: [{
                type: ContentChild,
                args: [HierarchyBarNodeIconDirective, { read: TemplateRef, static: false }]
            }] } });

class ClickOutsideModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideModule, declarations: [ClickOutsideDirective], exports: [ClickOutsideDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClickOutsideModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [ClickOutsideDirective],
                    declarations: [ClickOutsideDirective],
                }]
        }] });

class PopoverModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: PopoverModule, declarations: [PopoverComponent, PopoverDirective], imports: [CommonModule, OverlayModule, ObserversModule$1, ClickOutsideModule, TooltipModule], exports: [PopoverDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverModule, imports: [CommonModule, OverlayModule, ObserversModule$1, ClickOutsideModule, TooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PopoverModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, OverlayModule, ObserversModule$1, ClickOutsideModule, TooltipModule],
                    exports: [PopoverDirective],
                    declarations: [PopoverComponent, PopoverDirective],
                }]
        }] });

class HierarchyBarModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarModule, declarations: [HierarchyBarComponent,
            HierarchyBarStandardComponent,
            HierarchyBarCollapsedComponent,
            HierarchyBarNodeComponent,
            HierarchyBarPopoverComponent,
            HierarchyBarPopoverItemComponent,
            HierarchyBarNodeIconDirective], imports: [AccessibilityModule,
            CommonModule,
            FocusIfModule,
            IconModule,
            PopoverModule,
            ResizeModule], exports: [HierarchyBarComponent,
            HierarchyBarStandardComponent,
            HierarchyBarCollapsedComponent,
            HierarchyBarNodeIconDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarModule, imports: [AccessibilityModule,
            CommonModule,
            FocusIfModule,
            IconModule,
            PopoverModule,
            ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HierarchyBarModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        FocusIfModule,
                        IconModule,
                        PopoverModule,
                        ResizeModule,
                    ],
                    exports: [
                        HierarchyBarComponent,
                        HierarchyBarStandardComponent,
                        HierarchyBarCollapsedComponent,
                        HierarchyBarNodeIconDirective,
                    ],
                    declarations: [
                        HierarchyBarComponent,
                        HierarchyBarStandardComponent,
                        HierarchyBarCollapsedComponent,
                        HierarchyBarNodeComponent,
                        HierarchyBarPopoverComponent,
                        HierarchyBarPopoverItemComponent,
                        HierarchyBarNodeIconDirective,
                    ],
                }]
        }] });

class InputDropdownComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Filter text */
        this.filter = '';
        /** Controls the disabled state of the input-dropdown. */
        this.disabled = false;
        /** Define the placeholder for the filter input */
        this.placeholder = 'Type to filter...';
        /** Aria label of the filter field. If not specified, the placeholder will be used. */
        this.ariaLabel = '';
        /** Aria label of the search button icon. */
        this.searchFilterButtonAriaLabel = 'Search';
        /** Aria label of the clear button icon. */
        this.clearFilterButtonAriaLabel = 'Clear';
        /** Emit when the selected item is changed */
        this.selectedChange = new EventEmitter();
        /** Emit when the filter text is changed */
        this.filterChange = new EventEmitter();
        /** Emits when `dropdownOpen` changes. */
        this.dropdownOpenChange = new EventEmitter();
        /** The status of the dropdown. */
        this.dropdownOpen = false;
        /** Store the filter button aria label */
        this._filterButtonAriaLabel = this.searchFilterButtonAriaLabel;
        /** Store the change callback provided by Angular Forms */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChange = () => { };
        /** Store the touched callback provided by Angular Forms */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouched = () => { };
        /** Unsubscribe from all observables on component destroy */
        this._onDestroy$ = new Subject();
    }
    /** Define the max height of the dropdown */
    set maxHeight(value) {
        this._maxHeight = coerceCssPixelValue(value);
    }
    ngOnChanges(changes) {
        // if the dropdownOpen state changes via the input we should show or hide the input accordingly
        if (changes.dropdownOpen &&
            !changes.dropdownOpen.firstChange &&
            changes.dropdownOpen.currentValue !== changes.dropdownOpen.previousValue) {
            changes.dropdownOpen.currentValue
                ? this.menuTrigger.openMenu()
                : this.menuTrigger.closeMenu();
        }
        if (changes.selected) {
            // if an item is programmatically selected we should close the menu if it is open
            if (this.menuTrigger && !changes.selected.firstChange) {
                this.menuTrigger.closeMenu();
            }
            this.resetFilter();
            this.selectedChange.emit(changes.selected.currentValue);
            this.onChange(changes.selected.currentValue);
            this.onTouched();
        }
        if (changes.filter) {
            this.setFilterButtonAriaLabel();
        }
    }
    ngAfterViewInit() {
        // if the user has initially set the dropdownOpen input to true we should open the menu
        // once we have access to the ViewChild menu trigger directive
        if (this.dropdownOpen) {
            // trigger menu open on the next tick to avoid expression changed issues)
            Promise.resolve().then(() => this.menuTrigger.openMenu());
        }
        this._changeDetector.detectChanges();
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    resetFilter() {
        this.filter = '';
        this.filterChange.emit(this.filter);
        this._focusFilter();
    }
    registerOnChange(onChange) {
        this.onChange = onChange;
    }
    registerOnTouched(onTouched) {
        this.onTouched = onTouched;
    }
    writeValue(value) {
        this.selected = value;
        this._changeDetector.markForCheck();
    }
    resetValue(event) {
        if (this.disabled) {
            return;
        }
        this.writeValue(undefined);
        this.selectedChange.emit(undefined);
        event.stopPropagation();
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    onMenuOpen() {
        if (this.dropdownOpen !== true) {
            this.dropdownOpen = true;
            this.dropdownOpenChange.emit(this.dropdownOpen);
            this._focusFilter();
        }
    }
    onMenuClose() {
        if (this.dropdownOpen !== false) {
            this.dropdownOpen = false;
            this.dropdownOpenChange.emit(this.dropdownOpen);
        }
    }
    _focusFilter() {
        if (this.filterInputElement) {
            this.filterInputElement.nativeElement.focus();
        }
    }
    inputFocusHandler() {
        if (!this.dropdownOpen) {
            this.dropdownOpen = true;
            this.dropdownOpenChange.emit(this.dropdownOpen);
        }
    }
    toggleMenu() {
        if (this.disabled) {
            return;
        }
        this.dropdownOpen = !this.dropdownOpen;
        this.dropdownOpenChange.emit(this.dropdownOpen);
        this.menuTrigger.toggleMenu();
    }
    setFilterButtonAriaLabel() {
        this._filterButtonAriaLabel =
            this.filter === '' ? this.searchFilterButtonAriaLabel : this.clearFilterButtonAriaLabel;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: InputDropdownComponent, isStandalone: false, selector: "ux-input-dropdown", inputs: { selected: "selected", filter: "filter", hideFilter: "hideFilter", maxHeight: "maxHeight", disabled: "disabled", allowNull: "allowNull", placeholder: "placeholder", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: "ariaLabelledby", searchFilterButtonAriaLabel: "searchFilterButtonAriaLabel", clearFilterButtonAriaLabel: "clearFilterButtonAriaLabel", dropdownOpen: "dropdownOpen" }, outputs: { selectedChange: "selectedChange", filterChange: "filterChange", dropdownOpenChange: "dropdownOpenChange" }, host: { properties: { "class.ux-select-disabled": "disabled", "attr.aria-label": "null" } }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                multi: true,
                useExisting: forwardRef(() => InputDropdownComponent),
            },
        ], queries: [{ propertyName: "displayContentRef", first: true, predicate: ["displayContent"], descendants: true }], viewQueries: [{ propertyName: "menuTrigger", first: true, predicate: MenuTriggerDirective, descendants: true }, { propertyName: "filterInputElement", first: true, predicate: ["filterInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ux-select-container\">\n  <button\n    #button\n    type=\"button\"\n    class=\"form-control\"\n    [uxMenuTriggerFor]=\"menu\"\n    [disabled]=\"disabled\"\n  >\n    <ng-template #defaultDisplayContent>{{ selected ? (selected | json) : '-' }}</ng-template>\n    <ng-container [ngTemplateOutlet]=\"displayContentRef || defaultDisplayContent\"></ng-container>\n  </button>\n  <div class=\"ux-select-icons\">\n    @if (allowNull && selected) {\n      <ux-icon\n        name=\"close\"\n        uxFocusIndicator\n        class=\"ux-select-icon ux-select-clear-icon\"\n        (click)=\"resetValue($event)\"\n        (keydown.enter)=\"resetValue($event)\"\n        tabindex=\"0\"\n      >\n      </ux-icon>\n    }\n    <ux-icon\n      name=\"chevron-down\"\n      class=\"ux-select-icon ux-select-chevron-icon\"\n      (click)=\"toggleMenu(); $event.stopPropagation()\"\n    >\n    </ux-icon>\n  </div>\n</div>\n\n<ux-menu #menu menuClass=\"select-menu\" (opened)=\"onMenuOpen()\" (closed)=\"onMenuClose()\">\n  <div [style.max-height]=\"_maxHeight\" [style.width.px]=\"button.offsetWidth\">\n    @if (!hideFilter) {\n      <div class=\"filter-container\">\n        <input\n          #filterInput\n          type=\"text\"\n          [placeholder]=\"placeholder\"\n          class=\"form-control\"\n          [(ngModel)]=\"filter\"\n          (input)=\"filterChange.emit(filter)\"\n          (click)=\"$event.stopPropagation()\"\n          [attr.aria-label]=\"ariaLabel || placeholder\"\n          [attr.aria-labelledby]=\"ariaLabelledby\"\n          (focus)=\"inputFocusHandler()\"\n        />\n        <button\n          type=\"button\"\n          class=\"btn btn-flat filter-button\"\n          [attr.aria-label]=\"_filterButtonAriaLabel\"\n          (click)=\"resetFilter(); $event.stopPropagation()\"\n          [tabindex]=\"filter.length > 0 ? 0 : -1\"\n        >\n          <ux-icon [name]=\"filter.length === 0 ? 'search' : 'close'\"></ux-icon>\n        </button>\n      </div>\n    }\n\n    <ng-content></ng-content>\n  </div>\n</ux-menu>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "pipe", type: i1.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-input-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            multi: true,
                            useExisting: forwardRef(() => InputDropdownComponent),
                        },
                    ], host: {
                        '[class.ux-select-disabled]': 'disabled',
                        '[attr.aria-label]': 'null',
                    }, standalone: false, template: "<div class=\"ux-select-container\">\n  <button\n    #button\n    type=\"button\"\n    class=\"form-control\"\n    [uxMenuTriggerFor]=\"menu\"\n    [disabled]=\"disabled\"\n  >\n    <ng-template #defaultDisplayContent>{{ selected ? (selected | json) : '-' }}</ng-template>\n    <ng-container [ngTemplateOutlet]=\"displayContentRef || defaultDisplayContent\"></ng-container>\n  </button>\n  <div class=\"ux-select-icons\">\n    @if (allowNull && selected) {\n      <ux-icon\n        name=\"close\"\n        uxFocusIndicator\n        class=\"ux-select-icon ux-select-clear-icon\"\n        (click)=\"resetValue($event)\"\n        (keydown.enter)=\"resetValue($event)\"\n        tabindex=\"0\"\n      >\n      </ux-icon>\n    }\n    <ux-icon\n      name=\"chevron-down\"\n      class=\"ux-select-icon ux-select-chevron-icon\"\n      (click)=\"toggleMenu(); $event.stopPropagation()\"\n    >\n    </ux-icon>\n  </div>\n</div>\n\n<ux-menu #menu menuClass=\"select-menu\" (opened)=\"onMenuOpen()\" (closed)=\"onMenuClose()\">\n  <div [style.max-height]=\"_maxHeight\" [style.width.px]=\"button.offsetWidth\">\n    @if (!hideFilter) {\n      <div class=\"filter-container\">\n        <input\n          #filterInput\n          type=\"text\"\n          [placeholder]=\"placeholder\"\n          class=\"form-control\"\n          [(ngModel)]=\"filter\"\n          (input)=\"filterChange.emit(filter)\"\n          (click)=\"$event.stopPropagation()\"\n          [attr.aria-label]=\"ariaLabel || placeholder\"\n          [attr.aria-labelledby]=\"ariaLabelledby\"\n          (focus)=\"inputFocusHandler()\"\n        />\n        <button\n          type=\"button\"\n          class=\"btn btn-flat filter-button\"\n          [attr.aria-label]=\"_filterButtonAriaLabel\"\n          (click)=\"resetFilter(); $event.stopPropagation()\"\n          [tabindex]=\"filter.length > 0 ? 0 : -1\"\n        >\n          <ux-icon [name]=\"filter.length === 0 ? 'search' : 'close'\"></ux-icon>\n        </button>\n      </div>\n    }\n\n    <ng-content></ng-content>\n  </div>\n</ux-menu>\n" }]
        }], propDecorators: { selected: [{
                type: Input
            }], filter: [{
                type: Input
            }], hideFilter: [{
                type: Input
            }], maxHeight: [{
                type: Input
            }], disabled: [{
                type: Input
            }], allowNull: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], ariaLabelledby: [{
                type: Input
            }], searchFilterButtonAriaLabel: [{
                type: Input
            }], clearFilterButtonAriaLabel: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], filterChange: [{
                type: Output
            }], dropdownOpenChange: [{
                type: Output
            }], dropdownOpen: [{
                type: Input
            }], displayContentRef: [{
                type: ContentChild,
                args: ['displayContent', { static: false }]
            }], menuTrigger: [{
                type: ViewChild,
                args: [MenuTriggerDirective, { static: false }]
            }], filterInputElement: [{
                type: ViewChild,
                args: ['filterInput', { static: false }]
            }] } });

class InputDropdownModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownModule, declarations: [InputDropdownComponent], imports: [CommonModule, FormsModule, IconModule, MenuModule, AccessibilityModule], exports: [InputDropdownComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownModule, imports: [CommonModule, FormsModule, IconModule, MenuModule, AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InputDropdownModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, FormsModule, IconModule, MenuModule, AccessibilityModule],
                    declarations: [InputDropdownComponent],
                    exports: [InputDropdownComponent],
                }]
        }] });

var SidePanelAnimationState;
(function (SidePanelAnimationState) {
    SidePanelAnimationState["Closed"] = "closed";
    SidePanelAnimationState["Open"] = "open";
    SidePanelAnimationState["OpenImmediate"] = "openImmediate";
})(SidePanelAnimationState || (SidePanelAnimationState = {}));
const sidePanelStateAnimation = trigger('panelState', [
    state(SidePanelAnimationState.Closed, style({ visibility: 'hidden' })),
    state(`${SidePanelAnimationState.Open}, ${SidePanelAnimationState.OpenImmediate}`, style({ visibility: 'visible', transform: 'none' })),
    transition(`void <=> ${SidePanelAnimationState.Open}`, animate('0.2s cubic-bezier(0.49, 1, 0.38, 0.98)')),
    transition(`void <=> ${SidePanelAnimationState.OpenImmediate}`, animate('0s')),
]);

class SidePanelService {
    constructor() {
        /** Emit the open state when it changes */
        this.open$ = new BehaviorSubject(false);
    }
    open() {
        this.open$.next(true);
    }
    close() {
        this.open$.next(false);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelService, decorators: [{
            type: Injectable
        }] });

class SidePanelComponent {
    constructor() {
        this.service = inject(SidePanelService);
        this._elementRef = inject(ElementRef);
        this._focusOrigin = inject(FocusIndicatorOriginService);
        this.inline = false;
        this.attachTo = 'window';
        this.width = '50%';
        this.top = '0';
        this.modal = false;
        this.animate = false;
        this.closeOnExternalClick = false;
        this.focusOnShow = false;
        this.openChange = new EventEmitter();
        this.closeOnEscape = true;
        this.animationPanelState = SidePanelAnimationState.Closed;
        this._onDestroy = new Subject();
    }
    get open() {
        return this.service.open$.value;
    }
    set open(value) {
        this.service.open$.next(value);
    }
    get position() {
        if (this.inline) {
            return 'static';
        }
        if (this.attachTo === 'container') {
            return 'absolute';
        }
        return 'fixed';
    }
    get cssWidth() {
        return this.getCssValue(this.width);
    }
    get cssTop() {
        return this.getCssValue(this.top);
    }
    get cssMinWidth() {
        return this.getCssValue(this.minWidth);
    }
    get cssMaxWidth() {
        return this.getCssValue(this.maxWidth);
    }
    get componentWidth() {
        if (this.inline) {
            return this.open ? this.cssWidth : '0';
        }
        return null;
    }
    get hostWidth() {
        return this.inline ? '100%' : this.cssWidth;
    }
    get hostMinWidth() {
        return this.inline ? undefined : this.cssMinWidth;
    }
    get hostMaxWidth() {
        return this.inline ? undefined : this.cssMaxWidth;
    }
    ngOnInit() {
        this.service.open$
            .pipe(skip(1), distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(isOpen => this.openChange.emit(isOpen));
        this.service.open$
            .pipe(distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(isOpen => {
            this.animationPanelState = isOpen
                ? this.animate
                    ? SidePanelAnimationState.Open
                    : SidePanelAnimationState.OpenImmediate
                : SidePanelAnimationState.Closed;
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    openPanel() {
        this.service.open();
    }
    closePanel() {
        this.service.close();
    }
    _onDocumentEscape() {
        if (this.open && this.closeOnEscape) {
            this._focusOrigin.setOrigin('keyboard');
            this.closePanel();
        }
    }
    _onDocumentClick(target) {
        if (!this.open || !this.closeOnExternalClick) {
            return;
        }
        if (!this._elementRef.nativeElement.contains(target) ||
            (target && target.classList.contains('modal-backdrop'))) {
            this.closePanel();
        }
    }
    getCssValue(value) {
        if (typeof value === 'number') {
            return value === 0 ? '0' : value + 'px';
        }
        return value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SidePanelComponent, isStandalone: false, selector: "ux-side-panel", inputs: { open: "open", inline: "inline", attachTo: "attachTo", width: "width", minWidth: "minWidth", maxWidth: "maxWidth", top: "top", modal: "modal", animate: "animate", closeOnExternalClick: "closeOnExternalClick", focusOnShow: "focusOnShow", closeOnEscape: "closeOnEscape" }, outputs: { openChange: "openChange" }, host: { listeners: { "document:keyup.escape": "_onDocumentEscape()", "document:click": "_onDocumentClick($event.target)" }, properties: { "class.open": "this.open", "class.inline": "this.inline", "attr.aria-modal": "this.modal", "class.animate": "this.animate", "style.width": "this.componentWidth" }, classAttribute: "ux-side-panel" }, providers: [SidePanelService], exportAs: ["ux-side-panel"], ngImport: i0, template: "@if (modal && open) {\n  <div class=\"modal-backdrop\" [style.position]=\"position\" [style.top]=\"cssTop\"></div>\n}\n\n@if (open) {\n  <div\n    class=\"ux-side-panel-host\"\n    [class.modal-panel]=\"modal\"\n    [style.position]=\"position\"\n    [style.width]=\"hostWidth\"\n    [style.min-width]=\"hostMinWidth\"\n    [style.max-width]=\"hostMaxWidth\"\n    [style.top]=\"cssTop\"\n    [tabindex]=\"open ? 0 : -1\"\n    [@panelState]=\"animationPanelState\"\n    [focusIf]=\"open && focusOnShow\"\n    [focusIfScroll]=\"false\"\n    [cdkTrapFocus]=\"open && modal\"\n  >\n    <ng-content></ng-content>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: i1$1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }], animations: [sidePanelStateAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-side-panel', exportAs: 'ux-side-panel', providers: [SidePanelService], animations: [sidePanelStateAnimation], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        class: 'ux-side-panel',
                    }, standalone: false, template: "@if (modal && open) {\n  <div class=\"modal-backdrop\" [style.position]=\"position\" [style.top]=\"cssTop\"></div>\n}\n\n@if (open) {\n  <div\n    class=\"ux-side-panel-host\"\n    [class.modal-panel]=\"modal\"\n    [style.position]=\"position\"\n    [style.width]=\"hostWidth\"\n    [style.min-width]=\"hostMinWidth\"\n    [style.max-width]=\"hostMaxWidth\"\n    [style.top]=\"cssTop\"\n    [tabindex]=\"open ? 0 : -1\"\n    [@panelState]=\"animationPanelState\"\n    [focusIf]=\"open && focusOnShow\"\n    [focusIfScroll]=\"false\"\n    [cdkTrapFocus]=\"open && modal\"\n  >\n    <ng-content></ng-content>\n  </div>\n}\n" }]
        }], propDecorators: { open: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.open']
            }], inline: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.inline']
            }], attachTo: [{
                type: Input
            }], width: [{
                type: Input
            }], minWidth: [{
                type: Input
            }], maxWidth: [{
                type: Input
            }], top: [{
                type: Input
            }], modal: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.aria-modal']
            }], animate: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.animate']
            }], closeOnExternalClick: [{
                type: Input
            }], focusOnShow: [{
                type: Input
            }], openChange: [{
                type: Output
            }], closeOnEscape: [{
                type: Input
            }], componentWidth: [{
                type: HostBinding,
                args: ['style.width']
            }], _onDocumentEscape: [{
                type: HostListener,
                args: ['document:keyup.escape']
            }], _onDocumentClick: [{
                type: HostListener,
                args: ['document:click', ['$event.target']]
            }] } });

class ItemDisplayPanelContentDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ItemDisplayPanelContentDirective, isStandalone: false, selector: "[uxItemDisplayPanelContent]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelContentDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxItemDisplayPanelContent]',
                    standalone: false,
                }]
        }] });
class ItemDisplayPanelFooterDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ItemDisplayPanelFooterDirective, isStandalone: false, selector: "[uxItemDisplayPanelFooter]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelFooterDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxItemDisplayPanelFooter]',
                    standalone: false,
                }]
        }] });
class ItemDisplayPanelComponent extends SidePanelComponent {
    get preventClose() {
        return !this.closeOnExternalClick;
    }
    set preventClose(value) {
        this.closeOnExternalClick = !value;
    }
    set visible(visible) {
        this.open = visible;
    }
    get visible() {
        return this.open;
    }
    constructor() {
        super();
        this.boxShadow = true;
        this.closeVisible = true;
        /** Defines the aria-label for the close button */
        this.closeAriaLabel = 'Close';
        this.shadow = false;
        this.visibleChange = new EventEmitter();
        this.animate = false;
        this.closeOnExternalClick = true;
    }
    ngOnInit() {
        super.ngOnInit();
        this.service.open$
            .pipe(distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(isVisible => this.visibleChange.emit(isVisible));
    }
    focus() {
        if (this.panel) {
            this.panel.nativeElement.focus();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ItemDisplayPanelComponent, isStandalone: false, selector: "ux-item-display-panel", inputs: { header: "header", boxShadow: "boxShadow", closeVisible: "closeVisible", preventClose: "preventClose", closeAriaLabel: "closeAriaLabel", shadow: "shadow", visible: "visible" }, outputs: { visibleChange: "visibleChange" }, host: { classAttribute: "ux-side-panel ux-item-display-panel" }, providers: [SidePanelService], queries: [{ propertyName: "footer", first: true, predicate: ItemDisplayPanelFooterDirective, descendants: true }], viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "@if (open) {\n  <div\n    class=\"ux-side-panel-host ux-item-display-panel\"\n    #panel\n    [class.box-shadow]=\"boxShadow\"\n    [style.position]=\"position\"\n    [style.width]=\"hostWidth\"\n    [style.top]=\"cssTop\"\n    [@panelState]=\"animationPanelState\"\n    [tabindex]=\"open ? 0 : -1\"\n    [focusIf]=\"open && focusOnShow\"\n  >\n    <div class=\"ux-side-panel-header\" [class.item-display-panel-shadow]=\"shadow\">\n      <h3>{{ header }}</h3>\n      @if (closeVisible) {\n        <button\n          uxFocusIndicator\n          [attr.aria-label]=\"closeAriaLabel\"\n          type=\"button\"\n          class=\"btn btn-lg btn-link btn-icon button-secondary\"\n          (click)=\"visible = false\"\n        >\n          <ux-icon name=\"close\"></ux-icon>\n        </button>\n      }\n    </div>\n    <div class=\"ux-side-panel-content\">\n      <ng-content select=\"[uxItemDisplayPanelContent]\"></ng-content>\n    </div>\n    @if (footer) {\n      <div class=\"ux-side-panel-footer\">\n        <ng-content select=\"[uxItemDisplayPanelFooter]\"></ng-content>\n      </div>\n    }\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], animations: [sidePanelStateAnimation] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-item-display-panel', providers: [SidePanelService], animations: [sidePanelStateAnimation], host: {
                        class: 'ux-side-panel ux-item-display-panel',
                    }, standalone: false, template: "@if (open) {\n  <div\n    class=\"ux-side-panel-host ux-item-display-panel\"\n    #panel\n    [class.box-shadow]=\"boxShadow\"\n    [style.position]=\"position\"\n    [style.width]=\"hostWidth\"\n    [style.top]=\"cssTop\"\n    [@panelState]=\"animationPanelState\"\n    [tabindex]=\"open ? 0 : -1\"\n    [focusIf]=\"open && focusOnShow\"\n  >\n    <div class=\"ux-side-panel-header\" [class.item-display-panel-shadow]=\"shadow\">\n      <h3>{{ header }}</h3>\n      @if (closeVisible) {\n        <button\n          uxFocusIndicator\n          [attr.aria-label]=\"closeAriaLabel\"\n          type=\"button\"\n          class=\"btn btn-lg btn-link btn-icon button-secondary\"\n          (click)=\"visible = false\"\n        >\n          <ux-icon name=\"close\"></ux-icon>\n        </button>\n      }\n    </div>\n    <div class=\"ux-side-panel-content\">\n      <ng-content select=\"[uxItemDisplayPanelContent]\"></ng-content>\n    </div>\n    @if (footer) {\n      <div class=\"ux-side-panel-footer\">\n        <ng-content select=\"[uxItemDisplayPanelFooter]\"></ng-content>\n      </div>\n    }\n  </div>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { header: [{
                type: Input
            }], boxShadow: [{
                type: Input
            }], closeVisible: [{
                type: Input
            }], preventClose: [{
                type: Input
            }], closeAriaLabel: [{
                type: Input
            }], shadow: [{
                type: Input
            }], visibleChange: [{
                type: Output
            }], footer: [{
                type: ContentChild,
                args: [ItemDisplayPanelFooterDirective, { static: false }]
            }], panel: [{
                type: ViewChild,
                args: ['panel', { static: true }]
            }], visible: [{
                type: Input
            }] } });

const DECLARATIONS$6 = [
    ItemDisplayPanelComponent,
    ItemDisplayPanelContentDirective,
    ItemDisplayPanelFooterDirective,
];
class ItemDisplayPanelModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelModule, declarations: [ItemDisplayPanelComponent,
            ItemDisplayPanelContentDirective,
            ItemDisplayPanelFooterDirective], imports: [AccessibilityModule, CommonModule, FocusIfModule, IconModule], exports: [ItemDisplayPanelComponent,
            ItemDisplayPanelContentDirective,
            ItemDisplayPanelFooterDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelModule, imports: [AccessibilityModule, CommonModule, FocusIfModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ItemDisplayPanelModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, FocusIfModule, IconModule],
                    exports: DECLARATIONS$6,
                    declarations: DECLARATIONS$6,
                }]
        }] });

class MarqueeWizardStepIconDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardStepIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MarqueeWizardStepIconDirective, isStandalone: false, selector: "[uxMarqueeWizardStepIcon]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardStepIconDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMarqueeWizardStepIcon]',
                    standalone: false,
                }]
        }] });

/**
 * This service is required to provide a form of communication
 * between the marquee wizard steps and the containing marquee wizard.
 * We cannot inject the Host due to the steps being content children
 * rather than view children.
 */
class WizardService {
    constructor() {
        this.validChange$ = new Subject();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardService, decorators: [{
            type: Injectable
        }] });

class WizardStepComponent {
    constructor() {
        this._wizardService = inject(WizardService);
        this._changeDetector = inject(ChangeDetectorRef);
        /**
         * Defines whether a step is valid. The user will not be able to proceed to the next step if this property has a value of false.
         * If the new value is false is will also set the visited value to false.
         */
        this._valid = true;
        /**
         * Defines whether or not this step has previously been visited.
         * A visited step can be clicked on and jumped to at any time.
         * By default, steps will become 'visited' when the user navigates to a step for the first time.
         */
        this.visited = false;
        /** Emits when visited changes. */
        this.visitedChange = new EventEmitter();
        /**
         * Defines the currently visible step.
         */
        this._active = false;
    }
    set valid(value) {
        this.setValid(value);
    }
    get valid() {
        return this._valid;
    }
    set active(value) {
        const active = coerceBooleanProperty(value);
        // store the active state of the step
        this._active = active;
        // if the value is true then the step should also be marked as visited
        if (active && !this.visited) {
            this.setVisitedAndEmitChangeEvent(true);
        }
        // mark for change detection
        this._changeDetector.markForCheck();
    }
    get active() {
        return this._active;
    }
    setVisitedAndEmitChangeEvent(value) {
        if (value === this.visited) {
            return;
        }
        this.visited = value;
        this.visitedChange.emit(value);
    }
    setValid(value) {
        if (this._valid === value) {
            return;
        }
        this._valid = value;
        this._wizardService.validChange$.next({ step: this, valid: value });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: WizardStepComponent, isStandalone: false, selector: "ux-wizard-step", inputs: { header: "header", disableNextWhenInvalid: "disableNextWhenInvalid", valid: "valid", validator: "validator", visited: "visited" }, outputs: { visitedChange: "visitedChange" }, host: { attributes: { "role": "tabpanel" } }, ngImport: i0, template: "@if (active) {\n  <ng-content></ng-content>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardStepComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-wizard-step', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'tabpanel',
                    }, standalone: false, template: "@if (active) {\n  <ng-content></ng-content>\n}\n" }]
        }], propDecorators: { header: [{
                type: Input
            }], disableNextWhenInvalid: [{
                type: Input
            }], valid: [{
                type: Input
            }], validator: [{
                type: Input
            }], visited: [{
                type: Input
            }], visitedChange: [{
                type: Output
            }] } });

let uniqueId$5 = 0;
class WizardComponent {
    constructor() {
        this._wizardService = inject(WizardService);
        /** Defines whether or not the wizard should be displayed in a `horizontal` or `vertical` layout. */
        this.orientation = 'horizontal';
        /** Defines the text displayed in the 'Next' button. */
        this.nextText = 'Next';
        /** Defines the text displayed in the 'Previous' button. */
        this.previousText = 'Previous';
        /** Defines the text displayed in the 'Cancel' button. */
        this.cancelText = 'Cancel';
        /** Defines the text displayed in the 'Finish' button. */
        this.finishText = 'Finish';
        /** Defines the text displayed in the tooltip when the 'Next' button is hovered. */
        this.nextTooltip = 'Go to the next step';
        /** Defines the text displayed in the tooltip when the 'Previous' button is hovered. */
        this.previousTooltip = 'Go to the previous step';
        /** Defines the text displayed in the tooltip when the 'Cancel' button is hovered. */
        this.cancelTooltip = 'Cancel the wizard';
        /** Defines the text displayed in the tooltip when the 'Finish' button is hovered. */
        this.finishTooltip = 'Finish the wizard';
        /** Defines the text for the aria label on the 'Next' button. */
        this.nextAriaLabel = 'Go to the next step';
        /** Defines the text for the aria label on the 'Previous' button. */
        this.previousAriaLabel = 'Go to the previous step';
        /** Defines the text for the aria label on the 'Cancel' button. */
        this.cancelAriaLabel = 'Cancel the wizard';
        /** Defines the text for the aria label on the 'Finish' button. */
        this.finishAriaLabel = 'Finish the wizard';
        /** If set to `true` the 'Next' button will appear disabled and will not respond to clicks. */
        this.nextDisabled = false;
        /** If set to `true` the 'Previous' button will appear disabled and will not respond to clicks. */
        this.previousDisabled = false;
        /** If set to `true` the 'Cancel' button will appear disabled and will not respond to clicks. */
        this.cancelDisabled = false;
        /** If set to `true` the 'Finish' button will appear disabled and will not respond to clicks. */
        this.finishDisabled = false;
        /** If set to `false` the 'Next' button will be hidden. */
        this.nextVisible = true;
        /** If set to `false` the 'Previous' button will be hidden. */
        this.previousVisible = true;
        /** If set to `false` the 'Cancel' button will be hidden. */
        this.cancelVisible = true;
        /** If set to false the 'Finish' button will be hidden. */
        this.finishVisible = true;
        /** If set to `true` the 'Cancel' button will be visible even on the last step. By default it will be hidden on the final step. */
        this.cancelAlwaysVisible = false;
        /** If set to `true` the 'Finish' button will be visible on all steps of the wizard. By default this button will only be visible on the final step of the wizard. */
        this.finishAlwaysVisible = false;
        /** If set to `true` the 'Next' or 'Finish' button will become disabled when the current step is invalid. */
        this.disableNextWhenInvalid = false;
        /** Whether to set `visited` to false on subsequent steps after a validation fault. */
        this.resetVisitedOnValidationError = false;
        /** If set to false it will allow users to navigate to every step */
        this.sequential = true;
        /** Emits when the wizard has moved to the next step. It will receive the current step index as a parameter. */
        this.onNext = new EventEmitter();
        /** Emits when the wizard has moved to the previous step. It will receive the current step index as a parameter. */
        this.onPrevious = new EventEmitter();
        /** Emits when the 'Cancel' button has been pressed. */
        this.onCancel = new EventEmitter();
        /** Emits when the 'Finish' button is clicked, but before the finish event fires. This fires regardless of the validity of the final step. */
        this.onFinishing = new EventEmitter();
        /** Emits when the 'Finish' button has been pressed and the final step is valid. */
        this.onFinish = new EventEmitter();
        /** Emits before the current step changes. The event contains the current step index in the `from` property, and the requested step index in the `to` property. */
        this.stepChanging = new EventEmitter();
        /** Emits when the current step has changed. */
        this.stepChange = new EventEmitter();
        /** Emits when the user tries to continue but the current step is invalid. */
        this.stepError = new EventEmitter();
        this.steps = new QueryList();
        this.id = `ux-wizard-${uniqueId$5++}`;
        this.invalidIndicator = false;
        this._step = 0;
        this._onDestroy = new Subject();
    }
    /**
     * The current active step. When the step changes an event will be emitted containing the index of the newly active step.
     * If this is not specified the wizard will start on the first step.
     */
    get step() {
        return this._step;
    }
    set step(value) {
        // only accept numbers as valid options
        if (typeof value === 'number') {
            // store the active step
            this._step = value;
            // update which steps should be active
            this.update();
            // reset the invalid state
            this.invalidIndicator = false;
        }
    }
    ngOnInit() {
        // initially set the correct visibility of the steps
        setTimeout(this.update.bind(this));
        // watch for changes to valid subject
        this._wizardService.validChange$
            .pipe(filter((event) => !event.valid), takeUntil(this._onDestroy))
            .subscribe((event) => this.setFutureStepsUnvisited(event.step));
    }
    ngAfterContentInit() {
        this.steps.changes.pipe(tick(), takeUntil(this._onDestroy)).subscribe(this.update.bind(this));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Navigate to the next step
     */
    async next() {
        this.stepChanging.next(new StepChangingEvent(this.step, this.step + 1));
        const step = this.getCurrentStep();
        // Disable the button while waiting on validation
        this.nextDisabled = true;
        try {
            // Fetch validation status
            const validationResult = this.isStepValid();
            step.valid = validationResult instanceof Promise ? await validationResult : validationResult;
        }
        finally {
            // Re-enable button
            this.nextDisabled = false;
        }
        // check if current step is invalid
        if (!step.valid) {
            this.invalidIndicator = true;
            this.stepError.next(this.step);
            return;
        }
        // check if we are currently on the last step
        if (this.step + 1 < this.steps.length) {
            this.step++;
            this.stepChange.emit(this.step);
            // emit the current step
            this.onNext.next(this.step);
        }
    }
    /**
     * Whether the Next or Finish button should be disabled.
     */
    isNextDisabled() {
        const step = this.getCurrentStep();
        // ensure the step is not null before we try to access its properties. It may be null if an ngFor is being
        // used and the steps haven't rendered yet
        if (!step) {
            return false;
        }
        // Use the `disableNextWhenInvalid` setting to determine whether to disable the Next/Finish button
        // based on validation.
        // If not defined on the WizardStepComponent, use the value from WizardComponent.
        return ((step.disableNextWhenInvalid === undefined
            ? this.disableNextWhenInvalid
            : step.disableNextWhenInvalid) && !step.valid);
    }
    /**
     * Navigate to the previous step
     */
    previous() {
        this.stepChanging.next(new StepChangingEvent(this.step, this.step - 1));
        // check if we are currently on the last step
        if (this.step > 0) {
            this.step--;
            this.stepChange.emit(this.step);
            // emit the current step
            this.onPrevious.next(this.step);
        }
    }
    /**
     * Perform actions when the finish button is clicked
     */
    async finish() {
        // fires when the finish button is clicked always
        this.onFinishing.next();
        // Disable the button while waiting on validation
        this.finishDisabled = true;
        try {
            // Fetch validation status
            const validationResult = this.isStepValid();
            this.getCurrentStep().valid =
                validationResult instanceof Promise ? await validationResult : validationResult;
        }
        finally {
            // Re-enable button
            this.finishDisabled = false;
        }
        /**
         * This is required because we need to ensure change detection has run
         * to determine whether or not we have the latest value for the 'valid' input
         * on the current step. Unfortunately we can't use ChangeDetectorRef as we are looking to run
         * on content children, and we cant use ApplicationRef.tick() as this does not work in a hybrid app, eg. our docs
         */
        return new Promise(resolve => {
            setTimeout(() => {
                // only fires when the finish button is clicked and the step is valid
                if (this.getCurrentStep().valid) {
                    this.onFinish.emit();
                }
                else {
                    this.stepError.next(this.step);
                }
                resolve();
            });
        });
    }
    /**
     * Perform actions when the cancel button is clicked
     */
    cancel() {
        this.onCancel.next();
    }
    /**
     * Update the active state of each step
     */
    update() {
        // update which steps should be active
        this.steps.forEach((step, idx) => (step.active = idx === this.step));
    }
    /**
     * Jump to a specific step only if the step has previously been visited
     */
    gotoStep(step) {
        if (step.visited || !this.sequential) {
            const stepIndex = this.steps.toArray().findIndex(stp => stp === step);
            this.stepChanging.next(new StepChangingEvent(this.step, stepIndex));
            this.step = stepIndex;
            this.stepChange.emit(this.step);
        }
    }
    /**
     * Determine if the current step is the last step
     */
    isLastStep() {
        return this.step === this.steps.length - 1;
    }
    /**
     * Reset the wizard - goes to first step and resets visited state
     */
    reset() {
        // mark all steps as not visited
        this.steps.forEach(step => step.setVisitedAndEmitChangeEvent(false));
        // go to the first step
        this.step = 0;
        this.stepChange.emit(this.step);
    }
    /**
     * Get the step at the current index
     */
    getCurrentStep() {
        return this.getStepAtIndex(this.step);
    }
    /**
     * Return a step at a specific index
     */
    getStepAtIndex(index) {
        return this.steps.toArray()[index];
    }
    /**
     * If a step in the wizard becomes invalid, all steps sequentially after
     * it should become unvisited
     */
    setFutureStepsUnvisited(currentStep) {
        if (!this.resetVisitedOnValidationError) {
            return;
        }
        this.getFutureSteps(currentStep).forEach(step => {
            step.setVisitedAndEmitChangeEvent(false);
        });
    }
    /**
     * Get the currently active step and all steps beyond it
     */
    getFutureSteps(currentStep) {
        const currentIndex = this.steps.toArray().indexOf(currentStep);
        return this.steps.toArray().slice(currentIndex + 1);
    }
    /**
     * Returns the valid status of the current step, including the `validation` function (if provided).
     */
    isStepValid() {
        // get the current active step
        const currentStep = this.getCurrentStep();
        // if there is no validator then return the valid state
        if (!currentStep.validator) {
            return currentStep.valid;
        }
        // get the validator result
        return currentStep.validator();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: WizardComponent, isStandalone: false, selector: "ux-wizard", inputs: { orientation: "orientation", nextText: "nextText", previousText: "previousText", cancelText: "cancelText", finishText: "finishText", nextTooltip: "nextTooltip", previousTooltip: "previousTooltip", cancelTooltip: "cancelTooltip", finishTooltip: "finishTooltip", nextAriaLabel: "nextAriaLabel", previousAriaLabel: "previousAriaLabel", cancelAriaLabel: "cancelAriaLabel", finishAriaLabel: "finishAriaLabel", nextDisabled: "nextDisabled", previousDisabled: "previousDisabled", cancelDisabled: "cancelDisabled", finishDisabled: "finishDisabled", nextVisible: "nextVisible", previousVisible: "previousVisible", cancelVisible: "cancelVisible", finishVisible: "finishVisible", cancelAlwaysVisible: "cancelAlwaysVisible", finishAlwaysVisible: "finishAlwaysVisible", disableNextWhenInvalid: "disableNextWhenInvalid", resetVisitedOnValidationError: "resetVisitedOnValidationError", sequential: "sequential", step: "step" }, outputs: { onNext: "onNext", onPrevious: "onPrevious", onCancel: "onCancel", onFinishing: "onFinishing", onFinish: "onFinish", stepChanging: "stepChanging", stepChange: "stepChange", stepError: "stepError" }, host: { properties: { "class": "orientation" } }, providers: [WizardService], queries: [{ propertyName: "footerTemplate", first: true, predicate: ["footerTemplate"], descendants: true }, { propertyName: "steps", predicate: WizardStepComponent }], ngImport: i0, template: "<div class=\"wizard-body\">\n  <div\n    class=\"wizard-steps\"\n    uxTabbableList\n    [direction]=\"orientation\"\n    role=\"tablist\"\n    [attr.aria-orientation]=\"orientation\"\n  >\n    @for (stp of steps; track stp; let index = $index) {\n      <div\n        role=\"tab\"\n        class=\"wizard-step\"\n        [class.active]=\"stp._active\"\n        [class.visited]=\"stp.visited\"\n        [class.invalid]=\"!stp._valid && stp.visited\"\n        [attr.aria-posinset]=\"index + 1\"\n        [attr.aria-setsize]=\"steps.length\"\n        [attr.aria-selected]=\"stp._active\"\n        [attr.aria-controls]=\"id + '-step-' + index\"\n        [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n        [attr.aria-expanded]=\"stp._active\"\n        [id]=\"id + '-step-' + index + '-label'\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        uxTabbableListItem\n        [disabled]=\"index !== 0 && (!stp.visited || !sequential)\"\n        (click)=\"gotoStep(stp)\"\n        (keydown.enter)=\"gotoStep(stp)\"\n      >\n        <span class=\"wizard-step-text\">{{ stp.header }}</span>\n        @if (stp.visited && !stp._active) {\n          <ux-icon class=\"wizard-step-icon\" name=\"checkmark\"></ux-icon>\n        }\n      </div>\n    }\n  </div>\n\n  <div class=\"wizard-content\">\n    <ng-content></ng-content>\n  </div>\n</div>\n\n<div class=\"wizard-footer\">\n  @if (footerTemplate) {\n    <ng-container [ngTemplateOutlet]=\"footerTemplate\" [ngTemplateOutletContext]=\"{ step: step }\">\n    </ng-container>\n  }\n\n  @if (previousVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-secondary\"\n      [uxTooltip]=\"previousTooltip\"\n      [disabled]=\"previousDisabled || step === 0\"\n      [attr.aria-label]=\"previousAriaLabel\"\n      (click)=\"previous(); tip.hide()\"\n    >\n      {{ previousText }}\n    </button>\n  }\n\n  @if (nextVisible && !isLastStep()) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-primary\"\n      [uxTooltip]=\"nextTooltip\"\n      [disabled]=\"nextDisabled || isNextDisabled()\"\n      [attr.aria-label]=\"nextAriaLabel\"\n      (click)=\"next(); tip.hide()\"\n    >\n      {{ nextText }}\n    </button>\n  }\n\n  @if ((finishVisible && isLastStep()) || finishAlwaysVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-primary\"\n      [uxTooltip]=\"finishTooltip\"\n      [disabled]=\"finishDisabled || isNextDisabled()\"\n      [attr.aria-label]=\"finishAriaLabel\"\n      (click)=\"finish(); tip.hide()\"\n    >\n      {{ finishText }}\n    </button>\n  }\n\n  @if ((cancelVisible && !isLastStep()) || cancelAlwaysVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-secondary\"\n      [uxTooltip]=\"cancelTooltip\"\n      [disabled]=\"cancelDisabled\"\n      [attr.aria-label]=\"cancelAriaLabel\"\n      (click)=\"cancel(); tip.hide()\"\n    >\n      {{ cancelText }}\n    </button>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-wizard', providers: [WizardService], host: {
                        '[class]': 'orientation',
                    }, standalone: false, template: "<div class=\"wizard-body\">\n  <div\n    class=\"wizard-steps\"\n    uxTabbableList\n    [direction]=\"orientation\"\n    role=\"tablist\"\n    [attr.aria-orientation]=\"orientation\"\n  >\n    @for (stp of steps; track stp; let index = $index) {\n      <div\n        role=\"tab\"\n        class=\"wizard-step\"\n        [class.active]=\"stp._active\"\n        [class.visited]=\"stp.visited\"\n        [class.invalid]=\"!stp._valid && stp.visited\"\n        [attr.aria-posinset]=\"index + 1\"\n        [attr.aria-setsize]=\"steps.length\"\n        [attr.aria-selected]=\"stp._active\"\n        [attr.aria-controls]=\"id + '-step-' + index\"\n        [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n        [attr.aria-expanded]=\"stp._active\"\n        [id]=\"id + '-step-' + index + '-label'\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        uxTabbableListItem\n        [disabled]=\"index !== 0 && (!stp.visited || !sequential)\"\n        (click)=\"gotoStep(stp)\"\n        (keydown.enter)=\"gotoStep(stp)\"\n      >\n        <span class=\"wizard-step-text\">{{ stp.header }}</span>\n        @if (stp.visited && !stp._active) {\n          <ux-icon class=\"wizard-step-icon\" name=\"checkmark\"></ux-icon>\n        }\n      </div>\n    }\n  </div>\n\n  <div class=\"wizard-content\">\n    <ng-content></ng-content>\n  </div>\n</div>\n\n<div class=\"wizard-footer\">\n  @if (footerTemplate) {\n    <ng-container [ngTemplateOutlet]=\"footerTemplate\" [ngTemplateOutletContext]=\"{ step: step }\">\n    </ng-container>\n  }\n\n  @if (previousVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-secondary\"\n      [uxTooltip]=\"previousTooltip\"\n      [disabled]=\"previousDisabled || step === 0\"\n      [attr.aria-label]=\"previousAriaLabel\"\n      (click)=\"previous(); tip.hide()\"\n    >\n      {{ previousText }}\n    </button>\n  }\n\n  @if (nextVisible && !isLastStep()) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-primary\"\n      [uxTooltip]=\"nextTooltip\"\n      [disabled]=\"nextDisabled || isNextDisabled()\"\n      [attr.aria-label]=\"nextAriaLabel\"\n      (click)=\"next(); tip.hide()\"\n    >\n      {{ nextText }}\n    </button>\n  }\n\n  @if ((finishVisible && isLastStep()) || finishAlwaysVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-primary\"\n      [uxTooltip]=\"finishTooltip\"\n      [disabled]=\"finishDisabled || isNextDisabled()\"\n      [attr.aria-label]=\"finishAriaLabel\"\n      (click)=\"finish(); tip.hide()\"\n    >\n      {{ finishText }}\n    </button>\n  }\n\n  @if ((cancelVisible && !isLastStep()) || cancelAlwaysVisible) {\n    <button\n      #tip=\"ux-tooltip\"\n      type=\"button\"\n      class=\"btn button-secondary\"\n      [uxTooltip]=\"cancelTooltip\"\n      [disabled]=\"cancelDisabled\"\n      [attr.aria-label]=\"cancelAriaLabel\"\n      (click)=\"cancel(); tip.hide()\"\n    >\n      {{ cancelText }}\n    </button>\n  }\n</div>\n" }]
        }], propDecorators: { orientation: [{
                type: Input
            }], nextText: [{
                type: Input
            }], previousText: [{
                type: Input
            }], cancelText: [{
                type: Input
            }], finishText: [{
                type: Input
            }], nextTooltip: [{
                type: Input
            }], previousTooltip: [{
                type: Input
            }], cancelTooltip: [{
                type: Input
            }], finishTooltip: [{
                type: Input
            }], nextAriaLabel: [{
                type: Input
            }], previousAriaLabel: [{
                type: Input
            }], cancelAriaLabel: [{
                type: Input
            }], finishAriaLabel: [{
                type: Input
            }], nextDisabled: [{
                type: Input
            }], previousDisabled: [{
                type: Input
            }], cancelDisabled: [{
                type: Input
            }], finishDisabled: [{
                type: Input
            }], nextVisible: [{
                type: Input
            }], previousVisible: [{
                type: Input
            }], cancelVisible: [{
                type: Input
            }], finishVisible: [{
                type: Input
            }], cancelAlwaysVisible: [{
                type: Input
            }], finishAlwaysVisible: [{
                type: Input
            }], disableNextWhenInvalid: [{
                type: Input
            }], resetVisitedOnValidationError: [{
                type: Input
            }], sequential: [{
                type: Input
            }], onNext: [{
                type: Output
            }], onPrevious: [{
                type: Output
            }], onCancel: [{
                type: Output
            }], onFinishing: [{
                type: Output
            }], onFinish: [{
                type: Output
            }], stepChanging: [{
                type: Output
            }], stepChange: [{
                type: Output
            }], stepError: [{
                type: Output
            }], steps: [{
                type: ContentChildren,
                args: [WizardStepComponent]
            }], footerTemplate: [{
                type: ContentChild,
                args: ['footerTemplate', { static: false }]
            }], step: [{
                type: Input
            }] } });
class StepChangingEvent {
    constructor(from, to) {
        this.from = from;
        this.to = to;
    }
}

const DECLARATIONS$5 = [WizardComponent, WizardStepComponent];
class WizardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: WizardModule, declarations: [WizardComponent, WizardStepComponent], imports: [AccessibilityModule, CommonModule, IconModule, TooltipModule], exports: [WizardComponent, WizardStepComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardModule, imports: [AccessibilityModule, CommonModule, IconModule, TooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WizardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, IconModule, TooltipModule],
                    exports: DECLARATIONS$5,
                    declarations: DECLARATIONS$5,
                }]
        }] });

class MarqueeWizardStepComponent extends WizardStepComponent {
    constructor() {
        super(...arguments);
        /** Determine the completed state of this step */
        this.completed = false;
        /** Emit when the completed step changes */
        this.completedChange = new EventEmitter();
    }
    /**
     * Update the completed state and emit the latest value
     * @param completed whether or not the step is completed
     */
    setCompleted(completed) {
        this.completed = completed;
        this.completedChange.emit(completed);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardStepComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MarqueeWizardStepComponent, isStandalone: false, selector: "ux-marquee-wizard-step", inputs: { context: "context", completed: "completed" }, outputs: { completedChange: "completedChange" }, queries: [{ propertyName: "_iconTemplate", first: true, predicate: MarqueeWizardStepIconDirective, descendants: true, read: TemplateRef }], usesInheritance: true, ngImport: i0, template: "@if (active) {\n  <ng-content></ng-content>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardStepComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-marquee-wizard-step', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@if (active) {\n  <ng-content></ng-content>\n}\n" }]
        }], propDecorators: { context: [{
                type: Input
            }], completed: [{
                type: Input
            }], completedChange: [{
                type: Output
            }], _iconTemplate: [{
                type: ContentChild,
                args: [MarqueeWizardStepIconDirective, { read: TemplateRef, static: false }]
            }] } });

class MarqueeWizardComponent extends WizardComponent {
    get isTemplate() {
        return this.description && this.description instanceof TemplateRef;
    }
    constructor() {
        super();
        this.wizardService = inject(WizardService);
        this._resizeService = inject(ResizeService);
        this._elementRef = inject(ElementRef);
        /** Initial set to default width to match 240px on left but can be changed with a percentage value */
        this.sidePanelWidth = 25;
        /** Width of the splitter - default is 10 */
        this.gutterSize = 10;
        /** If set to true the resizable splitter will be enabled and set to the default width **/
        this.resizable = false;
        /** Emit the current width of the splitter*/
        this.sidePanelWidthChange = new EventEmitter();
        /** Access each step content component */
        this.steps = new QueryList();
        /**
         * If the wizard is in a modal it may initially have a size of 0 until the modal displays
         * in which case if we are using the splitter it will not render correctly. We use this
         * variable to only initialise the splitter when the content has a width.
         */
        this._isInitialised = false;
        // set to true as default for Marquee Wizard only
        this.resetVisitedOnValidationError = true;
        // watch for changes to the size
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.onResize.bind(this));
    }
    ngAfterViewChecked() {
        this.tabbableList?.setFirstItemTabbable();
    }
    ngOnDestroy() {
        super.ngOnDestroy();
        this._resizeService.removeResizeListener(this._elementRef.nativeElement);
    }
    /**
     * If the current step is valid, mark it as
     * complete and go to the next step
     */
    async next() {
        // get the current step
        const step = this.getCurrentStep();
        await super.next();
        if (step && step.valid) {
            // mark this step as completed
            step.setCompleted(true);
        }
        else {
            this.stepError.next(this.step);
        }
    }
    /**
     * Emit the onFinishing event and if valid the onFinish event.
     * Also mark the final step as completed if it is valid
     */
    async finish() {
        // get the current step
        const step = this.getCurrentStep();
        await super.finish();
        // if the step is valid indicate that it is now complete
        if (step.valid) {
            step.setCompleted(true);
        }
        else {
            this.stepError.next(this.step);
        }
    }
    onResize(event) {
        if (event.width !== 0 && event.height !== 0) {
            this._isInitialised = true;
        }
    }
    /** Whenever the drag event ends, update the internal value and emit the new size */
    onDragEnd({ sizes }) {
        // we need to only get the size of the first panel which will be the side panel
        this.sidePanelWidth = sizes[0];
        this.sidePanelWidthChange.emit(this.sidePanelWidth);
    }
    gotoStep(step) {
        const currentStep = this.getCurrentStep();
        if (currentStep !== step) {
            if (!this.sequential) {
                currentStep.setCompleted(true);
            }
            super.gotoStep(step);
        }
    }
    setFutureStepsUnvisited(currentStep) {
        super.setFutureStepsUnvisited(currentStep);
        // Marquee wizard steps have an additional completed property which must also be changed.
        // The base class implementation only changes the visited state
        this.getFutureSteps(currentStep).forEach((step) => (step.completed = false));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MarqueeWizardComponent, isStandalone: false, selector: "ux-marquee-wizard", inputs: { description: "description", stepTemplate: "stepTemplate", sidePanelWidth: "sidePanelWidth", gutterSize: "gutterSize", resizable: "resizable" }, outputs: { sidePanelWidthChange: "sidePanelWidthChange" }, providers: [WizardService], queries: [{ propertyName: "steps", predicate: MarqueeWizardStepComponent }], viewQueries: [{ propertyName: "tabbableList", first: true, predicate: TabbableListDirective, descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (resizable && _isInitialised) {\n  <as-split direction=\"horizontal\" [gutterSize]=\"gutterSize\" (dragEnd)=\"onDragEnd($event)\">\n    <as-split-area [size]=\"sidePanelWidth\">\n      <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n    </as-split-area>\n    <as-split-area [size]=\"100 - sidePanelWidth\">\n      <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n    </as-split-area>\n  </as-split>\n}\n\n@if (!resizable) {\n  <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n  <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n}\n\n<ng-template #sidePanel>\n  <div class=\"marquee-wizard-side-panel\" [class.marquee-wizard-side-panel-resize]=\"resizable\">\n    @if (description) {\n      <div class=\"marquee-wizard-description-container\">\n        <!-- If a template was provided display it -->\n        @if (isTemplate) {\n          <ng-container [ngTemplateOutlet]=\"$any(description)\"></ng-container>\n        }\n        <!-- Otherwise simply display the string -->\n        @if (!isTemplate) {\n          <p>{{ description }}</p>\n        }\n      </div>\n    }\n\n    <ul\n      class=\"marquee-wizard-steps\"\n      uxTabbableList\n      direction=\"vertical\"\n      role=\"tablist\"\n      aria-orientation=\"vertical\"\n    >\n      @for (step of steps; track step; let index = $index) {\n        <li\n          role=\"tab\"\n          class=\"marquee-wizard-step\"\n          [class.active]=\"step.active\"\n          [class.visited]=\"step.visited\"\n          [class.invalid]=\"!step.valid\"\n          [attr.aria-posinset]=\"index + 1\"\n          [attr.aria-setsize]=\"steps.length\"\n          [attr.aria-selected]=\"step.active\"\n          [attr.aria-controls]=\"id + '-step-' + index\"\n          [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n          [attr.aria-expanded]=\"step._active\"\n          [id]=\"id + '-step-' + index + '-label'\"\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          uxTabbableListItem\n          [disabled]=\"!step.visited\"\n          (click)=\"gotoStep(step)\"\n          (keydown.enter)=\"gotoStep(step)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"stepTemplate || defaultStepTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: step, index: index, context: step.context }\"\n          ></ng-container>\n        </li>\n      }\n    </ul>\n  </div>\n</ng-template>\n\n<ng-template #mainContentPanel>\n  <div class=\"marquee-wizard-content-panel\" [class.marquee-wizard-content-panel-resize]=\"resizable\">\n    <div class=\"marquee-wizard-content\">\n      <ng-content></ng-content>\n    </div>\n\n    <div class=\"modal-footer\">\n      @if (footerTemplate) {\n        <ng-container\n          [ngTemplateOutlet]=\"footerTemplate\"\n          [ngTemplateOutletContext]=\"{ step: step }\"\n        >\n        </ng-container>\n      }\n\n      @if (previousVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-secondary marquee-wizard-previous-button\"\n          [uxTooltip]=\"previousTooltip\"\n          [attr.aria-label]=\"previousAriaLabel\"\n          container=\"body\"\n          [disabled]=\"previousDisabled || step === 0\"\n          (click)=\"previous(); tip.hide()\"\n        >\n          {{ previousText }}\n        </button>\n      }\n\n      @if (nextVisible && !isLastStep()) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-primary marquee-wizard-next-button\"\n          [uxTooltip]=\"nextTooltip\"\n          [attr.aria-label]=\"nextAriaLabel\"\n          container=\"body\"\n          [disabled]=\"nextDisabled || isNextDisabled()\"\n          (click)=\"next(); tip.hide()\"\n        >\n          {{ nextText }}\n        </button>\n      }\n\n      @if ((finishVisible && isLastStep()) || finishAlwaysVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-primary marquee-wizard-finish-button\"\n          [uxTooltip]=\"finishTooltip\"\n          [attr.aria-label]=\"finishAriaLabel\"\n          container=\"body\"\n          [disabled]=\"finishDisabled || isNextDisabled()\"\n          (click)=\"finish(); tip.hide()\"\n        >\n          {{ finishText }}\n        </button>\n      }\n\n      @if ((cancelVisible && !isLastStep()) || cancelAlwaysVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-secondary marquee-wizard-cancel-button\"\n          [uxTooltip]=\"cancelTooltip\"\n          [attr.aria-label]=\"cancelAriaLabel\"\n          container=\"body\"\n          [disabled]=\"cancelDisabled\"\n          (click)=\"cancel(); tip.hide()\"\n        >\n          {{ cancelText }}\n        </button>\n      }\n    </div>\n  </div>\n</ng-template>\n\n<ng-template #defaultStepTemplate let-step>\n  <!-- Insert the icon -->\n  @if (step._iconTemplate) {\n    <div class=\"marquee-wizard-step-icon\">\n      <ng-container [ngTemplateOutlet]=\"step._iconTemplate\"></ng-container>\n    </div>\n  }\n\n  <span class=\"marquee-wizard-step-title\">{{ step.header }}</span>\n  @if (step.completed && step.valid) {\n    <ux-icon class=\"marquee-wizard-step-status\" name=\"checkmark\"></ux-icon>\n  }\n  @if (!step.valid) {\n    <ux-icon class=\"marquee-wizard-step-status\" name=\"close\"></ux-icon>\n  }\n</ng-template>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: SplitterAccessibilityDirective, selector: "as-split", outputs: ["gutterKeydown"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "component", type: i9.SplitComponent, selector: "as-split", inputs: ["direction", "unit", "gutterSize", "gutterStep", "restrictMove", "useTransition", "disabled", "dir", "gutterDblClickDuration", "gutterClickDeltaPx", "gutterAriaLabel"], outputs: ["transitionEnd", "dragStart", "dragEnd", "gutterClick", "gutterDblClick"], exportAs: ["asSplit"] }, { kind: "directive", type: i9.SplitAreaDirective, selector: "as-split-area, [as-split-area]", inputs: ["order", "size", "minSize", "maxSize", "lockSize", "visible"], exportAs: ["asSplitArea"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-marquee-wizard', providers: [WizardService], preserveWhitespaces: false, standalone: false, template: "@if (resizable && _isInitialised) {\n  <as-split direction=\"horizontal\" [gutterSize]=\"gutterSize\" (dragEnd)=\"onDragEnd($event)\">\n    <as-split-area [size]=\"sidePanelWidth\">\n      <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n    </as-split-area>\n    <as-split-area [size]=\"100 - sidePanelWidth\">\n      <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n    </as-split-area>\n  </as-split>\n}\n\n@if (!resizable) {\n  <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n  <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n}\n\n<ng-template #sidePanel>\n  <div class=\"marquee-wizard-side-panel\" [class.marquee-wizard-side-panel-resize]=\"resizable\">\n    @if (description) {\n      <div class=\"marquee-wizard-description-container\">\n        <!-- If a template was provided display it -->\n        @if (isTemplate) {\n          <ng-container [ngTemplateOutlet]=\"$any(description)\"></ng-container>\n        }\n        <!-- Otherwise simply display the string -->\n        @if (!isTemplate) {\n          <p>{{ description }}</p>\n        }\n      </div>\n    }\n\n    <ul\n      class=\"marquee-wizard-steps\"\n      uxTabbableList\n      direction=\"vertical\"\n      role=\"tablist\"\n      aria-orientation=\"vertical\"\n    >\n      @for (step of steps; track step; let index = $index) {\n        <li\n          role=\"tab\"\n          class=\"marquee-wizard-step\"\n          [class.active]=\"step.active\"\n          [class.visited]=\"step.visited\"\n          [class.invalid]=\"!step.valid\"\n          [attr.aria-posinset]=\"index + 1\"\n          [attr.aria-setsize]=\"steps.length\"\n          [attr.aria-selected]=\"step.active\"\n          [attr.aria-controls]=\"id + '-step-' + index\"\n          [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n          [attr.aria-expanded]=\"step._active\"\n          [id]=\"id + '-step-' + index + '-label'\"\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          uxTabbableListItem\n          [disabled]=\"!step.visited\"\n          (click)=\"gotoStep(step)\"\n          (keydown.enter)=\"gotoStep(step)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"stepTemplate || defaultStepTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: step, index: index, context: step.context }\"\n          ></ng-container>\n        </li>\n      }\n    </ul>\n  </div>\n</ng-template>\n\n<ng-template #mainContentPanel>\n  <div class=\"marquee-wizard-content-panel\" [class.marquee-wizard-content-panel-resize]=\"resizable\">\n    <div class=\"marquee-wizard-content\">\n      <ng-content></ng-content>\n    </div>\n\n    <div class=\"modal-footer\">\n      @if (footerTemplate) {\n        <ng-container\n          [ngTemplateOutlet]=\"footerTemplate\"\n          [ngTemplateOutletContext]=\"{ step: step }\"\n        >\n        </ng-container>\n      }\n\n      @if (previousVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-secondary marquee-wizard-previous-button\"\n          [uxTooltip]=\"previousTooltip\"\n          [attr.aria-label]=\"previousAriaLabel\"\n          container=\"body\"\n          [disabled]=\"previousDisabled || step === 0\"\n          (click)=\"previous(); tip.hide()\"\n        >\n          {{ previousText }}\n        </button>\n      }\n\n      @if (nextVisible && !isLastStep()) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-primary marquee-wizard-next-button\"\n          [uxTooltip]=\"nextTooltip\"\n          [attr.aria-label]=\"nextAriaLabel\"\n          container=\"body\"\n          [disabled]=\"nextDisabled || isNextDisabled()\"\n          (click)=\"next(); tip.hide()\"\n        >\n          {{ nextText }}\n        </button>\n      }\n\n      @if ((finishVisible && isLastStep()) || finishAlwaysVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-primary marquee-wizard-finish-button\"\n          [uxTooltip]=\"finishTooltip\"\n          [attr.aria-label]=\"finishAriaLabel\"\n          container=\"body\"\n          [disabled]=\"finishDisabled || isNextDisabled()\"\n          (click)=\"finish(); tip.hide()\"\n        >\n          {{ finishText }}\n        </button>\n      }\n\n      @if ((cancelVisible && !isLastStep()) || cancelAlwaysVisible) {\n        <button\n          #tip=\"ux-tooltip\"\n          type=\"button\"\n          class=\"btn button-secondary marquee-wizard-cancel-button\"\n          [uxTooltip]=\"cancelTooltip\"\n          [attr.aria-label]=\"cancelAriaLabel\"\n          container=\"body\"\n          [disabled]=\"cancelDisabled\"\n          (click)=\"cancel(); tip.hide()\"\n        >\n          {{ cancelText }}\n        </button>\n      }\n    </div>\n  </div>\n</ng-template>\n\n<ng-template #defaultStepTemplate let-step>\n  <!-- Insert the icon -->\n  @if (step._iconTemplate) {\n    <div class=\"marquee-wizard-step-icon\">\n      <ng-container [ngTemplateOutlet]=\"step._iconTemplate\"></ng-container>\n    </div>\n  }\n\n  <span class=\"marquee-wizard-step-title\">{{ step.header }}</span>\n  @if (step.completed && step.valid) {\n    <ux-icon class=\"marquee-wizard-step-status\" name=\"checkmark\"></ux-icon>\n  }\n  @if (!step.valid) {\n    <ux-icon class=\"marquee-wizard-step-status\" name=\"close\"></ux-icon>\n  }\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { tabbableList: [{
                type: ViewChild,
                args: [TabbableListDirective]
            }], description: [{
                type: Input
            }], stepTemplate: [{
                type: Input
            }], sidePanelWidth: [{
                type: Input
            }], gutterSize: [{
                type: Input
            }], resizable: [{
                type: Input
            }], sidePanelWidthChange: [{
                type: Output
            }], steps: [{
                type: ContentChildren,
                args: [MarqueeWizardStepComponent]
            }] } });

class MarqueeWizardModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardModule, declarations: [MarqueeWizardComponent,
            MarqueeWizardStepComponent,
            MarqueeWizardStepIconDirective], imports: [AccessibilityModule,
            CommonModule,
            IconModule,
            TooltipModule,
            WizardModule,
            AngularSplitModule,
            ResizeModule], exports: [MarqueeWizardComponent, MarqueeWizardStepComponent, MarqueeWizardStepIconDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardModule, imports: [AccessibilityModule,
            CommonModule,
            IconModule,
            TooltipModule,
            WizardModule,
            AngularSplitModule,
            ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MarqueeWizardModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        IconModule,
                        TooltipModule,
                        WizardModule,
                        AngularSplitModule,
                        ResizeModule,
                    ],
                    exports: [MarqueeWizardComponent, MarqueeWizardStepComponent, MarqueeWizardStepIconDirective],
                    declarations: [
                        MarqueeWizardComponent,
                        MarqueeWizardStepComponent,
                        MarqueeWizardStepIconDirective,
                    ],
                }]
        }] });

class FrameExtractionService {
    createVideoPlayer(source) {
        const videoPlayer = document.createElement('video');
        videoPlayer.preload = 'auto';
        videoPlayer.src = source;
        return videoPlayer;
    }
    createCanvas(width, height) {
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        return canvas;
    }
    goToFrame(videoPlayer, time) {
        videoPlayer.currentTime = time;
        return fromEvent(videoPlayer, time === 0 ? 'loadeddata' : 'seeked');
    }
    getThumbnail(videoPlayer, canvas, time, width = 160, height = 90) {
        return Observable.create((observer) => {
            // go to specified frame
            const subscription = this.goToFrame(videoPlayer, time).subscribe(() => {
                // create image from current frame
                canvas.getContext('2d').drawImage(videoPlayer, 0, 0, width, height);
                observer.next({ image: canvas.toDataURL(), width, height, time });
                observer.complete();
                subscription.unsubscribe();
            });
        });
    }
    getFrameThumbnail(source, width, height, time) {
        // create required elements
        let videoPlayer = this.createVideoPlayer(source);
        let canvas = this.createCanvas(width, height);
        const frameSubscription = this.getThumbnail(videoPlayer, canvas, time, width, height);
        // ensure we release memory after we are finished
        frameSubscription.subscribe(null, null, () => {
            videoPlayer = null;
            canvas = null;
        });
        return frameSubscription;
    }
    getFrameThumbnails(source, width, height, start, end, skip = 5) {
        // create required elements
        let videoPlayer = this.createVideoPlayer(source);
        let canvas = this.createCanvas(width, height);
        return Observable.create((observer) => {
            fromEvent(videoPlayer, 'loadedmetadata').subscribe(() => {
                // calculate the frames required
                const frames = [];
                for (let idx = start; idx < end; idx += skip) {
                    frames.push(this.getThumbnail(videoPlayer, canvas, idx, width, height));
                }
                concat(...frames).subscribe((frame) => observer.next(frame), null, () => {
                    videoPlayer = null;
                    canvas = null;
                    observer.complete();
                });
            });
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FrameExtractionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FrameExtractionService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FrameExtractionService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class MediaPlayerService {
    constructor() {
        this._frameExtractionService = inject(FrameExtractionService);
        this.type = 'video';
        this.loaded = false;
        /** Aria Labels */
        this.muteAriaLabel = this.getMuteAriaLabel;
        this.playAriaLabel = this.getPlayAriaLabel;
        this.fullscreenAriaLabel = this.getFullscreenAriaLabel;
        this.selectSubtitlesAriaLabel = this.getSubtitlesAriaLabel;
        this.goToStartAriaLabel = 'Go to start';
        this.goToEndAriaLabel = 'Go to end';
        this.subtitlesTitleAriaLabel = 'Subtitles';
        this.subtitlesOffAriaLabel = 'Subtitles Off';
        this.noSubtitlesAriaLabel = 'No subtitles';
        this.mediaPlayerAriaLabel = 'Media Player';
        this.seekAriaLabel = 'Seek Slider';
        /*
              Create observables for media player events
          */
        this.playing = new BehaviorSubject(false);
        this.initEvent = new ReplaySubject();
        this.abortEvent = new Subject();
        this.canPlayEvent = new BehaviorSubject(false);
        this.canPlayThroughEvent = new BehaviorSubject(false);
        this.durationChangeEvent = new Subject();
        this.endedEvent = new Subject();
        this.errorEvent = new Subject();
        this.loadedDataEvent = new Subject();
        this.loadedMetadataEvent = new Subject();
        this.loadStartEvent = new Subject();
        this.pauseEvent = new Subject();
        this.playEvent = new Subject();
        this.playingEvent = new Subject();
        this.rateChangeEvent = new Subject();
        this.seekedEvent = new Subject();
        this.seekingEvent = new Subject();
        this.stalledEvent = new Subject();
        this.suspendEvent = new Subject();
        this.timeUpdateEvent = new Subject();
        this.volumeChangeEvent = new Subject();
        this.waitingEvent = new Subject();
        this.mediaClickEvent = new Subject();
        this.fullscreenEvent = new BehaviorSubject(false);
        this.quietModeEvent = new BehaviorSubject(false);
        this.progressEvent = Observable.create((observer) => {
            // repeat until the whole video has fully loaded
            const interval = setInterval(() => {
                const buffered = this._mediaPlayer.buffered;
                observer.next(buffered);
                if (buffered.length === 1 && buffered.start(0) === 0 && buffered.end(0) === this.duration) {
                    observer.complete();
                    clearInterval(interval);
                }
            }, 1000);
        });
        this._fullscreen = false;
    }
    /*
          Create all the getters and setters the can be used by media player extensions
      */
    get mediaPlayer() {
        return this._mediaPlayer;
    }
    get quietMode() {
        return this._quietMode;
    }
    set quietMode(value) {
        // quiet mode cannot be enabled on audio player
        if (this.type === 'audio') {
            value = false;
        }
        this._quietMode = value;
        this.quietModeEvent.next(value);
    }
    get mediaPlayerWidth() {
        return this._mediaPlayer ? this._mediaPlayer.offsetWidth : 0;
    }
    get mediaPlayerHeight() {
        return this._mediaPlayer ? this._mediaPlayer.offsetHeight : 0;
    }
    get autoplay() {
        return this._mediaPlayer ? this._mediaPlayer.autoplay : false;
    }
    set autoplay(value) {
        this._mediaPlayer.autoplay = value;
    }
    get buffered() {
        return this._mediaPlayer ? this._mediaPlayer.buffered : new TimeRanges();
    }
    get crossOrigin() {
        return this._mediaPlayer ? this._mediaPlayer.crossOrigin : null;
    }
    set crossOrigin(value) {
        this._mediaPlayer.crossOrigin = value;
    }
    get currentSrc() {
        return this._mediaPlayer ? this._mediaPlayer.currentSrc : null;
    }
    get currentTime() {
        return this._mediaPlayer ? this._mediaPlayer.currentTime : 0;
    }
    set currentTime(value) {
        this._mediaPlayer.currentTime = value;
    }
    get defaultMuted() {
        return this._mediaPlayer ? this._mediaPlayer.defaultMuted : false;
    }
    set defaultMuted(value) {
        this._mediaPlayer.defaultMuted = value;
    }
    get defaultPlaybackRate() {
        return this._mediaPlayer ? this._mediaPlayer.defaultPlaybackRate : 1;
    }
    set defaultPlaybackRate(value) {
        this._mediaPlayer.defaultPlaybackRate = value;
    }
    get duration() {
        return this._mediaPlayer && !isNaN(this.mediaPlayer.duration) ? this._mediaPlayer.duration : 0;
    }
    get ended() {
        return this._mediaPlayer ? this._mediaPlayer.ended : false;
    }
    get loop() {
        return this._mediaPlayer ? this._mediaPlayer.loop : false;
    }
    set loop(value) {
        this._mediaPlayer.loop = value;
    }
    get muted() {
        return this._mediaPlayer ? this._mediaPlayer.muted : false;
    }
    set muted(value) {
        this._mediaPlayer.muted = value;
    }
    get networkState() {
        return this._mediaPlayer.networkState;
    }
    get paused() {
        return this._mediaPlayer ? this._mediaPlayer.paused : true;
    }
    get playbackRate() {
        return this._mediaPlayer ? this._mediaPlayer.playbackRate : 1;
    }
    set playbackRate(value) {
        this._mediaPlayer.playbackRate = value;
    }
    get played() {
        return this._mediaPlayer ? this._mediaPlayer.played : new TimeRanges();
    }
    get preload() {
        return this._mediaPlayer ? this._mediaPlayer.preload : 'auto';
    }
    set preload(value) {
        this._mediaPlayer.preload = value;
    }
    get readyState() {
        return this._mediaPlayer ? this._mediaPlayer.readyState : 0;
    }
    get seekable() {
        return this._mediaPlayer ? this._mediaPlayer.seekable : new TimeRanges();
    }
    get seeking() {
        return this._mediaPlayer ? this._mediaPlayer.seeking : false;
    }
    get src() {
        return this._mediaPlayer ? this._mediaPlayer.src : '';
    }
    set src(value) {
        this._mediaPlayer.src = value;
    }
    get textTracks() {
        return this._mediaPlayer ? Array.from(this._mediaPlayer.textTracks) : [];
    }
    get volume() {
        return this._mediaPlayer ? this._mediaPlayer.volume : 1;
    }
    set volume(value) {
        if (this._mediaPlayer) {
            this._mediaPlayer.volume = value;
        }
    }
    get fullscreen() {
        return this._mediaPlayer ? this._fullscreen : false;
    }
    set fullscreen(value) {
        this._fullscreen = value;
        this.fullscreenEvent.next(value);
    }
    setMediaPlayer(hostElement, mediaPlayer) {
        this._hostElement = hostElement;
        this._mediaPlayer = mediaPlayer;
        this.initEvent.next(true);
    }
    /**
     * Toggle playing state
     */
    togglePlay() {
        // prevent any action is not loaded
        if (this.loaded === false) {
            return;
        }
        if (this.paused) {
            this.play();
        }
        else {
            this.pause();
        }
    }
    /**
     * Starts playing the audio/video
     */
    play() {
        this._mediaPlayer.play();
    }
    /**
     * Pauses the currently playing audio/video
     */
    pause() {
        this._mediaPlayer.pause();
    }
    /**
     * Re-loads the audio/video element
     */
    load() {
        this._mediaPlayer.load();
    }
    /**
     * Checks if the browser can play the specified audio/video type
     */
    canPlayType(type) {
        return this._mediaPlayer.canPlayType(type);
    }
    /**
     * Adds a new text track to the audio/video
     */
    addTextTrack(kind, label, language) {
        return this._mediaPlayer.addTextTrack(kind, label, language);
    }
    /**
     * Attempt to display media in fullscreen mode
     */
    requestFullscreen() {
        // get the host element (we need to do some browser specific checks and typescript complains)
        const host = this._hostElement;
        const requestFullscreen = host.requestFullscreen ||
            host.webkitRequestFullscreen ||
            host.msRequestFullscreen ||
            host.mozRequestFullScreen;
        // if we can perform the action then perform it and update the state
        if (requestFullscreen) {
            requestFullscreen.call(host);
            // update the internal state
            this.fullscreen = true;
        }
    }
    /**
     * Exit full screen mode
     */
    exitFullscreen() {
        // get the document element (we need to do some browser specific checks and typescript complains)
        const host = document;
        const exitFullscreen = host.exitFullscreen ||
            host.webkitExitFullscreen ||
            host.msExitFullscreen ||
            host.mozCancelFullScreen;
        // if we can perform the action then perform it and update the state
        if (exitFullscreen) {
            exitFullscreen.call(host);
            // update the internal state
            this.fullscreen = false;
        }
    }
    /**
     * Toggle Fullscreen State
     */
    toggleFullscreen() {
        if (this.fullscreen) {
            this.exitFullscreen();
        }
        else {
            this.requestFullscreen();
        }
    }
    fullscreenChange() {
        // get the document element (we need to do some browser specific checks and typescript complains)
        const host = document;
        // set the fullscreen state (this also emits the event)
        this.fullscreen =
            host.fullscreen ||
                host.webkitIsFullScreen ||
                host.mozFullScreen ||
                (host.msFullscreenElement !== null && host.msFullscreenElement !== undefined);
    }
    /**
     * Extract the frames from the video
     */
    getFrames(width, height) {
        if (this.type === 'video') {
            return this._frameExtractionService.getFrameThumbnails(this.source, width, height, 0, this.duration, 10);
        }
        return from([]);
    }
    hideSubtitleTracks() {
        for (let index = 0; index < this.textTracks.length; index++) {
            this.textTracks[index].mode = 'hidden';
        }
    }
    getMuteAriaLabel(volume) {
        return volume === 0 ? 'Unmute' : 'Mute';
    }
    getPlayAriaLabel(isPlaying) {
        return isPlaying ? 'Pause' : 'Play';
    }
    getFullscreenAriaLabel(isFullscreen) {
        return isFullscreen ? 'Exit full screen' : 'Full screen';
    }
    getSubtitlesAriaLabel(track) {
        return `Select subtitles, ${track} currently selected.`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerService, decorators: [{
            type: Injectable
        }] });

class MediaPlayerBaseExtensionDirective {
    constructor() {
        this.mediaPlayerService = inject(MediaPlayerService);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerBaseExtensionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MediaPlayerBaseExtensionDirective, isStandalone: false, selector: "[mediaPlayerBaseExtension]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerBaseExtensionDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[mediaPlayerBaseExtension]',
                    standalone: false,
                }]
        }] });

class SliderComponent {
    /** A set of options to customize the appearance and behavior of the slider. */
    set options(options) {
        this._options = options;
        this.updateOptions();
    }
    /** Whether the slider is disabled. */
    set disabled(disabled) {
        this._disabled = coerceBooleanProperty(disabled);
    }
    get disabled() {
        return this._disabled;
    }
    get options() {
        return this._options;
    }
    constructor() {
        this.colorService = inject(ColorService);
        this._changeDetectorRef = inject(ChangeDetectorRef);
        /** A single number or a SliderValue object, depending on the slider type specified. */
        this.value = 0;
        /** Emits when the `value` changes. */
        this.valueChange = new EventEmitter();
        this._disabled = false;
        // expose enums to Angular view
        this.sliderType = SliderType;
        this.sliderStyle = SliderStyle;
        this.sliderSize = SliderSize;
        this.sliderSnap = SliderSnap;
        this.sliderThumb = SliderThumb;
        this.sliderTickType = SliderTickType;
        this.sliderThumbEvent = SliderThumbEvent;
        this.sliderCalloutTrigger = SliderCalloutTrigger;
        this.tracks = {
            lower: {
                size: 0,
                color: '',
            },
            middle: {
                size: 0,
                color: '',
            },
            upper: {
                size: 0,
                color: '',
            },
        };
        this.tooltips = {
            lower: {
                visible: false,
                position: 0,
                label: '',
            },
            upper: {
                visible: false,
                position: 0,
                label: '',
            },
        };
        this.thumbs = {
            lower: {
                hover: false,
                drag: false,
                position: 0,
                order: 100,
                value: null,
            },
            upper: {
                hover: false,
                drag: false,
                position: 0,
                order: 101,
                value: null,
            },
        };
        // store all the ticks to display
        this.ticks = [];
        // setup default options
        this.defaultOptions = {
            type: SliderType.Value,
            handles: {
                style: SliderStyle.Button,
                callout: {
                    trigger: SliderCalloutTrigger.None,
                    background: this.colorService.getColor('grey2').toHex(),
                    color: '#fff',
                    formatter: (value) => value,
                },
                keyboard: {
                    major: 5,
                    minor: 1,
                },
                aria: {
                    thumb: 'Slider value',
                    lowerThumb: 'Slider lower value',
                    upperThumb: 'Slider upper value',
                },
            },
            track: {
                height: SliderSize.Wide,
                min: 0,
                max: 100,
                ticks: {
                    snap: SliderSnap.None,
                    major: {
                        show: true,
                        steps: 10,
                        labels: true,
                        formatter: (value) => value,
                    },
                    minor: {
                        show: true,
                        steps: 5,
                        labels: false,
                        formatter: (value) => value,
                    },
                },
                colors: {},
            },
        };
    }
    ngOnInit() {
        this.updateValues();
        this.setThumbState(SliderThumb.Lower, false, false);
        this.setThumbState(SliderThumb.Upper, false, false);
        // emit the initial value
        this.valueChange.next(this.clone(this.value));
    }
    ngDoCheck() {
        if (this.detectValueChange(this.value, this._value)) {
            this.updateValues();
            this._value = this.clone(this.value);
        }
    }
    ngAfterViewInit() {
        // persistent tooltips will need positioned correctly at this stage
        setTimeout(() => {
            this.updateTooltipPosition(SliderThumb.Lower);
            this.updateTooltipPosition(SliderThumb.Upper);
            // mark as dirty
            this._changeDetectorRef.markForCheck();
        });
        this.updateOrder();
    }
    snapToNearestTick(thumb, snapTarget, forwards) {
        if (this.disabled) {
            return;
        }
        // get the value for the thumb
        const { value } = this.getThumbState(thumb);
        // get the closest ticks - remove any tick if we are currently on it
        const closest = this.getTickDistances(value, thumb, snapTarget)
            .filter(tick => tick.value !== value)
            .find(tick => (forwards ? tick.value > value : tick.value < value));
        // If we have no ticks then move by a predefined amount
        if (closest) {
            return this.setThumbValue(thumb, this.validateValue(thumb, closest.value));
        }
        const step = snapTarget === SliderSnap.Major
            ? this.options.handles.keyboard.major
            : this.options.handles.keyboard.minor;
        this.setThumbValue(thumb, this.validateValue(thumb, value + (forwards ? step : -step)));
    }
    snapToEnd(thumb, forwards) {
        this.setThumbValue(thumb, this.validateValue(thumb, forwards ? this.options.track.max : this.options.track.min));
    }
    getThumbValue(thumb) {
        return this.getThumbState(thumb).value;
    }
    getFormattedValue(thumb) {
        return this.options.handles.callout.formatter(this.getThumbState(thumb).value);
    }
    getThumbState(thumb) {
        return thumb === SliderThumb.Lower ? this.thumbs.lower : this.thumbs.upper;
    }
    setThumbState(thumb, hover, drag) {
        if (thumb === SliderThumb.Lower) {
            this.thumbs.lower.hover = hover;
            this.thumbs.lower.drag = drag;
        }
        else {
            this.thumbs.upper.hover = hover;
            this.thumbs.upper.drag = drag;
        }
        // update the visibility of the tooltips
        this.updateTooltips(thumb);
    }
    thumbEvent(thumb, event) {
        if (this.disabled) {
            return;
        }
        // get the current thumb state
        const state = this.getThumbState(thumb);
        // update based upon event
        switch (event) {
            case SliderThumbEvent.DragStart:
                state.drag = true;
                break;
            case SliderThumbEvent.DragEnd:
                state.drag = false;
                break;
            case SliderThumbEvent.MouseOver:
                state.hover = true;
                break;
            case SliderThumbEvent.MouseLeave:
                state.hover = false;
                break;
            case SliderThumbEvent.None:
                state.drag = false;
                state.hover = false;
                break;
        }
        // update the thumb state
        this.setThumbState(thumb, state.hover, state.drag);
        this.updateOrder();
    }
    getAriaValueText(thumb) {
        // get the current thumb value
        const value = this.getThumbValue(thumb);
        // get all the ticks
        const tick = this.ticks.find(_tick => _tick.value === value);
        if (tick && tick.label) {
            return tick.label;
        }
        // otherwise simply display the formatted value
        return this.getFormattedValue(thumb);
    }
    updateTooltips(thumb) {
        let visible = false;
        const state = this.getThumbState(thumb);
        switch (this.options.handles.callout.trigger) {
            case SliderCalloutTrigger.Persistent:
                visible = true;
                break;
            case SliderCalloutTrigger.Drag:
                visible = state.drag;
                break;
            case SliderCalloutTrigger.Hover:
                visible = state.hover || state.drag;
                break;
            case SliderCalloutTrigger.Dynamic:
                visible = true;
                break;
        }
        // update the state for the corresponding thumb
        this.getTooltip(thumb).visible = visible;
        // update the tooltip text
        this.updateTooltipText(thumb);
        // update the tooltip positions
        this.updateTooltipPosition(thumb);
    }
    updateTooltipText(thumb) {
        // get the thumb value
        const tooltip = this.getTooltip(thumb);
        // store the formatted label
        tooltip.label = this.getFormattedValue(thumb).toString();
    }
    getTooltipElement(thumb) {
        return thumb === SliderThumb.Lower ? this.lowerTooltip : this.upperTooltip;
    }
    getTooltip(thumb) {
        return thumb === SliderThumb.Lower ? this.tooltips.lower : this.tooltips.upper;
    }
    updateTooltipPosition(thumb) {
        const tooltip = this.getTooltip(thumb);
        // if tooltip is not visible then stop here
        if (tooltip.visible === false) {
            return;
        }
        const tooltipElement = this.getTooltipElement(thumb);
        // get the element widths
        let thumbWidth;
        if (this._options.handles.style === SliderStyle.Button) {
            thumbWidth = this._options.track.height === SliderSize.Narrow ? 16 : 24;
        }
        else {
            thumbWidth = 2;
        }
        const tooltipWidth = tooltipElement.nativeElement.offsetWidth;
        // calculate the tooltips new position
        const tooltipPosition = Math.ceil((tooltipWidth - thumbWidth) / 2);
        // update tooltip position
        tooltip.position = -tooltipPosition;
        if (this._options.type === SliderType.Range &&
            this._options.handles.callout.trigger === SliderCalloutTrigger.Dynamic) {
            this.preventTooltipOverlap(tooltip);
        }
    }
    preventTooltipOverlap(tooltip) {
        const trackWidth = this.track.nativeElement.offsetWidth;
        const lower = (trackWidth / 100) * this.thumbs.lower.position;
        const upper = (trackWidth / 100) * this.thumbs.upper.position;
        const lowerWidth = this.lowerTooltip.nativeElement.offsetWidth / 2;
        const upperWidth = this.upperTooltip.nativeElement.offsetWidth / 2;
        const diff = lower + lowerWidth - (upper - upperWidth);
        // if the tooltips are closer than 16px then adjust so the dont move any close
        if (diff > 0) {
            if (tooltip === this.tooltips.lower && this.thumbs.lower.drag === false) {
                tooltip.position -= diff / 2;
            }
            else if (tooltip === this.tooltips.upper && this.thumbs.upper.drag === false) {
                tooltip.position += diff / 2;
            }
        }
    }
    clamp(value, min, max) {
        return Math.min(Math.max(value, min), max);
    }
    updateThumbPosition(event, thumb) {
        if (this.disabled) {
            return;
        }
        // get event position - either mouse or touch
        const eventPosition = event instanceof MouseEvent
            ? event.clientX
            : event.touches && event.touches.length > 0
                ? event.touches[0].clientX
                : null;
        // if event position is null do nothing
        if (eventPosition === null) {
            return;
        }
        // get mouse position
        const mouseX = window.pageXOffset + eventPosition;
        // get track size and position
        const trackBounds = this.track.nativeElement.getBoundingClientRect();
        // restrict the value within the range size
        const position = this.clamp(mouseX - trackBounds.left, 0, trackBounds.width);
        // get fraction representation of location within the track
        const fraction = position / trackBounds.width;
        // convert to value within the range
        let value = (this._options.track.max - this._options.track.min) * fraction + this._options.track.min;
        // ensure value is valid
        value = this.validateValue(thumb, value);
        // snap to a tick if required
        value = this.snapToTick(value, thumb);
        // update the value accordingly
        this.setThumbValue(thumb, value);
        this.updateOrderOnDrag(thumb);
        this.updateValues();
        // update tooltip text & position
        this.updateTooltipText(thumb);
        // update the position of all visible tooltips
        this.updateTooltipPosition(SliderThumb.Lower);
        this.updateTooltipPosition(SliderThumb.Upper);
        // mark as dirty for change detection
        this._changeDetectorRef.markForCheck();
    }
    updateOrderOnDrag(thumb) {
        const lower = thumb === SliderThumb.Lower ? 101 : 100;
        const upper = thumb === SliderThumb.Lower ? 100 : 101;
        // Ensure currently dragged thumb is on top
        this.thumbs.lower.order = lower;
        this.thumbs.upper.order = upper;
    }
    updateOrder() {
        const isDragged = this.thumbs.lower.drag || this.thumbs.upper.drag;
        if (this._options && !isDragged) {
            const lowerValue = this.getThumbValue(this.sliderThumb.Lower);
            const upperValue = this.getThumbValue(this.sliderThumb.Upper);
            const max = this._options.track.max;
            const min = this._options.track.min;
            const range = max - min;
            const median = (range / 100) * 50 + min;
            if (upperValue <= median) {
                this.thumbs.lower.order = 100;
                this.thumbs.upper.order = 101;
            }
            if (lowerValue > median) {
                this.thumbs.lower.order = 101;
                this.thumbs.upper.order = 100;
            }
        }
    }
    getTickDistances(value, thumb, snapTarget) {
        // if snap target is none then return original value
        if (snapTarget === SliderSnap.None) {
            return [];
        }
        // get filtered ticks
        let ticks;
        switch (snapTarget) {
            case SliderSnap.Minor:
                ticks = this.ticks.filter(tick => tick.type === SliderTickType.Minor);
                break;
            case SliderSnap.Major:
                ticks = this.ticks.filter(tick => tick.type === SliderTickType.Major);
                break;
            default:
                ticks = this.ticks.slice(0);
        }
        // get the track limit
        let lowerLimit = this._options.track.min;
        let upperLimit = this._options.track.max;
        if (this._options.type === SliderType.Range && thumb === SliderThumb.Lower) {
            upperLimit = this.thumbs.upper.value;
        }
        if (this._options.type === SliderType.Range && thumb === SliderThumb.Upper) {
            lowerLimit = this.thumbs.lower.value;
        }
        // Find the closest tick to the current position
        const range = ticks.filter(tick => tick.value >= lowerLimit && tick.value <= upperLimit);
        // If there are no close ticks in the valid range then dont snap
        if (range.length === 0) {
            return [];
        }
        return range.sort((tickOne, tickTwo) => {
            const tickOneDelta = Math.max(tickOne.value, value) - Math.min(tickOne.value, value);
            const tickTwoDelta = Math.max(tickTwo.value, value) - Math.min(tickTwo.value, value);
            return tickOneDelta - tickTwoDelta;
        });
    }
    snapToTick(value, thumb) {
        const tickDistances = this.getTickDistances(value, thumb, this._options.track.ticks.snap);
        // if there are no ticks return the current value
        if (tickDistances.length === 0) {
            return value;
        }
        // get the closest tick
        return tickDistances[0].value;
    }
    validateValue(thumb, value) {
        // if slider is not a range value is always valid providing it is within the chart min and max values
        if (this._options.type === SliderType.Value) {
            return Math.max(Math.min(value, this._options.track.max), this._options.track.min);
        }
        // check if value is with chart ranges
        if (value > this._options.track.max) {
            return thumb === SliderThumb.Lower
                ? Math.min(this._options.track.max, this.thumbs.upper.value)
                : this._options.track.max;
        }
        if (value < this._options.track.min) {
            return thumb === SliderThumb.Upper
                ? Math.max(this._options.track.min, this.thumbs.lower.value)
                : this._options.track.min;
        }
        // otherwise we need to check to make sure lower thumb cannot go above higher and vice versa
        if (thumb === SliderThumb.Lower) {
            if (this.thumbs.upper.value === null) {
                return value;
            }
            return value <= this.thumbs.upper.value ? value : this.thumbs.upper.value;
        }
        if (thumb === SliderThumb.Upper) {
            if (this.thumbs.lower.value === null) {
                return value;
            }
            return value >= this.thumbs.lower.value ? value : this.thumbs.lower.value;
        }
    }
    updateOptions() {
        // add in the default options that user hasn't specified
        this._options = this.deepMerge(this._options || {}, this.defaultOptions);
        this.updateTrackColors();
        this.updateTicks();
        this.updateValues();
    }
    updateValues() {
        if (this.value === undefined || this.value === null) {
            this.value = 0;
        }
        let lowerValue = typeof this.value === 'number' ? this.value : this.value.low;
        let upperValue = typeof this.value === 'number' ? this.value : this.value.high;
        // validate values
        lowerValue = this.validateValue(SliderThumb.Lower, Number(lowerValue.toFixed(4)));
        upperValue = this.validateValue(SliderThumb.Upper, Number(upperValue.toFixed(4)));
        // calculate the positions as percentages
        const lowerPosition = ((lowerValue - this._options.track.min) /
            (this._options.track.max - this._options.track.min)) *
            100;
        const upperPosition = ((upperValue - this._options.track.min) /
            (this._options.track.max - this._options.track.min)) *
            100;
        // update thumb positions
        this.thumbs.lower.position = lowerPosition;
        this.thumbs.upper.position = upperPosition;
        // calculate the track sizes
        this.tracks.lower.size = lowerPosition;
        this.tracks.middle.size = upperPosition - lowerPosition;
        this.tracks.upper.size =
            this._options.type === SliderType.Value ? 100 - lowerPosition : 100 - upperPosition;
        // update the value input
        this.setValue(lowerValue, upperValue);
    }
    setValue(low, high) {
        this.thumbs.lower.value = low;
        this.thumbs.upper.value = high;
        const previousValue = this.clone(this._value);
        this.value = this._options.type === SliderType.Value ? low : { low, high };
        // call the event emitter if changes occured
        if (this.detectValueChange(this.value, previousValue)) {
            this.valueChange.emit(this.clone(this.value));
            this.updateTooltipText(SliderThumb.Lower);
            this.updateTooltipText(SliderThumb.Upper);
        }
        else {
            this.valueChange.emit(this.clone(this.value));
        }
    }
    setThumbValue(thumb, value) {
        // update the thumb value
        this.getThumbState(thumb).value = value;
        // forward these changes to the value
        this.setValue(this.thumbs.lower.value, this.thumbs.upper.value);
    }
    updateTicks() {
        // get tick options
        const majorOptions = this._options.track.ticks.major;
        const minorOptions = this._options.track.ticks.minor;
        // check if we should show ticks
        if (majorOptions.show === false && minorOptions.show === false) {
            this.ticks = [];
        }
        // create ticks for both major and minor - only get the ones to be shown
        const majorTicks = this.getTicks(majorOptions, SliderTickType.Major).filter(tick => tick.showTicks);
        const minorTicks = this.getTicks(minorOptions, SliderTickType.Minor).filter(tick => tick.showTicks);
        // remove any minor ticks that are on a major interval
        this.ticks = this.unionTicks(majorTicks, minorTicks);
    }
    updateTrackColors() {
        // get colors for each part of the track
        const { lower, range, higher } = this._options.track.colors;
        // update the controller value
        this.tracks.lower.color = this.getTrackColorStyle(lower);
        this.tracks.middle.color = this.getTrackColorStyle(range);
        this.tracks.upper.color = this.getTrackColorStyle(higher);
    }
    /** Map the color value to the correct CSS color value */
    getTrackColorStyle(color) {
        return Array.isArray(color) ? `linear-gradient(to right, ${color.join(', ')})` : color;
    }
    getSteps(steps) {
        // if they are already an array just return it
        if (steps instanceof Array) {
            return steps;
        }
        const output = [];
        // otherwise calculate the steps
        for (let idx = this._options.track.min; idx <= this._options.track.max; idx += steps) {
            output.push(idx);
        }
        return output;
    }
    getTicks(options, type) {
        // create an array to store the ticks and step points
        const steps = this.getSteps(options.steps);
        // get some chart options
        const min = this._options.track.min;
        const max = this._options.track.max;
        // convert each step to a slider tick and remove invalid ticks
        return steps
            .map(step => {
            return {
                showTicks: options.show,
                showLabels: options.labels,
                type,
                position: ((step - min) / (max - min)) * 100,
                value: step,
                label: options.formatter(step),
            };
        })
            .filter(tick => tick.position >= 0 && tick.position <= 100);
    }
    unionTicks(majorTicks, minorTicks) {
        // get all ticks combined removing any minor ticks with the same value as major ticks
        return majorTicks
            .concat(minorTicks)
            .filter((tick, index, array) => tick.type === SliderTickType.Major ||
            !array.find(tk => tk.type === SliderTickType.Major && tk.position === tick.position))
            .sort((t1, t2) => t1.value - t2.value);
    }
    deepMerge(destination, source) {
        // loop though all of the properties in the source object
        for (const prop in source) {
            // check if the destination object has the property
            // eslint-disable-next-line no-prototype-builtins
            if (!destination.hasOwnProperty(prop)) {
                // copy the property across
                destination[prop] = source[prop];
                continue;
            }
            // if the property exists and is not an object then skip
            if (typeof destination[prop] !== 'object') {
                continue;
            }
            // check if property is an array
            if (destination[prop] instanceof Array) {
                continue;
            }
            // if it is an object then perform a recursive check
            destination[prop] = this.deepMerge(destination[prop], source[prop]);
        }
        return destination;
    }
    detectValueChange(value1, value2) {
        // compare two slider values
        if (this.isSliderValue(value1) && this.isSliderValue(value2)) {
            // references to the objects in the correct types
            const obj1 = value1;
            const obj2 = value2;
            return obj1.low !== obj2.low || obj1.high !== obj2.high;
        }
        // if not a slider value - should be number of nullable type - compare normally
        return value1 !== value2;
    }
    /**
     * Determines whether or not an object conforms to the
     * SliderValue interface.
     * @param value - The object to check - this must be type any
     */
    isSliderValue(value) {
        // check if is an object
        if (typeof value !== 'object') {
            return false;
        }
        // next check if it contains the necessary properties
        return 'low' in value && 'high' in value;
    }
    clone(value) {
        // if it is not an object simply return the value
        if (typeof value !== 'object') {
            return value;
        }
        // create a new object from the existing one
        const instance = { ...value };
        // delete remove the value from the old object
        value = undefined;
        // return the new instance of the object
        return instance;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SliderComponent, isStandalone: false, selector: "ux-slider", inputs: { value: "value", options: "options", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.disabled": "disabled" } }, viewQueries: [{ propertyName: "lowerTooltip", first: true, predicate: ["lowerTooltip"], descendants: true, static: true }, { propertyName: "upperTooltip", first: true, predicate: ["upperTooltip"], descendants: true, static: true }, { propertyName: "track", first: true, predicate: ["track"], descendants: true, static: true }], ngImport: i0, template: "<div\n  class=\"track\"\n  #track\n  [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n  [class.wide]=\"_options.track.height === sliderSize.Wide\"\n  [class.range]=\"_options.type === sliderType.Range\"\n>\n  <!-- Section Beneath Lower Thumb -->\n  <div\n    class=\"track-section track-lower\"\n    [style.flex-grow]=\"tracks.lower.size\"\n    [style.background]=\"this.disabled ? null : tracks.lower.color\"\n  ></div>\n\n  <!-- Lower Thumb Button / Line -->\n  <div\n    class=\"thumb lower\"\n    uxDrag\n    uxFocusIndicator\n    role=\"slider\"\n    [tabindex]=\"disabled ? -1 : 0\"\n    #lowerthumb\n    [attr.aria-label]=\"\n      _options.type === sliderType.Range\n        ? _options.handles.aria.lowerThumb\n        : _options.handles.aria.thumb\n    \"\n    [attr.aria-valuemin]=\"_options?.track?.min\"\n    [attr.aria-valuemax]=\"\n      _options.type === sliderType.Range ? getThumbValue(sliderThumb.Upper) : _options?.track?.max\n    \"\n    [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Lower)\"\n    [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Lower)\"\n    [style.left.%]=\"thumbs.lower.position\"\n    [class.active]=\"thumbs.lower.drag\"\n    [style.z-index]=\"thumbs.lower.order\"\n    [class.button]=\"_options.handles.style === sliderStyle.Button\"\n    [class.line]=\"_options.handles.style === sliderStyle.Line\"\n    [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n    [class.wide]=\"_options.track.height === sliderSize.Wide\"\n    (onDragStart)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragStart); lowerthumb.focus()\"\n    (onDrag)=\"updateThumbPosition($event, sliderThumb.Lower)\"\n    (onDragEnd)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragEnd)\"\n    (mouseenter)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n    (mouseleave)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n    (focus)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n    (blur)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n    (keydown.ArrowLeft)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowRight)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.ArrowUp)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowDown)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.PageDown)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, false); $event.preventDefault()\n    \"\n    (keydown.PageUp)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, true); $event.preventDefault()\n    \"\n    (keydown.Home)=\"snapToEnd(sliderThumb.Lower, false); $event.preventDefault()\"\n    (keydown.End)=\"snapToEnd(sliderThumb.Lower, true); $event.preventDefault()\"\n  >\n    <!-- Lower Thumb Callout -->\n    <div\n      class=\"tooltip top tooltip-lower\"\n      #lowerTooltip\n      [class.tooltip-dynamic]=\"\n        _options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n        thumbs.lower.drag === false\n      \"\n      [style.opacity]=\"tooltips.lower.visible ? 1 : 0\"\n      [style.left.px]=\"tooltips.lower.position\"\n    >\n      <div\n        class=\"tooltip-arrow\"\n        [style.border-top-color]=\"_options.handles.callout.background\"\n      ></div>\n\n      <div\n        class=\"tooltip-inner\"\n        [style.background-color]=\"_options.handles.callout.background\"\n        [style.color]=\"_options.handles.callout.color\"\n      >\n        {{ tooltips.lower.label }}\n      </div>\n    </div>\n  </div>\n\n  <!-- Section of Track Between Lower and Upper Thumbs -->\n  @if (_options.type === sliderType.Range) {\n    <div\n      class=\"track-section track-range\"\n      [style.flex-grow]=\"tracks.middle.size\"\n      [style.background]=\"this.disabled ? null : tracks.middle.color\"\n    ></div>\n  }\n\n  <!-- Upper Thumb Button / Line -->\n  <div\n    class=\"thumb upper\"\n    uxDrag\n    uxFocusIndicator\n    role=\"slider\"\n    [tabindex]=\"disabled ? -1 : 0\"\n    #upperthumb\n    [attr.aria-label]=\"_options.handles.aria.upperThumb\"\n    [attr.aria-valuemin]=\"getThumbValue(sliderThumb.Lower) || _options?.track?.min\"\n    [attr.aria-valuemax]=\"_options?.track?.max\"\n    [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Upper)\"\n    [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Upper)\"\n    [hidden]=\"_options.type !== sliderType.Range\"\n    [class.active]=\"thumbs.upper.drag\"\n    [style.left.%]=\"thumbs.upper.position\"\n    [style.z-index]=\"thumbs.upper.order\"\n    [class.button]=\"_options.handles.style === sliderStyle.Button\"\n    [class.line]=\"_options.handles.style === sliderStyle.Line\"\n    [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n    [class.wide]=\"_options.track.height === sliderSize.Wide\"\n    (onDragStart)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragStart); upperthumb.focus()\"\n    (onDrag)=\"updateThumbPosition($event, sliderThumb.Upper)\"\n    (onDragEnd)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragEnd)\"\n    (mouseenter)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n    (mouseleave)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n    (focus)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n    (blur)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n    (keydown.ArrowLeft)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowRight)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.ArrowUp)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowDown)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.PageDown)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, false); $event.preventDefault()\n    \"\n    (keydown.PageUp)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, true); $event.preventDefault()\n    \"\n    (keydown.Home)=\"snapToEnd(sliderThumb.Upper, false); $event.preventDefault()\"\n    (keydown.End)=\"snapToEnd(sliderThumb.Upper, true); $event.preventDefault()\"\n  >\n    <!-- Upper Thumb Callout -->\n    <div\n      class=\"tooltip top tooltip-upper\"\n      #upperTooltip\n      [class.tooltip-dynamic]=\"\n        _options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n        thumbs.upper.drag === false\n      \"\n      [style.opacity]=\"tooltips.upper.visible ? 1 : 0\"\n      [style.left.px]=\"tooltips.upper.position\"\n    >\n      <div\n        class=\"tooltip-arrow\"\n        [style.border-top-color]=\"_options.handles.callout.background\"\n      ></div>\n\n      @if (_options.type === sliderType.Range) {\n        <div\n          class=\"tooltip-inner\"\n          [style.background-color]=\"_options.handles.callout.background\"\n          [style.color]=\"_options.handles.callout.color\"\n        >\n          {{ tooltips.upper.label }}\n        </div>\n      }\n    </div>\n  </div>\n\n  <!-- Section of Track Abover Upper Thumb -->\n  <div\n    class=\"track-section track-higher\"\n    [style.flex-grow]=\"tracks.upper.size\"\n    [style.background]=\"this.disabled ? null : tracks.upper.color\"\n  ></div>\n</div>\n\n<!-- Chart Ticks and Tick Labels -->\n@if (\n  (_options.track.ticks.major.show || _options.track.ticks.minor.show) &&\n  _options.handles.callout.trigger !== sliderCalloutTrigger.Dynamic\n) {\n  <div\n    class=\"tick-container\"\n    role=\"presentation\"\n    [class.show-labels]=\"_options.track.ticks.major.labels || _options.track.ticks.minor.labels\"\n  >\n    @for (tick of ticks; track tick) {\n      <div\n        class=\"tick\"\n        [class.major]=\"tick.type === sliderTickType.Major\"\n        [class.minor]=\"tick.type === sliderTickType.Minor\"\n        [style.left.%]=\"tick.position\"\n        [hidden]=\"!tick.showTicks\"\n      >\n        <div class=\"tick-indicator\"></div>\n        <div class=\"tick-label\" aria-hidden=\"true\" [hidden]=\"!tick.showLabels\">\n          {{ tick.label }}\n        </div>\n      </div>\n    }\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: DragDirective, selector: "[uxDrag]", inputs: ["clone", "group", "model", "draggable"], outputs: ["onDragStart", "onDrag", "onDragScroll", "onDragEnd", "onDrop", "onDropEnter", "onDropLeave"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SliderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-slider', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.disabled]': 'disabled',
                    }, standalone: false, template: "<div\n  class=\"track\"\n  #track\n  [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n  [class.wide]=\"_options.track.height === sliderSize.Wide\"\n  [class.range]=\"_options.type === sliderType.Range\"\n>\n  <!-- Section Beneath Lower Thumb -->\n  <div\n    class=\"track-section track-lower\"\n    [style.flex-grow]=\"tracks.lower.size\"\n    [style.background]=\"this.disabled ? null : tracks.lower.color\"\n  ></div>\n\n  <!-- Lower Thumb Button / Line -->\n  <div\n    class=\"thumb lower\"\n    uxDrag\n    uxFocusIndicator\n    role=\"slider\"\n    [tabindex]=\"disabled ? -1 : 0\"\n    #lowerthumb\n    [attr.aria-label]=\"\n      _options.type === sliderType.Range\n        ? _options.handles.aria.lowerThumb\n        : _options.handles.aria.thumb\n    \"\n    [attr.aria-valuemin]=\"_options?.track?.min\"\n    [attr.aria-valuemax]=\"\n      _options.type === sliderType.Range ? getThumbValue(sliderThumb.Upper) : _options?.track?.max\n    \"\n    [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Lower)\"\n    [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Lower)\"\n    [style.left.%]=\"thumbs.lower.position\"\n    [class.active]=\"thumbs.lower.drag\"\n    [style.z-index]=\"thumbs.lower.order\"\n    [class.button]=\"_options.handles.style === sliderStyle.Button\"\n    [class.line]=\"_options.handles.style === sliderStyle.Line\"\n    [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n    [class.wide]=\"_options.track.height === sliderSize.Wide\"\n    (onDragStart)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragStart); lowerthumb.focus()\"\n    (onDrag)=\"updateThumbPosition($event, sliderThumb.Lower)\"\n    (onDragEnd)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.DragEnd)\"\n    (mouseenter)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n    (mouseleave)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n    (focus)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseOver)\"\n    (blur)=\"thumbEvent(sliderThumb.Lower, sliderThumbEvent.MouseLeave)\"\n    (keydown.ArrowLeft)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowRight)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.ArrowUp)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowDown)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.PageDown)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, false); $event.preventDefault()\n    \"\n    (keydown.PageUp)=\"\n      snapToNearestTick(sliderThumb.Lower, sliderSnap.Major, true); $event.preventDefault()\n    \"\n    (keydown.Home)=\"snapToEnd(sliderThumb.Lower, false); $event.preventDefault()\"\n    (keydown.End)=\"snapToEnd(sliderThumb.Lower, true); $event.preventDefault()\"\n  >\n    <!-- Lower Thumb Callout -->\n    <div\n      class=\"tooltip top tooltip-lower\"\n      #lowerTooltip\n      [class.tooltip-dynamic]=\"\n        _options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n        thumbs.lower.drag === false\n      \"\n      [style.opacity]=\"tooltips.lower.visible ? 1 : 0\"\n      [style.left.px]=\"tooltips.lower.position\"\n    >\n      <div\n        class=\"tooltip-arrow\"\n        [style.border-top-color]=\"_options.handles.callout.background\"\n      ></div>\n\n      <div\n        class=\"tooltip-inner\"\n        [style.background-color]=\"_options.handles.callout.background\"\n        [style.color]=\"_options.handles.callout.color\"\n      >\n        {{ tooltips.lower.label }}\n      </div>\n    </div>\n  </div>\n\n  <!-- Section of Track Between Lower and Upper Thumbs -->\n  @if (_options.type === sliderType.Range) {\n    <div\n      class=\"track-section track-range\"\n      [style.flex-grow]=\"tracks.middle.size\"\n      [style.background]=\"this.disabled ? null : tracks.middle.color\"\n    ></div>\n  }\n\n  <!-- Upper Thumb Button / Line -->\n  <div\n    class=\"thumb upper\"\n    uxDrag\n    uxFocusIndicator\n    role=\"slider\"\n    [tabindex]=\"disabled ? -1 : 0\"\n    #upperthumb\n    [attr.aria-label]=\"_options.handles.aria.upperThumb\"\n    [attr.aria-valuemin]=\"getThumbValue(sliderThumb.Lower) || _options?.track?.min\"\n    [attr.aria-valuemax]=\"_options?.track?.max\"\n    [attr.aria-valuenow]=\"getThumbValue(sliderThumb.Upper)\"\n    [attr.aria-valuetext]=\"getAriaValueText(sliderThumb.Upper)\"\n    [hidden]=\"_options.type !== sliderType.Range\"\n    [class.active]=\"thumbs.upper.drag\"\n    [style.left.%]=\"thumbs.upper.position\"\n    [style.z-index]=\"thumbs.upper.order\"\n    [class.button]=\"_options.handles.style === sliderStyle.Button\"\n    [class.line]=\"_options.handles.style === sliderStyle.Line\"\n    [class.narrow]=\"_options.track.height === sliderSize.Narrow\"\n    [class.wide]=\"_options.track.height === sliderSize.Wide\"\n    (onDragStart)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragStart); upperthumb.focus()\"\n    (onDrag)=\"updateThumbPosition($event, sliderThumb.Upper)\"\n    (onDragEnd)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.DragEnd)\"\n    (mouseenter)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n    (mouseleave)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n    (focus)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseOver)\"\n    (blur)=\"thumbEvent(sliderThumb.Upper, sliderThumbEvent.MouseLeave)\"\n    (keydown.ArrowLeft)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowRight)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.ArrowUp)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, false); $event.preventDefault()\n    \"\n    (keydown.ArrowDown)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.All, true); $event.preventDefault()\n    \"\n    (keydown.PageDown)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, false); $event.preventDefault()\n    \"\n    (keydown.PageUp)=\"\n      snapToNearestTick(sliderThumb.Upper, sliderSnap.Major, true); $event.preventDefault()\n    \"\n    (keydown.Home)=\"snapToEnd(sliderThumb.Upper, false); $event.preventDefault()\"\n    (keydown.End)=\"snapToEnd(sliderThumb.Upper, true); $event.preventDefault()\"\n  >\n    <!-- Upper Thumb Callout -->\n    <div\n      class=\"tooltip top tooltip-upper\"\n      #upperTooltip\n      [class.tooltip-dynamic]=\"\n        _options.handles.callout.trigger === sliderCalloutTrigger.Dynamic &&\n        thumbs.upper.drag === false\n      \"\n      [style.opacity]=\"tooltips.upper.visible ? 1 : 0\"\n      [style.left.px]=\"tooltips.upper.position\"\n    >\n      <div\n        class=\"tooltip-arrow\"\n        [style.border-top-color]=\"_options.handles.callout.background\"\n      ></div>\n\n      @if (_options.type === sliderType.Range) {\n        <div\n          class=\"tooltip-inner\"\n          [style.background-color]=\"_options.handles.callout.background\"\n          [style.color]=\"_options.handles.callout.color\"\n        >\n          {{ tooltips.upper.label }}\n        </div>\n      }\n    </div>\n  </div>\n\n  <!-- Section of Track Abover Upper Thumb -->\n  <div\n    class=\"track-section track-higher\"\n    [style.flex-grow]=\"tracks.upper.size\"\n    [style.background]=\"this.disabled ? null : tracks.upper.color\"\n  ></div>\n</div>\n\n<!-- Chart Ticks and Tick Labels -->\n@if (\n  (_options.track.ticks.major.show || _options.track.ticks.minor.show) &&\n  _options.handles.callout.trigger !== sliderCalloutTrigger.Dynamic\n) {\n  <div\n    class=\"tick-container\"\n    role=\"presentation\"\n    [class.show-labels]=\"_options.track.ticks.major.labels || _options.track.ticks.minor.labels\"\n  >\n    @for (tick of ticks; track tick) {\n      <div\n        class=\"tick\"\n        [class.major]=\"tick.type === sliderTickType.Major\"\n        [class.minor]=\"tick.type === sliderTickType.Minor\"\n        [style.left.%]=\"tick.position\"\n        [hidden]=\"!tick.showTicks\"\n      >\n        <div class=\"tick-indicator\"></div>\n        <div class=\"tick-label\" aria-hidden=\"true\" [hidden]=\"!tick.showLabels\">\n          {{ tick.label }}\n        </div>\n      </div>\n    }\n  </div>\n}\n" }]
        }], ctorParameters: () => [], propDecorators: { value: [{
                type: Input
            }], options: [{
                type: Input
            }], disabled: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], lowerTooltip: [{
                type: ViewChild,
                args: ['lowerTooltip', { static: true }]
            }], upperTooltip: [{
                type: ViewChild,
                args: ['upperTooltip', { static: true }]
            }], track: [{
                type: ViewChild,
                args: ['track', { static: true }]
            }] } });
var SliderType;
(function (SliderType) {
    SliderType[SliderType["Value"] = 0] = "Value";
    SliderType[SliderType["Range"] = 1] = "Range";
})(SliderType || (SliderType = {}));
var SliderStyle;
(function (SliderStyle) {
    SliderStyle[SliderStyle["Button"] = 0] = "Button";
    SliderStyle[SliderStyle["Line"] = 1] = "Line";
})(SliderStyle || (SliderStyle = {}));
var SliderSize;
(function (SliderSize) {
    SliderSize[SliderSize["Narrow"] = 0] = "Narrow";
    SliderSize[SliderSize["Wide"] = 1] = "Wide";
})(SliderSize || (SliderSize = {}));
var SliderCalloutTrigger;
(function (SliderCalloutTrigger) {
    SliderCalloutTrigger[SliderCalloutTrigger["None"] = 0] = "None";
    SliderCalloutTrigger[SliderCalloutTrigger["Hover"] = 1] = "Hover";
    SliderCalloutTrigger[SliderCalloutTrigger["Drag"] = 2] = "Drag";
    SliderCalloutTrigger[SliderCalloutTrigger["Persistent"] = 3] = "Persistent";
    SliderCalloutTrigger[SliderCalloutTrigger["Dynamic"] = 4] = "Dynamic";
})(SliderCalloutTrigger || (SliderCalloutTrigger = {}));
var SliderSnap;
(function (SliderSnap) {
    SliderSnap[SliderSnap["None"] = 0] = "None";
    SliderSnap[SliderSnap["Minor"] = 1] = "Minor";
    SliderSnap[SliderSnap["Major"] = 2] = "Major";
    SliderSnap[SliderSnap["All"] = 3] = "All";
})(SliderSnap || (SliderSnap = {}));
var SliderTickType;
(function (SliderTickType) {
    SliderTickType[SliderTickType["Minor"] = 0] = "Minor";
    SliderTickType[SliderTickType["Major"] = 1] = "Major";
})(SliderTickType || (SliderTickType = {}));
var SliderThumbEvent;
(function (SliderThumbEvent) {
    SliderThumbEvent[SliderThumbEvent["None"] = 0] = "None";
    SliderThumbEvent[SliderThumbEvent["MouseOver"] = 1] = "MouseOver";
    SliderThumbEvent[SliderThumbEvent["MouseLeave"] = 2] = "MouseLeave";
    SliderThumbEvent[SliderThumbEvent["DragStart"] = 3] = "DragStart";
    SliderThumbEvent[SliderThumbEvent["DragEnd"] = 4] = "DragEnd";
})(SliderThumbEvent || (SliderThumbEvent = {}));
var SliderThumb;
(function (SliderThumb) {
    SliderThumb[SliderThumb["Lower"] = 0] = "Lower";
    SliderThumb[SliderThumb["Upper"] = 1] = "Upper";
})(SliderThumb || (SliderThumb = {}));

class SliderModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SliderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SliderModule, declarations: [SliderComponent], imports: [AccessibilityModule, CommonModule, ColorServiceModule, DragModule], exports: [SliderComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SliderModule, imports: [AccessibilityModule, CommonModule, ColorServiceModule, DragModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SliderModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, ColorServiceModule, DragModule],
                    exports: [SliderComponent],
                    declarations: [SliderComponent],
                }]
        }] });

let uniqueId$4 = 1;
class MediaPlayerControlsExtensionComponent {
    constructor() {
        this.mediaPlayerService = inject(MediaPlayerService);
        this.volumeActive = false;
        this.volumeFocus = false;
        this.returnFocus = true;
        this.subtitlesId = `ux-media-player-subtitle-popover-${uniqueId$4++}`;
        this.subtitlesOpen = false;
        this.mouseEnterVolume = new Subject();
        this.mouseLeaveVolume = new Subject();
        this.options = {
            handles: {
                aria: {
                    thumb: 'Volume',
                },
            },
            track: {
                colors: {
                    lower: '#666',
                },
                height: SliderSize.Narrow,
                ticks: {
                    major: {
                        show: false,
                    },
                    minor: {
                        show: false,
                    },
                },
            },
        };
        this._volume = 100;
        this._previousVolume = 100;
        this._onDestroy = new Subject();
    }
    get volume() {
        return this._volume;
    }
    set volume(value) {
        if (value === 0 && this._volume !== 0) {
            this._previousVolume = this._volume;
        }
        this._volume = Math.min(Math.max(value, 0), 100);
        this.mediaPlayerService.volume = this._volume / 100;
    }
    ngOnInit() {
        this.mediaPlayerService.volumeChangeEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(volume => (this.volume = volume * 100));
        this.mediaPlayerService.initEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this.volume = this.mediaPlayerService.volume * 100));
        this.mouseEnterVolume
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this.volumeActive = true));
        this.mouseLeaveVolume
            .pipe(switchMap(() => timer(1500).pipe(takeUntil(this.mouseEnterVolume))), takeUntil(this._onDestroy))
            .subscribe(() => (this.volumeActive = false));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    toggleMute() {
        this.volume = this.volume === 0 ? this._previousVolume : 0;
    }
    goToStart() {
        this.mediaPlayerService.currentTime = 0;
    }
    goToEnd() {
        this.mediaPlayerService.currentTime = this.mediaPlayerService.duration;
    }
    isSubtitleActive() {
        for (let idx = 0; idx < this.mediaPlayerService.textTracks.length; idx++) {
            if (this.mediaPlayerService.textTracks[idx].mode === 'showing') {
                return true;
            }
        }
        return false;
    }
    setSubtitleTrack(track) {
        // hide all tracks
        this.mediaPlayerService.hideSubtitleTracks();
        // set the position of the subtitle track
        for (let idx = 0; idx < track.cues.length; idx++) {
            const cue = track.cues[idx];
            cue.line = -3;
        }
        // activate the selected one
        track.mode = 'showing';
    }
    getSubtitleTrack() {
        for (let idx = 0; idx < this.mediaPlayerService.textTracks.length; idx++) {
            if (this.mediaPlayerService.textTracks[idx].mode === 'showing') {
                return this.mediaPlayerService.textTracks[idx].label;
            }
        }
        return this.mediaPlayerService.noSubtitlesAriaLabel;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerControlsExtensionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MediaPlayerControlsExtensionComponent, isStandalone: false, selector: "ux-media-player-controls", host: { properties: { "class.quiet": "mediaPlayerService.quietMode || mediaPlayerService.fullscreen" } }, ngImport: i0, template: "<div class=\"volume-container\">\n  <div\n    class=\"volume-slider-container\"\n    #volumeContainer\n    [class.active]=\"volumeActive || volumeFocus\"\n    (mouseenter)=\"mouseEnterVolume.next()\"\n    (mouseleave)=\"mouseLeaveVolume.next()\"\n    (uxFocusWithin)=\"volumeFocus = true\"\n    (uxBlurWithin)=\"volumeFocus = false\"\n  >\n    <button\n      #volumeIcon\n      uxFocusIndicator\n      type=\"button\"\n      class=\"volume-slider-icon\"\n      [attr.aria-label]=\"mediaPlayerService.muteAriaLabel(volume)\"\n      [uxTooltip]=\"muteTooltip\"\n      [showTriggers]=\"['mouseenter']\"\n      [hideTriggers]=\"['mouseleave']\"\n      (click)=\"toggleMute()\"\n      (mouseup)=\"volumeIcon.blur()\"\n    >\n      @if (volume === 0) {\n        <ux-icon name=\"volume-mute\"></ux-icon>\n      }\n      @if (volume > 0 && volume <= 70) {\n        <ux-icon name=\"volume-low\"></ux-icon>\n      }\n      @if (volume > 70) {\n        <ux-icon name=\"volume\"></ux-icon>\n      }\n    </button>\n\n    <div class=\"volume-slider-node\">\n      <ux-slider\n        [value]=\"volume\"\n        (valueChange)=\"volume = $any($event)\"\n        [options]=\"options\"\n      ></ux-slider>\n    </div>\n  </div>\n</div>\n\n<button\n  #startButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  (click)=\"goToStart()\"\n  (mouseup)=\"startButton.blur()\"\n  [attr.aria-label]=\"mediaPlayerService.goToStartAriaLabel\"\n>\n  <svg viewBox=\"0 0 51.5 64\" width=\"14\" height=\"17\" focusable=\"false\">\n    <rect x=\"0\" y=\"0\" width=\"7.5\" height=\"64\" />\n    <polygon points=\"51.5,64 51.5,0 7.4,32 \" />\n  </svg>\n</button>\n\n<button\n  #playButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  [attr.aria-label]=\"mediaPlayerService.playAriaLabel(mediaPlayerService.playing | async)\"\n  (click)=\"mediaPlayerService.togglePlay()\"\n  (mouseup)=\"playButton.blur()\"\n>\n  @if ((mediaPlayerService.playing | async) === false) {\n    <svg viewBox=\"0 0 45 64\" width=\"20\" height=\"29\" focusable=\"false\">\n      <polygon points=\"0.4,0 0.4,64 44.6,32\" />\n    </svg>\n  }\n  @if (mediaPlayerService.playing | async) {\n    <svg viewBox=\"0 0 43 56.9\" width=\"20\" height=\"29\" focusable=\"false\">\n      <rect y=\"0.1\" width=\"15.7\" height=\"56.9\" />\n      <rect x=\"27.3\" y=\"0.1\" width=\"15.7\" height=\"56.9\" />\n    </svg>\n  }\n</button>\n\n<button\n  #endButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  (click)=\"goToEnd()\"\n  (mouseup)=\"endButton.blur()\"\n  [attr.aria-label]=\"mediaPlayerService.goToEndAriaLabel\"\n>\n  <svg viewBox=\"0 0 51.5 64\" width=\"14\" height=\"17\" focusable=\"false\">\n    <rect x=\"44.1\" y=\"0\" width=\"7.5\" height=\"64\" />\n    <polygon points=\"0,64 0,0 44.1,32\" />\n  </svg>\n</button>\n\n<div class=\"actions-list\">\n  <ng-content></ng-content>\n\n  @if (mediaPlayerService.textTracks.length > 0 && mediaPlayerService.type === 'video') {\n    <div class=\"action-button-container\">\n      <button\n        #subtitlesButton\n        uxFocusIndicator\n        type=\"button\"\n        class=\"action-button\"\n        (keydown)=\"returnFocus = true\"\n        (click)=\"subtitlesOpen = !subtitlesOpen\"\n        (mouseup)=\"subtitlesButton.blur(); returnFocus = false\"\n        [attr.aria-label]=\"mediaPlayerService.selectSubtitlesAriaLabel(getSubtitleTrack())\"\n        [attr.aria-expanded]=\"subtitlesOpen\"\n        [attr.aria-describedby]=\"subtitlesId\"\n        aria-haspopup=\"true\"\n      >\n        <ux-icon name=\"subtitles\"></ux-icon>\n      </button>\n      @if (subtitlesOpen) {\n        <div\n          #subtitles\n          [style.top.px]=\"-subtitles.offsetHeight\"\n          class=\"popover top media-player-subtitles-popover show\"\n          [id]=\"subtitlesId\"\n          (keydown.escape)=\"subtitlesOpen = false\"\n          (uxClickOutside)=\"subtitlesOpen = false\"\n        >\n          <div class=\"arrow\"></div>\n          <h3 class=\"popover-title\">{{ mediaPlayerService.subtitlesTitleAriaLabel }}</h3>\n          <div class=\"popover-content\">\n            <ul\n              class=\"subtitles-list\"\n              uxTabbableList\n              [focusOnShow]=\"returnFocus\"\n              [returnFocus]=\"returnFocus\"\n            >\n              <li\n                uxTabbableListItem\n                tabindex=\"0\"\n                class=\"subtitles-list-item\"\n                [class.active]=\"!isSubtitleActive()\"\n                [attr.aria-selected]=\"isSubtitleActive()\"\n                (click)=\"mediaPlayerService.hideSubtitleTracks(); subtitlesOpen = false\"\n                (keydown.enter)=\"\n                  mediaPlayerService.hideSubtitleTracks(); subtitlesOpen = false; returnFocus = true\n                \"\n              >\n                <ux-icon name=\"checkmark\" class=\"subtitles-list-item-checkmark\"></ux-icon>\n                <span>{{ mediaPlayerService.subtitlesOffAriaLabel }}</span>\n              </li>\n              @for (track of mediaPlayerService.textTracks; track track) {\n                <li\n                  uxTabbableListItem\n                  class=\"subtitles-list-item\"\n                  [class.active]=\"track.mode === 'showing'\"\n                  [attr.aria-selected]=\"isSubtitleActive()\"\n                  (click)=\"setSubtitleTrack(track); subtitlesOpen = false\"\n                  (keydown.enter)=\"\n                    setSubtitleTrack(track); subtitlesOpen = false; returnFocus = true\n                  \"\n                >\n                  <ux-icon name=\"checkmark\" class=\"subtitles-list-item-checkmark\"></ux-icon>\n                  <span>{{ track.label }}</span>\n                </li>\n              }\n            </ul>\n          </div>\n        </div>\n      }\n    </div>\n  }\n\n  <div class=\"action-button-container\">\n    @if (mediaPlayerService.type !== 'audio') {\n      <button\n        #fullscreenButton\n        uxFocusIndicator\n        type=\"button\"\n        class=\"action-button\"\n        [attr.aria-label]=\"mediaPlayerService.fullscreenAriaLabel(mediaPlayerService.fullscreen)\"\n        (click)=\"mediaPlayerService.toggleFullscreen()\"\n        (mouseup)=\"fullscreenButton.blur()\"\n      >\n        <ux-icon [name]=\"mediaPlayerService.fullscreen ? 'contract' : 'expand'\"></ux-icon>\n      </button>\n    }\n  </div>\n</div>\n\n<ng-template #muteTooltip>\n  <span aria-hidden=\"true\">{{ volume === 0 ? 'Unmute' : 'Mute' }}</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusWithinDirective, selector: "[uxFocusWithin],[uxBlurWithin]", outputs: ["uxFocusWithin", "uxBlurWithin"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[uxClickOutside]", outputs: ["uxClickOutside"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: SliderComponent, selector: "ux-slider", inputs: ["value", "options", "disabled"], outputs: ["valueChange"] }, { kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerControlsExtensionComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-media-player-controls', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.quiet]': 'mediaPlayerService.quietMode || mediaPlayerService.fullscreen',
                    }, standalone: false, template: "<div class=\"volume-container\">\n  <div\n    class=\"volume-slider-container\"\n    #volumeContainer\n    [class.active]=\"volumeActive || volumeFocus\"\n    (mouseenter)=\"mouseEnterVolume.next()\"\n    (mouseleave)=\"mouseLeaveVolume.next()\"\n    (uxFocusWithin)=\"volumeFocus = true\"\n    (uxBlurWithin)=\"volumeFocus = false\"\n  >\n    <button\n      #volumeIcon\n      uxFocusIndicator\n      type=\"button\"\n      class=\"volume-slider-icon\"\n      [attr.aria-label]=\"mediaPlayerService.muteAriaLabel(volume)\"\n      [uxTooltip]=\"muteTooltip\"\n      [showTriggers]=\"['mouseenter']\"\n      [hideTriggers]=\"['mouseleave']\"\n      (click)=\"toggleMute()\"\n      (mouseup)=\"volumeIcon.blur()\"\n    >\n      @if (volume === 0) {\n        <ux-icon name=\"volume-mute\"></ux-icon>\n      }\n      @if (volume > 0 && volume <= 70) {\n        <ux-icon name=\"volume-low\"></ux-icon>\n      }\n      @if (volume > 70) {\n        <ux-icon name=\"volume\"></ux-icon>\n      }\n    </button>\n\n    <div class=\"volume-slider-node\">\n      <ux-slider\n        [value]=\"volume\"\n        (valueChange)=\"volume = $any($event)\"\n        [options]=\"options\"\n      ></ux-slider>\n    </div>\n  </div>\n</div>\n\n<button\n  #startButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  (click)=\"goToStart()\"\n  (mouseup)=\"startButton.blur()\"\n  [attr.aria-label]=\"mediaPlayerService.goToStartAriaLabel\"\n>\n  <svg viewBox=\"0 0 51.5 64\" width=\"14\" height=\"17\" focusable=\"false\">\n    <rect x=\"0\" y=\"0\" width=\"7.5\" height=\"64\" />\n    <polygon points=\"51.5,64 51.5,0 7.4,32 \" />\n  </svg>\n</button>\n\n<button\n  #playButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  [attr.aria-label]=\"mediaPlayerService.playAriaLabel(mediaPlayerService.playing | async)\"\n  (click)=\"mediaPlayerService.togglePlay()\"\n  (mouseup)=\"playButton.blur()\"\n>\n  @if ((mediaPlayerService.playing | async) === false) {\n    <svg viewBox=\"0 0 45 64\" width=\"20\" height=\"29\" focusable=\"false\">\n      <polygon points=\"0.4,0 0.4,64 44.6,32\" />\n    </svg>\n  }\n  @if (mediaPlayerService.playing | async) {\n    <svg viewBox=\"0 0 43 56.9\" width=\"20\" height=\"29\" focusable=\"false\">\n      <rect y=\"0.1\" width=\"15.7\" height=\"56.9\" />\n      <rect x=\"27.3\" y=\"0.1\" width=\"15.7\" height=\"56.9\" />\n    </svg>\n  }\n</button>\n\n<button\n  #endButton\n  uxFocusIndicator\n  type=\"button\"\n  class=\"control-button\"\n  (click)=\"goToEnd()\"\n  (mouseup)=\"endButton.blur()\"\n  [attr.aria-label]=\"mediaPlayerService.goToEndAriaLabel\"\n>\n  <svg viewBox=\"0 0 51.5 64\" width=\"14\" height=\"17\" focusable=\"false\">\n    <rect x=\"44.1\" y=\"0\" width=\"7.5\" height=\"64\" />\n    <polygon points=\"0,64 0,0 44.1,32\" />\n  </svg>\n</button>\n\n<div class=\"actions-list\">\n  <ng-content></ng-content>\n\n  @if (mediaPlayerService.textTracks.length > 0 && mediaPlayerService.type === 'video') {\n    <div class=\"action-button-container\">\n      <button\n        #subtitlesButton\n        uxFocusIndicator\n        type=\"button\"\n        class=\"action-button\"\n        (keydown)=\"returnFocus = true\"\n        (click)=\"subtitlesOpen = !subtitlesOpen\"\n        (mouseup)=\"subtitlesButton.blur(); returnFocus = false\"\n        [attr.aria-label]=\"mediaPlayerService.selectSubtitlesAriaLabel(getSubtitleTrack())\"\n        [attr.aria-expanded]=\"subtitlesOpen\"\n        [attr.aria-describedby]=\"subtitlesId\"\n        aria-haspopup=\"true\"\n      >\n        <ux-icon name=\"subtitles\"></ux-icon>\n      </button>\n      @if (subtitlesOpen) {\n        <div\n          #subtitles\n          [style.top.px]=\"-subtitles.offsetHeight\"\n          class=\"popover top media-player-subtitles-popover show\"\n          [id]=\"subtitlesId\"\n          (keydown.escape)=\"subtitlesOpen = false\"\n          (uxClickOutside)=\"subtitlesOpen = false\"\n        >\n          <div class=\"arrow\"></div>\n          <h3 class=\"popover-title\">{{ mediaPlayerService.subtitlesTitleAriaLabel }}</h3>\n          <div class=\"popover-content\">\n            <ul\n              class=\"subtitles-list\"\n              uxTabbableList\n              [focusOnShow]=\"returnFocus\"\n              [returnFocus]=\"returnFocus\"\n            >\n              <li\n                uxTabbableListItem\n                tabindex=\"0\"\n                class=\"subtitles-list-item\"\n                [class.active]=\"!isSubtitleActive()\"\n                [attr.aria-selected]=\"isSubtitleActive()\"\n                (click)=\"mediaPlayerService.hideSubtitleTracks(); subtitlesOpen = false\"\n                (keydown.enter)=\"\n                  mediaPlayerService.hideSubtitleTracks(); subtitlesOpen = false; returnFocus = true\n                \"\n              >\n                <ux-icon name=\"checkmark\" class=\"subtitles-list-item-checkmark\"></ux-icon>\n                <span>{{ mediaPlayerService.subtitlesOffAriaLabel }}</span>\n              </li>\n              @for (track of mediaPlayerService.textTracks; track track) {\n                <li\n                  uxTabbableListItem\n                  class=\"subtitles-list-item\"\n                  [class.active]=\"track.mode === 'showing'\"\n                  [attr.aria-selected]=\"isSubtitleActive()\"\n                  (click)=\"setSubtitleTrack(track); subtitlesOpen = false\"\n                  (keydown.enter)=\"\n                    setSubtitleTrack(track); subtitlesOpen = false; returnFocus = true\n                  \"\n                >\n                  <ux-icon name=\"checkmark\" class=\"subtitles-list-item-checkmark\"></ux-icon>\n                  <span>{{ track.label }}</span>\n                </li>\n              }\n            </ul>\n          </div>\n        </div>\n      }\n    </div>\n  }\n\n  <div class=\"action-button-container\">\n    @if (mediaPlayerService.type !== 'audio') {\n      <button\n        #fullscreenButton\n        uxFocusIndicator\n        type=\"button\"\n        class=\"action-button\"\n        [attr.aria-label]=\"mediaPlayerService.fullscreenAriaLabel(mediaPlayerService.fullscreen)\"\n        (click)=\"mediaPlayerService.toggleFullscreen()\"\n        (mouseup)=\"fullscreenButton.blur()\"\n      >\n        <ux-icon [name]=\"mediaPlayerService.fullscreen ? 'contract' : 'expand'\"></ux-icon>\n      </button>\n    }\n  </div>\n</div>\n\n<ng-template #muteTooltip>\n  <span aria-hidden=\"true\">{{ volume === 0 ? 'Unmute' : 'Mute' }}</span>\n</ng-template>\n" }]
        }] });

class MediaPlayerCustomControlDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerCustomControlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MediaPlayerCustomControlDirective, isStandalone: false, selector: "[uxMediaPlayerCustomControl]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerCustomControlDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMediaPlayerCustomControl]',
                    standalone: false,
                }]
        }] });

class DurationPipe {
    transform(seconds) {
        let minutes = Math.floor(seconds / 60);
        let hours = Math.floor(minutes / 60);
        const days = Math.floor(hours / 24);
        hours = hours - days * 24;
        minutes = minutes - days * 24 * 60 - hours * 60;
        seconds = Math.floor(seconds - days * 24 * 60 * 60 - hours * 60 * 60 - minutes * 60);
        if (hours > 0) {
            return `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(seconds)}`;
        }
        else {
            return `${this.pad(minutes)}:${this.pad(seconds)}`;
        }
    }
    pad(value) {
        if (value < 10) {
            return `0${value}`;
        }
        return value.toString();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DurationPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DurationPipe, isStandalone: false, name: "duration" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DurationPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'duration',
                    standalone: false,
                }]
        }] });

class MediaPlayerTimelineExtensionComponent {
    constructor() {
        this.mediaPlayerService = inject(MediaPlayerService);
        this.current = 0;
        this.position = 0;
        this.buffered = [];
        this.mouseDown = false;
        this.scrub = { visible: false, position: 0, time: 0 };
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        // watch for changes to the current time
        this.mediaPlayerService.fullscreenEvent.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            this.scrub.position = 0;
        });
        this.mediaPlayerService.timeUpdateEvent.pipe(takeUntil(this._onDestroy)).subscribe(current => {
            this.current = current;
            this.position = (this.current / this.mediaPlayerService.duration) * 100;
        });
        this.mediaPlayerService.progressEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe((buffered) => {
            this.buffered = [];
            for (let idx = 0; idx < buffered.length; idx++) {
                this.buffered.push({
                    start: (buffered.start(idx) / this.mediaPlayerService.duration) * 100,
                    end: (buffered.end(idx) / this.mediaPlayerService.duration) * 100,
                });
            }
        });
    }
    ngAfterViewInit() {
        const mousedown$ = fromEvent(this.thumb.nativeElement, 'mousedown');
        const mousemove$ = fromEvent(document, 'mousemove');
        const mouseup$ = fromEvent(document, 'mouseup');
        mousedown$
            .pipe(switchMap(() => mousemove$.pipe(takeUntil(mouseup$))), takeUntil(this._onDestroy))
            .subscribe(() => (this.scrub.visible = false));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    updateScrub(event) {
        const target = event.target;
        if (target.classList.contains('media-progress-bar-thumb')) {
            return;
        }
        const timeline = this.timelineRef.nativeElement;
        const bounds = timeline.getBoundingClientRect();
        this.scrub.position = event.offsetX;
        this.scrub.time = (event.offsetX / bounds.width) * this.mediaPlayerService.duration;
        if (this.mouseDown) {
            this.mediaPlayerService.pause();
            this.mediaPlayerService.currentTime = this.scrub.time;
        }
    }
    /** Skip a number of seconds in any direction */
    skip(seconds) {
        let target = this.current + seconds;
        // ensure that the target position is within the bounds of the clip
        if (target < 0) {
            target = 0;
        }
        if (target > this.mediaPlayerService.duration) {
            target = this.mediaPlayerService.duration;
        }
        this.mediaPlayerService.currentTime = target;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerTimelineExtensionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MediaPlayerTimelineExtensionComponent, isStandalone: false, selector: "ux-media-player-timeline", host: { listeners: { "document:mouseup": "mouseDown = false" }, properties: { "class.quiet": "mediaPlayerService.quietMode || mediaPlayerService.fullscreen" } }, viewQueries: [{ propertyName: "thumb", first: true, predicate: ["progressThumb"], descendants: true, static: true }, { propertyName: "timelineRef", first: true, predicate: ["timeline"], descendants: true, static: true }], ngImport: i0, template: "<p class=\"current-time\">{{ current | duration }}</p>\n\n<div\n  #timeline\n  class=\"timeline-bar\"\n  tabindex=\"0\"\n  role=\"slider\"\n  [attr.aria-label]=\"mediaPlayerService.seekAriaLabel\"\n  aria-valuemin=\"0\"\n  [attr.aria-valuemax]=\"mediaPlayerService.duration | number: '0.0-0'\"\n  [attr.aria-valuenow]=\"mediaPlayerService.currentTime | number: '0.0-0'\"\n  attr.aria-valuetext=\"{{ mediaPlayerService.currentTime | duration }} of {{\n    mediaPlayerService.duration | duration\n  }}\"\n  (keydown.ArrowLeft)=\"skip(-5)\"\n  (keydown.ArrowRight)=\"skip(5)\"\n  (mouseenter)=\"scrub.visible = true; tooltip.show()\"\n  (mouseleave)=\"scrub.visible = false; tooltip.hide()\"\n  (mousemove)=\"updateScrub($event); tooltip.reposition()\"\n  (mouseup)=\"updateScrub($event)\"\n  (mousedown)=\"mouseDown = true; $event.preventDefault()\"\n>\n  @for (buffer of buffered; track buffer) {\n    <div\n      class=\"buffered-bar\"\n      [style.left.%]=\"buffer.start\"\n      [style.width.%]=\"buffer.end - buffer.start\"\n    ></div>\n  }\n\n  <div class=\"media-progress-bar\" [style.width.%]=\"position\">\n    <div\n      #progressThumb\n      class=\"media-progress-bar-thumb\"\n      (mouseenter)=\"scrub.visible = false; tooltip.hide(); $event.stopPropagation()\"\n      (mouseleave)=\"scrub.visible = true; tooltip.show(); $event.stopPropagation()\"\n    ></div>\n  </div>\n\n  <div\n    #tooltip=\"ux-tooltip\"\n    class=\"scrub-handle\"\n    [class.scrub-handle-hidden]=\"!scrub.visible\"\n    [style.left.px]=\"scrub.position\"\n    [uxTooltip]=\"popTemplate\"\n    tooltipClass=\"ux-media-player-timeline-tooltip\"\n    placement=\"top\"\n    [showTriggers]=\"[]\"\n    [hideTriggers]=\"[]\"\n    [tooltipDelay]=\"100\"\n    [tooltipDisabled]=\"mediaPlayerService.duration === 0\"\n  ></div>\n</div>\n\n<p class=\"duration-time\">{{ mediaPlayerService.duration | duration }}</p>\n\n<ng-template #popTemplate>\n  <span>{{ scrub.time | duration }}</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: DurationPipe, name: "duration" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerTimelineExtensionComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-media-player-timeline', host: {
                        '(document:mouseup)': 'mouseDown = false',
                        '[class.quiet]': 'mediaPlayerService.quietMode || mediaPlayerService.fullscreen',
                    }, standalone: false, template: "<p class=\"current-time\">{{ current | duration }}</p>\n\n<div\n  #timeline\n  class=\"timeline-bar\"\n  tabindex=\"0\"\n  role=\"slider\"\n  [attr.aria-label]=\"mediaPlayerService.seekAriaLabel\"\n  aria-valuemin=\"0\"\n  [attr.aria-valuemax]=\"mediaPlayerService.duration | number: '0.0-0'\"\n  [attr.aria-valuenow]=\"mediaPlayerService.currentTime | number: '0.0-0'\"\n  attr.aria-valuetext=\"{{ mediaPlayerService.currentTime | duration }} of {{\n    mediaPlayerService.duration | duration\n  }}\"\n  (keydown.ArrowLeft)=\"skip(-5)\"\n  (keydown.ArrowRight)=\"skip(5)\"\n  (mouseenter)=\"scrub.visible = true; tooltip.show()\"\n  (mouseleave)=\"scrub.visible = false; tooltip.hide()\"\n  (mousemove)=\"updateScrub($event); tooltip.reposition()\"\n  (mouseup)=\"updateScrub($event)\"\n  (mousedown)=\"mouseDown = true; $event.preventDefault()\"\n>\n  @for (buffer of buffered; track buffer) {\n    <div\n      class=\"buffered-bar\"\n      [style.left.%]=\"buffer.start\"\n      [style.width.%]=\"buffer.end - buffer.start\"\n    ></div>\n  }\n\n  <div class=\"media-progress-bar\" [style.width.%]=\"position\">\n    <div\n      #progressThumb\n      class=\"media-progress-bar-thumb\"\n      (mouseenter)=\"scrub.visible = false; tooltip.hide(); $event.stopPropagation()\"\n      (mouseleave)=\"scrub.visible = true; tooltip.show(); $event.stopPropagation()\"\n    ></div>\n  </div>\n\n  <div\n    #tooltip=\"ux-tooltip\"\n    class=\"scrub-handle\"\n    [class.scrub-handle-hidden]=\"!scrub.visible\"\n    [style.left.px]=\"scrub.position\"\n    [uxTooltip]=\"popTemplate\"\n    tooltipClass=\"ux-media-player-timeline-tooltip\"\n    placement=\"top\"\n    [showTriggers]=\"[]\"\n    [hideTriggers]=\"[]\"\n    [tooltipDelay]=\"100\"\n    [tooltipDisabled]=\"mediaPlayerService.duration === 0\"\n  ></div>\n</div>\n\n<p class=\"duration-time\">{{ mediaPlayerService.duration | duration }}</p>\n\n<ng-template #popTemplate>\n  <span>{{ scrub.time | duration }}</span>\n</ng-template>\n" }]
        }], propDecorators: { thumb: [{
                type: ViewChild,
                args: ['progressThumb', { static: true }]
            }], timelineRef: [{
                type: ViewChild,
                args: ['timeline', { static: true }]
            }] } });

class AudioService {
    constructor() {
        this._http = inject(HttpClient);
    }
    getAudioFileMetadata(mediaElement) {
        return Observable.create((observer) => {
            this._http.get(mediaElement.src, { responseType: 'blob' }).subscribe(response => {
                let description;
                const extension = mediaElement.src
                    .substring(mediaElement.src.lastIndexOf('.') + 1)
                    .toLowerCase();
                const filename = mediaElement.src.indexOf('base64') !== -1
                    ? ''
                    : mediaElement.src.substring(mediaElement.src.lastIndexOf('/') + 1);
                switch (extension) {
                    case 'mp3':
                        description = 'MPEG audio layer 3 file';
                        break;
                    case 'wma':
                        description = 'Windows media audio file';
                        break;
                    case 'wav':
                        description = 'WAVE audio file';
                        break;
                    case 'ogg':
                        description = 'Ogg Vorbis file';
                        break;
                    case 'aac':
                        description = 'Advanced audio coding file';
                        break;
                    case 'midi':
                        description = 'Musical instrument digital interface file';
                        break;
                    default:
                        description = 'Audio file';
                        break;
                }
                observer.next({
                    filename,
                    extension,
                    description,
                    size: response.size,
                });
            });
        });
    }
    getWaveformFromUrl(url) {
        // if audio context is not support return a stream of empty data
        if (!window.AudioContext) {
            return of([new Float32Array(0)]);
        }
        this._audioContext = new AudioContext();
        this.createVolumeNode();
        this.createAnalyserNode();
        return Observable.create((observer) => {
            // load the media from the URL provided
            this._http.get(url, { responseType: 'arraybuffer' }).subscribe(response => {
                this.getAudioBuffer(response).subscribe(audioBuffer => {
                    // create the buffer source
                    this.createBufferSource(audioBuffer);
                    let dataPoints = [];
                    const channels = this._audioBuffer.numberOfChannels;
                    // extract the data from each channel
                    for (let channelIdx = 0; channelIdx < channels; channelIdx++) {
                        dataPoints[channelIdx] = this._audioBuffer.getChannelData(channelIdx);
                    }
                    observer.next(dataPoints);
                    observer.complete();
                    // cleanup after ourselves
                    dataPoints = null;
                }, error => observer.error(error));
            }, error => observer.error(error));
        });
    }
    getWaveformPoints(channels = [], skip = 1000) {
        const waveform = [];
        const duration = channels.length > 0 ? channels[0].length : 0;
        // convert each channel data to a series of waveform points
        for (let idx = 0; idx < duration; idx += skip) {
            // get all the channel data for a specific point
            const points = channels.map(channel => channel[idx]);
            // find the minimum point and maximum points at each position across all channels
            waveform.push({
                min: points.reduce((previous, current) => (current < previous ? current : previous)),
                max: points.reduce((previous, current) => (current > previous ? current : previous)),
            });
        }
        return waveform;
    }
    getAudioBuffer(arrayBuffer) {
        return Observable.create((observer) => {
            this.getOfflineAudioContext().decodeAudioData(arrayBuffer, (audioBuffer) => {
                observer.next(audioBuffer);
                observer.complete();
            }, error => observer.error(error));
        });
    }
    getOfflineAudioContext() {
        return new OfflineAudioContext(1, 2, this._audioContext.sampleRate || 44100);
    }
    createBufferSource(audioBuffer) {
        this.disconnectSource();
        this._audioBuffer = audioBuffer;
        this._audioBufferSource = this._audioContext.createBufferSource();
        this._audioBufferSource.buffer = this._audioBuffer;
        this._audioBufferSource.connect(this._analyserNode);
    }
    createVolumeNode() {
        this._gainNode = this._audioContext.createGain();
        this._gainNode.connect(this._audioContext.destination);
    }
    createAnalyserNode() {
        this._analyserNode = this._audioContext.createAnalyser();
        this._analyserNode.connect(this._gainNode);
    }
    disconnectSource() {
        if (this._audioBufferSource) {
            this._audioBufferSource.disconnect();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioService, decorators: [{
            type: Injectable
        }] });

class AudioServiceModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioServiceModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AudioServiceModule }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioServiceModule, providers: [AudioService, provideHttpClient(withInterceptorsFromDi())] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioServiceModule, decorators: [{
            type: NgModule,
            args: [{ imports: [], providers: [AudioService, provideHttpClient(withInterceptorsFromDi())] }]
        }] });

class FileSizePipe {
    transform(value) {
        // allow for async values
        if (!value) {
            return value;
        }
        const units = ['B', 'KB', 'MB', 'GB', 'TB'];
        // calculate the which unit bracket the values should be a part of
        const idx = Math.floor(Math.log(value) / Math.log(1024));
        const formattedValue = value / Math.pow(1024, idx);
        return `${formattedValue.toFixed(2)} ${units[idx]}`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipe, isStandalone: false, name: "fileSize" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'fileSize',
                    standalone: false,
                }]
        }] });

class MediaPlayerComponent {
    get source() {
        return this.mediaPlayerService.source;
    }
    /** The url to the media file to be loaded by the media player. */
    set source(value) {
        this.mediaPlayerService.source = value;
    }
    get type() {
        return this.mediaPlayerService.type;
    }
    /**
     * Defines the appearance of the media player. The two possible values are `video` and `audio`.
     * The media player will adapt it's appearance to best suit the type specified.
     */
    set type(value) {
        this.mediaPlayerService.type = value;
    }
    get quietMode() {
        return this.mediaPlayerService.quietMode;
    }
    /**
     * If enabled, the controls in the media player will be hidden unless the mouse is over the player and will appear in a darker style.
     * Dark mode is automatically enabled in full screen mode. Quiet mode is only available for videos.
     */
    set quietMode(value) {
        this.mediaPlayerService.quietMode = value;
    }
    /**
     * If specified the function will be called passing the current volume as an argument.
     * It should return an appropriate aria-label for the mute/unmute button.
     */
    set muteAriaLabel(fn) {
        this.mediaPlayerService.muteAriaLabel = fn;
    }
    /**
     * If specified the function will be called passing the current playing state as an argument.
     * It should return an appropriate aria-label for the play/pause button.
     */
    set playAriaLabel(fn) {
        this.mediaPlayerService.playAriaLabel = fn;
    }
    /**
     * If specified the function will be called passing the current fullscreen state as an argument.
     * It should return an appropriate aria-label for the fullscreen toggle button.
     */
    set fullscreenAriaLabel(fn) {
        this.mediaPlayerService.fullscreenAriaLabel = fn;
    }
    /**
     * If specified the function will be called passing the current track as an argument.
     * It should return an appropriate aria-label for the subtitle selection button.
     */
    set selectSubtitlesAriaLabel(fn) {
        this.mediaPlayerService.selectSubtitlesAriaLabel = fn;
    }
    /** Defines an aria-label for the go to start button. */
    set goToStartAriaLabel(ariaLabel) {
        this.mediaPlayerService.goToStartAriaLabel = ariaLabel;
    }
    /** Defines an aria-label for the go to end button. */
    set goToEndAriaLabel(ariaLabel) {
        this.mediaPlayerService.goToEndAriaLabel = ariaLabel;
    }
    /** Defines an aria-label for the title displayed in the subtitle selection popover. */
    set subtitlesTitleAriaLabel(ariaLabel) {
        this.mediaPlayerService.subtitlesTitleAriaLabel = ariaLabel;
    }
    /** Defines an aria-label to indicate subtitle are not currently enabled. */
    set subtitlesOffAriaLabel(ariaLabel) {
        this.mediaPlayerService.subtitlesOffAriaLabel = ariaLabel;
    }
    /** Define an aria-label to indicate there are no subtitles available. */
    set noSubtitlesAriaLabel(ariaLabel) {
        this.mediaPlayerService.noSubtitlesAriaLabel = ariaLabel;
    }
    /** Define an aria-label for the media player. */
    set mediaPlayerAriaLabel(ariaLabel) {
        this.mediaPlayerService.mediaPlayerAriaLabel = ariaLabel;
    }
    /** Define an aria-label for the the seek element. */
    set seekAriaLabel(ariaLabel) {
        this.mediaPlayerService.seekAriaLabel = ariaLabel;
    }
    constructor() {
        this.mediaPlayerService = inject(MediaPlayerService);
        this._audioService = inject(AudioService);
        this._elementRef = inject(ElementRef);
        this.hovering = false;
        this.focused = false;
        /** The `anonymous` keyword means that there will be no exchange of user credentials when the media source is fetched. */
        this.crossorigin = 'use-credentials';
        this._onDestroy = new Subject();
        // show controls when hovering and in quiet mode
        fromEvent(this._elementRef.nativeElement, 'mousemove')
            .pipe(tap(() => (this.hovering = true)), debounceTime(2000), takeUntil(this._onDestroy))
            .subscribe(() => (this.hovering = false));
    }
    ngAfterViewInit() {
        this.mediaPlayerService.setMediaPlayer(this._elementRef.nativeElement, this._playerRef.nativeElement);
        this.audioMetadata = this._audioService.getAudioFileMetadata(this._playerRef.nativeElement);
        this.mediaPlayerService.playingEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.mediaPlayerService.playing.next(true));
        this.mediaPlayerService.pauseEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.mediaPlayerService.playing.next(false));
        this.mediaPlayerService.mediaClickEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.mediaPlayerService.togglePlay());
        this.mediaPlayerService.loadedMetadataEvent
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this.mediaPlayerService.loaded = true));
        // initially hide all text tracks
        this.mediaPlayerService.hideSubtitleTracks();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MediaPlayerComponent, isStandalone: false, selector: "ux-media-player", inputs: { crossorigin: "crossorigin", filename: "filename", source: "source", type: "type", quietMode: "quietMode", muteAriaLabel: "muteAriaLabel", playAriaLabel: "playAriaLabel", fullscreenAriaLabel: "fullscreenAriaLabel", selectSubtitlesAriaLabel: "selectSubtitlesAriaLabel", goToStartAriaLabel: "goToStartAriaLabel", goToEndAriaLabel: "goToEndAriaLabel", subtitlesTitleAriaLabel: "subtitlesTitleAriaLabel", subtitlesOffAriaLabel: "subtitlesOffAriaLabel", noSubtitlesAriaLabel: "noSubtitlesAriaLabel", mediaPlayerAriaLabel: "mediaPlayerAriaLabel", seekAriaLabel: "seekAriaLabel" }, host: { listeners: { "keydown.Space": "mediaPlayerService.togglePlay(); $event.preventDefault()", "mouseenter": "hovering = true", "mouseleave": "hovering = false", "document:fullscreenchange": "mediaPlayerService.fullscreenChange()", "document:webkitfullscreenchange": "mediaPlayerService.fullscreenChange()", "document:mozfullscreenchange": "mediaPlayerService.fullscreenChange()", "document:MSFullscreenChange": "mediaPlayerService.fullscreenChange()" }, properties: { "class.standard": "!mediaPlayerService.fullscreen", "class.fullscreen": "mediaPlayerService.fullscreen", "class.quiet": "quietMode && type === \"video\" || mediaPlayerService.fullscreen", "class.hover": "hovering || focused", "class.video": "type === \"video\"", "class.audio": "type === \"audio\"" } }, providers: [MediaPlayerService], viewQueries: [{ propertyName: "_playerRef", first: true, predicate: ["player"], descendants: true }], ngImport: i0, template: "<div\n  class=\"player-container\"\n  uxFocusIndicator\n  tabindex=\"0\"\n  [attr.aria-label]=\"mediaPlayerService.mediaPlayerAriaLabel\"\n  [cdkTrapFocus]=\"mediaPlayerService.fullscreen\"\n>\n  @if (type === 'video') {\n    <div class=\"video-player-container\">\n      <video\n        class=\"video-player\"\n        #player\n        tabindex=\"-1\"\n        [src]=\"source\"\n        [crossOrigin]=\"crossorigin\"\n        (abort)=\"mediaPlayerService.abortEvent.next()\"\n        (canplay)=\"mediaPlayerService.canPlayEvent.next(true)\"\n        (canplaythrough)=\"mediaPlayerService.canPlayThroughEvent.next(true)\"\n        (durationchange)=\"mediaPlayerService.durationChangeEvent.next(player.duration)\"\n        (ended)=\"mediaPlayerService.endedEvent.next()\"\n        (error)=\"mediaPlayerService.errorEvent.next($event)\"\n        (loadeddata)=\"mediaPlayerService.loadedDataEvent.next($event)\"\n        (loadedmetadata)=\"mediaPlayerService.loadedMetadataEvent.next($event)\"\n        (loadstart)=\"mediaPlayerService.loadStartEvent.next()\"\n        (pause)=\"mediaPlayerService.pauseEvent.next()\"\n        (play)=\"mediaPlayerService.playEvent.next()\"\n        (playing)=\"mediaPlayerService.playingEvent.next(!player.paused)\"\n        (ratechange)=\"mediaPlayerService.rateChangeEvent.next(player.playbackRate)\"\n        (seeked)=\"mediaPlayerService.seekedEvent.next(player.currentTime)\"\n        (seeking)=\"mediaPlayerService.seekingEvent.next(player.currentTime)\"\n        (stalled)=\"mediaPlayerService.stalledEvent.next()\"\n        (suspend)=\"mediaPlayerService.suspendEvent.next()\"\n        (timeupdate)=\"mediaPlayerService.timeUpdateEvent.next(player.currentTime)\"\n        (volumechange)=\"mediaPlayerService.volumeChangeEvent.next(player.volume)\"\n        (waiting)=\"mediaPlayerService.waitingEvent.next()\"\n        (click)=\"mediaPlayerService.mediaClickEvent.next($event)\"\n      >\n        <ng-content select=\"track\"></ng-content>\n      </video>\n      <div class=\"video-overlay\" [class.playing]=\"mediaPlayerService.playing | async\">\n        <svg class=\"play-graphic\" x=\"0px\" y=\"0px\" viewBox=\"0 0 64 64\">\n          <circle class=\"play-circle\" cx=\"32.2\" cy=\"31.8\" r=\"31.8\" />\n          <polygon class=\"play-triangle\" points=\"23,14.1 23,50.8 48.3,32.5\" />\n        </svg>\n      </div>\n    </div>\n  }\n\n  @if (type === 'audio') {\n    <div class=\"audio-player\">\n      <svg width=\"24px\" height=\"24px\" viewBox=\"0 0 24 24\">\n        <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n          <g transform=\"translate(-98.000000, -458.000000)\">\n            <g transform=\"translate(98.000000, 458.000000)\">\n              <path\n                d=\"M4.5,0.5 L18.0435308,0.5 L23.5,6.22251502 L23.5,23.5 L4.5,23.5 L4.5,0.5 Z\"\n                fill=\"#CCEAE2\"\n              ></path>\n              <path\n                d=\"M4.5,8 L4.5,0.5 L18,0.5 L23.5,6 L23.5,23.5 L18,23.5\"\n                stroke=\"#60798D\"\n                fill=\"#CCEAE2\"\n              ></path>\n              <path\n                d=\"M4,13.5 L0.5,13.5 L0.5,18.5 L4,18.5 L9.5,22.5 L9.5,9.5 L4,13.5 Z\"\n                stroke=\"#60798D\"\n                fill=\"#85D2BE\"\n              ></path>\n              <path\n                d=\"M11.5,12.5137939 C13.7576225,12.5137939 14.5,14.3709236 14.5,16 C14.5,17.6849236 13.7089152,19.5420532 11.5,19.5420532\"\n                stroke=\"#60798D\"\n              ></path>\n              <path\n                d=\"M11.5,9 C15.8037643,9.04168701 18.5,11.6604805 18.5,16 C18.5,20.3395195 15.8804302,23.0079956 11.5,23\"\n                stroke=\"#60798D\"\n              ></path>\n              <path\n                d=\"M17.5219116,0.761413574 L17.5219116,6 L23,6\"\n                stroke=\"#60798D\"\n                fill=\"#85D2BE\"\n              ></path>\n            </g>\n          </g>\n        </g>\n      </svg>\n      <p class=\"audio-file-name\">\n        {{ this.filename ? this.filename : (audioMetadata | async)?.filename }}\n      </p>\n      <p class=\"audio-file-format\">{{ (audioMetadata | async)?.description }}</p>\n      <p class=\"audio-file-size\">{{ (audioMetadata | async)?.size | fileSize }}</p>\n      <audio\n        #player\n        [src]=\"source\"\n        (abort)=\"mediaPlayerService.abortEvent.next()\"\n        (canplay)=\"mediaPlayerService.canPlayEvent.next(true)\"\n        (canplaythrough)=\"mediaPlayerService.canPlayThroughEvent.next(true)\"\n        (durationchange)=\"mediaPlayerService.durationChangeEvent.next(player.duration)\"\n        (ended)=\"mediaPlayerService.endedEvent.next()\"\n        (error)=\"mediaPlayerService.errorEvent.next($event)\"\n        (loadeddata)=\"mediaPlayerService.loadedDataEvent.next($event)\"\n        (loadedmetadata)=\"mediaPlayerService.loadedMetadataEvent.next($event)\"\n        (loadstart)=\"mediaPlayerService.loadStartEvent.next()\"\n        (pause)=\"mediaPlayerService.pauseEvent.next()\"\n        (play)=\"mediaPlayerService.playEvent.next()\"\n        (playing)=\"mediaPlayerService.playingEvent.next(!player.paused)\"\n        (ratechange)=\"mediaPlayerService.rateChangeEvent.next(player.playbackRate)\"\n        (seeked)=\"mediaPlayerService.seekedEvent.next(player.currentTime)\"\n        (seeking)=\"mediaPlayerService.seekingEvent.next(player.currentTime)\"\n        (stalled)=\"mediaPlayerService.stalledEvent.next()\"\n        (suspend)=\"mediaPlayerService.suspendEvent.next()\"\n        (timeupdate)=\"mediaPlayerService.timeUpdateEvent.next(player.currentTime)\"\n        (volumechange)=\"mediaPlayerService.volumeChangeEvent.next(player.volume)\"\n        (waiting)=\"mediaPlayerService.waitingEvent.next()\"\n        (click)=\"mediaPlayerService.mediaClickEvent.next($event)\"\n      ></audio>\n    </div>\n  }\n\n  <div class=\"control-bar\" (uxFocusWithin)=\"focused = true\" (uxBlurWithin)=\"focused = false\">\n    <ux-media-player-timeline></ux-media-player-timeline>\n    <ux-media-player-controls>\n      <ng-content select=\"[uxMediaPlayerCustomControl]\"></ng-content>\n    </ux-media-player-controls>\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusWithinDirective, selector: "[uxFocusWithin],[uxBlurWithin]", outputs: ["uxFocusWithin", "uxBlurWithin"] }, { kind: "component", type: MediaPlayerTimelineExtensionComponent, selector: "ux-media-player-timeline" }, { kind: "component", type: MediaPlayerControlsExtensionComponent, selector: "ux-media-player-controls" }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-media-player', providers: [MediaPlayerService], host: {
                        '(keydown.Space)': 'mediaPlayerService.togglePlay(); $event.preventDefault()',
                        '[class.standard]': '!mediaPlayerService.fullscreen',
                        '[class.fullscreen]': 'mediaPlayerService.fullscreen',
                        '[class.quiet]': 'quietMode && type === "video" || mediaPlayerService.fullscreen',
                        '[class.hover]': 'hovering || focused',
                        '[class.video]': 'type === "video"',
                        '[class.audio]': 'type === "audio"',
                        '(mouseenter)': 'hovering = true',
                        '(mouseleave)': 'hovering = false',
                        '(document:fullscreenchange)': 'mediaPlayerService.fullscreenChange()',
                        '(document:webkitfullscreenchange)': 'mediaPlayerService.fullscreenChange()',
                        '(document:mozfullscreenchange)': 'mediaPlayerService.fullscreenChange()',
                        '(document:MSFullscreenChange)': 'mediaPlayerService.fullscreenChange()',
                    }, standalone: false, template: "<div\n  class=\"player-container\"\n  uxFocusIndicator\n  tabindex=\"0\"\n  [attr.aria-label]=\"mediaPlayerService.mediaPlayerAriaLabel\"\n  [cdkTrapFocus]=\"mediaPlayerService.fullscreen\"\n>\n  @if (type === 'video') {\n    <div class=\"video-player-container\">\n      <video\n        class=\"video-player\"\n        #player\n        tabindex=\"-1\"\n        [src]=\"source\"\n        [crossOrigin]=\"crossorigin\"\n        (abort)=\"mediaPlayerService.abortEvent.next()\"\n        (canplay)=\"mediaPlayerService.canPlayEvent.next(true)\"\n        (canplaythrough)=\"mediaPlayerService.canPlayThroughEvent.next(true)\"\n        (durationchange)=\"mediaPlayerService.durationChangeEvent.next(player.duration)\"\n        (ended)=\"mediaPlayerService.endedEvent.next()\"\n        (error)=\"mediaPlayerService.errorEvent.next($event)\"\n        (loadeddata)=\"mediaPlayerService.loadedDataEvent.next($event)\"\n        (loadedmetadata)=\"mediaPlayerService.loadedMetadataEvent.next($event)\"\n        (loadstart)=\"mediaPlayerService.loadStartEvent.next()\"\n        (pause)=\"mediaPlayerService.pauseEvent.next()\"\n        (play)=\"mediaPlayerService.playEvent.next()\"\n        (playing)=\"mediaPlayerService.playingEvent.next(!player.paused)\"\n        (ratechange)=\"mediaPlayerService.rateChangeEvent.next(player.playbackRate)\"\n        (seeked)=\"mediaPlayerService.seekedEvent.next(player.currentTime)\"\n        (seeking)=\"mediaPlayerService.seekingEvent.next(player.currentTime)\"\n        (stalled)=\"mediaPlayerService.stalledEvent.next()\"\n        (suspend)=\"mediaPlayerService.suspendEvent.next()\"\n        (timeupdate)=\"mediaPlayerService.timeUpdateEvent.next(player.currentTime)\"\n        (volumechange)=\"mediaPlayerService.volumeChangeEvent.next(player.volume)\"\n        (waiting)=\"mediaPlayerService.waitingEvent.next()\"\n        (click)=\"mediaPlayerService.mediaClickEvent.next($event)\"\n      >\n        <ng-content select=\"track\"></ng-content>\n      </video>\n      <div class=\"video-overlay\" [class.playing]=\"mediaPlayerService.playing | async\">\n        <svg class=\"play-graphic\" x=\"0px\" y=\"0px\" viewBox=\"0 0 64 64\">\n          <circle class=\"play-circle\" cx=\"32.2\" cy=\"31.8\" r=\"31.8\" />\n          <polygon class=\"play-triangle\" points=\"23,14.1 23,50.8 48.3,32.5\" />\n        </svg>\n      </div>\n    </div>\n  }\n\n  @if (type === 'audio') {\n    <div class=\"audio-player\">\n      <svg width=\"24px\" height=\"24px\" viewBox=\"0 0 24 24\">\n        <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n          <g transform=\"translate(-98.000000, -458.000000)\">\n            <g transform=\"translate(98.000000, 458.000000)\">\n              <path\n                d=\"M4.5,0.5 L18.0435308,0.5 L23.5,6.22251502 L23.5,23.5 L4.5,23.5 L4.5,0.5 Z\"\n                fill=\"#CCEAE2\"\n              ></path>\n              <path\n                d=\"M4.5,8 L4.5,0.5 L18,0.5 L23.5,6 L23.5,23.5 L18,23.5\"\n                stroke=\"#60798D\"\n                fill=\"#CCEAE2\"\n              ></path>\n              <path\n                d=\"M4,13.5 L0.5,13.5 L0.5,18.5 L4,18.5 L9.5,22.5 L9.5,9.5 L4,13.5 Z\"\n                stroke=\"#60798D\"\n                fill=\"#85D2BE\"\n              ></path>\n              <path\n                d=\"M11.5,12.5137939 C13.7576225,12.5137939 14.5,14.3709236 14.5,16 C14.5,17.6849236 13.7089152,19.5420532 11.5,19.5420532\"\n                stroke=\"#60798D\"\n              ></path>\n              <path\n                d=\"M11.5,9 C15.8037643,9.04168701 18.5,11.6604805 18.5,16 C18.5,20.3395195 15.8804302,23.0079956 11.5,23\"\n                stroke=\"#60798D\"\n              ></path>\n              <path\n                d=\"M17.5219116,0.761413574 L17.5219116,6 L23,6\"\n                stroke=\"#60798D\"\n                fill=\"#85D2BE\"\n              ></path>\n            </g>\n          </g>\n        </g>\n      </svg>\n      <p class=\"audio-file-name\">\n        {{ this.filename ? this.filename : (audioMetadata | async)?.filename }}\n      </p>\n      <p class=\"audio-file-format\">{{ (audioMetadata | async)?.description }}</p>\n      <p class=\"audio-file-size\">{{ (audioMetadata | async)?.size | fileSize }}</p>\n      <audio\n        #player\n        [src]=\"source\"\n        (abort)=\"mediaPlayerService.abortEvent.next()\"\n        (canplay)=\"mediaPlayerService.canPlayEvent.next(true)\"\n        (canplaythrough)=\"mediaPlayerService.canPlayThroughEvent.next(true)\"\n        (durationchange)=\"mediaPlayerService.durationChangeEvent.next(player.duration)\"\n        (ended)=\"mediaPlayerService.endedEvent.next()\"\n        (error)=\"mediaPlayerService.errorEvent.next($event)\"\n        (loadeddata)=\"mediaPlayerService.loadedDataEvent.next($event)\"\n        (loadedmetadata)=\"mediaPlayerService.loadedMetadataEvent.next($event)\"\n        (loadstart)=\"mediaPlayerService.loadStartEvent.next()\"\n        (pause)=\"mediaPlayerService.pauseEvent.next()\"\n        (play)=\"mediaPlayerService.playEvent.next()\"\n        (playing)=\"mediaPlayerService.playingEvent.next(!player.paused)\"\n        (ratechange)=\"mediaPlayerService.rateChangeEvent.next(player.playbackRate)\"\n        (seeked)=\"mediaPlayerService.seekedEvent.next(player.currentTime)\"\n        (seeking)=\"mediaPlayerService.seekingEvent.next(player.currentTime)\"\n        (stalled)=\"mediaPlayerService.stalledEvent.next()\"\n        (suspend)=\"mediaPlayerService.suspendEvent.next()\"\n        (timeupdate)=\"mediaPlayerService.timeUpdateEvent.next(player.currentTime)\"\n        (volumechange)=\"mediaPlayerService.volumeChangeEvent.next(player.volume)\"\n        (waiting)=\"mediaPlayerService.waitingEvent.next()\"\n        (click)=\"mediaPlayerService.mediaClickEvent.next($event)\"\n      ></audio>\n    </div>\n  }\n\n  <div class=\"control-bar\" (uxFocusWithin)=\"focused = true\" (uxBlurWithin)=\"focused = false\">\n    <ux-media-player-timeline></ux-media-player-timeline>\n    <ux-media-player-controls>\n      <ng-content select=\"[uxMediaPlayerCustomControl]\"></ng-content>\n    </ux-media-player-controls>\n  </div>\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { _playerRef: [{
                type: ViewChild,
                args: ['player', { static: false }]
            }], crossorigin: [{
                type: Input
            }], filename: [{
                type: Input
            }], source: [{
                type: Input
            }], type: [{
                type: Input
            }], quietMode: [{
                type: Input
            }], muteAriaLabel: [{
                type: Input
            }], playAriaLabel: [{
                type: Input
            }], fullscreenAriaLabel: [{
                type: Input
            }], selectSubtitlesAriaLabel: [{
                type: Input
            }], goToStartAriaLabel: [{
                type: Input
            }], goToEndAriaLabel: [{
                type: Input
            }], subtitlesTitleAriaLabel: [{
                type: Input
            }], subtitlesOffAriaLabel: [{
                type: Input
            }], noSubtitlesAriaLabel: [{
                type: Input
            }], mediaPlayerAriaLabel: [{
                type: Input
            }], seekAriaLabel: [{
                type: Input
            }] } });

class DurationPipeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DurationPipeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DurationPipeModule, declarations: [DurationPipe], exports: [DurationPipe] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DurationPipeModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DurationPipeModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [DurationPipe],
                    declarations: [DurationPipe],
                }]
        }] });

class FileSizePipeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipeModule, declarations: [FileSizePipe], exports: [FileSizePipe] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipeModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileSizePipeModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [FileSizePipe],
                    declarations: [FileSizePipe],
                }]
        }] });

const DECLARATIONS$4 = [
    MediaPlayerComponent,
    MediaPlayerTimelineExtensionComponent,
    MediaPlayerBaseExtensionDirective,
    MediaPlayerControlsExtensionComponent,
    MediaPlayerCustomControlDirective,
];
class MediaPlayerModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerModule, declarations: [MediaPlayerComponent,
            MediaPlayerTimelineExtensionComponent,
            MediaPlayerBaseExtensionDirective,
            MediaPlayerControlsExtensionComponent,
            MediaPlayerCustomControlDirective], imports: [A11yModule,
            AccessibilityModule,
            AudioServiceModule,
            ClickOutsideModule,
            CommonModule,
            DurationPipeModule,
            FileSizePipeModule,
            IconModule,
            SliderModule,
            TooltipModule], exports: [MediaPlayerComponent,
            MediaPlayerTimelineExtensionComponent,
            MediaPlayerBaseExtensionDirective,
            MediaPlayerControlsExtensionComponent,
            MediaPlayerCustomControlDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerModule, imports: [A11yModule,
            AccessibilityModule,
            AudioServiceModule,
            ClickOutsideModule,
            CommonModule,
            DurationPipeModule,
            FileSizePipeModule,
            IconModule,
            SliderModule,
            TooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MediaPlayerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        AudioServiceModule,
                        ClickOutsideModule,
                        CommonModule,
                        DurationPipeModule,
                        FileSizePipeModule,
                        IconModule,
                        SliderModule,
                        TooltipModule,
                    ],
                    exports: DECLARATIONS$4,
                    declarations: DECLARATIONS$4,
                }]
        }] });

class NavigationItemComponent {
    /** Get the active state of this item from the router */
    get active() {
        return this.link ? this._router.isActive(this.link, true) : false;
    }
    get children() {
        return this._children.filter(item => item !== this);
    }
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._router = inject(Router);
        this._parent = inject(NavigationItemComponent, { optional: true, skipSelf: true });
        /** Whether the navigation item is expanded, displaying the items from the `children` array. */
        this.expanded = false;
        /** Indicate whether the indentation should include the arrow */
        this._indentWithoutArrow = true;
        /** Automatically unsubscribe when the component is destroyed */
        this._onDestroy = new Subject();
        this._level = this._parent ? this._parent._level + 1 : 1;
        // Expand this component if it or a descendant is active.
        this._router.events
            .pipe(filter(event => event instanceof NavigationEnd), takeUntil(this._onDestroy))
            .subscribe(() => {
            this.expanded = this.hasActiveLink(this.link);
        });
    }
    ngAfterViewInit() {
        // Add classes to parent for styling
        const parentListElement = this._elementRef.nativeElement.parentElement;
        if (parentListElement) {
            const levelClass = this.getLevelClass();
            if (levelClass.length > 0) {
                this._renderer.addClass(parentListElement, 'nav');
                this._renderer.addClass(parentListElement, levelClass);
            }
        }
    }
    ngAfterContentInit() {
        // Set 'indentWithoutArrow'
        this.setIndentWithoutArrow();
        // Update 'indentWithoutArrow' in response to changes to children
        this._children.changes
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.setIndentWithoutArrow());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Check if this item or any children are active */
    hasActiveLink(link) {
        // If this component has a link, check if it is active.
        if (link && this._router.isActive(link, true)) {
            return true;
        }
        // If this component has children, check if any of them, or their descendants, are active.
        return this.children.some(item => item.hasActiveLink(item.link));
    }
    getLevelClass() {
        switch (this._level) {
            case 2:
                return 'nav-second-level';
            case 3:
                return 'nav-third-level';
            case 4:
                return 'nav-fourth-level';
            case 5:
                return 'nav-fifth-level';
        }
        return '';
    }
    setIndentWithoutArrow() {
        if (this.children.length > 0) {
            // If this element has children it will be indented and will have an arrow
            this._indentWithoutArrow = false;
        }
        else if (this._parent) {
            // If this element has a parent, indent it if any of its siblings have children
            this._indentWithoutArrow = !this._parent.children.every(item => item.children.length === 0);
        }
        else {
            // Top-level elements should be indented
            this._indentWithoutArrow = true;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NavigationItemComponent, isStandalone: false, selector: "[ux-navigation-item]", inputs: { header: "header", icon: "icon", expanded: "expanded", link: "link" }, host: { properties: { "class.active": "active", "class.selected": "expanded" } }, queries: [{ propertyName: "_children", predicate: NavigationItemComponent, descendants: true }], ngImport: i0, template: "@if (link) {\n  <a\n    [class.has-arrow]=\"children.length > 0\"\n    [class.no-arrow]=\"_indentWithoutArrow\"\n    [routerLink]=\"link\"\n  >\n    <span>{{ header }}</span>\n  </a>\n}\n\n@if (!link) {\n  <a\n    (click)=\"expanded = !expanded\"\n    [class.has-arrow]=\"children.length > 0\"\n    [class.no-arrow]=\"_indentWithoutArrow\"\n  >\n    <span>{{ header }}</span>\n  </a>\n}\n\n<ng-content></ng-content>\n", dependencies: [{ kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationItemComponent, decorators: [{
            type: Component,
            args: [{ selector: '[ux-navigation-item]', host: {
                        '[class.active]': 'active',
                        '[class.selected]': 'expanded',
                    }, standalone: false, template: "@if (link) {\n  <a\n    [class.has-arrow]=\"children.length > 0\"\n    [class.no-arrow]=\"_indentWithoutArrow\"\n    [routerLink]=\"link\"\n  >\n    <span>{{ header }}</span>\n  </a>\n}\n\n@if (!link) {\n  <a\n    (click)=\"expanded = !expanded\"\n    [class.has-arrow]=\"children.length > 0\"\n    [class.no-arrow]=\"_indentWithoutArrow\"\n  >\n    <span>{{ header }}</span>\n  </a>\n}\n\n<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [], propDecorators: { header: [{
                type: Input
            }], icon: [{
                type: Input
            }], expanded: [{
                type: Input
            }], link: [{
                type: Input
            }], _children: [{
                type: ContentChildren,
                args: [NavigationItemComponent, { descendants: true }]
            }] } });

const NAVIGATION_MODULE_OPTIONS = new InjectionToken('NAVIGATION_MODULE_OPTIONS');

class NavigationService {
    constructor() {
        /** Whether to collapse other menu items when expanding a menu item. */
        this.autoCollapse = true;
        /** Emit when the expanded state has changed */
        this.expanded$ = new Subject();
    }
    ngOnDestroy() {
        this.expanded$.complete();
    }
    /** Set the expanded state of an item */
    setExpanded(source, expanded) {
        if (expanded && this.autoCollapse) {
            this.collapseSiblings(source);
            this.expanded$.next();
        }
    }
    /** Collapse all siblings nodes */
    collapseSiblings(source) {
        let siblings = this.items;
        for (const item of this.items) {
            const parent = this.getParent(source, item);
            if (parent) {
                siblings = parent.children;
                break;
            }
        }
        // collapse every sibling
        siblings.filter(item => item !== source).forEach(item => this.collapseAll(item));
    }
    /** Collapse an item and all its children */
    collapseAll(item) {
        item.expanded = false;
        if (item.children) {
            item.children.forEach(child => this.collapseAll(child));
        }
    }
    /** Get a nodes parent if it has one */
    getParent(target, item) {
        return (item.children || []).find(child => child === target) ? item : null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationService, decorators: [{
            type: Injectable
        }] });

class NavigationLinkDirective {
    constructor() {
        this._router = inject(Router);
        this._locationStrategy = inject(LocationStrategy);
        this._navigationService = inject(NavigationService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._route = inject(ActivatedRoute);
        this._options = inject(NAVIGATION_MODULE_OPTIONS, { optional: true });
        /** Emit with the current expaned state */
        this._expanded$ = new Subject();
        /** Unsubscribe from all observables when this directive is destroyed */
        this._onDestroy = new Subject();
    }
    /** The expaned state of this item */
    set expanded(value) {
        this._expanded$.next(value);
    }
    ngOnInit() {
        // any time expanded state anywhere change we should run change detection in case we should collapse
        this._navigationService.expanded$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._changeDetector.markForCheck());
        this._expanded$.pipe(tick(), takeUntil(this._onDestroy)).subscribe(expanded => {
            if (this.navigationItem.children && this.navigationItem.children.length > 0) {
                this.ariaExpanded = expanded;
                this._navigationService.setExpanded(this.navigationItem, expanded);
            }
        });
        this._router.events
            .pipe(filter(event => event instanceof NavigationEnd), takeUntil(this._onDestroy))
            .subscribe(this.updateNavigationState.bind(this));
        this.updateNavigationState();
        this.updateAttributes();
    }
    ngOnChanges() {
        this.updateAttributes();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    activated(event) {
        if (this.navigationItem.disabled) {
            return false;
        }
        if (this.navigationItem.routerLink) {
            const commands = Array.isArray(this.navigationItem.routerLink)
                ? this.navigationItem.routerLink
                : [this.navigationItem.routerLink];
            this._router.navigate(commands, this.navigationItem.routerExtras);
        }
        // Toggle expanded state (relevant only if it has children)
        this.navigationItem.expanded = !this.navigationItem.expanded;
        // Invoke the custom click handler if specified
        if (this.navigationItem.click) {
            this.navigationItem.click(event, this.navigationItem);
        }
        return false;
    }
    updateNavigationState() {
        this.isActive = this.isActiveItem(this.navigationItem);
        if (this.navigationItem.children) {
            const activeChild = this.navigationItem.children.find(child => this.isActiveItem(child));
            if (activeChild) {
                this.navigationItem.expanded = true;
            }
        }
        this._changeDetector.markForCheck();
    }
    updateAttributes() {
        this.href = this.getHref();
        this.role =
            this.navigationItem.children && this.navigationItem.children.length > 0
                ? 'button'
                : 'treeitem';
        this.indentChildren =
            this.navigationItem.children &&
                this.navigationItem.children.some(item => item.children && item.children.length > 0);
    }
    getHref() {
        if (this.navigationItem.disabled) {
            return null;
        }
        if (this.navigationItem.routerLink) {
            const commands = Array.isArray(this.navigationItem.routerLink)
                ? this.navigationItem.routerLink
                : [this.navigationItem.routerLink];
            const urlTree = this._router.createUrlTree(commands, this.navigationItem.routerExtras);
            return this._locationStrategy.prepareExternalUrl(this._router.serializeUrl(urlTree));
        }
        return null;
    }
    isActiveItem(item) {
        const { exact, ignoreQueryParams } = this.getRouterOptions(item);
        if (item.routerLink) {
            let routerExtras = item.routerExtras;
            // if we are to ignore the query params we must remove them
            if (ignoreQueryParams) {
                // get the current actual query params
                const { queryParams } = this._route.snapshot;
                // override the provided query params with the actual query params so they will alway match
                routerExtras = { ...routerExtras, queryParams };
            }
            const commands = Array.isArray(item.routerLink) ? item.routerLink : [item.routerLink];
            const urlTree = this._router.createUrlTree(commands, routerExtras);
            return this._router.isActive(urlTree, exact);
        }
        return false;
    }
    /** Get the router options with defaults for missing properties */
    getRouterOptions(item) {
        // get the default options based on the ones provided in `forRoot`
        const defaultOptions = {
            exact: true,
            ignoreQueryParams: false,
            ...(this._options ? this._options.routerOptions : {}),
        };
        // if there are item specific router options they should take precendence
        return { ...defaultOptions, ...item.routerOptions };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: NavigationLinkDirective, isStandalone: false, selector: "[uxNavigationLink]", inputs: { navigationItem: "navigationItem", expanded: "expanded", canExpand: "canExpand", indent: "indent" }, host: { listeners: { "click": "activated($event)", "keydown.enter": "activated($event)" }, properties: { "class.indent": "this.indent", "attr.href": "this.href", "attr.role": "this.role", "attr.aria-expanded": "this.ariaExpanded" } }, exportAs: ["uxNavigationLink"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationLinkDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxNavigationLink]',
                    exportAs: 'uxNavigationLink',
                    standalone: false,
                }]
        }], propDecorators: { navigationItem: [{
                type: Input
            }], expanded: [{
                type: Input
            }], canExpand: [{
                type: Input
            }], indent: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.indent']
            }], href: [{
                type: HostBinding,
                args: ['attr.href']
            }], role: [{
                type: HostBinding,
                args: ['attr.role']
            }], ariaExpanded: [{
                type: HostBinding,
                args: ['attr.aria-expanded']
            }], activated: [{
                type: HostListener,
                args: ['click', ['$event']]
            }, {
                type: HostListener,
                args: ['keydown.enter', ['$event']]
            }] } });

class NavigationComponent {
    constructor() {
        this._navigationService = inject(NavigationService);
        /** Whether to present the menu as a hierarchical tree. */
        this.tree = true;
        /** The classes to be added to each different level */
        this._hierarchyClasses = [
            '',
            'nav-second-level',
            'nav-third-level',
            'nav-fourth-level',
            'nav-fifth-level',
        ];
    }
    /** The navigation items to populate the menu with. */
    set items(items) {
        this._navigationService.items = items;
    }
    get items() {
        return this._navigationService.items;
    }
    /** Whether to collapse other menu items when expanding a menu item. */
    set autoCollapse(autoCollapse) {
        this._navigationService.autoCollapse = autoCollapse;
    }
    get _depthLimit() {
        return this.tree ? this._hierarchyClasses.length : 2;
    }
    /**
     * Returns true if the sets of items needs to be indented to make room for one or more expander.
     */
    _needsIndent(items) {
        return items && items.some(item => item.children && item.children.length > 0);
    }
    /** Determine the type of icon to display. We support `ux-icon` or `component` */
    _getIconType(item) {
        return getIconType(item.icon);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NavigationComponent, isStandalone: false, selector: "ux-navigation", inputs: { items: "items", tree: "tree", autoCollapse: "autoCollapse" }, providers: [NavigationService], queries: [{ propertyName: "navigationItemTemplate", first: true, predicate: ["uxNavigationItem"], descendants: true }], ngImport: i0, template: "<nav class=\"ux-side-nav\" [class.tree]=\"tree\" role=\"navigation\">\n  @if (items) {\n    <ol role=\"tree\" class=\"nav\" uxTabbableList [hierarchy]=\"true\">\n      @for (item of items; track item; let rank = $index) {\n        <ng-container\n          [ngTemplateOutlet]=\"navigationNode\"\n          [ngTemplateOutletContext]=\"{\n            item: item,\n            level: 1,\n            rank: rank,\n            indent: _needsIndent(items)\n          }\"\n        >\n        </ng-container>\n      }\n      <ng-template\n        #navigationNode\n        let-item=\"item\"\n        let-parent=\"parent\"\n        let-level=\"level\"\n        let-rank=\"rank\"\n        let-indent=\"indent\"\n      >\n        <li\n          [attr.role]=\"item.children && item.children.length > 0 ? 'treeitem' : 'none'\"\n          [attr.aria-expanded]=\"item.children && item.children.length > 0 ? item.expanded : null\"\n          [class.selected]=\"item.expanded\"\n          [class.disabled]=\"item.disabled\"\n          [class.active]=\"navigationLink.isActive\"\n        >\n          <a\n            uxNavigationLink\n            #navigationLink=\"uxNavigationLink\"\n            #tli=\"ux-tabbable-list-item\"\n            [navigationItem]=\"item\"\n            [expanded]=\"item.expanded\"\n            [canExpand]=\"level < _depthLimit\"\n            [indent]=\"indent\"\n            uxTabbableListItem\n            [disabled]=\"item.disabled\"\n            [parent]=\"parent\"\n            [rank]=\"rank\"\n            [(expanded)]=\"item.expanded\"\n          >\n            @if (\n              !navigationItemTemplate &&\n              item.children &&\n              item.children.length > 0 &&\n              level < _depthLimit\n            ) {\n              <span\n                aria-hidden=\"true\"\n                class=\"nav-expander\"\n                (click)=\"\n                  item.expanded = !item.expanded; $event.stopPropagation(); $event.preventDefault()\n                \"\n              >\n              </span>\n            }\n            <!-- Support UX Icons and Icon Component -->\n            @if (!navigationItemTemplate && item.icon && !tree) {\n              @if (_getIconType(item) !== 'component') {\n                <span class=\"nav-icon\" [ngClass]=\"[_getIconType(item), item.icon]\"> </span>\n              }\n              @if (_getIconType(item) === 'component') {\n                <ux-icon class=\"nav-icon\" [name]=\"item.icon\"> </ux-icon>\n              }\n            }\n            @if (!navigationItemTemplate && item.iconUrl && !tree) {\n              <img class=\"nav-icon\" [src]=\"item.iconUrl\" alt=\"item.iconLabel\" />\n            }\n            @if (!navigationItemTemplate) {\n              <span class=\"nav-title\">{{ item.title }}</span>\n            }\n            <ng-container\n              [ngTemplateOutlet]=\"navigationItemTemplate\"\n              [ngTemplateOutletContext]=\"{ item: item, level: level }\"\n            >\n            </ng-container>\n          </a>\n          @if (item.children && item.expanded && level < _depthLimit) {\n            <ol role=\"group\" class=\"nav\" [ngClass]=\"_hierarchyClasses[level]\">\n              @for (child of item.children; track child; let rank = $index) {\n                <ng-container\n                  [ngTemplateOutlet]=\"navigationNode\"\n                  [ngTemplateOutletContext]=\"{\n                    item: child,\n                    parent: tli,\n                    level: level + 1,\n                    rank: rank,\n                    indent: navigationLink.indentChildren\n                  }\"\n                >\n                </ng-container>\n              }\n            </ol>\n          }\n        </li>\n      </ng-template>\n    </ol>\n  }\n\n  <!-- Backward compatibility with the original ux-navigation -->\n  @if (!items) {\n    <ol role=\"tree\" class=\"nav\">\n      <ng-content></ng-content>\n    </ol>\n  }\n</nav>\n", dependencies: [{ kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: NavigationLinkDirective, selector: "[uxNavigationLink]", inputs: ["navigationItem", "expanded", "canExpand", "indent"], exportAs: ["uxNavigationLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-navigation', providers: [NavigationService], changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, standalone: false, template: "<nav class=\"ux-side-nav\" [class.tree]=\"tree\" role=\"navigation\">\n  @if (items) {\n    <ol role=\"tree\" class=\"nav\" uxTabbableList [hierarchy]=\"true\">\n      @for (item of items; track item; let rank = $index) {\n        <ng-container\n          [ngTemplateOutlet]=\"navigationNode\"\n          [ngTemplateOutletContext]=\"{\n            item: item,\n            level: 1,\n            rank: rank,\n            indent: _needsIndent(items)\n          }\"\n        >\n        </ng-container>\n      }\n      <ng-template\n        #navigationNode\n        let-item=\"item\"\n        let-parent=\"parent\"\n        let-level=\"level\"\n        let-rank=\"rank\"\n        let-indent=\"indent\"\n      >\n        <li\n          [attr.role]=\"item.children && item.children.length > 0 ? 'treeitem' : 'none'\"\n          [attr.aria-expanded]=\"item.children && item.children.length > 0 ? item.expanded : null\"\n          [class.selected]=\"item.expanded\"\n          [class.disabled]=\"item.disabled\"\n          [class.active]=\"navigationLink.isActive\"\n        >\n          <a\n            uxNavigationLink\n            #navigationLink=\"uxNavigationLink\"\n            #tli=\"ux-tabbable-list-item\"\n            [navigationItem]=\"item\"\n            [expanded]=\"item.expanded\"\n            [canExpand]=\"level < _depthLimit\"\n            [indent]=\"indent\"\n            uxTabbableListItem\n            [disabled]=\"item.disabled\"\n            [parent]=\"parent\"\n            [rank]=\"rank\"\n            [(expanded)]=\"item.expanded\"\n          >\n            @if (\n              !navigationItemTemplate &&\n              item.children &&\n              item.children.length > 0 &&\n              level < _depthLimit\n            ) {\n              <span\n                aria-hidden=\"true\"\n                class=\"nav-expander\"\n                (click)=\"\n                  item.expanded = !item.expanded; $event.stopPropagation(); $event.preventDefault()\n                \"\n              >\n              </span>\n            }\n            <!-- Support UX Icons and Icon Component -->\n            @if (!navigationItemTemplate && item.icon && !tree) {\n              @if (_getIconType(item) !== 'component') {\n                <span class=\"nav-icon\" [ngClass]=\"[_getIconType(item), item.icon]\"> </span>\n              }\n              @if (_getIconType(item) === 'component') {\n                <ux-icon class=\"nav-icon\" [name]=\"item.icon\"> </ux-icon>\n              }\n            }\n            @if (!navigationItemTemplate && item.iconUrl && !tree) {\n              <img class=\"nav-icon\" [src]=\"item.iconUrl\" alt=\"item.iconLabel\" />\n            }\n            @if (!navigationItemTemplate) {\n              <span class=\"nav-title\">{{ item.title }}</span>\n            }\n            <ng-container\n              [ngTemplateOutlet]=\"navigationItemTemplate\"\n              [ngTemplateOutletContext]=\"{ item: item, level: level }\"\n            >\n            </ng-container>\n          </a>\n          @if (item.children && item.expanded && level < _depthLimit) {\n            <ol role=\"group\" class=\"nav\" [ngClass]=\"_hierarchyClasses[level]\">\n              @for (child of item.children; track child; let rank = $index) {\n                <ng-container\n                  [ngTemplateOutlet]=\"navigationNode\"\n                  [ngTemplateOutletContext]=\"{\n                    item: child,\n                    parent: tli,\n                    level: level + 1,\n                    rank: rank,\n                    indent: navigationLink.indentChildren\n                  }\"\n                >\n                </ng-container>\n              }\n            </ol>\n          }\n        </li>\n      </ng-template>\n    </ol>\n  }\n\n  <!-- Backward compatibility with the original ux-navigation -->\n  @if (!items) {\n    <ol role=\"tree\" class=\"nav\">\n      <ng-content></ng-content>\n    </ol>\n  }\n</nav>\n" }]
        }], propDecorators: { items: [{
                type: Input
            }], tree: [{
                type: Input
            }], autoCollapse: [{
                type: Input
            }], navigationItemTemplate: [{
                type: ContentChild,
                args: ['uxNavigationItem', { static: false }]
            }] } });

class NavigationModule {
    // allow options to be specified globally
    static forRoot(options) {
        return {
            ngModule: NavigationModule,
            providers: [{ provide: NAVIGATION_MODULE_OPTIONS, useValue: options }],
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NavigationModule, declarations: [NavigationComponent, NavigationItemComponent, NavigationLinkDirective], imports: [AccessibilityModule, CommonModule, IconModule, RouterModule], exports: [NavigationComponent, NavigationItemComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationModule, imports: [AccessibilityModule, CommonModule, IconModule, RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NavigationModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, IconModule, RouterModule],
                    exports: [NavigationComponent, NavigationItemComponent],
                    declarations: [NavigationComponent, NavigationItemComponent, NavigationLinkDirective],
                }]
        }] });

class NestedDonutChartComponent {
    constructor() {
        this._colorService = inject(ColorService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._elementRef = inject(ElementRef);
        this._resizeService = inject(ResizeService);
        /** Define the maximum range of the arcs */
        this.max = 100;
        /** Define the thickness of each arc */
        this.thickness = 8;
        /** Define the spacing of each arc */
        this.spacing = 8;
        /** Determine if we should show the hover effect */
        this.disableHover = false;
        /** Determine if we should show a tooltip on arc hover */
        this.disableTooltip = false;
        /** Determine the position of the tooltip */
        this.tooltipPlacement = 'top';
        /** Set the duration of the animation */
        this.animationDuration = 750;
        /** Emit whenever an arc is clicked */
        this.itemClick = new EventEmitter();
        /** Indicate if the tooltip should be visible */
        this._tooltipVisible = false;
        /** Store the previously processed data */
        this._arcData = [];
        /** Determine if the intial render has taken place */
        this._isInitialized = false;
        /** Unsubscribe from all observables automatically */
        this._onDestroy = new Subject();
    }
    /** Determine the radius of the chart based on the specified size */
    get _radius() {
        return this._size / 2;
    }
    /**
     * Get the size of the chart. The chart will always be square to
     * the size will be the smaller of the width/height properties
     */
    get _size() {
        return Math.min(this._elementRef.nativeElement.offsetWidth, this._elementRef.nativeElement.offsetHeight);
    }
    /** Perform the initial render */
    ngOnInit() {
        // create the selection where we will draw the tracks
        this._trackLayer = select(this._chartElement.nativeElement).append('g');
        // create the selection where we will draw the arcs
        this._arcLayer = select(this._chartElement.nativeElement).append('g');
        // create the arcs representing the data
        this.render();
        // mark the component as initialized
        this._isInitialized = true;
        // listen for any resizing - skip the first emission as it always emits on first subscribe
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => {
            this.render();
            this._changeDetector.markForCheck();
        });
    }
    /** Any time an input changes we must re-render the chart */
    ngOnChanges() {
        if (this._isInitialized) {
            this.render();
        }
    }
    ngOnDestroy() {
        this._resizeService.removeResizeListener(this._elementRef.nativeElement);
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Inset the content so it never overlaps the arcs */
    _getContentInset() {
        return this.dataset.length * (this.spacing + this.thickness);
    }
    /** Get the dimensions of the content area */
    _getContentSize() {
        return this._size - this._getContentInset() * 2;
    }
    /** Get the dataset formated in an accessible manner */
    _getAriaLabel() {
        return this.dataset.map(data => `${data.value} ${data.name}`).join('. ');
    }
    /**
     * Display the tracks and arcs defined by the dataset.
     * We also provide the transition configuration so anytime the dataset
     * changes we will animate the update.
     */
    render() {
        // update the transform of the layers
        this._trackLayer.attr('transform', `translate(${this._radius}, ${this._radius})`);
        this._arcLayer.attr('transform', `translate(${this._radius}, ${this._radius})`);
        // create the arcs based on the dataset
        this._arcs = this._arcLayer.selectAll('path').data(this.getChartData());
        // create the default transition based on the specified duration
        const arcTransition = transition$1('nestedDonutArcTransition')
            .ease(easeCubic)
            .duration(this.animationDuration);
        // create the tracks based on the dataset
        this._tracks = this._trackLayer
            .selectAll('path')
            .data(this.getChartData())
            .enter()
            .append('path')
            .attr('class', 'ux-nested-donut-chart-track');
        // set the track color on each render in case the input has changed
        this._trackLayer
            .selectAll('path')
            .attr('d', this.getTrackArc())
            .style('fill', () => this.getTrackColor());
        // if an arc is removed then also remove the track
        this._tracks.exit().remove();
        // When a new arc is added we should create the element
        // size it and provide the background color and begin the
        // animation until it reaches its final angle
        this._arcs
            .enter()
            .append('path')
            .attr('class', 'ux-nested-donut-chart-arc')
            .style('fill', data => this.getColor(data.color))
            .attr('opacity', 1)
            .on('click', data => this.itemClick.emit(data))
            .on('mouseenter', (event, node) => this.onArcMouseEnter(event.srcElement, node))
            .on('mousemove', event => this.onArcMouseMove(pointer(event, this._chartElement.nativeElement)))
            .on('mouseleave', event => this.onArcMouseLeave(event.srcElement))
            .transition(arcTransition)
            .attrTween('d', this.getArcTween.bind(this));
        // any time an existing dataset value changes
        // we should update the angle with an animation
        // we also animate any color changes also.
        this._arcs
            .transition(arcTransition)
            .style('fill', data => this.getColor(data.color))
            .attrTween('d', this.getArcTween.bind(this));
        // when a dataset it removed animate the arc out
        // and then remove the associated DOM element
        this._arcs
            .exit()
            .transition(arcTransition)
            .attrTween('d', this.getArcTween.bind(this))
            .remove();
    }
    /** Get the interpolation function based on the new and previous angle */
    getArcTween(data) {
        // create a new interpolation function with a new endAngle
        const interpolation = interpolate({ ...data, endAngle: data.previousEndAngle }, data);
        // return the function that will produce the interpolation
        return (delta) => this.getArc()(interpolation(delta));
    }
    /** Get the arc layout for a specific item in the dataset */
    getArc() {
        return arc()
            .innerRadius(data => this.getArcRadius(data.index))
            .outerRadius(data => this.getArcRadius(data.index) + this.thickness)
            .startAngle(data => data.startAngle)
            .endAngle(data => data.endAngle);
    }
    /**
     * Get the track arc layout for a specific item in the dataset.
     * This will match the arc of that represents the actual data
     * however the endAngle will always be a complete circle
     */
    getTrackArc() {
        return this.getArc().endAngle(() => Math.PI * 2);
    }
    /**
     * Get the radius of an arc. This is calculated
     * based on the chart radius that has been defined,
     * minus the thickness defined, then taking into account
     * the depth of the arc and the spacing between each arc.
     */
    getArcRadius(index) {
        return this._radius - this.thickness - index * (this.thickness + this.spacing);
    }
    /**
     * Map the dataset to the NestedDonutChartArc interface
     */
    getChartData() {
        const dataset = this.dataset.map((data, index) => {
            let previousEndAngle = 0;
            // check if there was a previous dataset at this index
            if (this._arcData && this._arcData[index]) {
                previousEndAngle = this._arcData[index].endAngle;
            }
            return { ...data, index, startAngle: 0, endAngle: this.getAngle(data), previousEndAngle };
        });
        // store the latest processed arc data
        this._arcData = dataset;
        return dataset;
    }
    /** Convert the data value to radians */
    getAngle(data) {
        const fraction = data.value / this.max;
        const degrees = fraction * 360.0;
        return degrees * (Math.PI / 180);
    }
    /**
     * Get the color of the arc, this may be a CSS color value, the name of a color
     * from the color set or a ThemeColor object. We return this as a rgba color to
     * support the alpha channel
     */
    getColor(color) {
        return ThemeColor.isInstanceOf(color)
            ? color.toRgba()
            : this._colorService.resolve(color);
    }
    /** If no track color is specified then default to a specific color based on the active colorset */
    getTrackColor() {
        if (this.trackColor) {
            return this.getColor(this.trackColor);
        }
        // otherwise default to a color based on the colorset (note we can't use the Color enum from MF package)
        if (this._colorService.colorExists(Color.Grey6)) {
            return this.getColor(Color.Grey6);
        }
        if (this._colorService.colorExists('bright-gray')) {
            return this.getColor('bright-gray');
        }
    }
    /** Define the on hover event */
    onArcMouseEnter(target, data) {
        // update the hover effect if it is enabled
        if (this.disableHover === false) {
            select(target).transition().duration(250).attr('opacity', 0.5);
        }
        // update the tooltip context
        this._tooltipContext = { ...data, color: this.getColor(data.color) };
        // update the tooltip visibility
        this._tooltipVisible = true;
        // run change detection to ensure the visibility is updated
        this._changeDetector.detectChanges();
    }
    /** Update the tooltip position on mouse move */
    onArcMouseMove([x, y]) {
        this._tooltipX = x;
        this._tooltipY = y - 2; // subtract 2 so that it appears slightly above the cursor
        // run change detection to update the element position
        this._changeDetector.detectChanges();
    }
    /** Define the on hover out event */
    onArcMouseLeave(target) {
        // update the hover effect if it is enabled
        if (this.disableHover === false) {
            select(target).transition().duration(250).attr('opacity', 1);
        }
        // clear the tooltip context
        this._tooltipContext = null;
        // update the tooltip visibility
        this._tooltipVisible = false;
        // run change detection to ensure the visibility is updated
        this._changeDetector.detectChanges();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NestedDonutChartComponent, isStandalone: false, selector: "ux-nested-donut-chart", inputs: { dataset: "dataset", max: "max", thickness: "thickness", spacing: "spacing", trackColor: "trackColor", disableHover: "disableHover", disableTooltip: "disableTooltip", tooltipPlacement: "tooltipPlacement", animationDuration: "animationDuration" }, outputs: { itemClick: "itemClick" }, queries: [{ propertyName: "_customTooltip", first: true, predicate: ["tooltip"], descendants: true }], viewQueries: [{ propertyName: "_chartElement", first: true, predicate: ["chart"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<svg\n  #chart\n  class=\"ux-nested-donut-chart\"\n  [attr.focusable]=\"false\"\n  [attr.width]=\"_size\"\n  [attr.height]=\"_size\"\n  [attr.aria-label]=\"_getAriaLabel()\"\n></svg>\n\n<!-- Custom content in center of the chart -->\n<div\n  class=\"ux-nested-donut-chart-content\"\n  [style.top.px]=\"_getContentInset()\"\n  [style.right.px]=\"_getContentInset()\"\n  [style.bottom.px]=\"_getContentInset()\"\n  [style.left.px]=\"_getContentInset()\"\n  [style.width.px]=\"_getContentSize()\"\n  [style.height.px]=\"_getContentSize()\"\n>\n  <ng-content></ng-content>\n</div>\n\n<!-- Tooltip to appear on arc hover -->\n@if (_tooltipVisible && !disableTooltip) {\n  <div class=\"ux-nested-donut-chart-tooltip\">\n    <ux-tooltip\n      [placement]=\"tooltipPlacement\"\n      [content]=\"_customTooltip || tooltip\"\n      [context]=\"_tooltipContext\"\n      [style.top.px]=\"_tooltipY\"\n      [style.left.px]=\"_tooltipX\"\n    >\n    </ux-tooltip>\n  </div>\n}\n\n<!-- Default tooltip template -->\n<ng-template #tooltip let-name=\"name\" let-value=\"value\">\n  <span class=\"ux-nested-donut-chart-tooltip-content\"> {{ name }}: {{ value }} </span>\n</ng-template>\n", dependencies: [{ kind: "component", type: TooltipComponent, selector: "ux-tooltip", inputs: ["content", "context", "placement", "alignment"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-nested-donut-chart', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<svg\n  #chart\n  class=\"ux-nested-donut-chart\"\n  [attr.focusable]=\"false\"\n  [attr.width]=\"_size\"\n  [attr.height]=\"_size\"\n  [attr.aria-label]=\"_getAriaLabel()\"\n></svg>\n\n<!-- Custom content in center of the chart -->\n<div\n  class=\"ux-nested-donut-chart-content\"\n  [style.top.px]=\"_getContentInset()\"\n  [style.right.px]=\"_getContentInset()\"\n  [style.bottom.px]=\"_getContentInset()\"\n  [style.left.px]=\"_getContentInset()\"\n  [style.width.px]=\"_getContentSize()\"\n  [style.height.px]=\"_getContentSize()\"\n>\n  <ng-content></ng-content>\n</div>\n\n<!-- Tooltip to appear on arc hover -->\n@if (_tooltipVisible && !disableTooltip) {\n  <div class=\"ux-nested-donut-chart-tooltip\">\n    <ux-tooltip\n      [placement]=\"tooltipPlacement\"\n      [content]=\"_customTooltip || tooltip\"\n      [context]=\"_tooltipContext\"\n      [style.top.px]=\"_tooltipY\"\n      [style.left.px]=\"_tooltipX\"\n    >\n    </ux-tooltip>\n  </div>\n}\n\n<!-- Default tooltip template -->\n<ng-template #tooltip let-name=\"name\" let-value=\"value\">\n  <span class=\"ux-nested-donut-chart-tooltip-content\"> {{ name }}: {{ value }} </span>\n</ng-template>\n" }]
        }], propDecorators: { dataset: [{
                type: Input
            }], max: [{
                type: Input
            }], thickness: [{
                type: Input
            }], spacing: [{
                type: Input
            }], trackColor: [{
                type: Input
            }], disableHover: [{
                type: Input
            }], disableTooltip: [{
                type: Input
            }], tooltipPlacement: [{
                type: Input
            }], animationDuration: [{
                type: Input
            }], itemClick: [{
                type: Output
            }], _chartElement: [{
                type: ViewChild,
                args: ['chart', { static: true }]
            }], _customTooltip: [{
                type: ContentChild,
                args: ['tooltip', { static: false }]
            }] } });

class NestedDonutChartModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartModule, declarations: [NestedDonutChartComponent], imports: [CommonModule, ColorServiceModule, TooltipModule, ResizeModule], exports: [NestedDonutChartComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartModule, imports: [CommonModule, ColorServiceModule, TooltipModule, ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NestedDonutChartModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, ColorServiceModule, TooltipModule, ResizeModule],
                    declarations: [NestedDonutChartComponent],
                    exports: [NestedDonutChartComponent],
                }]
        }] });

class NotificationService {
    constructor() {
        this._colorService = inject(ColorService);
        /**
           *  Sets the order in which notifications are displayed:
              `above` - newer notifications will appear above older ones.
              `below` - newer notifications will appear below older ones.
           */
        this.direction = 'above';
        /**
         * The list of notifications including notifications that have been dismissed
         */
        this.notifications$ = new BehaviorSubject([]);
        /**
         * Define the default set of notification options
         */
        this.options = {
            duration: 4,
            backgroundColor: this._colorService.getColor('accent').toHex(),
            iconColor: this._colorService.getColor('accent').toHex(),
        };
    }
    /**
     * Access the list of notifications as an array
     */
    get notifications() {
        return this.notifications$.value;
    }
    /**
     * This function should be called to show a notification.
     * It should be given a TemplateRef containing the content to be displayed.
     * @param templateRef - A TemplateRef containing the content to be displayed
     * @param options - The properties to configure the notification.
     * @param context - The context passed to the notification TemplateRef. This can be accessed by adding a let-data="data" to the ng-template element.
     */
    show(templateRef, options = this.options, context = {}) {
        // populate the specified options with the default values for any missing properties
        options = { ...this.options, ...options };
        // create the notificationRef based on the options and context specified
        const notificationRef = {
            templateRef,
            duration: options.duration,
            date: new Date(),
            visible: true,
            height: options.height,
            spacing: options.spacing,
            backgroundColor: options.backgroundColor,
            iconColor: options.iconColor,
            data: context,
        };
        // add the new notification to the list (either above or below based on direction)
        this.direction === 'above'
            ? this.notifications.unshift(notificationRef)
            : this.notifications.push(notificationRef);
        // update the notifications list
        this.notifications$.next(this.notifications);
        // remove notification after delay
        if (options.duration !== 0) {
            setTimeout(() => this.dismiss(notificationRef), options.duration * 1000);
        }
        return notificationRef;
    }
    /**
     * This function will return a list of all the notifications that have been shown.
     */
    getHistory() {
        return this.notifications;
    }
    /**
     * This function can be called to dismiss a notification. It should be passed the object to dismiss.
     * @param notificationRef - The notification that should be dismissed
     */
    dismiss(notificationRef) {
        notificationRef.visible = false;
        this.notifications$.next(this.notifications);
    }
    /**
     * This function will dismiss any currently visible notifications.
     */
    dismissAll() {
        this.notifications.forEach(notificationRef => (notificationRef.visible = false));
        this.notifications$.next(this.notifications);
    }
    /** Remove the notification from the screen and from the notification history */
    remove(notificationRef) {
        this.notifications$.next(this.notifications.filter(_notificationRef => _notificationRef !== notificationRef));
    }
    /** Remove all notifications from the screen and from the notification history */
    removeAll() {
        this.notifications$.next([]);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class NotificationListComponent {
    constructor() {
        this._notificationService = inject(NotificationService);
        this._changeDetectorRef = inject(ChangeDetectorRef);
        this._renderer = inject(Renderer2);
        /** Sets the position of the list of notifications within the browser window. */
        this.position = 'bottom-right';
        /** The list of notifications that have not been dismissed */
        this.notifications$ = this._notificationService.notifications$.pipe(map(() => this._notifications));
        /** Unsubscribe from all subscriptions on component destroy */
        this._onDestroy = new Subject();
    }
    /**
       *  Sets the order in which notifications are displayed:
          `above` - newer notifications will appear above older ones.
          `below` - newer notifications will appear below older ones.
       */
    set direction(direction) {
        this._notificationService.direction = direction;
    }
    /** Filter out any hidden notifications */
    get _notifications() {
        return this._notificationService.notifications.filter(notification => notification.visible);
    }
    ngAfterViewInit() {
        // whenever the notifications change we want to recalculate the positions and height
        this._elements.changes
            .pipe(takeUntil(this._onDestroy), tick(), map(changes => changes.toArray()), withLatestFrom(this.notifications$))
            .subscribe(([elements, notifications]) => {
            // Set the `top` style property of each element
            this.applyElementPositions(elements, notifications);
            this.updateListPosition(elements, notifications);
            this._changeDetectorRef.markForCheck();
        });
    }
    ngOnChanges() {
        if (this._elements) {
            this.updateListPosition(this._elements.toArray(), this._notifications);
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    applyElementPositions(elements, notifications) {
        let top = 0;
        for (let i = 0; i < elements.length; i += 1) {
            const element = elements[i].nativeElement;
            const notification = notifications[i];
            this._renderer.setStyle(element, 'top', `${top}px`);
            top = top + this.getNotificationHeightInPixels(notification, elements[i]);
        }
    }
    updateListPosition(elements, notifications) {
        if (this.position === 'bottom-left' || this.position === 'bottom-right') {
            this._bottom = notifications.reduce((totalHeight, notification, index) => totalHeight + this.getNotificationHeightInPixels(notification, elements[index]), 0);
        }
        else {
            // In a top position, bottom should be unset.
            this._bottom = undefined;
        }
    }
    /** Get the height of the notification, including spacing if configured. */
    getNotificationHeightInPixels(notification, elementRef) {
        if (notification.spacing === undefined) {
            return this.getElementOuterHeightInPixels(elementRef);
        }
        return elementRef.nativeElement.offsetHeight + notification.spacing;
    }
    /** Get the total height of the element including margins. */
    getElementOuterHeightInPixels(elementRef) {
        const element = elementRef.nativeElement;
        const { marginTop, marginBottom } = getComputedStyle(element);
        return element.offsetHeight + parseInt(marginTop) + parseInt(marginBottom);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NotificationListComponent, isStandalone: false, selector: "ux-notification-list", inputs: { direction: "direction", position: "position" }, host: { properties: { "class": "this.position", "style.bottom.px": "this._bottom" } }, viewQueries: [{ propertyName: "_elements", predicate: ["notification"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@for (notificationRef of notifications$ | async; track notificationRef; let index = $index) {\n  <div\n    #notification\n    class=\"notification\"\n    [style.height.px]=\"notificationRef.height\"\n    [style.background-color]=\"notificationRef.backgroundColor\"\n    [@notificationState]\n  >\n    <!-- Notification Content -->\n    <ng-container\n      [ngTemplateOutlet]=\"notificationRef.templateRef\"\n      [ngTemplateOutletContext]=\"{ $implicit: notificationRef, data: notificationRef.data }\"\n    >\n    </ng-container>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], animations: [
            trigger('notificationState', [
                state('in', style({ transform: 'translateY(0)', opacity: 0.9 })),
                transition(':enter', [style({ transform: 'translateY(-50px)', opacity: 0 }), animate(500)]),
                transition(':leave', [animate(500, style({ transform: 'translateY(50px)', opacity: 0 }))]),
            ]),
        ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationListComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-notification-list', changeDetection: ChangeDetectionStrategy.OnPush, animations: [
                        trigger('notificationState', [
                            state('in', style({ transform: 'translateY(0)', opacity: 0.9 })),
                            transition(':enter', [style({ transform: 'translateY(-50px)', opacity: 0 }), animate(500)]),
                            transition(':leave', [animate(500, style({ transform: 'translateY(50px)', opacity: 0 }))]),
                        ]),
                    ], standalone: false, template: "@for (notificationRef of notifications$ | async; track notificationRef; let index = $index) {\n  <div\n    #notification\n    class=\"notification\"\n    [style.height.px]=\"notificationRef.height\"\n    [style.background-color]=\"notificationRef.backgroundColor\"\n    [@notificationState]\n  >\n    <!-- Notification Content -->\n    <ng-container\n      [ngTemplateOutlet]=\"notificationRef.templateRef\"\n      [ngTemplateOutletContext]=\"{ $implicit: notificationRef, data: notificationRef.data }\"\n    >\n    </ng-container>\n  </div>\n}\n" }]
        }], propDecorators: { direction: [{
                type: Input
            }], position: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class']
            }], _bottom: [{
                type: HostBinding,
                args: ['style.bottom.px']
            }], _elements: [{
                type: ViewChildren,
                args: ['notification']
            }] } });

class NotificationModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NotificationModule, declarations: [NotificationListComponent], imports: [CommonModule, ColorServiceModule], exports: [NotificationListComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationModule, imports: [CommonModule, ColorServiceModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, ColorServiceModule],
                    exports: [NotificationListComponent],
                    declarations: [NotificationListComponent],
                }]
        }] });

/* eslint-disable @typescript-eslint/no-empty-object-type */
class OrganizationChartComponent {
    constructor() {
        this._resizeService = inject(ResizeService);
        this._componentFactoryResolver = inject(ComponentFactoryResolver);
        this._injector = inject(Injector);
        this._elementRef = inject(ElementRef);
        this._appRef = inject(ApplicationRef);
        this._viewContainerRef = inject(ViewContainerRef);
        this._renderer = inject(Renderer2);
        this._focusIndicator = inject(FocusIndicatorService);
        this._ngZone = inject(NgZone);
        /** Define the presentation of the connectors */
        this.connector = 'elbow';
        /** Define the duration of the transition animations */
        this.duration = 750;
        /** Define whether or not we can reveal additional parents */
        this.showReveal = false;
        /** Define the aria label for the reveal button */
        this.revealAriaLabel = 'Reveal More';
        /** Emit whenever a node is selected */
        this.selectedChange = new EventEmitter(true);
        /** Emit whenever the reveal button is pressed */
        this.reveal = new EventEmitter();
        /** Emit when the transition ends */
        this.transitionEnd = new EventEmitter();
        this._toggleNodesOnClick = true;
        /** Store a flattened array of nodes */
        this._nodeLayout = [];
        /** Store a flattened array of links */
        this._linkLayout = [];
        /** Store the portal/outlets associated with some data */
        this._portals = new Map();
        /** Store the focus indicators associated with nodes */
        this._indicators = new Map();
        /** Store whether or not a transition is in progress */
        this._isTransitioning = false;
        /** Store whether or not a camera pan is in progress */
        this._isPanning = false;
        /** Determine if the component is initialised */
        this._isInitialised = false;
        /** Determine if the connector type has changed since the last render */
        this._hasConnectorChanged = false;
        /** Automatically unsubscribe from all subscriptions on destroy */
        this._onDestroy = new Subject();
    }
    /** Defines whether nodes can be toggled or not */
    set toggleNodesOnClick(toggleNodesOnClick) {
        this._toggleNodesOnClick = coerceBooleanProperty(toggleNodesOnClick);
    }
    get toggleNodesOnClick() {
        return this._toggleNodesOnClick;
    }
    /** Programmatically select an item */
    set selected(selected) {
        if (this.selected === selected || !selected) {
            return;
        }
        if (this._isInitialised) {
            this.select(selected);
            this.centerNode(selected);
        }
        else {
            this._pendingSelection = selected;
        }
    }
    ngAfterViewInit() {
        // before we do anything ensure they have provided a template
        if (!this.nodeTemplate) {
            throw new Error('Organization Chart - You must provide a node template!');
        }
        if (!this.nodeWidth || !this.nodeHeight) {
            throw new Error('Organization Chart - You must specify a nodeWidth and nodeHeight');
        }
        // create the zoom drag listener
        this._zoom = zoom()
            .scaleExtent([1, 1])
            .interpolate(interpolate)
            .on('zoom', this.applyCameraPosition.bind(this))
            .on('end', () => {
            if (!this._isPanning) {
                this.ensureNodesAreVisible();
            }
        });
        // set up the selections
        this._linksContainer = select(this.linksContainer.nativeElement);
        this._nodesContainer = select(this.nodesContainer.nativeElement);
        // setup the zoom on the node layer
        this._ngZone.runOutsideAngular(() => this._nodesContainer.call(this._zoom));
        // perform the initial render
        this.render();
        // ensure we set the initial chart size
        this._width = this._elementRef.nativeElement.offsetWidth;
        this._height = this._elementRef.nativeElement.offsetHeight;
        // watch for any resizing of the chart
        const resize$ = this._resizeService.addResizeListener(this._elementRef.nativeElement);
        // on size change immediate update the width and height measurements
        resize$.pipe(takeUntil(this._onDestroy)).subscribe(this.onResize.bind(this));
        // after a debounce ensure nodes are visible
        resize$
            .pipe(takeUntil(this._onDestroy), debounceTime(this.duration))
            .subscribe(this.ensureNodesAreVisible.bind(this));
        // initially horizontally center the root node
        this.centerNode(this.dataset, OrganizationChartAxis.Horizontal, false);
        // initally move the camera down slightly so the root node does not appear at the very top of the chart
        this.moveCamera(0, 150, false);
        // mark this component as initialised
        this._isInitialised = true;
    }
    ngOnChanges(changes) {
        if (changes.connector && !changes.connector.firstChange) {
            this._hasConnectorChanged = true;
        }
        // if only the selected property has changed then don't re-render as this is handled by the setter
        if (Object.keys(changes).length === 1 && changes.selected) {
            return;
        }
        if (this._isInitialised) {
            this.render();
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        // correctly dispose all portals and outlets
        this._portals.forEach(node => {
            node.portal.detach();
            node.outlet.dispose();
        });
    }
    /** Perform the actual rendering of the chart */
    render() {
        // perform the layout algorithm on the current dataset
        this.updateLayout();
        // select all the existing links and nodes
        this.updateSelections();
        // create a d3 transition based in the specified transition time
        const defaultTransition = transition$1('organizationChartDefaultTransition')
            .duration(this.duration)
            .on('start', () => (this._isTransitioning = true))
            .on('end', () => {
            this._isTransitioning = false;
            this.transitionEnd.emit();
        });
        // render the links when they are first added to the DOM
        this._links
            .enter()
            .insert('path')
            .attr('class', 'ux-organization-chart-link')
            .attr('d', link => this.getLinkPath(link))
            .attr('opacity', -2)
            .transition(defaultTransition)
            .attr('d', link => this.getLinkPath(link))
            .attr('opacity', 1);
        // define the standard transition while the link is 'alive'
        this._links
            .transition()
            .duration(this._hasConnectorChanged ? 0 : this.duration)
            .attr('d', link => this.getLinkPath(link));
        // apply transitions when removing nodes
        this._links
            .exit()
            .transition(defaultTransition)
            .attr('d', (link) => this.getCollapsedLinkPath(link))
            .attr('opacity', 0)
            .remove();
        // when a node is first added to the DOM position it
        this._nodes
            .enter()
            .append('div')
            .attr('class', 'ux-organization-chart-node')
            .style('width', this.nodeWidth + 'px')
            .style('height', this.nodeHeight + 'px')
            .style('left', node => (node.parent ? node.parent.x : node.x) + 'px')
            .style('top', node => (node.parent ? node.parent.y : node.y) + 'px')
            .style('opacity', 0)
            .on('keydown', (event, node) => this.onKeydown(event, node))
            .on('focus', (event, node) => this.onFocus(node))
            .on('click', (event, node) => this.onClick(node))
            .each(this.renderNodeTemplate.bind(this))
            .each((node, index, group) => this.monitorFocus(group[index], node))
            .transition(defaultTransition)
            .style('left', node => node.x + 'px')
            .style('top', node => node.y + 'px')
            .style('opacity', 1);
        // apply any movements while nodes are 'alive'
        this._nodes
            .transition(defaultTransition)
            .style('left', node => node.x + 'px')
            .style('top', node => node.y + 'px');
        // apply transitions when removing nodes
        this._nodes
            .exit()
            .transition(defaultTransition)
            .style('left', (node) => (node.parent ? node.parent.x : node.x) + 'px')
            .style('opacity', 0)
            .remove()
            .on('end', (node) => this.destroyNode(node));
        // update the position of the reveal button
        select(this.revealElement.nativeElement)
            .style('left', this.nodeWidth / 2 - this.revealElement.nativeElement.offsetWidth / 2 + 'px')
            .style('top', -(this.nodeHeight / 2 + this.revealElement.nativeElement.offsetHeight / 2) + 'px');
        // after any new links and nodes have been created or removed we should update the selections
        this.updateSelections();
        // update the selected classes - ensure there is always a selected node
        if (!this._selected) {
            this.select(this._pendingSelection || this.dataset);
            this._pendingSelection = null;
        }
        // set the tab indexes and aria labels for any newly added items
        this.setNodeAttributes();
        // apply the current camera position to any new nodes/links
        this.applyCameraPosition();
        // reset the connector changed status
        this._hasConnectorChanged = false;
    }
    /** Select a specified node */
    select(node) {
        // get the node in the desired format
        node = this.coerceDataNode(node);
        // check if the node is already selected
        if (this._selected === node) {
            return;
        }
        // ensure all parents are expanded
        this.expandParents(node);
        // deselect any current node
        this.deselect(false);
        // if the selected item has changed then store the latest selection
        this._selected = node;
        // emit the latest selection
        this.selectedChange.emit(this._selected);
        // show reveal any nodes that may previously have been hidden but are now visible due to selection
        if (this._isInitialised) {
            this.render();
        }
        // add the styling to the selected node
        this._renderer.addClass(this.getNodeElement(this._selected), 'ux-organization-chart-node-selected');
        // update the styling and tabindexes
        this.setNodeAttributes();
    }
    /** Deselect the currently selected node */
    deselect(emit = true) {
        if (this._nodes) {
            this._nodes
                .nodes()
                .forEach(element => this._renderer.removeClass(element, 'ux-organization-chart-node-selected'));
        }
        if (emit && !!this._selected) {
            this._selected = null;
            this.selectedChange.next(null);
            // update the tab indexes and aria labels
            this.setNodeAttributes();
        }
    }
    /** Toggle the collapsed state of a node */
    toggle(node) {
        if (this._isTransitioning) {
            return;
        }
        // get the node in the desired format
        node = this.coercePointNode(node);
        // ensure the clicked node is selected
        this.select(node);
        // apply the appropriate action
        this.isExpanded(node) ? this.collapse(node) : this.expand(node);
    }
    /** Expand a node */
    expand(node) {
        if (this._isTransitioning || !this.toggleNodesOnClick) {
            return;
        }
        // get the node in the desired format
        node = this.coercePointNode(node);
        // ensure this node and all parent nodes are expanded
        node.ancestors().forEach(_node => (_node.data.expanded = true));
        // re-render the nodes
        this.render();
        // if the node has children then we want to move the camera to a child node
        if (Array.isArray(node.data.children) && node.data.children.length > 0) {
            // center on the middle child
            this.centerNode(node.data.children[Math.floor(node.data.children.length / 2)]);
        }
        else {
            this.centerNode(node);
        }
    }
    /** Collapse a node */
    collapse(node) {
        // do nothing if a transition is currently in progress
        if (this._isTransitioning) {
            return;
        }
        // get the node in the desired format
        node = this.coercePointNode(node);
        // ensure this node and all child nodes are collapse
        node.descendants().forEach(_node => (_node.data.expanded = false));
        // re-render the nodes
        this.render();
        // center the node that has just been collapsed
        this.centerNode(node);
    }
    /** Move a specific node to the center of the screen */
    centerNode(node, axis = OrganizationChartAxis.Both, animate = true) {
        // get the node in the desired format
        node = this.coercePointNode(node);
        // get the current camera position
        const camera = this.getCameraPosition();
        const x = axis === OrganizationChartAxis.Vertical
            ? camera.x
            : this._width / 2 - (node.x + this.nodeWidth / 2);
        const y = axis === OrganizationChartAxis.Horizontal
            ? camera.y
            : this._height / 2 - (node.y + this.nodeHeight / 2);
        // update the camera position
        this.setCameraPosition(x, y, animate);
    }
    /** Explicity set the position of the camera */
    setCameraPosition(x, y, animate = true) {
        // get the current transform
        let camera = zoomTransform(this._nodesContainer.node());
        // do nothing if the co-orindates have not changed
        if (camera.x === x && camera.y === y) {
            return;
        }
        // update the camera position
        camera = camera.translate(x - camera.x, y - camera.y);
        // indicate that the camera is panning programmatically
        this._isPanning = true;
        if (animate) {
            this._nodesContainer
                .transition()
                .duration(this.duration)
                .call(this._zoom.transform, camera)
                .on('end interrupt cancel', () => (this._isPanning = false));
        }
        else {
            this._nodesContainer.call(this._zoom.transform, camera);
            this._isPanning = false;
        }
    }
    /** Move the camera an amount from its current position */
    moveCamera(x, y, animate = true) {
        // get the current camera position
        const camera = this.getCameraPosition();
        this.setCameraPosition(camera.x + x, camera.y + y, animate);
    }
    /** Focus a given node */
    focus(node) {
        this.focusNode(this.coercePointNode(node));
    }
    /** Focus the root node */
    _focusRootNode() {
        this.focusNode(this.coercePointNode(this.dataset));
    }
    /** Destroy the outlet and portal associated with a node */
    destroyNode(node) {
        // get the node in a consistent format
        node = this.coercePointNode(node);
        // remove focus monitoring
        if (this._indicators.has(node.data)) {
            // remove the focus monitoring
            this._indicators.get(node.data).destroy();
            // remove the indicator from the list of indicators
            this._indicators.delete(node.data);
        }
        // if there is not portal/outlets associated with this node then do nothing
        if (!this._portals.has(node.data)) {
            return;
        }
        // get the portal and outlet from the map
        const portalRef = this._portals.get(node.data);
        // perform the cleanup
        portalRef.portal.detach();
        portalRef.outlet.dispose();
        // remove this entry from the map
        this._portals.delete(node.data);
    }
    // update the data structure for the node and link layouts
    updateLayout() {
        this._layout = this.getLayout();
        this._nodeLayout = this._layout.descendants();
        this._linkLayout = this._layout.links();
    }
    /** Ensure the selections stay in sync with the view */
    updateSelections() {
        // select all the newly added dom nodes and associate the dataset
        this._nodes = this._nodesContainer
            .selectAll('.ux-organization-chart-node')
            .data(this._nodeLayout, (node) => node.data.id.toString());
        // select all the newly added path nodes
        this._links = this._linksContainer
            .selectAll('.ux-organization-chart-link')
            .data(this._linkLayout, (link) => {
            return `${link.source.data.id}-${link.target.data.id}`;
        });
    }
    /** Render the content of the node based on the template provided */
    renderNodeTemplate(node, index, group) {
        // create the context for the node
        const context = {
            data: node.data.data,
            node: node.data,
            focused: false,
        };
        // the focused state should be a getter
        Object.defineProperty(context, 'focused', {
            get: () => this._focused === node.data,
        });
        // create the outlet to insert the Template and the portal from the TemplateRef
        const outlet = this.createPortalOutlet(group[index]);
        const portal = new TemplatePortal(this.nodeTemplate, this._viewContainerRef, context);
        // insert the TemplateRef into the specified region
        portal.attach(outlet);
        // store the portal and outlet so we can correctly dispose of the nodes
        this._portals.set(node.data, { portal, outlet });
    }
    /** Handle any zoom events (we use zoom for panning behaviour) */
    applyCameraPosition() {
        // get the new x and y position
        let { x, y } = zoomTransform(this._nodesContainer.node());
        // round the precision to integers to prevent any anti-aliasing
        x = Math.round(x);
        y = Math.round(y);
        // transform the position of the reveal button
        this._renderer.setStyle(this.revealElement.nativeElement, 'transform', `translate(${x}px, ${y}px)`);
        // transform the position of the nodes
        this._nodesContainer
            .selectAll('.ux-organization-chart-node')
            .style('transform', `translate(${x}px, ${y}px)`);
        // transform the position of the links
        this._linksContainer
            .selectAll('.ux-organization-chart-link')
            .attr('transform', `translate(${x} ${y})`);
    }
    /** Get the data in with the required layout information */
    getLayout() {
        // create a hierarchical representation of the data - don't include collapsed nodes
        const treeHierarchy = hierarchy(this.dataset, node => Array.isArray(node.children) && node.expanded ? node.children : []);
        // create our layout
        const layout = tree()
            .nodeSize([this.nodeWidth, this.nodeHeight])
            .separation(this.getNodeSpacing.bind(this));
        // process the data with the layout
        const treeLayout = layout(treeHierarchy);
        // calculate the vertical spacing
        const verticalSpacing = this.verticalSpacing === undefined ? this.nodeHeight : this.verticalSpacing;
        // set the vertical spacing
        treeLayout.each(data => (data.y = data.depth * (this.nodeHeight + verticalSpacing)));
        return treeLayout;
    }
    /** Determine how much horizontal spacing should be between nodes */
    getNodeSpacing(nodeOne, nodeTwo) {
        // if the nodes are not siblings then space further apart
        if (nodeOne.parent !== nodeTwo.parent) {
            return 2;
        }
        // if they are siblings they should be closer together
        return 1.5;
    }
    /** Ensure we consistently use the HierarchyPoint data structure */
    coercePointNode(node) {
        // determine if this is a raw data node or a hierarchy point
        // eslint-disable-next-line no-prototype-builtins
        if (node.hasOwnProperty('depth') && node.hasOwnProperty('x') && node.hasOwnProperty('y')) {
            return node;
        }
        // otherwise find the matching node
        const match = this._nodeLayout.find(_node => _node.data === node);
        // if the data does not exist in the hierarchy throw an exception
        if (!match) {
            throw new Error('The node does not exist in the hierarchy');
        }
        return match;
    }
    coerceDataNode(node) {
        // eslint-disable-next-line no-prototype-builtins
        if (node.hasOwnProperty('depth') && node.hasOwnProperty('x') && node.hasOwnProperty('y')) {
            return node.data;
        }
        return node;
    }
    /** Handle chart resize events */
    onResize({ width, height }) {
        this._width = width;
        this._height = height;
    }
    /** Deteremine if a node is expanded or collapsed */
    isExpanded(node) {
        return !!node.data.expanded;
    }
    /** Get the current position of the camera */
    getCameraPosition() {
        return zoomTransform(this._nodesContainer.node());
    }
    /** Get the SVG line definition for each link */
    getLinkPath(pointLink) {
        if (this.connector === 'elbow') {
            const source = {
                x: pointLink.source.x + this.nodeWidth / 2,
                y: pointLink.source.y + this.nodeHeight,
            };
            const target = { x: pointLink.target.x + this.nodeWidth / 2, y: pointLink.target.y };
            return ('M' +
                source.x +
                ',' +
                source.y +
                'v' +
                (target.y - source.y) / 2 +
                'h' +
                (target.x - source.x) +
                'v' +
                (target.y - source.y) / 2);
        }
        else {
            const source = {
                x: pointLink.source.x + this.nodeWidth / 2,
                y: pointLink.source.y + this.nodeHeight / 2,
            };
            const target = {
                x: pointLink.target.x + this.nodeWidth / 2,
                y: pointLink.target.y + this.nodeHeight / 2,
            };
            return linkVertical()({ source: [source.x, source.y], target: [target.x, target.y] });
        }
    }
    /** Get the link path line defintion when the link is collapsing */
    getCollapsedLinkPath(pointLink) {
        return this.getLinkPath({ source: pointLink.source, target: pointLink.source });
    }
    /** Create a dynamic region that Angular can insert into */
    createPortalOutlet(element) {
        return new DomPortalOutlet(element, this._componentFactoryResolver, this._appRef, this._injector);
    }
    /** Make the appropriate node tabbable and update aria attributes */
    setNodeAttributes() {
        for (const element of this._nodes.nodes()) {
            // intially the tab index of all items to -1
            this._renderer.setAttribute(element, 'tabindex', '-1');
            // set the expanded aria attribute
            this._renderer.setAttribute(element, 'aria-expanded', this.getNodeData(element).data.expanded ? 'true' : 'false');
        }
        // if there is a selected item then it should be tabbable otherwise make the root tabbable
        if (this._selected) {
            this._renderer.setAttribute(this.getNodeElement(this._selected), 'tabindex', '0');
        }
    }
    /** Get the element that represents a given node */
    getNodeElement(node) {
        node = this.coercePointNode(node);
        // find the element that matches the node data
        const index = this._nodes.data().indexOf(node);
        return this._nodes.nodes()[index];
    }
    /** Get the element that represents a given node */
    getNodeData(node) {
        // find the element that matches the node element
        const index = this._nodes.nodes().indexOf(node);
        return this._nodes.data()[index];
    }
    /** Handle click events */
    onClick(node) {
        if (!this.toggleNodesOnClick) {
            return;
        }
        this.toggle(node);
    }
    /** Handle keyboard events */
    onKeydown(event, node) {
        if (!this.toggleNodesOnClick) {
            return;
        }
        switch (event.keyCode) {
            case DOWN_ARROW:
                event.preventDefault();
                // if the node is collapsed and has children expand
                if (!node.data.expanded &&
                    Array.isArray(node.data.children) &&
                    node.data.children.length > 0) {
                    return this.expand(node);
                }
                return this.focusChild(node);
            case RIGHT_ARROW:
                event.preventDefault();
                return this.focusNextSibling(node);
            case UP_ARROW:
                event.preventDefault();
                return this.focusParent(node);
            case LEFT_ARROW:
                event.preventDefault();
                return this.focusPreviousSibling(node);
            case ENTER:
                return this.toggle(node);
        }
    }
    /** When a node receives focus */
    onFocus(node) {
        if (!this.isNodeInViewport(node, this._width * 0.1, this._height * 0.1)) {
            this.centerNode(node);
        }
    }
    /** Move focus to the parent node */
    focusParent(node) {
        if (node.parent) {
            this.focusNode(node.parent);
        }
        else if (this.revealElement) {
            this.revealElement.nativeElement.focus();
            // center the root node to ensure the reveal button is in view
            this.centerNode(this.dataset);
        }
    }
    /** Move focus to the child node */
    focusChild(node) {
        if (Array.isArray(node.children) && node.children.length > 0) {
            this.focusNode(node.children[Math.floor(node.children.length / 2)]);
        }
    }
    /** Move focus to the sibling on the left */
    focusPreviousSibling(node) {
        if (node.parent) {
            this.focusNode(node.parent.children[node.parent.children.indexOf(node) - 1]);
        }
    }
    /** Move focus to the sibling on the right */
    focusNextSibling(node) {
        if (node.parent) {
            this.focusNode(node.parent.children[node.parent.children.indexOf(node) + 1]);
        }
    }
    /** Focus a given node */
    focusNode(node) {
        if (node) {
            this.getNodeElement(node).focus({ preventScroll: true });
            // ensure we don't perform scrolling if the node is not in view (we rely on preventScroll as IE doesn't support it)
            this.nodesContainer.nativeElement.scrollTop = 0;
            this.nodesContainer.nativeElement.scrollLeft = 0;
        }
    }
    /** Determine if a node is fully visible within the viewport */
    isNodeInViewport(node, insetX = 0, insetY = 0) {
        const { x, y } = this.getCameraPosition();
        const left = node.x + x;
        const top = node.y + y;
        const right = node.x + x + this.nodeWidth;
        const bottom = node.y + y + this.nodeHeight;
        return (left >= insetX &&
            top >= insetY &&
            right <= this._width - insetX &&
            bottom <= this._height - insetY);
    }
    /** Determine if a node is fully outside of the viewport */
    isNodeOutsideViewport(node, insetX = 0, insetY = 0) {
        const { x, y } = this.getCameraPosition();
        const left = node.x + x + this.nodeWidth;
        const top = node.y + y + this.nodeHeight;
        const right = node.x + x;
        const bottom = node.y + y;
        return (left < insetX ||
            top < insetY ||
            right > this._width - insetX ||
            bottom > this._height - insetY);
    }
    /** Determine how far a node is from being within the viewport */
    getDistanceFromViewport(node, insetX = 0, insetY = 0) {
        // if the node is in the viewport then it will always be 0, 0
        if (!this.isNodeOutsideViewport(node, insetX, insetY)) {
            return [0, 0];
        }
        const { x, y } = this.getCameraPosition();
        const left = insetX - (node.x + x + this.nodeWidth);
        const top = insetY - (node.y + y + this.nodeHeight);
        const right = node.x + x - (this._width - insetX);
        const bottom = node.y + y - (this._height - insetY);
        let horizontal = 0;
        let vertical = 0;
        if (left > 0 && left > right) {
            horizontal = left;
        }
        if (right > 0 && left < right) {
            horizontal = -right;
        }
        if (top > 0 && top > bottom) {
            vertical = top;
        }
        if (bottom > 0 && top < bottom) {
            vertical = -bottom;
        }
        // calculate the distances on both axis
        return [horizontal, vertical];
    }
    /** Begin monitoring the element focus so we only show styling when navigated by keyboard */
    monitorFocus(element, node) {
        // create the focus indicator
        const indicator = this._focusIndicator.monitor(element, {
            checkChildren: false,
            programmaticFocusIndicator: true,
        });
        // store the currently selected node as an instance variable
        indicator.isFocused$.pipe(takeUntil(this._onDestroy)).subscribe(isFocused => {
            // by default the CDK runs this outside of NgZone however we need it to run inside NgZone to update the node template
            this._ngZone.run(() => {
                if (isFocused) {
                    this._focused = node.data;
                }
                else if (node.data === this._focused) {
                    this._focused = null;
                }
            });
        });
        // store the focus indicator reference
        this._indicators.set(node.data, indicator);
    }
    // ensure that there are at least some nodes visible
    ensureNodesAreVisible() {
        // determine how many nodes are currently visible
        const visibleCount = this._nodes.filter(node => !this.isNodeOutsideViewport(node)).size();
        if (visibleCount > 0) {
            return;
        }
        // get the distance each node is from being within the viewport
        const distances = this._nodes
            .data()
            .map(node => this.getDistanceFromViewport(node, this.nodeWidth * 1.25, this.nodeHeight * 1.5));
        // find the closest node
        const [x, y] = distances.reduce((previous, current) => {
            const [previousX, previousY] = previous;
            const [currentX, currentY] = current;
            return Math.abs(previousX) + Math.abs(previousY) < Math.abs(currentX) + Math.abs(currentY)
                ? previous
                : current;
        });
        // move the camera by the required amount
        this.moveCamera(x, y);
    }
    /** Expand all parent nodes */
    expandParents(node) {
        // get the parent node
        let parent = this.getParent(node);
        while (parent) {
            parent.expanded = true;
            parent = this.getParent(parent);
        }
    }
    /** Get the parent of a given node */
    getParent(node) {
        return [this.coerceDataNode(this.dataset), ...this.getAllChildren(this.dataset)].find(_node => {
            if (!Array.isArray(_node.children)) {
                return false;
            }
            return _node.children.find((child) => child.id === node.id);
        });
    }
    /** Get a flat array of all the nodes childrent */
    getAllChildren(node) {
        const children = node.children || [];
        // check for any children on the children
        return [
            ...children,
            ...children.reduce((accumulation, child) => [...accumulation, ...this.getAllChildren(child)], []),
        ].map(child => this.coerceDataNode(child));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: OrganizationChartComponent, isStandalone: false, selector: "ux-organization-chart", inputs: { dataset: "dataset", connector: "connector", nodeWidth: "nodeWidth", nodeHeight: "nodeHeight", duration: "duration", verticalSpacing: "verticalSpacing", showReveal: "showReveal", revealAriaLabel: "revealAriaLabel", toggleNodesOnClick: "toggleNodesOnClick", selected: "selected" }, outputs: { selectedChange: "selectedChange", reveal: "reveal", transitionEnd: "transitionEnd" }, queries: [{ propertyName: "revealTemplate", first: true, predicate: ["revealTemplate"], descendants: true }, { propertyName: "nodeTemplate", first: true, predicate: ["nodeTemplate"], descendants: true }], viewQueries: [{ propertyName: "revealElement", first: true, predicate: ["revealElement"], descendants: true, static: true }, { propertyName: "linksContainer", first: true, predicate: ["links"], descendants: true, static: true }, { propertyName: "nodesContainer", first: true, predicate: ["nodes"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Add a button above the root node to load additional parent items -->\n<button\n  #revealElement\n  uxFocusIndicatorOrigin\n  class=\"ux-organization-chart-reveal\"\n  tabindex=\"-1\"\n  [attr.aria-label]=\"revealAriaLabel\"\n  [hidden]=\"!showReveal\"\n  (click)=\"reveal.emit(); _focusRootNode()\"\n  (keydown.ArrowDown)=\"_focusRootNode(); $event.preventDefault()\"\n>\n  <!-- Display Reveal Template -->\n  <ng-container [ngTemplateOutlet]=\"revealTemplate || defaultRevealTemplate\"></ng-container>\n</button>\n\n<!-- Show the links connecting each node -->\n<svg #links class=\"ux-organization-chart-links\"></svg>\n\n<!-- Show the nodes containing information about each item -->\n<div #nodes class=\"ux-organization-chart-nodes\"></div>\n\n<!-- Provide a default reveal template -->\n<ng-template #defaultRevealTemplate>\n  <ux-icon name=\"tab-up\"></ux-icon>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorOriginDirective, selector: "[uxFocusIndicatorOrigin]" }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-organization-chart', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Add a button above the root node to load additional parent items -->\n<button\n  #revealElement\n  uxFocusIndicatorOrigin\n  class=\"ux-organization-chart-reveal\"\n  tabindex=\"-1\"\n  [attr.aria-label]=\"revealAriaLabel\"\n  [hidden]=\"!showReveal\"\n  (click)=\"reveal.emit(); _focusRootNode()\"\n  (keydown.ArrowDown)=\"_focusRootNode(); $event.preventDefault()\"\n>\n  <!-- Display Reveal Template -->\n  <ng-container [ngTemplateOutlet]=\"revealTemplate || defaultRevealTemplate\"></ng-container>\n</button>\n\n<!-- Show the links connecting each node -->\n<svg #links class=\"ux-organization-chart-links\"></svg>\n\n<!-- Show the nodes containing information about each item -->\n<div #nodes class=\"ux-organization-chart-nodes\"></div>\n\n<!-- Provide a default reveal template -->\n<ng-template #defaultRevealTemplate>\n  <ux-icon name=\"tab-up\"></ux-icon>\n</ng-template>\n" }]
        }], propDecorators: { dataset: [{
                type: Input
            }], connector: [{
                type: Input
            }], nodeWidth: [{
                type: Input
            }], nodeHeight: [{
                type: Input
            }], duration: [{
                type: Input
            }], verticalSpacing: [{
                type: Input
            }], showReveal: [{
                type: Input
            }], revealAriaLabel: [{
                type: Input
            }], toggleNodesOnClick: [{
                type: Input
            }], selected: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], reveal: [{
                type: Output
            }], transitionEnd: [{
                type: Output
            }], revealTemplate: [{
                type: ContentChild,
                args: ['revealTemplate', { static: false }]
            }], nodeTemplate: [{
                type: ContentChild,
                args: ['nodeTemplate', { static: false }]
            }], revealElement: [{
                type: ViewChild,
                args: ['revealElement', { static: true }]
            }], linksContainer: [{
                type: ViewChild,
                args: ['links', { static: true }]
            }], nodesContainer: [{
                type: ViewChild,
                args: ['nodes', { static: true }]
            }] } });
var OrganizationChartAxis;
(function (OrganizationChartAxis) {
    OrganizationChartAxis[OrganizationChartAxis["Horizontal"] = 0] = "Horizontal";
    OrganizationChartAxis[OrganizationChartAxis["Vertical"] = 1] = "Vertical";
    OrganizationChartAxis[OrganizationChartAxis["Both"] = 2] = "Both";
})(OrganizationChartAxis || (OrganizationChartAxis = {}));

class OrganizationChartModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartModule, declarations: [OrganizationChartComponent], imports: [AccessibilityModule, CommonModule, IconModule, ResizeModule], exports: [OrganizationChartComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartModule, imports: [AccessibilityModule, CommonModule, IconModule, ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OrganizationChartModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [OrganizationChartComponent],
                    imports: [AccessibilityModule, CommonModule, IconModule, ResizeModule],
                    exports: [OrganizationChartComponent],
                }]
        }] });

class TabHeadingDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabHeadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TabHeadingDirective, isStandalone: false, selector: "[uxTabHeading]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabHeadingDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTabHeading]',
                    standalone: false,
                }]
        }] });

class TabsetService {
    constructor() {
        /** Store the list of tabs */
        this.tabs = [];
        this.activeTab$ = new BehaviorSubject(null);
        /** Store the manual state */
        this.manual = false;
    }
    /** Update the array of tabs - required to preserve order */
    update(tabs) {
        this.tabs = [...tabs];
    }
    /** Select a tab (from user input) */
    select(tab) {
        if (tab.disabled) {
            return;
        }
        if (this.manual) {
            // In manual mode, emit the activated/deactivated events.
            // The application is responsible for updating the active state on each tab, which will then update the UI.
            this.tabs.forEach(_tab => (_tab === tab ? _tab.activate() : _tab.deactivate()));
        }
        else {
            this.activeTab$.next(tab);
        }
    }
    /** Set tab active state */
    setTabActive(tab) {
        if (!tab.disabled) {
            this.activeTab$.next(tab);
        }
    }
    /** Determine if there is a selected tab */
    isTabActive() {
        return this.activeTab$.getValue() !== null;
    }
    /** Select the first non-disabled tab */
    selectFirstTab() {
        // find the index of the first non-disabled tab
        const tab = this.tabs.find(_tab => !_tab.disabled);
        if (tab) {
            this.select(tab);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetService, decorators: [{
            type: Injectable
        }] });

/**
 * This token is used to avoid circular dependency
 */
const TabsetToken = new InjectionToken('Tabset');

let uniqueTabId = 0;
class TabComponent {
    constructor() {
        this._tabsetService = inject(TabsetService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._tabset = inject(TabsetToken);
        /** Define if this tab is disabled */
        this.disabled = false;
        /** Emits when the active state changes. */
        this.activeChange = new EventEmitter();
        /** Emit when this tab is selected */
        this.activated = new EventEmitter();
        /** Emit when this tab is deselected */
        this.deactivated = new EventEmitter();
        // Active state of the tab, for use in the template
        this._active = false;
        // Id of tab, for use in the template
        this._id = `ux-tab-${++uniqueTabId}`;
        /** Unsubscribe from all subscriptions when component is destroyed */
        this._onDestroy = new Subject();
    }
    /** Define the tab unique id */
    set id(id) {
        if (id) {
            this._id = id;
        }
    }
    get id() {
        return this._id;
    }
    /** Define the active state of this tab */
    set active(active) {
        if (active) {
            this._tabsetService.setTabActive(this);
        }
    }
    ngOnInit() {
        this._tabsetService.activeTab$
            .pipe(tick(), distinctUntilChanged(), takeUntil(this._onDestroy))
            .subscribe(activeTab => {
            const isActive = activeTab === this;
            if (this._active !== isActive) {
                this.setActive(isActive);
            }
        });
    }
    ngOnChanges(changes) {
        if (changes.disabled && changes.disabled.previousValue !== changes.disabled.currentValue) {
            this._tabset.markForCheck();
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    activate() {
        this.activated.emit();
    }
    deactivate() {
        this.deactivated.emit();
    }
    /**
     * Update the internal active state and emit appropriate events.
     */
    setActive(active) {
        this._active = active;
        this.activeChange.emit(active);
        if (!this._tabsetService.manual) {
            if (active) {
                this.activate();
            }
            else {
                this.deactivate();
            }
        }
        this._changeDetector.detectChanges();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TabComponent, isStandalone: false, selector: "ux-tab", inputs: { id: "id", active: "active", disabled: "disabled", heading: "heading", route: "route", routerLinkExtras: "routerLinkExtras", customClass: "customClass" }, outputs: { activeChange: "activeChange", activated: "activated", deactivated: "deactivated" }, queries: [{ propertyName: "headingRef", first: true, predicate: TabHeadingDirective, descendants: true, read: TemplateRef }], usesOnChanges: true, ngImport: i0, template: "<div\n  role=\"tabpanel\"\n  class=\"tab-pane\"\n  [style.display]=\"_active ? 'block' : 'none'\"\n  [id]=\"id + '-panel'\"\n  [attr.aria-labelledby]=\"id\"\n  [attr.aria-hidden]=\"!_active\"\n>\n  <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-tab', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div\n  role=\"tabpanel\"\n  class=\"tab-pane\"\n  [style.display]=\"_active ? 'block' : 'none'\"\n  [id]=\"id + '-panel'\"\n  [attr.aria-labelledby]=\"id\"\n  [attr.aria-hidden]=\"!_active\"\n>\n  <ng-content></ng-content>\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], active: [{
                type: Input
            }], disabled: [{
                type: Input
            }], heading: [{
                type: Input
            }], route: [{
                type: Input
            }], routerLinkExtras: [{
                type: Input
            }], customClass: [{
                type: Input
            }], activeChange: [{
                type: Output
            }], activated: [{
                type: Output
            }], deactivated: [{
                type: Output
            }], headingRef: [{
                type: ContentChild,
                args: [TabHeadingDirective, { read: TemplateRef, static: false }]
            }] } });

class TabsetComponent {
    constructor() {
        this._tabset = inject(TabsetService);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Determine if the appearance of the tabset */
        this.minimal = true;
        /** Determine if the tabset should appear stacked */
        this.stacked = 'none';
        /** Remove subscriptions on destroy */
        this._onDestroy$ = new Subject();
    }
    /** Determine if we want to manually update the active state */
    set manual(manual) {
        this._tabset.manual = manual;
    }
    ngAfterViewInit() {
        // provide the service with the initial array of items
        this._tabset.update(this._tabs.toArray());
        // Make sure a tab is selected
        if (!this._tabset.isTabActive()) {
            this._tabset.selectFirstTab();
        }
        // run change detection once we have setup the tabs
        this._changeDetector.detectChanges();
        // watch for any future changes
        this._tabs.changes.pipe(takeUntil(this._onDestroy$)).subscribe(tabs => {
            // update the internal list of tabs
            this._tabset.update(tabs);
            // run change detection
            this._changeDetector.detectChanges();
        });
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    selectTab(tab) {
        // pass tab to select method
        this._tabset.select(tab instanceof TabComponent ? tab : this._tabs.toArray()[tab]);
        // run change detection
        this._changeDetector.detectChanges();
    }
    handleKeyDown(event, tab) {
        if (event.keyCode === SPACE) {
            event.preventDefault();
            tab.click();
        }
    }
    markForCheck() {
        this._changeDetector.detectChanges();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TabsetComponent, isStandalone: false, selector: "ux-tabset", inputs: { minimal: "minimal", stacked: "stacked", manual: "manual", ariaLabel: ["aria-label", "ariaLabel"] }, host: { properties: { "class.tabs-left": "stacked === \"left\"", "class.tabs-right": "stacked === \"right\"" } }, providers: [
            TabsetService,
            {
                provide: TabsetToken,
                useExisting: forwardRef(() => TabsetComponent),
            },
        ], queries: [{ propertyName: "_tabs", predicate: TabComponent }], ngImport: i0, template: "<!-- Nav tabs -->\n<ul\n  role=\"tablist\"\n  uxTabbableList\n  [direction]=\"stacked === 'none' ? 'horizontal' : 'vertical'\"\n  [allowBoundaryKeys]=\"true\"\n  class=\"nav nav-tabs\"\n  [class.minimal-tab]=\"minimal\"\n  [attr.aria-label]=\"ariaLabel\"\n  [attr.aria-orientation]=\"stacked === 'none' ? 'horizontal' : 'vertical'\"\n>\n  @for (tab of _tabset.tabs; track tab; let index = $index) {\n    <li\n      role=\"presentation\"\n      class=\"nav-item\"\n      [class.active]=\"(_tabset.activeTab$ | async) === tab\"\n      [class.disabled]=\"tab.disabled\"\n      [ngClass]=\"tab.customClass\"\n    >\n      <ng-template #tabDetails>\n        @if (!tab.headingRef) {\n          <span>{{ tab.heading }}</span>\n        }\n        @if (tab.headingRef) {\n          <ng-container [ngTemplateOutlet]=\"tab.headingRef\"></ng-container>\n        }\n      </ng-template>\n      @if (tab.route) {\n        <a\n          class=\"nav-link\"\n          [attr.id]=\"tab.id\"\n          role=\"tab\"\n          #anchorTab\n          uxTabbableListItem\n          uxFocusIndicator\n          [attr.aria-controls]=\"tab.id\"\n          [attr.aria-selected]=\"(_tabset.activeTab$ | async) === tab\"\n          [attr.aria-disabled]=\"tab.disabled\"\n          [routerLink]=\"tab.route\"\n          [fragment]=\"tab.routerLinkExtras?.fragment\"\n          [queryParams]=\"tab.routerLinkExtras?.queryParams\"\n          [queryParamsHandling]=\"tab.routerLinkExtras?.queryParamsHandling\"\n          [preserveFragment]=\"tab.routerLinkExtras?.preserveFragment\"\n          [skipLocationChange]=\"tab.routerLinkExtras?.skipLocationChange\"\n          [replaceUrl]=\"tab.routerLinkExtras?.replaceUrl\"\n          [state]=\"tab.routerLinkExtras?.state\"\n          (keydown)=\"handleKeyDown($event, anchorTab)\"\n        >\n          <ng-container [ngTemplateOutlet]=\"tabDetails\"> </ng-container>\n        </a>\n      }\n      @if (!tab.route) {\n        <a\n          class=\"nav-link\"\n          [attr.id]=\"tab.id\"\n          role=\"tab\"\n          uxTabbableListItem\n          uxFocusIndicator\n          (mousedown)=\"_tabset.select(tab)\"\n          (activated)=\"_tabset.select(tab)\"\n          [attr.aria-controls]=\"tab.id\"\n          [attr.aria-selected]=\"(_tabset.activeTab$ | async) === tab\"\n          [attr.aria-disabled]=\"tab.disabled\"\n        >\n          <ng-container [ngTemplateOutlet]=\"tabDetails\"> </ng-container>\n        </a>\n      }\n    </li>\n  }\n</ul>\n\n<!-- Tab panes -->\n<div class=\"tab-content\">\n  <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-tabset', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
                        TabsetService,
                        {
                            provide: TabsetToken,
                            useExisting: forwardRef(() => TabsetComponent),
                        },
                    ], host: {
                        '[class.tabs-left]': 'stacked === "left"',
                        '[class.tabs-right]': 'stacked === "right"',
                    }, standalone: false, template: "<!-- Nav tabs -->\n<ul\n  role=\"tablist\"\n  uxTabbableList\n  [direction]=\"stacked === 'none' ? 'horizontal' : 'vertical'\"\n  [allowBoundaryKeys]=\"true\"\n  class=\"nav nav-tabs\"\n  [class.minimal-tab]=\"minimal\"\n  [attr.aria-label]=\"ariaLabel\"\n  [attr.aria-orientation]=\"stacked === 'none' ? 'horizontal' : 'vertical'\"\n>\n  @for (tab of _tabset.tabs; track tab; let index = $index) {\n    <li\n      role=\"presentation\"\n      class=\"nav-item\"\n      [class.active]=\"(_tabset.activeTab$ | async) === tab\"\n      [class.disabled]=\"tab.disabled\"\n      [ngClass]=\"tab.customClass\"\n    >\n      <ng-template #tabDetails>\n        @if (!tab.headingRef) {\n          <span>{{ tab.heading }}</span>\n        }\n        @if (tab.headingRef) {\n          <ng-container [ngTemplateOutlet]=\"tab.headingRef\"></ng-container>\n        }\n      </ng-template>\n      @if (tab.route) {\n        <a\n          class=\"nav-link\"\n          [attr.id]=\"tab.id\"\n          role=\"tab\"\n          #anchorTab\n          uxTabbableListItem\n          uxFocusIndicator\n          [attr.aria-controls]=\"tab.id\"\n          [attr.aria-selected]=\"(_tabset.activeTab$ | async) === tab\"\n          [attr.aria-disabled]=\"tab.disabled\"\n          [routerLink]=\"tab.route\"\n          [fragment]=\"tab.routerLinkExtras?.fragment\"\n          [queryParams]=\"tab.routerLinkExtras?.queryParams\"\n          [queryParamsHandling]=\"tab.routerLinkExtras?.queryParamsHandling\"\n          [preserveFragment]=\"tab.routerLinkExtras?.preserveFragment\"\n          [skipLocationChange]=\"tab.routerLinkExtras?.skipLocationChange\"\n          [replaceUrl]=\"tab.routerLinkExtras?.replaceUrl\"\n          [state]=\"tab.routerLinkExtras?.state\"\n          (keydown)=\"handleKeyDown($event, anchorTab)\"\n        >\n          <ng-container [ngTemplateOutlet]=\"tabDetails\"> </ng-container>\n        </a>\n      }\n      @if (!tab.route) {\n        <a\n          class=\"nav-link\"\n          [attr.id]=\"tab.id\"\n          role=\"tab\"\n          uxTabbableListItem\n          uxFocusIndicator\n          (mousedown)=\"_tabset.select(tab)\"\n          (activated)=\"_tabset.select(tab)\"\n          [attr.aria-controls]=\"tab.id\"\n          [attr.aria-selected]=\"(_tabset.activeTab$ | async) === tab\"\n          [attr.aria-disabled]=\"tab.disabled\"\n        >\n          <ng-container [ngTemplateOutlet]=\"tabDetails\"> </ng-container>\n        </a>\n      }\n    </li>\n  }\n</ul>\n\n<!-- Tab panes -->\n<div class=\"tab-content\">\n  <ng-content></ng-content>\n</div>\n" }]
        }], propDecorators: { minimal: [{
                type: Input
            }], stacked: [{
                type: Input
            }], manual: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], _tabs: [{
                type: ContentChildren,
                args: [TabComponent]
            }] } });

class TabsetModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TabsetModule, declarations: [TabsetComponent, TabComponent, TabHeadingDirective], imports: [AccessibilityModule, CommonModule, RouterModule], exports: [TabsetComponent, TabComponent, TabHeadingDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetModule, imports: [AccessibilityModule, CommonModule, RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsetModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, RouterModule],
                    exports: [TabsetComponent, TabComponent, TabHeadingDirective],
                    declarations: [TabsetComponent, TabComponent, TabHeadingDirective],
                }]
        }] });

class PageHeaderCustomMenuDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderCustomMenuDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PageHeaderCustomMenuDirective, isStandalone: false, selector: "[uxPageHeaderCustomMenu], [uxPageHeaderCustomItem]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderCustomMenuDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxPageHeaderCustomMenu], [uxPageHeaderCustomItem]',
                    standalone: false,
                }]
        }] });

class PageHeaderIconMenuComponent {
    select(item) {
        if (item.select) {
            item.select.call(item, item);
        }
    }
    keydownHandler(item, event) {
        switch (event.keyCode) {
            case ENTER:
            case SPACE:
                this.select(item);
                event.preventDefault();
                event.stopPropagation();
                break;
        }
    }
    _getIconType(identifier) {
        return identifier ? getIconType(identifier) : '';
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderIconMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PageHeaderIconMenuComponent, isStandalone: false, selector: "ux-page-header-icon-menu", inputs: { menu: "menu" }, ngImport: i0, template: "<div class=\"page-header-icon-menu\">\n  <button\n    type=\"button\"\n    class=\"page-header-icon-menu-button\"\n    [attr.aria-label]=\"menu.label\"\n    [uxMenuTriggerFor]=\"iconMenu\"\n    (click)=\"select(menu)\"\n  >\n    <!-- Support all icon types -->\n    @if (_getIconType(menu.icon) !== 'component') {\n      <i [ngClass]=\"[_getIconType(menu.icon), menu.icon]\"> </i>\n    }\n\n    @if (_getIconType(menu.icon) === 'component') {\n      <ux-icon [name]=\"menu.icon\"> </ux-icon>\n    }\n\n    @if (menu?.badge) {\n      <span class=\"label label-primary\" aria-hidden=\"true\">{{ menu.badge }}</span>\n    }\n  </button>\n\n  <ux-menu #iconMenu alignment=\"end\" menuClass=\"ux-page-header-icon-menu\">\n    @for (dropdown of menu?.dropdown; track dropdown) {\n      @if (dropdown.header) {\n        <div class=\"dropdown-header\">\n          <span class=\"font-bold\">{{ dropdown.title }}</span>\n        </div>\n      }\n      @if (!dropdown.header) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          (click)=\"select(dropdown)\"\n          (keydown)=\"keydownHandler(dropdown, $event)\"\n        >\n          <span class=\"dropdown-item-title\">\n            <!-- Support all icon types -->\n            @if (_getIconType(dropdown.icon) !== 'component') {\n              <i\n                class=\"ux-fw\"\n                [ngClass]=\"[_getIconType(dropdown.icon) || 'ux-icon', dropdown.icon || '']\"\n              >\n              </i>\n            }\n            @if (_getIconType(dropdown.icon) === 'component') {\n              <ux-icon class=\"m-r-xs\" [name]=\"dropdown.icon\"> </ux-icon>\n            }\n            {{ dropdown.title }}\n          </span>\n          @if (dropdown.subtitle) {\n            <span class=\"dropdown-item-subtitle\">{{ dropdown.subtitle }}</span>\n          }\n        </button>\n      }\n      @if (dropdown.divider) {\n        <ux-menu-divider></ux-menu-divider>\n      }\n    }\n  </ux-menu>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "component", type: MenuItemComponent, selector: "[uxMenuItem]", inputs: ["disabled", "closeOnSelect", "role"], outputs: ["activate"] }, { kind: "component", type: MenuDividerComponent, selector: "ux-menu-divider" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderIconMenuComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-page-header-icon-menu', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"page-header-icon-menu\">\n  <button\n    type=\"button\"\n    class=\"page-header-icon-menu-button\"\n    [attr.aria-label]=\"menu.label\"\n    [uxMenuTriggerFor]=\"iconMenu\"\n    (click)=\"select(menu)\"\n  >\n    <!-- Support all icon types -->\n    @if (_getIconType(menu.icon) !== 'component') {\n      <i [ngClass]=\"[_getIconType(menu.icon), menu.icon]\"> </i>\n    }\n\n    @if (_getIconType(menu.icon) === 'component') {\n      <ux-icon [name]=\"menu.icon\"> </ux-icon>\n    }\n\n    @if (menu?.badge) {\n      <span class=\"label label-primary\" aria-hidden=\"true\">{{ menu.badge }}</span>\n    }\n  </button>\n\n  <ux-menu #iconMenu alignment=\"end\" menuClass=\"ux-page-header-icon-menu\">\n    @for (dropdown of menu?.dropdown; track dropdown) {\n      @if (dropdown.header) {\n        <div class=\"dropdown-header\">\n          <span class=\"font-bold\">{{ dropdown.title }}</span>\n        </div>\n      }\n      @if (!dropdown.header) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          (click)=\"select(dropdown)\"\n          (keydown)=\"keydownHandler(dropdown, $event)\"\n        >\n          <span class=\"dropdown-item-title\">\n            <!-- Support all icon types -->\n            @if (_getIconType(dropdown.icon) !== 'component') {\n              <i\n                class=\"ux-fw\"\n                [ngClass]=\"[_getIconType(dropdown.icon) || 'ux-icon', dropdown.icon || '']\"\n              >\n              </i>\n            }\n            @if (_getIconType(dropdown.icon) === 'component') {\n              <ux-icon class=\"m-r-xs\" [name]=\"dropdown.icon\"> </ux-icon>\n            }\n            {{ dropdown.title }}\n          </span>\n          @if (dropdown.subtitle) {\n            <span class=\"dropdown-item-subtitle\">{{ dropdown.subtitle }}</span>\n          }\n        </button>\n      }\n      @if (dropdown.divider) {\n        <ux-menu-divider></ux-menu-divider>\n      }\n    }\n  </ux-menu>\n</div>\n" }]
        }], propDecorators: { menu: [{
                type: Input
            }] } });

class PageHeaderService {
    constructor() {
        this._router = inject(Router);
        this.items$ = new BehaviorSubject([]);
        this.selected$ = new BehaviorSubject(null);
        this.selectedRoot$ = new BehaviorSubject(null);
        this.secondary$ = new BehaviorSubject(false);
        this.secondaryNavigationAutoselect = false;
        this._onDestroy = new Subject();
        this.selected$
            .pipe(takeUntil(this._onDestroy), map(selected => this.getRoot(selected)))
            .subscribe(root => this.selectedRoot$.next(root));
        this._router.events
            .pipe(filter(event => event instanceof NavigationEnd ||
            event instanceof NavigationCancel ||
            event instanceof NavigationError), takeUntil(this._onDestroy))
            .subscribe(() => this.updateItemsWithActiveRoute());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    select(item, navigate = true) {
        if (!item) {
            return;
        }
        if (item.routerLink && navigate) {
            // Trigger router navigation
            const routerLink = Array.isArray(item.routerLink) ? item.routerLink : [item.routerLink];
            this._router.navigate(routerLink, item.routerExtras);
        }
        else if (this.secondaryNavigationAutoselect && item.children && item.children.length > 0) {
            // check to see if there is a child that is already marked as selected
            const selectedChild = item.children.find(child => child.selected);
            if (selectedChild) {
                this.select(selectedChild);
                this.selected$.next(selectedChild);
                return;
            }
            // Select the first child that isn't disabled in secondaryNavigationAutoselect mode
            const firstChild = item.children.find(_item => !_item.disabled);
            if (firstChild) {
                this.select(firstChild);
            }
        }
        else {
            // if we are in secondary navigation mode and we click a parent - dont deselect the child
            if (this.secondary$.getValue() === true && this.isParentOf(this.selected$.getValue(), item)) {
                return;
            }
            // Otherwise select the given item
            this.selected$.next(item);
        }
    }
    deselect(item) {
        // deselect the current item
        item.selected = false;
        // iterate any children and deselect them
        if (item.children) {
            item.children.forEach(_item => this.deselect(_item));
        }
    }
    deselectAll() {
        this.items$.getValue().forEach(item => this.deselect(item));
    }
    updateItem(item, selected) {
        // Item is selected if it is the selected item, or one of the selected item's ancestors.
        item.selected = item === selected || this.isParentOf(selected, item);
        if (item === selected) {
            // call the select function if present
            if (item.select) {
                item.select.call(item, item);
            }
        }
    }
    setItems(items = []) {
        // identify all parent elements
        items.forEach(item => this.setParent(item));
        this.items$.next(items);
        // Set up the initially selected item
        // If nothing is set as selected, using the initial route
        const initialSelectedItem = items.find(item => item.selected === true);
        if (initialSelectedItem) {
            this.select(initialSelectedItem);
        }
        else {
            this.updateItemsWithActiveRoute();
        }
    }
    setSecondaryNavigation(enabled) {
        this.secondary$.next(enabled);
    }
    getRoot(item) {
        return item && item.parent ? this.getRoot(item.parent) : item;
    }
    setParent(item, parent) {
        // set the parent field
        item.parent = parent;
        // call this function recursively on all children
        if (item.children) {
            item.children.forEach(child => this.setParent(child, item));
        }
    }
    isParentOf(node, parent) {
        // if there are no parents return false
        if (!node || !node.parent) {
            return false;
        }
        // if the parent is the match we are looking for return true
        if (node.parent === parent) {
            return true;
        }
        // if there are potentially grandparents then check them too
        return this.isParentOf(node.parent, parent);
    }
    updateItemsWithActiveRoute() {
        const activeItem = new PageHeaderActiveNavigationItem();
        for (const item of this.items$.getValue()) {
            this.findActiveItem(item, activeItem);
            if (activeItem.exact) {
                break;
            }
        }
        if (activeItem.item) {
            this.selected$.next(activeItem.item);
        }
    }
    findActiveItem(item, activeItem) {
        if (item.routerLink) {
            const routerLink = Array.isArray(item.routerLink) ? item.routerLink : [item.routerLink];
            const urlTree = this._router.createUrlTree(routerLink, item.routerExtras);
            if (this._router.isActive(urlTree, true) && !activeItem.exact) {
                // When the item route is an exact match, no need to look any further
                activeItem.item = item;
                activeItem.exact = true;
                return;
            }
            if (this._router.isActive(urlTree, false)) {
                // Store an inexact match and continue looking
                activeItem.item = item;
                activeItem.exact = false;
            }
        }
        if (item.children) {
            for (const childItem of item.children) {
                this.findActiveItem(childItem, activeItem);
                if (activeItem.exact) {
                    return;
                }
            }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });
class PageHeaderActiveNavigationItem {
}

class PageHeaderNavigationDropdownItemComponent {
    constructor() {
        this._pageHeaderService = inject(PageHeaderService);
    }
    select(item) {
        // clicking on an item that is disabled or with children then return
        if (item.disabled || item.children) {
            return;
        }
        // emit the selected item in an event
        this._pageHeaderService.select(item);
    }
    keydownHandler(event, item) {
        switch (event.keyCode) {
            case ENTER:
            case SPACE:
                this.select(item);
                event.preventDefault();
                event.stopPropagation();
                break;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationDropdownItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PageHeaderNavigationDropdownItemComponent, isStandalone: false, selector: "ux-page-header-horizontal-navigation-dropdown-item", inputs: { item: "item" }, exportAs: ["ux-page-header-horizontal-navigation-dropdown-item"], ngImport: i0, template: "@if (item.children && item.children.length > 0) {\n  <div>\n    <button\n      type=\"button\"\n      uxMenuItem\n      [attr.id]=\"item.id\"\n      [disabled]=\"item.disabled\"\n      [class.selected]=\"item.selected\"\n      [attr.aria-selected]=\"item.selected\"\n      [uxMenuTriggerFor]=\"menu\"\n    >\n      <span class=\"dropdown-item-title\">{{ item.title }}</span>\n      <ux-icon class=\"dropdown-item-icon\" name=\"next\"></ux-icon>\n    </button>\n    <ux-menu #menu placement=\"right\" menuClass=\"horizontal-navigation-dropdown-submenu\">\n      @for (subItem of item.children; track subItem) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          [attr.id]=\"subItem.id\"\n          [disabled]=\"subItem.disabled\"\n          [class.selected]=\"subItem.selected\"\n          [attr.aria-selected]=\"subItem.selected\"\n          (click)=\"select(subItem)\"\n          (keydown)=\"keydownHandler($event, subItem)\"\n        >\n          <span class=\"dropdown-item-title\">{{ subItem.title }}</span>\n        </button>\n      }\n    </ux-menu>\n  </div>\n}\n\n@if (!item.children || item.children.length === 0) {\n  <div>\n    <button\n      type=\"button\"\n      uxMenuItem\n      [attr.id]=\"item.id\"\n      [disabled]=\"item.disabled\"\n      [class.selected]=\"item.selected\"\n      [attr.aria-selected]=\"item.selected\"\n      (click)=\"select(item)\"\n      (keydown)=\"keydownHandler($event, item)\"\n    >\n      <span class=\"dropdown-item-title\">{{ item.title }}</span>\n    </button>\n  </div>\n}\n", dependencies: [{ kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "component", type: MenuItemComponent, selector: "[uxMenuItem]", inputs: ["disabled", "closeOnSelect", "role"], outputs: ["activate"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationDropdownItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-page-header-horizontal-navigation-dropdown-item', exportAs: 'ux-page-header-horizontal-navigation-dropdown-item', standalone: false, template: "@if (item.children && item.children.length > 0) {\n  <div>\n    <button\n      type=\"button\"\n      uxMenuItem\n      [attr.id]=\"item.id\"\n      [disabled]=\"item.disabled\"\n      [class.selected]=\"item.selected\"\n      [attr.aria-selected]=\"item.selected\"\n      [uxMenuTriggerFor]=\"menu\"\n    >\n      <span class=\"dropdown-item-title\">{{ item.title }}</span>\n      <ux-icon class=\"dropdown-item-icon\" name=\"next\"></ux-icon>\n    </button>\n    <ux-menu #menu placement=\"right\" menuClass=\"horizontal-navigation-dropdown-submenu\">\n      @for (subItem of item.children; track subItem) {\n        <button\n          type=\"button\"\n          uxMenuItem\n          [attr.id]=\"subItem.id\"\n          [disabled]=\"subItem.disabled\"\n          [class.selected]=\"subItem.selected\"\n          [attr.aria-selected]=\"subItem.selected\"\n          (click)=\"select(subItem)\"\n          (keydown)=\"keydownHandler($event, subItem)\"\n        >\n          <span class=\"dropdown-item-title\">{{ subItem.title }}</span>\n        </button>\n      }\n    </ux-menu>\n  </div>\n}\n\n@if (!item.children || item.children.length === 0) {\n  <div>\n    <button\n      type=\"button\"\n      uxMenuItem\n      [attr.id]=\"item.id\"\n      [disabled]=\"item.disabled\"\n      [class.selected]=\"item.selected\"\n      [attr.aria-selected]=\"item.selected\"\n      (click)=\"select(item)\"\n      (keydown)=\"keydownHandler($event, item)\"\n    >\n      <span class=\"dropdown-item-title\">{{ item.title }}</span>\n    </button>\n  </div>\n}\n" }]
        }], propDecorators: { item: [{
                type: Input
            }] } });

class PageHeaderNavigationService {
    constructor() {
        /**
         * Emit when focus changes. We can't directly use the FocusKeyManager
         * `change` observable as it cannot be instantiate until after the view
         * has been instantiated.
         */
        this._onChange = new Subject();
        /** Unsubscribe on destroy */
        this._onDestroy = new Subject();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Make menu items navigable using arrow keys */
    initialize(items) {
        // store the query list for future lookups
        this._items = items;
        // create new focus key manager with horizontal orientation
        this._focusManager = new FocusKeyManager(items).withHorizontalOrientation('ltr');
        // listen for changes to the focused item
        this._focusManager.change
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this._onChange.next());
        // make the first item tabbable initially
        this._focusManager.updateActiveItem(0);
        // on changes ensure there is always an active item
        this._items.changes.subscribe(() => {
            if (this._items.length > 0 &&
                this._items.toArray().indexOf(this._focusManager.activeItem) === -1) {
                this._focusManager.updateActiveItem(0);
            }
        });
        // emit the initial change
        this._onChange.next();
    }
    /** Listen for keyboard events */
    onKeydown(event) {
        this._focusManager.onKeydown(event);
    }
    /** Get the tab index for this item as an observable */
    getTabIndex(item) {
        return this._onChange.pipe(map(() => this.getItemTabIndex(item)), tick(), takeUntil(this._onDestroy));
    }
    /** Determine the tab index of a given item */
    getItemTabIndex(item) {
        // until the focus key manager is set up make everything tabbable
        if (!this._items) {
            return 0;
        }
        // get the index within the query list
        const index = this._items.toArray().indexOf(item);
        // if it is the current active element then it is tabbable
        return index === this._focusManager.activeItemIndex ? 0 : -1;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationService, decorators: [{
            type: Injectable
        }] });

class PageHeaderNavigationItemComponent {
    constructor() {
        this.elementRef = inject(ElementRef);
        this._pageHeaderService = inject(PageHeaderService);
        this._navigationService = inject(PageHeaderNavigationService);
        /** Store the secondary state */
        this.secondary$ = this._pageHeaderService.secondary$;
        /** Update the tabindex based on keyboard input */
        this._tabindex = this._navigationService.getTabIndex(this);
        /** Unsubscribe when the component is destroyed */
        this._onDestroy = new Subject();
    }
    /** Access the data for this dropdown item */
    set item(item) {
        this._item = item;
        this._iconType = getIconType(item.icon);
    }
    get item() {
        return this._item;
    }
    ngAfterViewInit() {
        this._pageHeaderService.selected$
            .pipe(tick(), takeUntil(this._onDestroy))
            .subscribe(selectedItem => {
            // Update selected state for this item
            this._pageHeaderService.updateItem(this.item, selectedItem);
            if (selectedItem && this.isOpen) {
                this.isOpen = false;
            }
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    focus() {
        this.navigationBtn.nativeElement.focus();
    }
    select() {
        // if the item is disabled or has children then do nothing at this stage
        if (this.item.disabled ||
            (this.item.children && this._pageHeaderService.secondary$.getValue() === false)) {
            return;
        }
        // if autoselect is enabled the first child should be selected when we click on the parent item (this element).
        // We should remove the selected state on all children as the service will perform the selection
        // of the first item and handle any routing etc..
        if (this._pageHeaderService.secondaryNavigationAutoselect &&
            Array.isArray(this.item.children)) {
            this.item.children.forEach(item => (item.selected = false));
        }
        // otherwise select the current item
        this._pageHeaderService.select(this.item);
    }
    onKeydown(event, target) {
        if (event.keyCode === LEFT_ARROW || event.keyCode === RIGHT_ARROW) {
            this._navigationService.onKeydown(event);
        }
        if (event.keyCode === SPACE) {
            event.preventDefault();
            target.click();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PageHeaderNavigationItemComponent, isStandalone: false, selector: "ux-page-header-horizontal-navigation-item", inputs: { item: "item" }, host: { listeners: { "keydown": "onKeydown($event,$event.target)" } }, viewQueries: [{ propertyName: "navigationBtn", first: true, predicate: ["navigationBtn"], descendants: true }], ngImport: i0, template: "@if (_item.children && _item.children.length > 0 && (secondary$ | async) === false) {\n  <div>\n    <button\n      #navigationBtn\n      type=\"button\"\n      [attr.id]=\"_item.id\"\n      [tabindex]=\"_tabindex | async\"\n      [uxMenuTriggerFor]=\"menu\"\n      [disabled]=\"_item.disabled\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      [class.open]=\"isOpen\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n      <ux-icon class=\"navigation-item-dropdown-icon\" name=\"down\"></ux-icon>\n    </button>\n    <ux-menu\n      #menu\n      menuClass=\"horizontal-navigation-dropdown-menu\"\n      (opened)=\"isOpen = true\"\n      (closed)=\"isOpen = false\"\n    >\n      @for (item of _item?.children; track item) {\n        <ux-page-header-horizontal-navigation-dropdown-item [item]=\"item\">\n        </ux-page-header-horizontal-navigation-dropdown-item>\n      }\n    </ux-menu>\n  </div>\n}\n\n@if (!_item.children || _item.children.length === 0 || (secondary$ | async)) {\n  @if (_item.routerLink) {\n    <a\n      uxFocusIndicator\n      #navigationBtn\n      [tabindex]=\"_tabindex | async\"\n      [attr.id]=\"_item.id\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      [routerLink]=\"_item.routerLink\"\n      [fragment]=\"_item.routerExtras?.fragment\"\n      [queryParams]=\"_item.routerExtras?.queryParams\"\n      [queryParamsHandling]=\"_item.routerExtras?.queryParamsHandling\"\n      [preserveFragment]=\"_item.routerExtras?.preserveFragment\"\n      [skipLocationChange]=\"_item.routerExtras?.skipLocationChange\"\n      [replaceUrl]=\"_item.routerExtras?.replaceUrl\"\n      [state]=\"_item.routerExtras?.state\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n    </a>\n  }\n  @if (!_item.routerLink) {\n    <button\n      uxFocusIndicator\n      #navigationBtn\n      type=\"button\"\n      [tabindex]=\"_tabindex | async\"\n      [attr.id]=\"_item.id\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      (click)=\"select()\"\n      [disabled]=\"_item.disabled\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n    </button>\n  }\n}\n\n<!-- Support all icon types -->\n<ng-template #navigationItemContent>\n  @if (_item.icon) {\n    @if (_iconType !== 'component') {\n      <i class=\"navigation-item-icon\" [ngClass]=\"[_iconType, _item.icon]\"> </i>\n    }\n    @if (_iconType === 'component') {\n      <ux-icon class=\"navigation-item-icon\" [name]=\"_item.icon\"> </ux-icon>\n    }\n  }\n\n  <span class=\"navigation-item-label\">{{ _item?.title }}</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: MenuComponent, selector: "ux-menu", inputs: ["id", "placement", "alignment", "animate", "menuClass"], outputs: ["opening", "opened", "closing", "closed"] }, { kind: "directive", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: ["uxMenuTriggerFor", "disabled", "uxMenuParent", "closeOnBlur"], outputs: ["closed"], exportAs: ["ux-menu-trigger"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PageHeaderNavigationDropdownItemComponent, selector: "ux-page-header-horizontal-navigation-dropdown-item", inputs: ["item"], exportAs: ["ux-page-header-horizontal-navigation-dropdown-item"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-page-header-horizontal-navigation-item', standalone: false, template: "@if (_item.children && _item.children.length > 0 && (secondary$ | async) === false) {\n  <div>\n    <button\n      #navigationBtn\n      type=\"button\"\n      [attr.id]=\"_item.id\"\n      [tabindex]=\"_tabindex | async\"\n      [uxMenuTriggerFor]=\"menu\"\n      [disabled]=\"_item.disabled\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      [class.open]=\"isOpen\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n      <ux-icon class=\"navigation-item-dropdown-icon\" name=\"down\"></ux-icon>\n    </button>\n    <ux-menu\n      #menu\n      menuClass=\"horizontal-navigation-dropdown-menu\"\n      (opened)=\"isOpen = true\"\n      (closed)=\"isOpen = false\"\n    >\n      @for (item of _item?.children; track item) {\n        <ux-page-header-horizontal-navigation-dropdown-item [item]=\"item\">\n        </ux-page-header-horizontal-navigation-dropdown-item>\n      }\n    </ux-menu>\n  </div>\n}\n\n@if (!_item.children || _item.children.length === 0 || (secondary$ | async)) {\n  @if (_item.routerLink) {\n    <a\n      uxFocusIndicator\n      #navigationBtn\n      [tabindex]=\"_tabindex | async\"\n      [attr.id]=\"_item.id\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      [routerLink]=\"_item.routerLink\"\n      [fragment]=\"_item.routerExtras?.fragment\"\n      [queryParams]=\"_item.routerExtras?.queryParams\"\n      [queryParamsHandling]=\"_item.routerExtras?.queryParamsHandling\"\n      [preserveFragment]=\"_item.routerExtras?.preserveFragment\"\n      [skipLocationChange]=\"_item.routerExtras?.skipLocationChange\"\n      [replaceUrl]=\"_item.routerExtras?.replaceUrl\"\n      [state]=\"_item.routerExtras?.state\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n    </a>\n  }\n  @if (!_item.routerLink) {\n    <button\n      uxFocusIndicator\n      #navigationBtn\n      type=\"button\"\n      [tabindex]=\"_tabindex | async\"\n      [attr.id]=\"_item.id\"\n      role=\"menuitem\"\n      class=\"horizontal-navigation-button\"\n      [class.disabled]=\"_item.disabled\"\n      [class.selected]=\"_item.selected\"\n      (click)=\"select()\"\n      [disabled]=\"_item.disabled\"\n    >\n      <ng-container [ngTemplateOutlet]=\"navigationItemContent\"> </ng-container>\n    </button>\n  }\n}\n\n<!-- Support all icon types -->\n<ng-template #navigationItemContent>\n  @if (_item.icon) {\n    @if (_iconType !== 'component') {\n      <i class=\"navigation-item-icon\" [ngClass]=\"[_iconType, _item.icon]\"> </i>\n    }\n    @if (_iconType === 'component') {\n      <ux-icon class=\"navigation-item-icon\" [name]=\"_item.icon\"> </ux-icon>\n    }\n  }\n\n  <span class=\"navigation-item-label\">{{ _item?.title }}</span>\n</ng-template>\n" }]
        }], propDecorators: { item: [{
                type: Input
            }], navigationBtn: [{
                type: ViewChild,
                args: ['navigationBtn', { static: false }]
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event', '$event.target']]
            }] } });

class PageHeaderNavigationSecondaryItemDirective {
    constructor() {
        this._pageHeaderService = inject(PageHeaderService);
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        this._pageHeaderService.selected$.pipe(delay(0), takeUntil(this._onDestroy)).subscribe(next => {
            // Update selected state for this item
            this._pageHeaderService.updateItem(this.item, next);
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationSecondaryItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PageHeaderNavigationSecondaryItemDirective, isStandalone: false, selector: "[uxPageHeaderNavigationSecondaryItem]", inputs: { item: ["uxPageHeaderNavigationSecondaryItem", "item"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationSecondaryItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxPageHeaderNavigationSecondaryItem]',
                    standalone: false,
                }]
        }], propDecorators: { item: [{
                type: Input,
                args: ['uxPageHeaderNavigationSecondaryItem']
            }] } });

class PageHeaderNavigationComponent {
    constructor() {
        this.elementRef = inject(ElementRef);
        this.resizeService = inject(ResizeService);
        this._navigationService = inject(PageHeaderNavigationService);
        this._pageHeaderService = inject(PageHeaderService);
        this._changeDetectorRef = inject(ChangeDetectorRef);
        this.items$ = this._pageHeaderService.items$;
        this.indicatorVisible = false;
        this.indicatorX = 0;
        this.indicatorWidth = 0;
        this._onDestroy = new Subject();
        this.resizeService
            .addResizeListener(this.elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(this.updateSelectedIndicator.bind(this));
        this._pageHeaderService.selected$
            .pipe(takeUntil(this._onDestroy), distinctUntilChanged())
            .subscribe(this.updateSelectedIndicator.bind(this));
        this._pageHeaderService.secondary$
            .pipe(takeUntil(this._onDestroy), distinctUntilChanged())
            .subscribe(this.updateSelectedIndicator.bind(this));
    }
    ngAfterViewInit() {
        this.updateSelectedIndicator();
        // setup the page focus key manager
        this._navigationService.initialize(this.menuItems);
        // add or remove the menubar role if there are not menuitems to remove accessibility errors
        this.menuItems?.changes
            .pipe(startWith(this.menuItems.toArray()), takeUntil(this._onDestroy))
            .subscribe(() => {
            if (this.menuItems.length > 0) {
                this.elementRef.nativeElement.setAttribute('role', 'menubar');
            }
            else {
                this.elementRef.nativeElement.removeAttribute('role');
            }
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    updateSelectedIndicator() {
        setTimeout(() => {
            // find the selected item
            const selected = this.menuItems.find(item => item.item.selected);
            // determine whether or not to show the indicator
            this.indicatorVisible = !!selected;
            // set the width of the indicator to match the width of the navigation item
            if (selected) {
                const styles = getComputedStyle(selected.elementRef.nativeElement);
                this.indicatorX = selected.elementRef.nativeElement.offsetLeft;
                this.indicatorWidth = parseInt(styles.getPropertyValue('width'));
            }
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PageHeaderNavigationComponent, isStandalone: false, selector: "ux-page-header-horizontal-navigation", providers: [PageHeaderNavigationService], viewQueries: [{ propertyName: "menuItems", predicate: PageHeaderNavigationItemComponent, descendants: true }], ngImport: i0, template: "@for (item of items$ | async; track item) {\n  <ux-page-header-horizontal-navigation-item [item]=\"item\">\n  </ux-page-header-horizontal-navigation-item>\n}\n\n<div\n  class=\"selected-indicator\"\n  [style.opacity]=\"indicatorVisible ? 1 : 0\"\n  [style.margin-left.px]=\"indicatorX\"\n  [style.width.px]=\"indicatorWidth\"\n></div>\n", dependencies: [{ kind: "component", type: PageHeaderNavigationItemComponent, selector: "ux-page-header-horizontal-navigation-item", inputs: ["item"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderNavigationComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-page-header-horizontal-navigation', providers: [PageHeaderNavigationService], standalone: false, template: "@for (item of items$ | async; track item) {\n  <ux-page-header-horizontal-navigation-item [item]=\"item\">\n  </ux-page-header-horizontal-navigation-item>\n}\n\n<div\n  class=\"selected-indicator\"\n  [style.opacity]=\"indicatorVisible ? 1 : 0\"\n  [style.margin-left.px]=\"indicatorX\"\n  [style.width.px]=\"indicatorWidth\"\n></div>\n" }]
        }], ctorParameters: () => [], propDecorators: { menuItems: [{
                type: ViewChildren,
                args: [PageHeaderNavigationItemComponent]
            }] } });

class PageHeaderComponent {
    constructor() {
        this._colorService = inject(ColorService);
        this._pageHeaderService = inject(PageHeaderService);
        /** The alignment of the primary navigation tabs. */
        this.alignment = 'center';
        /** Determines whether or not to display the page header in the regular or condensed form. */
        this.condensed = false;
        /** Determines whether or not a back button should be visible in the page header. */
        this.backVisible = true;
        /** The alignment of the secondary navigation tabs. */
        this.secondaryNavigationAlignment = 'center';
        /**
         * The style of the breadcrumbs.
         *   - standard: The breadcrumbs use the same styling as the navigation tabs.
         *   - small: The breadcrumbs use a smaller font, and case is not adjusted.
         */
        this.crumbsStyle = 'standard';
        /** Emit whenever the back button is clicked */
        this.backClick = new EventEmitter();
        /** Emit whenever the product logo in the left corner is clicked. */
        this.logoClick = new EventEmitter();
        /** The currently selected page header item */
        this.selected$ = this._pageHeaderService.selected$;
        /** The currently selected root menu item - this may be different from selected$ if a child menu item is selected */
        this.selectedRoot$ = this._pageHeaderService.selectedRoot$;
        this._crumbs = [];
    }
    /** If set, the first child item will get selected when the parent item is selected. */
    set secondaryNavigationAutoselect(value) {
        this._pageHeaderService.secondaryNavigationAutoselect = value;
    }
    get secondaryNavigationAutoselect() {
        return this._pageHeaderService.secondaryNavigationAutoselect;
    }
    /** The primary navigation tabs. Use the children property in combination with [secondaryNavigation]="true" to include secondary navigation tabs. */
    set items(items) {
        this._pageHeaderService.setItems(items);
    }
    /** Whether to show a second level of navigation for any items with children. */
    set secondaryNavigation(enabled) {
        this._pageHeaderService.setSecondaryNavigation(enabled);
    }
    get secondaryNavigation() {
        return this._pageHeaderService.secondary$.getValue();
    }
    /** The optional set of breadcrumbs to display on the left side of the masthead. */
    set crumbs(crumbs) {
        this._crumbs = crumbs;
    }
    get crumbs() {
        return this.condensed ? [...this._crumbs, { title: this.header }] : this._crumbs;
    }
    /** The logo background color. This can either be the name of a color from the color palette, or a CSS color value. */
    set logoBackground(color) {
        this._logoBackground = this._colorService.resolve(color);
    }
    get logoBackground() {
        return this._logoBackground;
    }
    /** The logo text color, when a product acronym is specified via header. This can either be the name of a color from the color palette, or a CSS color value. */
    set logoForeground(color) {
        this._logoForeground = this._colorService.resolve(color);
    }
    get logoForeground() {
        return this._logoForeground;
    }
    get _hasLogoClick() {
        return this.logoClick.observers.length > 0;
    }
    select(item, navigate) {
        this._pageHeaderService.select(item, navigate);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PageHeaderComponent, isStandalone: false, selector: "ux-page-header", inputs: { logo: "logo", header: "header", subheader: "subheader", alignment: "alignment", condensed: "condensed", iconMenus: "iconMenus", backVisible: "backVisible", secondaryNavigationAlignment: "secondaryNavigationAlignment", secondaryNavigationAutoselect: "secondaryNavigationAutoselect", items: "items", secondaryNavigation: "secondaryNavigation", crumbs: "crumbs", crumbsStyle: "crumbsStyle", logoBackground: "logoBackground", logoForeground: "logoForeground" }, outputs: { backClick: "backClick", logoClick: "logoClick" }, providers: [PageHeaderService], queries: [{ propertyName: "subheaderTemplate", first: true, predicate: ["subheader"], descendants: true }, { propertyName: "logoTemplate", first: true, predicate: ["logoTemplate"], descendants: true }, { propertyName: "secondaryNavigationLeadingContentTemplate", first: true, predicate: ["secondaryNavigationLeadingContent"], descendants: true }, { propertyName: "secondaryNavigationTrailingContentTemplate", first: true, predicate: ["secondaryNavigationTrailingContent"], descendants: true }, { propertyName: "customMenus", predicate: PageHeaderCustomMenuDirective, read: TemplateRef }], exportAs: ["ux-page-header"], ngImport: i0, template: "<div class=\"ux-page-header\" [class.page-header-condensed]=\"condensed\" role=\"banner\">\n  @if (!condensed) {\n    <div class=\"page-header-content\">\n      <!-- Logo/product acronym -->\n      <div\n        uxFocusIndicator\n        (keydown.enter)=\"logoClick.emit($event)\"\n        [attr.tabindex]=\"_hasLogoClick ? 0 : -1\"\n        [class.page-header-logo-template]=\"logoTemplate\"\n        [class.clickable]=\"_hasLogoClick\"\n        class=\"page-header-logo-container\"\n        role=\"presentation\"\n        [style.backgroundColor]=\"logoBackground\"\n        [style.color]=\"logoForeground\"\n        (click)=\"logoClick.emit($event)\"\n      >\n        @if (logo && !logoTemplate) {\n          <img [attr.src]=\"logo\" [alt]=\"header\" class=\"page-header-logo\" />\n        }\n        @if (header && !logo && !logoTemplate) {\n          <h1 class=\"page-header-acronym\">{{ header }}</h1>\n        }\n        @if (logoTemplate) {\n          <ng-container [ngTemplateOutlet]=\"logoTemplate\"></ng-container>\n        }\n      </div>\n      <!-- Sub-title -->\n      @if (subheader || subheaderTemplate) {\n        <div class=\"page-header-subtitle-container\">\n          @if (subheader) {\n            <span class=\"page-header-subtitle\">{{ subheader }}</span>\n          }\n          <ng-container [ngTemplateOutlet]=\"subheaderTemplate\"></ng-container>\n        </div>\n      }\n      <div class=\"page-header-state-container\" role=\"navigation\">\n        <!-- Back button -->\n        @if (backVisible === true) {\n          <button\n            uxFocusIndicator\n            type=\"button\"\n            class=\"page-header-back-button\"\n            (click)=\"backClick.emit($event)\"\n            aria-label=\"Go Back\"\n          >\n            <ux-icon name=\"previous\" class=\"text-primary\"></ux-icon>\n          </button>\n        }\n        <!-- Breadcrumbs and header -->\n        <div class=\"page-header-title-container\">\n          @if (crumbs && crumbs.length > 0) {\n            <ux-breadcrumbs\n              [class.ux-breadcrumbs-small]=\"crumbsStyle === 'small'\"\n              [crumbs]=\"crumbs\"\n            ></ux-breadcrumbs>\n          }\n          <h1 class=\"page-header-title\">{{ header }}</h1>\n        </div>\n      </div>\n      <!-- Primary navigation -->\n      <div\n        class=\"page-header-navigation\"\n        [ngClass]=\"alignment\"\n        role=\"navigation\"\n        aria-label=\"Primary Navigation\"\n      >\n        <ux-page-header-horizontal-navigation></ux-page-header-horizontal-navigation>\n      </div>\n      <!-- Icon menus -->\n      <div class=\"page-header-icon-menus\" role=\"toolbar\">\n        @for (menu of customMenus; track menu) {\n          <ng-container [ngTemplateOutlet]=\"menu\"></ng-container>\n        }\n        @for (menu of iconMenus; track menu) {\n          <ux-page-header-icon-menu [menu]=\"menu\"></ux-page-header-icon-menu>\n        }\n      </div>\n    </div>\n  }\n\n  <!-- Display This Section Optimized for Condensed Mode -->\n  @if (condensed) {\n    <div class=\"page-header-condensed-content\">\n      <div class=\"page-header-breadcrumbs\" role=\"navigation\">\n        <ux-breadcrumbs [crumbs]=\"crumbs\"></ux-breadcrumbs>\n      </div>\n      <div\n        class=\"page-header-navigation\"\n        [ngClass]=\"alignment\"\n        role=\"navigation\"\n        aria-label=\"Primary Navigation\"\n      >\n        <!-- The Top Navigation Options -->\n        <ux-page-header-horizontal-navigation></ux-page-header-horizontal-navigation>\n      </div>\n      <div class=\"page-header-icon-menus\" role=\"toolbar\">\n        @for (menu of customMenus; track menu) {\n          <ng-container [ngTemplateOutlet]=\"menu\"></ng-container>\n        }\n        @for (menu of iconMenus; track menu) {\n          <ux-page-header-icon-menu [menu]=\"menu\"></ux-page-header-icon-menu>\n        }\n      </div>\n    </div>\n  }\n</div>\n\n<!-- Secondary Header Section -->\n@if (secondaryNavigation && (selectedRoot$ | async) !== (selected$ | async)) {\n  <div class=\"ux-page-header-secondary\">\n    <!-- Secondary Navigation Leading Content -->\n    @if (secondaryNavigationLeadingContentTemplate || secondaryNavigationTrailingContentTemplate) {\n      <div class=\"page-header-secondary-leading-content\">\n        <ng-container [ngTemplateOutlet]=\"secondaryNavigationLeadingContentTemplate\"></ng-container>\n      </div>\n    }\n    <!-- Secondary Navigation (children of top level items) -->\n    <div [ngClass]=\"['page-header-navigation', secondaryNavigationAlignment]\" role=\"navigation\">\n      @if ((selectedRoot$ | async)?.children; as children) {\n        <ux-tabset [manual]=\"true\">\n          @for (child of children; track child) {\n            <ux-tab\n              [heading]=\"child.title\"\n              [route]=\"child.routerLink\"\n              [id]=\"child.id\"\n              [routerLinkExtras]=\"child.routerExtras\"\n              [active]=\"child === (selected$ | async)\"\n              (activated)=\"select(child, false)\"\n              [uxPageHeaderNavigationSecondaryItem]=\"child\"\n              [disabled]=\"child.disabled\"\n            >\n            </ux-tab>\n          }\n        </ux-tabset>\n      }\n    </div>\n    <!-- Secondary Navigation Trialing Content -->\n    @if (secondaryNavigationTrailingContentTemplate) {\n      <div class=\"page-header-secondary-trailing-content\">\n        <ng-container\n          [ngTemplateOutlet]=\"secondaryNavigationTrailingContentTemplate\"\n        ></ng-container>\n      </div>\n    }\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "component", type: BreadcrumbsComponent, selector: "ux-breadcrumbs", inputs: ["crumbs"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "component", type: TabsetComponent, selector: "ux-tabset", inputs: ["minimal", "stacked", "manual", "aria-label"] }, { kind: "component", type: TabComponent, selector: "ux-tab", inputs: ["id", "active", "disabled", "heading", "route", "routerLinkExtras", "customClass"], outputs: ["activeChange", "activated", "deactivated"] }, { kind: "component", type: PageHeaderIconMenuComponent, selector: "ux-page-header-icon-menu", inputs: ["menu"] }, { kind: "component", type: PageHeaderNavigationComponent, selector: "ux-page-header-horizontal-navigation" }, { kind: "directive", type: PageHeaderNavigationSecondaryItemDirective, selector: "[uxPageHeaderNavigationSecondaryItem]", inputs: ["uxPageHeaderNavigationSecondaryItem"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-page-header', exportAs: 'ux-page-header', providers: [PageHeaderService], standalone: false, template: "<div class=\"ux-page-header\" [class.page-header-condensed]=\"condensed\" role=\"banner\">\n  @if (!condensed) {\n    <div class=\"page-header-content\">\n      <!-- Logo/product acronym -->\n      <div\n        uxFocusIndicator\n        (keydown.enter)=\"logoClick.emit($event)\"\n        [attr.tabindex]=\"_hasLogoClick ? 0 : -1\"\n        [class.page-header-logo-template]=\"logoTemplate\"\n        [class.clickable]=\"_hasLogoClick\"\n        class=\"page-header-logo-container\"\n        role=\"presentation\"\n        [style.backgroundColor]=\"logoBackground\"\n        [style.color]=\"logoForeground\"\n        (click)=\"logoClick.emit($event)\"\n      >\n        @if (logo && !logoTemplate) {\n          <img [attr.src]=\"logo\" [alt]=\"header\" class=\"page-header-logo\" />\n        }\n        @if (header && !logo && !logoTemplate) {\n          <h1 class=\"page-header-acronym\">{{ header }}</h1>\n        }\n        @if (logoTemplate) {\n          <ng-container [ngTemplateOutlet]=\"logoTemplate\"></ng-container>\n        }\n      </div>\n      <!-- Sub-title -->\n      @if (subheader || subheaderTemplate) {\n        <div class=\"page-header-subtitle-container\">\n          @if (subheader) {\n            <span class=\"page-header-subtitle\">{{ subheader }}</span>\n          }\n          <ng-container [ngTemplateOutlet]=\"subheaderTemplate\"></ng-container>\n        </div>\n      }\n      <div class=\"page-header-state-container\" role=\"navigation\">\n        <!-- Back button -->\n        @if (backVisible === true) {\n          <button\n            uxFocusIndicator\n            type=\"button\"\n            class=\"page-header-back-button\"\n            (click)=\"backClick.emit($event)\"\n            aria-label=\"Go Back\"\n          >\n            <ux-icon name=\"previous\" class=\"text-primary\"></ux-icon>\n          </button>\n        }\n        <!-- Breadcrumbs and header -->\n        <div class=\"page-header-title-container\">\n          @if (crumbs && crumbs.length > 0) {\n            <ux-breadcrumbs\n              [class.ux-breadcrumbs-small]=\"crumbsStyle === 'small'\"\n              [crumbs]=\"crumbs\"\n            ></ux-breadcrumbs>\n          }\n          <h1 class=\"page-header-title\">{{ header }}</h1>\n        </div>\n      </div>\n      <!-- Primary navigation -->\n      <div\n        class=\"page-header-navigation\"\n        [ngClass]=\"alignment\"\n        role=\"navigation\"\n        aria-label=\"Primary Navigation\"\n      >\n        <ux-page-header-horizontal-navigation></ux-page-header-horizontal-navigation>\n      </div>\n      <!-- Icon menus -->\n      <div class=\"page-header-icon-menus\" role=\"toolbar\">\n        @for (menu of customMenus; track menu) {\n          <ng-container [ngTemplateOutlet]=\"menu\"></ng-container>\n        }\n        @for (menu of iconMenus; track menu) {\n          <ux-page-header-icon-menu [menu]=\"menu\"></ux-page-header-icon-menu>\n        }\n      </div>\n    </div>\n  }\n\n  <!-- Display This Section Optimized for Condensed Mode -->\n  @if (condensed) {\n    <div class=\"page-header-condensed-content\">\n      <div class=\"page-header-breadcrumbs\" role=\"navigation\">\n        <ux-breadcrumbs [crumbs]=\"crumbs\"></ux-breadcrumbs>\n      </div>\n      <div\n        class=\"page-header-navigation\"\n        [ngClass]=\"alignment\"\n        role=\"navigation\"\n        aria-label=\"Primary Navigation\"\n      >\n        <!-- The Top Navigation Options -->\n        <ux-page-header-horizontal-navigation></ux-page-header-horizontal-navigation>\n      </div>\n      <div class=\"page-header-icon-menus\" role=\"toolbar\">\n        @for (menu of customMenus; track menu) {\n          <ng-container [ngTemplateOutlet]=\"menu\"></ng-container>\n        }\n        @for (menu of iconMenus; track menu) {\n          <ux-page-header-icon-menu [menu]=\"menu\"></ux-page-header-icon-menu>\n        }\n      </div>\n    </div>\n  }\n</div>\n\n<!-- Secondary Header Section -->\n@if (secondaryNavigation && (selectedRoot$ | async) !== (selected$ | async)) {\n  <div class=\"ux-page-header-secondary\">\n    <!-- Secondary Navigation Leading Content -->\n    @if (secondaryNavigationLeadingContentTemplate || secondaryNavigationTrailingContentTemplate) {\n      <div class=\"page-header-secondary-leading-content\">\n        <ng-container [ngTemplateOutlet]=\"secondaryNavigationLeadingContentTemplate\"></ng-container>\n      </div>\n    }\n    <!-- Secondary Navigation (children of top level items) -->\n    <div [ngClass]=\"['page-header-navigation', secondaryNavigationAlignment]\" role=\"navigation\">\n      @if ((selectedRoot$ | async)?.children; as children) {\n        <ux-tabset [manual]=\"true\">\n          @for (child of children; track child) {\n            <ux-tab\n              [heading]=\"child.title\"\n              [route]=\"child.routerLink\"\n              [id]=\"child.id\"\n              [routerLinkExtras]=\"child.routerExtras\"\n              [active]=\"child === (selected$ | async)\"\n              (activated)=\"select(child, false)\"\n              [uxPageHeaderNavigationSecondaryItem]=\"child\"\n              [disabled]=\"child.disabled\"\n            >\n            </ux-tab>\n          }\n        </ux-tabset>\n      }\n    </div>\n    <!-- Secondary Navigation Trialing Content -->\n    @if (secondaryNavigationTrailingContentTemplate) {\n      <div class=\"page-header-secondary-trailing-content\">\n        <ng-container\n          [ngTemplateOutlet]=\"secondaryNavigationTrailingContentTemplate\"\n        ></ng-container>\n      </div>\n    }\n  </div>\n}\n" }]
        }], propDecorators: { logo: [{
                type: Input
            }], header: [{
                type: Input
            }], subheader: [{
                type: Input
            }], alignment: [{
                type: Input
            }], condensed: [{
                type: Input
            }], iconMenus: [{
                type: Input
            }], backVisible: [{
                type: Input
            }], secondaryNavigationAlignment: [{
                type: Input
            }], secondaryNavigationAutoselect: [{
                type: Input
            }], items: [{
                type: Input
            }], secondaryNavigation: [{
                type: Input
            }], crumbs: [{
                type: Input
            }], crumbsStyle: [{
                type: Input
            }], logoBackground: [{
                type: Input
            }], logoForeground: [{
                type: Input
            }], backClick: [{
                type: Output
            }], logoClick: [{
                type: Output
            }], subheaderTemplate: [{
                type: ContentChild,
                args: ['subheader', { static: false }]
            }], logoTemplate: [{
                type: ContentChild,
                args: ['logoTemplate', { static: false }]
            }], secondaryNavigationLeadingContentTemplate: [{
                type: ContentChild,
                args: ['secondaryNavigationLeadingContent', { static: false }]
            }], secondaryNavigationTrailingContentTemplate: [{
                type: ContentChild,
                args: ['secondaryNavigationTrailingContent', { static: false }]
            }], customMenus: [{
                type: ContentChildren,
                args: [PageHeaderCustomMenuDirective, { read: TemplateRef }]
            }] } });

class PageHeaderModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderModule, declarations: [PageHeaderComponent,
            PageHeaderIconMenuComponent,
            PageHeaderCustomMenuDirective,
            PageHeaderNavigationComponent,
            PageHeaderNavigationItemComponent,
            PageHeaderNavigationDropdownItemComponent,
            PageHeaderNavigationSecondaryItemDirective], imports: [A11yModule,
            AccessibilityModule,
            BreadcrumbsModule,
            ColorServiceModule,
            CommonModule,
            IconModule,
            MenuModule,
            ResizeModule,
            TabsetModule,
            RouterModule], exports: [PageHeaderComponent, PageHeaderCustomMenuDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderModule, imports: [A11yModule,
            AccessibilityModule,
            BreadcrumbsModule,
            ColorServiceModule,
            CommonModule,
            IconModule,
            MenuModule,
            ResizeModule,
            TabsetModule,
            RouterModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PageHeaderModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        BreadcrumbsModule,
                        ColorServiceModule,
                        CommonModule,
                        IconModule,
                        MenuModule,
                        ResizeModule,
                        TabsetModule,
                        RouterModule,
                    ],
                    exports: [PageHeaderComponent, PageHeaderCustomMenuDirective],
                    declarations: [
                        PageHeaderComponent,
                        PageHeaderIconMenuComponent,
                        PageHeaderCustomMenuDirective,
                        PageHeaderNavigationComponent,
                        PageHeaderNavigationItemComponent,
                        PageHeaderNavigationDropdownItemComponent,
                        PageHeaderNavigationSecondaryItemDirective,
                    ],
                }]
        }] });

const PAGINATION_CONTROL_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => PaginationComponent),
    multi: true,
};
class PaginationComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Specify if we should show the next and previous buttons */
        this.directionButtons = true;
        /** Limit the number of pages shown at any given time */
        this.maxSize = 5;
        /** Specify if the component should be disabled */
        this.disabled = false;
        /** Aria Label for the component navigation */
        this.ariaLabel = 'Pagination Navigation';
        /** Aria label for the previous button */
        this.previousAriaLabel = 'Navigate to the previous page';
        /** Aria label for the next button */
        this.nextAriaLabel = 'Navigate to the next page';
        /** Emit the current page number */
        this.pageChange = new EventEmitter();
        /** Emit the total number of pages */
        this.numPages = new EventEmitter();
        /** Store a list of pages to display in the UI */
        this.pages = [];
        /** ControlValueAccessor functions */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouched = () => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChange = () => { };
        this.isKeyboardEvent = false;
        this._page = 1;
        this._total = 100;
        this._pagesize = 10;
    }
    /** Specify the index of the active page */
    set page(page) {
        // do nothing if the page has not changed
        if (page === this._page) {
            return;
        }
        this._page = page;
        this.pages = this.getPages();
        // mark this component as changed
        this.onChange(this.page);
    }
    get page() {
        return this._page;
    }
    /** Specify the page size */
    set itemsPerPage(pagesize) {
        this._pagesize = pagesize;
        this.pages = this.getPages();
    }
    /** Specify how many items there are in total */
    set totalItems(total) {
        this._total = total;
        this.pages = this.getPages();
    }
    get pageCount() {
        return Math.ceil(this._total / this._pagesize);
    }
    ngOnInit() {
        this.pages = this.getPages();
    }
    select(index) {
        // find the page we want to go to
        const target = this.pages.find(page => page.index === index);
        // if the page is out of bounds then do nothing
        if (!target) {
            return;
        }
        // mark this component as touched
        this.onTouched();
        // set this as the current page
        this.page = target.index;
        // update the visible pages
        this.pages = this.getPages();
        // emit the current page
        this.pageChange.emit(this.page);
    }
    trackByFn(_index, item) {
        return item.index;
    }
    registerOnChange(fn) {
        this.onChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    writeValue(page) {
        this.page = page;
        this._changeDetector.markForCheck();
    }
    getPages() {
        // create a new array to store the pages
        const pages = [];
        // create all possible pages
        for (let index = 1; index <= this.pageCount; index++) {
            pages.push({ index, visible: this.isPageVisible(index) });
        }
        // emit the number of pages
        this.numPages.emit(this.pageCount);
        return pages;
    }
    isPageVisible(index) {
        // if we do not have a max size specified or the number of pages is less than the max size then it is always visible
        if (!this.maxSize || this.pageCount <= this.maxSize) {
            return true;
        }
        // find the starting position
        let start = Math.max(1, Math.ceil(this.page - this.maxSize / 2));
        const end = Math.min(start + this.maxSize, this.pageCount + 1);
        // if the range is less than the max size we need to adjust the starting point
        const range = end - start;
        if (range < this.maxSize) {
            start = start - (this.maxSize - range);
        }
        // if the item equals the start position or is less than the end position then show it
        return index >= start && index < end;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PaginationComponent, isStandalone: false, selector: "ux-pagination", inputs: { directionButtons: "directionButtons", maxSize: "maxSize", disabled: "disabled", classes: ["class", "classes"], pageBtnClass: "pageBtnClass", ariaLabel: ["aria-label", "ariaLabel"], previousAriaLabel: "previousAriaLabel", nextAriaLabel: "nextAriaLabel", page: "page", previousBtnTemplate: "previousBtnTemplate", nextBtnTemplate: "nextBtnTemplate", itemsPerPage: "itemsPerPage", totalItems: "totalItems" }, outputs: { pageChange: "pageChange", numPages: "numPages" }, providers: [PAGINATION_CONTROL_VALUE_ACCESSOR], ngImport: i0, template: "<nav role=\"navigation\" [attr.aria-label]=\"ariaLabel\">\n  <ul\n    #container\n    class=\"pagination\"\n    [ngClass]=\"classes\"\n    direction=\"horizontal\"\n    (blur)=\"isKeyboardEvent = false\"\n    (keydown.ArrowLeft)=\"select(page - 1); isKeyboardEvent = true\"\n    (keydown.ArrowRight)=\"select(page + 1); isKeyboardEvent = true\"\n    (keydown.Home)=\"select(1); isKeyboardEvent = true; $event.preventDefault()\"\n    (keydown.End)=\"select(pageCount); isKeyboardEvent = true; $event.preventDefault()\"\n  >\n    @if (directionButtons) {\n      <li\n        class=\"pagination-prev page-item\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        [checkChildren]=\"true\"\n        [class.disabled]=\"page === 1 || disabled\"\n      >\n        <button\n          class=\"page-link\"\n          [tabindex]=\"page === 1 || disabled ? -1 : 0\"\n          [attr.aria-label]=\"previousAriaLabel\"\n          [ngClass]=\"pageBtnClass\"\n          (click)=\"select(page - 1)\"\n          (keydown.enter)=\"select(page - 1)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"previousBtnTemplate || defaultPreviousBtnTemplate\"\n          ></ng-container>\n        </button>\n      </li>\n    }\n\n    @for (pg of pages; track trackByFn($index, pg)) {\n      @if (pg.visible) {\n        <li\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          [checkChildren]=\"true\"\n          [class.disabled]=\"disabled\"\n          [attr.aria-setsize]=\"pageCount\"\n          [attr.aria-posinset]=\"pg.index\"\n          [class.active]=\"page === pg.index\"\n          class=\"pagination-page page-item\"\n        >\n          <button\n            class=\"page-link\"\n            tabindex=\"0\"\n            [ngClass]=\"pageBtnClass\"\n            [focusIf]=\"isKeyboardEvent && page === pg.index\"\n            [attr.aria-current]=\"page === pg.index\"\n            (click)=\"select(pg.index)\"\n            (keydown.enter)=\"select(pg.index)\"\n          >\n            {{ pg.index }}\n          </button>\n        </li>\n      }\n    }\n\n    @if (directionButtons) {\n      <li\n        class=\"pagination-next page-item\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        [checkChildren]=\"true\"\n        [class.disabled]=\"page === pageCount || disabled\"\n      >\n        <button\n          class=\"page-link\"\n          [tabindex]=\"page === pageCount || disabled ? -1 : 0\"\n          [attr.aria-label]=\"nextAriaLabel\"\n          [ngClass]=\"pageBtnClass\"\n          (click)=\"select(page + 1)\"\n          (keydown.enter)=\"select(page + 1)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"nextBtnTemplate || defaultNextBtnTemplate\"\n          ></ng-container>\n        </button>\n      </li>\n    }\n  </ul>\n</nav>\n\n<ng-template #defaultPreviousBtnTemplate>\n  <ux-icon class=\"pagination-prev-icon-default\" name=\"previous\"></ux-icon>\n</ng-template>\n\n<ng-template #defaultNextBtnTemplate>\n  <ux-icon class=\"pagination-next-icon-default\" name=\"next\"></ux-icon>\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PaginationComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-pagination', providers: [PAGINATION_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<nav role=\"navigation\" [attr.aria-label]=\"ariaLabel\">\n  <ul\n    #container\n    class=\"pagination\"\n    [ngClass]=\"classes\"\n    direction=\"horizontal\"\n    (blur)=\"isKeyboardEvent = false\"\n    (keydown.ArrowLeft)=\"select(page - 1); isKeyboardEvent = true\"\n    (keydown.ArrowRight)=\"select(page + 1); isKeyboardEvent = true\"\n    (keydown.Home)=\"select(1); isKeyboardEvent = true; $event.preventDefault()\"\n    (keydown.End)=\"select(pageCount); isKeyboardEvent = true; $event.preventDefault()\"\n  >\n    @if (directionButtons) {\n      <li\n        class=\"pagination-prev page-item\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        [checkChildren]=\"true\"\n        [class.disabled]=\"page === 1 || disabled\"\n      >\n        <button\n          class=\"page-link\"\n          [tabindex]=\"page === 1 || disabled ? -1 : 0\"\n          [attr.aria-label]=\"previousAriaLabel\"\n          [ngClass]=\"pageBtnClass\"\n          (click)=\"select(page - 1)\"\n          (keydown.enter)=\"select(page - 1)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"previousBtnTemplate || defaultPreviousBtnTemplate\"\n          ></ng-container>\n        </button>\n      </li>\n    }\n\n    @for (pg of pages; track trackByFn($index, pg)) {\n      @if (pg.visible) {\n        <li\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          [checkChildren]=\"true\"\n          [class.disabled]=\"disabled\"\n          [attr.aria-setsize]=\"pageCount\"\n          [attr.aria-posinset]=\"pg.index\"\n          [class.active]=\"page === pg.index\"\n          class=\"pagination-page page-item\"\n        >\n          <button\n            class=\"page-link\"\n            tabindex=\"0\"\n            [ngClass]=\"pageBtnClass\"\n            [focusIf]=\"isKeyboardEvent && page === pg.index\"\n            [attr.aria-current]=\"page === pg.index\"\n            (click)=\"select(pg.index)\"\n            (keydown.enter)=\"select(pg.index)\"\n          >\n            {{ pg.index }}\n          </button>\n        </li>\n      }\n    }\n\n    @if (directionButtons) {\n      <li\n        class=\"pagination-next page-item\"\n        uxFocusIndicator\n        [programmaticFocusIndicator]=\"true\"\n        [checkChildren]=\"true\"\n        [class.disabled]=\"page === pageCount || disabled\"\n      >\n        <button\n          class=\"page-link\"\n          [tabindex]=\"page === pageCount || disabled ? -1 : 0\"\n          [attr.aria-label]=\"nextAriaLabel\"\n          [ngClass]=\"pageBtnClass\"\n          (click)=\"select(page + 1)\"\n          (keydown.enter)=\"select(page + 1)\"\n        >\n          <ng-container\n            [ngTemplateOutlet]=\"nextBtnTemplate || defaultNextBtnTemplate\"\n          ></ng-container>\n        </button>\n      </li>\n    }\n  </ul>\n</nav>\n\n<ng-template #defaultPreviousBtnTemplate>\n  <ux-icon class=\"pagination-prev-icon-default\" name=\"previous\"></ux-icon>\n</ng-template>\n\n<ng-template #defaultNextBtnTemplate>\n  <ux-icon class=\"pagination-next-icon-default\" name=\"next\"></ux-icon>\n</ng-template>\n" }]
        }], propDecorators: { directionButtons: [{
                type: Input
            }], maxSize: [{
                type: Input
            }], disabled: [{
                type: Input
            }], classes: [{
                type: Input,
                args: ['class']
            }], pageBtnClass: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], previousAriaLabel: [{
                type: Input
            }], nextAriaLabel: [{
                type: Input
            }], page: [{
                type: Input
            }], previousBtnTemplate: [{
                type: Input
            }], nextBtnTemplate: [{
                type: Input
            }], itemsPerPage: [{
                type: Input
            }], totalItems: [{
                type: Input
            }], pageChange: [{
                type: Output
            }], numPages: [{
                type: Output
            }] } });

class PaginationModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PaginationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: PaginationModule, declarations: [PaginationComponent], imports: [A11yModule, AccessibilityModule, CommonModule, FocusIfModule, IconModule], exports: [PaginationComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PaginationModule, imports: [A11yModule, AccessibilityModule, CommonModule, FocusIfModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PaginationModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [A11yModule, AccessibilityModule, CommonModule, FocusIfModule, IconModule],
                    declarations: [PaginationComponent],
                    exports: [PaginationComponent],
                }]
        }] });

class PartitionMapSegmentEventsDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        /** Emit when the segment receives focus */
        this.segmentFocus = new EventEmitter();
        /** Emit when the segment is blurred */
        this.segmentBlur = new EventEmitter();
        /** Unsubscribe from observables */
        this._onDestroy = new Subject();
    }
    ngAfterViewInit() {
        // Get the parent segment element
        // Note we cannot use DI to get the element as this is a template
        // and the context has no knowledge of the partition map template
        const segment = this.getSegmentElement();
        if (segment) {
            fromEvent(segment, 'focus')
                .pipe(takeUntil(this._onDestroy))
                .subscribe(event => this.segmentFocus.emit(event));
            fromEvent(segment, 'blur')
                .pipe(takeUntil(this._onDestroy))
                .subscribe(event => this.segmentBlur.emit(event));
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Find the parent element that is a partition map segment
     */
    getSegmentElement() {
        let ancestor = this._elementRef.nativeElement.parentElement;
        while (ancestor !== null) {
            if (ancestor.classList.contains('partition-map-segment')) {
                return ancestor;
            }
            ancestor = ancestor.parentElement;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapSegmentEventsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PartitionMapSegmentEventsDirective, isStandalone: false, selector: "[segmentFocus],[segmentBlur]", outputs: { segmentFocus: "segmentFocus", segmentBlur: "segmentBlur" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapSegmentEventsDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[segmentFocus],[segmentBlur]',
                    standalone: false,
                }]
        }], propDecorators: { segmentFocus: [{
                type: Output
            }], segmentBlur: [{
                type: Output
            }] } });

/* eslint-disable @typescript-eslint/no-empty-object-type */
class PartitionMapComponent {
    constructor() {
        this._colorService = inject(ColorService);
        this._elementRef = inject(ElementRef);
        this._changeDetector = inject(ChangeDetectorRef);
        this._ngZone = inject(NgZone);
        this._focusOrigin = inject(FocusIndicatorOriginService);
        this._contrastRatio = inject(ContrastService);
        this._liveAnnouncer = inject(LiveAnnouncer);
        this._resizeService = inject(ResizeService);
        this._segmentCache = new WeakMap();
        /** Determine the pixel height of collapsed segments. */
        this.collapsedHeight = 40;
        /** Define a minimum desired pixel width for a segment. */
        this.minSegmentWidth = 5;
        /** Define the function that will return the aria announcement for a given segment. */
        this.segmentAnnouncement = this.defaultSegmentAnnouncement;
        /** Emits whenever a segment is selected. */
        this.selectedChange = new EventEmitter();
        /** Store the processed segments */
        this._segments = [];
        /** Store the specified color sequences */
        this._colors = [[]];
        /** Store the assigned colors for each segment */
        this._segmentColors = new Map();
        /** Store the visible x scale */
        this._x = scaleLinear().range([0, 100]);
        /** Store the visible y scale */
        this._y = scaleLinear().range([0, 100]);
        /** Store the width of the chart on resize to avoid any reflow */
        this._width = this._elementRef.nativeElement.offsetWidth;
        /** Store the height of the chart on resize to avoid any reflow */
        this._height = this._elementRef.nativeElement.offsetHeight;
        /** Flag to determine when the inputs have all been bound */
        this._initialized = false;
        /** Unsubscribe from any observables on destroy */
        this._onDestroy = new Subject();
    }
    /** Define the colors to be used for each row and the order they should appear. */
    set colors(colors) {
        this._colors = colors;
        // clear the save color mappings
        this._segmentColors.clear();
    }
    /** Define the dataset to display in the chart. */
    set dataset(dataset) {
        // store the current dataset
        this._dataset = dataset;
        // clear any existing color assignments
        this._segmentColors.clear();
        // update the segment layout
        this.setDataset(dataset);
    }
    get dataset() {
        return this._dataset;
    }
    /** Define the currently selected item. */
    set selected(selected) {
        // if this is set before the dataset is process then store it to be selected later
        if (this._segments.length === 0) {
            this._awaitingSelection = selected;
            return;
        }
        // perform the selection
        this.select(this.getHierarchyNodeFromSegment(selected));
    }
    ngOnInit() {
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(dimensions => {
            this._width = dimensions.width;
            this._height = dimensions.height;
            this._changeDetector.detectChanges();
            // set our new ranges
            if (this._selected) {
                this._x.domain([
                    this.getSegmentX(this._selected),
                    this.getSegmentX(this._selected) + this.getSegmentWidth(this._selected),
                ]);
                this._y.domain([this._selected.y0, 1]).range([this.getTotalCollapsedHeight(), 100]);
            }
            // render the chart to ensure positions and sizes are correct
            this.updateSegments();
        });
        this._initialized = true;
        // Run again so that the colors get applied
        this._changeDetector.detectChanges();
    }
    ngOnDestroy() {
        this._resizeService.removeResizeListener(this._elementRef.nativeElement);
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Handle segment clicks */
    _onSegmentSelect(segment) {
        // if the clicked node is already selected, navigate to the parent node
        this.select(this._isSelected(segment) && segment.parent ? segment.parent : segment);
    }
    /** Get the background color for a given segment */
    _getBackgroundColor(segment) {
        // This can be called before `colors` is initialized, in which case return the default color
        if (!this._initialized) {
            return '#fff';
        }
        // each segment has a determinable color key based on the name and depth
        const key = `${segment.data.name} - ${segment.depth}`;
        // check if a segment with the same name (and depth) has previously
        if (this._segmentColors.has(key)) {
            return this._segmentColors.get(key);
        }
        // get the corresponding row of colors
        const sequence = this.getColorSequence(segment.depth);
        // if the sequence has not been specified return a default of white
        if (!sequence || sequence.length === 0) {
            return '#fff';
        }
        // get siblings
        const siblings = this.getAllSiblings(segment);
        // get the previous sibling if there is one
        const sibling = siblings[siblings.indexOf(segment) - 1];
        // if there is a previous sibling then get its color and use the next one in the sequence
        if (sibling) {
            const index = sequence.indexOf(this._getBackgroundColor(sibling));
            const color = sequence[(index + 1) % sequence.length];
            // store the color by key
            this._segmentColors.set(key, color);
            return color;
        }
        // store the color by key
        this._segmentColors.set(key, sequence[0]);
        // if there is no previous sibling then simply return the first color in the sequence
        return sequence[0];
    }
    /** Get the tab index of a segment */
    _getTabIndex(segment) {
        return segment === this._focusableSegment ? 0 : -1;
    }
    /** Shift focus to the parent segment */
    _focusParent(segment) {
        // if there is no parent (ie, we are the root segment) then retain focus
        if (!segment.parent) {
            return;
        }
        // otherwise focus the parent
        this.focusSegment(segment.parent);
    }
    /** Shift focus to the child segment */
    _focusChild(segment) {
        // if there are no children (ie, we are a leaf segment) then retain focus
        if (!segment.children) {
            return;
        }
        // find the first visible child
        const child = segment.children.find(_segment => this.isVisible(_segment));
        // otherwise focus the first visible child
        if (child) {
            this.focusSegment(child);
        }
    }
    /** Shift focus to the sibling segment */
    _focusSibling(segment, delta) {
        // if we are the root node then do nothing
        if (!segment.parent) {
            return;
        }
        // get a list of all the siblings (at the same row regardless of the same parent)
        const siblings = this.getAllSiblings(segment);
        // get the index of the segment in the list of siblings
        const index = siblings.indexOf(segment);
        // get the target sibling
        const sibling = siblings[index + delta];
        // ensure the sibling is visible otherwise we can't select it
        if (!sibling || !this.isVisible(sibling)) {
            return;
        }
        // otherwise focus the sibling
        this.focusSegment(sibling);
    }
    _focusFirstSibling(segment) {
        // if we are the root node then do nothing
        if (!segment.parent) {
            return;
        }
        // get a list of all the siblings (at the same row regardless of the same parent)
        const siblings = this.getAllSiblings(segment);
        // find the first visible sibling
        const sibling = siblings.find(_sibling => this.isVisible(_sibling));
        // ensure there is a sibling
        if (!sibling) {
            return;
        }
        // otherwise focus the sibling
        this.focusSegment(sibling);
    }
    _focusLastSibling(segment) {
        // if we are the root node then do nothing
        if (!segment.parent) {
            return;
        }
        // get a list of all the siblings (at the same row regardless of the same parent)
        const siblings = this.getAllSiblings(segment);
        // find the last visible sibling
        const sibling = siblings.reverse().find(_sibling => this.isVisible(_sibling));
        // ensure there is a sibling
        if (!sibling) {
            return;
        }
        // otherwise focus the sibling
        this.focusSegment(sibling);
    }
    /** Determine if a given segment is currently collapsed */
    _isCollapsed(segment) {
        return this._selected && segment.depth < this._selected.depth;
    }
    /** Determine if a given segment is currently selected */
    _isSelected(segment) {
        return this._selected === segment;
    }
    /** Get the contast color class for the segment */
    _getContrastColor(segment) {
        const backgroundColor = this._getBackgroundColor(segment);
        const lightColor = ThemeColor.parse('#fff');
        const darkColor = ThemeColor.parse('#000');
        const color = this._contrastRatio.getContrastColor(ThemeColor.parse(backgroundColor), lightColor, darkColor);
        return color === lightColor ? 'partition-map-segment-light' : 'partition-map-segment-dark';
    }
    /** Provide an aria announcement when the node is focused */
    _onFocus(segment) {
        // get all ancestors
        const ancestors = segment.ancestors().map(ancestor => ancestor.data);
        // get the current node and the parent nodes
        const [item, ...parents] = ancestors;
        // get the hierarchy node data from the item
        const hierarchichalItem = this.getHierarchyNodeFromSegment(item);
        // get the function that creates the announcement
        const announcement = this.segmentAnnouncement({
            item,
            parents,
            value: this._getSegmentValue(segment.data),
            collapsed: this._isCollapsed(hierarchichalItem),
            selected: this._isSelected(hierarchichalItem),
        });
        // make aria announcement
        this._liveAnnouncer.announce(announcement);
    }
    /** Determine if the content is smaller than the width of an ellipsis */
    _getSegmentContentHidden(segment) {
        // get the width of the segment as a pixel value
        const width = (this._width / 100) * this.getNormalizedSegmentWidth(segment);
        // if the width is less than 50 px hide the content
        return width < 50;
    }
    /** Get the value of a segment based on the accumulation of all child values */
    _getSegmentValue(segment) {
        // it it has a value then return the value
        // eslint-disable-next-line no-prototype-builtins
        if (segment.hasOwnProperty('value')) {
            return segment.value;
        }
        return segment.children.reduce((value, child) => value + this._getSegmentValue(child), 0);
    }
    _getContext(segment) {
        const context = {
            segment: segment.data,
            value: this._getSegmentValue(segment.data),
            color: this._getBackgroundColor(segment),
            expanded: !this._isCollapsed(segment),
            depth: segment.depth,
            children: [],
        };
        // map the children to their contexts
        if (segment.children) {
            context.children = segment.children.map(this._getContext.bind(this));
        }
        return context;
    }
    trackByIndex(index) {
        return index;
    }
    /** Convert the public facing data structure into the layout format we require */
    setDataset(dataset) {
        // convert the segments to a hierarchichal structure
        const segmentHierarchy = hierarchy(dataset).sum(this.getSegmentValue); // calculate segment values based on their children
        // store the processed segments
        const root = partition()(segmentHierarchy);
        // store the flattened form of the segments
        this._segments = root.descendants();
        // mark the root node as focusable
        this._focusableSegment = root;
        // we need to run change detection here so the `*ngFor` will update and add all the segments to the DOM
        this._changeDetector.detectChanges();
        // select all the segments within the chart
        this._segmentsSelection = select(this._elementRef.nativeElement)
            .selectAll('.partition-map-segment')
            .data(this._segments);
        // set the correct sizing and position of the segments
        this.updateSegments();
        // if there is an item waiting to be selected then select it
        if (this._awaitingSelection) {
            // select the desired segment
            this.select(this.getHierarchyNodeFromSegment(this._awaitingSelection));
            // clear the pending selection in case the dataset changes we don't want to attempt another selection
            this._awaitingSelection = null;
        }
    }
    /** Update the size and position of the segments */
    updateSegments() {
        // if the chart has not yet been initialised do nothing
        if (!this._segmentsSelection) {
            return;
        }
        // clear the segment cache
        this._segmentCache = new WeakMap();
        // perform the chart positioning and sizing
        this._segmentsSelection
            .style('left', data => this.getNormalizedSegmentX(data) + '%')
            .style('top', data => this.getNormalizedSegmentY(data) + '%')
            .style('width', data => this.getNormalizedSegmentWidth(data) + 0.01 + '%')
            .style('height', data => this.getNormalizedSegmentHeight(data) + '%')
            .style('padding-right', data => this.getSegmentPaddingRight(data) + '%')
            .style('padding-left', data => this.getSegmentPaddingLeft(data) + '%');
    }
    cacheSegment(segment, data) {
        // get any existing cache data
        const existing = this._segmentCache.get(segment) ?? {};
        // store the new data
        this._segmentCache.set(segment, { ...existing, ...data });
    }
    getSegmentCache(segment, key) {
        return this._segmentCache.get(segment)?.[key];
    }
    /**
     * Get the X position of a given segment. The X position can be determined
     * by calculating the width of every sibling segment to the left of it
     */
    getSegmentX(segment) {
        // if root node then return the position
        if (!segment.parent) {
            return segment.x0;
        }
        const cachedX = this.getSegmentCache(segment.data, 'x');
        if (cachedX !== undefined) {
            return cachedX;
        }
        // set initial start position equal to that of the parent
        let accumulation = this.getSegmentX(segment.parent);
        // iterate each previous sibling to accumulate the widths
        for (const sibling of segment.parent.children) {
            // if we have reached the current node then return all previous widths
            if (sibling === segment) {
                this.cacheSegment(segment.data, { x: accumulation });
                return accumulation;
            }
            // keep a tally of all the widths of previous siblings
            accumulation += this.getSegmentWidth(sibling);
        }
    }
    /** Calculate width based of each segment */
    getSegmentWidth(segment) {
        // if root node then return 1 always
        if (!segment.parent) {
            return 1;
        }
        const cachedWidth = this.getSegmentCache(segment.data, 'width');
        if (cachedWidth !== undefined) {
            return cachedWidth;
        }
        // get width of parent
        const parentOffset = this.getSegmentWidth(segment.parent) / (segment.parent.x1 - segment.parent.x0);
        // get the original width of the segment
        const width = segment.x1 - segment.x0;
        // if the item is a descendant of the selected item then apply the modifier
        if (this.isDescendantOfSelected(segment)) {
            // we want to try an ensure that children are at least the specified minimum width
            // however it may not always be possible, but we should be able to at least distribute the widths better
            // even if we cannot meet the minimum desired width.
            const modifier = this.getDistributionModifier(segment);
            // return the width of the current node relative to the parent
            const result = width * modifier * parentOffset;
            // store the result in the cache
            this.cacheSegment(segment.data, { width: result });
            return result;
        }
        const result = width * parentOffset;
        // store the result in the cache
        this.cacheSegment(segment.data, { width: result });
        return result;
    }
    /** Return the X position of the segment in a normalized form based on the specifiec domain */
    getNormalizedSegmentX(segment) {
        return this._x(this.getSegmentX(segment));
    }
    /** Return the Y position of the segment in a normalized form based on the specifiec domain */
    getNormalizedSegmentY(segment) {
        // if there is a selected node we should take into account any collapsed nodes
        if (this._isCollapsed(segment)) {
            return segment.depth * this.getCollapsedHeight();
        }
        // otherwise simply return the normalized value
        return this._y(segment.y0);
    }
    /** Return the width of the segment in a normalized form based on the specifiec domain */
    getNormalizedSegmentWidth(segment) {
        return (this._x(this.getSegmentX(segment) + this.getSegmentWidth(segment)) -
            this._x(this.getSegmentX(segment)));
    }
    /** Return the height of the segment in a normalized form based on the specifiec domain */
    getNormalizedSegmentHeight(segment) {
        // if there is a selected node we should take into account any collapsed nodes
        if (this._isCollapsed(segment)) {
            return this.getCollapsedHeight();
        }
        // otherwise simply return the normalized value
        return this._y(segment.y0 + (segment.y1 - segment.y0)) - this._y(segment.y0);
    }
    /**
     * As parent segments collapse they increase in size, as the content is centered this can
     * cause the content to appear either mis-aligned or off screen. We can calculate the padding
     * required to always ensure the content appears visibly centered within the node.
     */
    getSegmentPaddingRight(segment) {
        // non-collapsed node do not require any padding
        if (!this._isCollapsed(segment)) {
            return 0;
        }
        return (this.getNormalizedSegmentWidth(segment) -
            this.getSegmentPaddingLeft(segment) -
            this.getNormalizedSegmentWidth(this._selected));
    }
    getSegmentPaddingLeft(segment) {
        // non-collapsed node do not require any padding
        if (!this._isCollapsed(segment)) {
            return 0;
        }
        return Math.abs(this.getNormalizedSegmentX(segment));
    }
    /**
     * This function returns the value for each segment. Leaf segments will have a value property which we can simply return, however
     * non-leaf segments should get their values based on the leaf segments that are children, in which case we can return 0
     */
    getSegmentValue(segment) {
        // eslint-disable-next-line no-prototype-builtins
        if (segment.hasOwnProperty('value')) {
            const value = segment.value;
            // we must ensure that a leaf node never has no width otherwise things can get weird
            return Math.max(value, 1);
        }
        // if it has children then return 0 to base the value of the width of the children
        return 0;
    }
    /** Get the total height of all the collapse rows */
    getTotalCollapsedHeight() {
        return this._selected ? this._selected.depth * this.getCollapsedHeight() : 0;
    }
    /** Get the collapsed height in percentage format */
    getCollapsedHeight() {
        return parseFloat(((this.collapsedHeight / this._height) * 100).toPrecision(3));
    }
    /** Determine if a given segment is currently visible based on the selected segment */
    isVisible(segment) {
        // if no segment is selected then all segments are visible
        if (!this._selected) {
            return true;
        }
        // if there is a selected node then it should be a direct ancestor or descendant to be visible
        return !![...this._selected.ancestors(), ...this._selected.descendants()].find(_segment => _segment === segment);
    }
    /** Update the focusable item and perform a focus */
    focusSegment(segment) {
        // get the segment element from the data
        const element = this._segmentsSelection
            .nodes()
            .find(node => select(node).data()[0] === segment);
        // if for some reason an element isn't found then stop here
        if (!element) {
            return;
        }
        // update the focusable segment
        this._focusableSegment = segment;
        // set the focus origin as a keyboard event
        this._focusOrigin.setOrigin('keyboard');
        // focus the element
        element.focus();
        // ensure we do not change scroll position when focusing
        this._elementRef.nativeElement.scrollLeft = 0;
        this._elementRef.nativeElement.scrollTop = 0;
    }
    /** Get all the segments at a given depth */
    getAllSiblings(segment) {
        return this._segments.filter(_segment => _segment.depth === segment.depth);
    }
    getHierarchyNodeFromSegment(segment) {
        return this._segments.find(_segment => _segment.data === segment);
    }
    /** Select a specified segment */
    select(segment) {
        // if no segment is specified or it is already selected then do nothing
        if (!segment || this._isSelected(segment)) {
            return;
        }
        // clear the segment cache
        this._segmentCache = new WeakMap();
        // emit the selection
        this.selectedChange.emit(segment.data);
        // store the selected segment
        this._selected = segment;
        // update the focusable segment
        this._focusableSegment = segment;
        // set our new ranges
        this._x.domain([
            this.getSegmentX(segment),
            this.getSegmentX(segment) + this.getSegmentWidth(segment),
        ]);
        this._y.domain([segment.y0, 1]).range([this.getTotalCollapsedHeight(), 100]);
        // create the transition
        const segmentTransition = transition$1('organizationChartSegmentTransition').duration(500);
        // update the segment sizes - outside angular zone as there is lots of `requestAnimationFrames` triggering lots of change detection
        this._ngZone.runOutsideAngular(() => {
            this._segmentsSelection
                .transition(segmentTransition)
                .style('left', data => this.getNormalizedSegmentX(data) + '%')
                .style('top', data => this.getNormalizedSegmentY(data) + '%')
                .style('width', data => this.getNormalizedSegmentWidth(data) + 0.01 + '%')
                .style('height', data => this.getNormalizedSegmentHeight(data) + '%')
                .style('padding-right', data => this.getSegmentPaddingRight(data) + '%')
                .style('padding-left', data => this.getSegmentPaddingLeft(data) + '%');
        });
    }
    /** Normalize the available colors to a string[][] from portentially a ThemeColor[][] */
    getColorSequence(depth) {
        // get the target row
        const colorSet = this._colors[depth];
        // if no color set available throw an error
        if (!colorSet) {
            throw new Error('Partition Map: Please provide a color sequence for items with a depth of ' + depth);
        }
        // convert this row to an array of strings
        return colorSet.map(color => ThemeColor.isInstanceOf(color)
            ? color.toRgba()
            : this._colorService.resolve(color));
    }
    /** Determine if a segment is a descendant of the currently selected item */
    isDescendantOfSelected(segment) {
        // if there are no segments selected then return true
        if (!this._selected) {
            return true;
        }
        // if the segment is the selected segment then it is not a descendant
        if (this._selected === segment) {
            return false;
        }
        return !!this._selected.descendants().find(_segment => _segment === segment);
    }
    /**
     * We have an option to allow a minimum desired width for items. This will
     * allow us to attempt to determine the size a segment would be accounting for very
     * small segments that have their widths artifically increased to make them more visible
     */
    getDistributionModifier(segment) {
        // calculate the desired number of pixels as a percentage
        const minSegmentWidth = (this.minSegmentWidth / this._width) * 100;
        // map to a segment width pair
        const siblings = segment.parent.children.map(_segment => {
            return { segment: _segment, width: this._x(_segment.x1 - _segment.x0) };
        });
        // a simple closure to check if we now have acceptable sizes
        const isAcceptable = (segments) => !segments.find(_segment => _segment.width < minSegmentWidth) ||
            segments.filter(_segment => _segment.width < minSegmentWidth).length === siblings.length;
        // if all segments are above or below the desired width then we can stop here
        if (isAcceptable(siblings)) {
            return 1;
        }
        // find the total amount we need to reclaim for other segments
        let amountToReclaim = siblings.reduce((accumulation, _segment) => accumulation + (_segment.width < minSegmentWidth ? minSegmentWidth - _segment.width : 0), 0);
        // loop through adjusting the segments until we either make all acceptable sizes or cannot resize any further
        while (!isAcceptable(siblings) && amountToReclaim !== 0) {
            // determine which segments can shrink
            const shrinkableSiblings = siblings.filter(sibling => sibling.width > minSegmentWidth);
            // determine which segments need to grow
            const growableSiblings = siblings.filter(sibling => sibling.width < minSegmentWidth);
            // if there are no items that can be shrunk/grown then do nothing
            if (shrinkableSiblings.length === 0 || growableSiblings.length === 0) {
                break;
            }
            // determine the target amount to remove from each segment
            const shrinkTarget = amountToReclaim / shrinkableSiblings.length;
            // store the amount we have reclaimed in this pass
            let reclaimed = 0;
            // iterate each segment and subtract accordingly
            for (const sibling of shrinkableSiblings) {
                // determine how much we can actually subtract - as subtracting the target may bring the width down below the
                // minimum which we don't want, so instead determine if we can subtract the target amount, otherwise figure out
                // how much we can subtract without bringing the width below the desired minimum
                const subtractAmount = sibling.width - shrinkTarget > minSegmentWidth
                    ? shrinkTarget
                    : sibling.width - minSegmentWidth;
                // update the amount to reclaim with the new value
                reclaimed += subtractAmount;
                // update the sibling width
                sibling.width -= subtractAmount;
            }
            // update the amount left to reclaim
            amountToReclaim -= reclaimed;
            // determine the target amount to add to each segment
            const growTarget = reclaimed / growableSiblings.length;
            // add the available reclaimed amount to the segment that need to grow
            for (const sibling of growableSiblings) {
                // determine the amount we need to add. The target amount may be larger than the amount we need
                // to add so ensure we only add the amount we need and no more.
                const addAmount = sibling.width + growTarget < minSegmentWidth
                    ? growTarget
                    : minSegmentWidth - sibling.width;
                // update the sibling width
                sibling.width += addAmount;
            }
        }
        // identify the current widget from all the siblings
        const matchingSegment = siblings.find(sibling => sibling.segment === segment);
        // check if we are the last sibling
        const isLast = siblings.findIndex(sibling => sibling.segment === segment) === siblings.length - 1;
        // if we are the last and somehow we are smaller than the parent node, we want to bump up the size of the last node
        if (isLast) {
            // get the total parent width
            const parentWidth = this._x(segment.parent.x1 - segment.parent.x0);
            // get the total width of all the children
            const width = siblings.reduce((total, sibling) => total + sibling.width, 0);
            // check if need to expand the last node
            if (parentWidth !== width) {
                return ((matchingSegment.width + (parentWidth - width)) /
                    this._x(matchingSegment.segment.x1 - matchingSegment.segment.x0));
            }
        }
        // determine the amount the size has changed
        return matchingSegment.width / this._x(matchingSegment.segment.x1 - matchingSegment.segment.x0);
    }
    /** Get the default announcement when a segment is focused */
    defaultSegmentAnnouncement(info) {
        // create the announcement
        if (info.parents.length === 0) {
            return `This is the root segment. It has a value of ${info.value}.`;
        }
        // otherwise inform the user of the parent hierarchy
        return `${info.item.name} has a value of ${info.value} and is a ${info.parents.map(parent => `descendant of ${parent.name}`).join(' and a ')}`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: PartitionMapComponent, isStandalone: false, selector: "ux-partition-map", inputs: { colors: "colors", collapsedHeight: "collapsedHeight", minSegmentWidth: "minSegmentWidth", dataset: "dataset", selected: "selected", segmentAnnouncement: "segmentAnnouncement" }, outputs: { selectedChange: "selectedChange" }, host: { attributes: { "role": "tree", "aria-orientation": "vertical" } }, queries: [{ propertyName: "segmentTemplate", first: true, predicate: ["partitionMapSegment"], descendants: true }], ngImport: i0, template: "@for (segment of _segments; track trackByIndex($index)) {\n  <div\n    class=\"partition-map-segment\"\n    uxFocusIndicator\n    [ngClass]=\"_getContrastColor(segment)\"\n    [style.background-color]=\"_getBackgroundColor(segment)\"\n    [tabIndex]=\"_getTabIndex(segment)\"\n    (click)=\"_onSegmentSelect(segment)\"\n    (focus)=\"_onFocus(segment)\"\n    role=\"treeitem\"\n    [attr.aria-expanded]=\"!_isCollapsed(segment)\"\n    [attr.aria-selected]=\"_isSelected(segment)\"\n    [attr.aria-level]=\"segment.depth + 1\"\n    [attr.aria-label]=\"segment.data.name\"\n    (keydown.Enter)=\"_onSegmentSelect(segment)\"\n    (keydown.ArrowUp)=\"_focusParent(segment); $event.preventDefault()\"\n    (keydown.ArrowDown)=\"_focusChild(segment); $event.preventDefault()\"\n    (keydown.ArrowLeft)=\"_focusSibling(segment, -1); $event.preventDefault()\"\n    (keydown.ArrowRight)=\"_focusSibling(segment, 1); $event.preventDefault()\"\n    (keydown.Home)=\"_focusFirstSibling(segment); $event.preventDefault()\"\n    (keydown.End)=\"_focusLastSibling(segment); $event.preventDefault()\"\n  >\n    <div\n      class=\"partition-map-segment-content\"\n      [class.partition-map-segment-content-hidden]=\"_getSegmentContentHidden(segment)\"\n    >\n      <!-- Show default template if provided -->\n      @if (!segmentTemplate) {\n        <span class=\"partition-map-segment-label\">\n          {{ segment.data.name }}\n        </span>\n      }\n      <!-- Show custom template if provided -->\n      @if (segmentTemplate) {\n        <ng-container\n          [ngTemplateOutlet]=\"segmentTemplate\"\n          [ngTemplateOutletContext]=\"_getContext(segment)\"\n        >\n        </ng-container>\n      }\n    </div>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-partition-map', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'tree',
                        'aria-orientation': 'vertical',
                    }, standalone: false, template: "@for (segment of _segments; track trackByIndex($index)) {\n  <div\n    class=\"partition-map-segment\"\n    uxFocusIndicator\n    [ngClass]=\"_getContrastColor(segment)\"\n    [style.background-color]=\"_getBackgroundColor(segment)\"\n    [tabIndex]=\"_getTabIndex(segment)\"\n    (click)=\"_onSegmentSelect(segment)\"\n    (focus)=\"_onFocus(segment)\"\n    role=\"treeitem\"\n    [attr.aria-expanded]=\"!_isCollapsed(segment)\"\n    [attr.aria-selected]=\"_isSelected(segment)\"\n    [attr.aria-level]=\"segment.depth + 1\"\n    [attr.aria-label]=\"segment.data.name\"\n    (keydown.Enter)=\"_onSegmentSelect(segment)\"\n    (keydown.ArrowUp)=\"_focusParent(segment); $event.preventDefault()\"\n    (keydown.ArrowDown)=\"_focusChild(segment); $event.preventDefault()\"\n    (keydown.ArrowLeft)=\"_focusSibling(segment, -1); $event.preventDefault()\"\n    (keydown.ArrowRight)=\"_focusSibling(segment, 1); $event.preventDefault()\"\n    (keydown.Home)=\"_focusFirstSibling(segment); $event.preventDefault()\"\n    (keydown.End)=\"_focusLastSibling(segment); $event.preventDefault()\"\n  >\n    <div\n      class=\"partition-map-segment-content\"\n      [class.partition-map-segment-content-hidden]=\"_getSegmentContentHidden(segment)\"\n    >\n      <!-- Show default template if provided -->\n      @if (!segmentTemplate) {\n        <span class=\"partition-map-segment-label\">\n          {{ segment.data.name }}\n        </span>\n      }\n      <!-- Show custom template if provided -->\n      @if (segmentTemplate) {\n        <ng-container\n          [ngTemplateOutlet]=\"segmentTemplate\"\n          [ngTemplateOutletContext]=\"_getContext(segment)\"\n        >\n        </ng-container>\n      }\n    </div>\n  </div>\n}\n" }]
        }], propDecorators: { colors: [{
                type: Input
            }], collapsedHeight: [{
                type: Input
            }], minSegmentWidth: [{
                type: Input
            }], dataset: [{
                type: Input
            }], selected: [{
                type: Input
            }], segmentAnnouncement: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], segmentTemplate: [{
                type: ContentChild,
                args: ['partitionMapSegment', { static: false }]
            }] } });

class PartitionMapModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapModule, declarations: [PartitionMapComponent, PartitionMapSegmentEventsDirective], imports: [A11yModule, AccessibilityModule, CommonModule, ColorServiceModule, ResizeModule], exports: [PartitionMapComponent, PartitionMapSegmentEventsDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapModule, imports: [A11yModule, AccessibilityModule, CommonModule, ColorServiceModule, ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PartitionMapModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [A11yModule, AccessibilityModule, CommonModule, ColorServiceModule, ResizeModule],
                    declarations: [PartitionMapComponent, PartitionMapSegmentEventsDirective],
                    exports: [PartitionMapComponent, PartitionMapSegmentEventsDirective],
                }]
        }] });

class ProgressBarComponent {
    constructor() {
        this.value = 0;
        this.min = 0;
        this.max = 100;
        this.indeterminate = false;
    }
    /** When indeteminate we should omit the valuenow label */
    get valueNow() {
        return this.indeterminate ? null : this.value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ProgressBarComponent, isStandalone: false, selector: "ux-progress-bar", inputs: { value: "value", min: "min", max: "max", indeterminate: "indeterminate", trackColor: "trackColor", barColor: "barColor" }, host: { attributes: { "role": "progressbar" }, properties: { "attr.aria-valuemin": "this.min", "attr.aria-valuemax": "this.max", "attr.aria-valuenow": "this.valueNow" } }, ngImport: i0, template: "@if (!indeterminate) {\n  <div\n    class=\"progressbar-track\"\n    [style.width.%]=\"((value - min) / (max - min)) * 100\"\n    [style.backgroundColor]=\"barColor\"\n  >\n    <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n  </div>\n}\n@if (indeterminate) {\n  <div class=\"progressbar-track indeterminate\" [style.backgroundColor]=\"barColor\">\n    <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n  </div>\n}\n\n<!-- Workaround for Multiple ng-content tags issue: https://github.com/angular/angular/issues/22972 -->\n<ng-template #content><ng-content></ng-content></ng-template>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-progress-bar', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'progressbar',
                    }, standalone: false, template: "@if (!indeterminate) {\n  <div\n    class=\"progressbar-track\"\n    [style.width.%]=\"((value - min) / (max - min)) * 100\"\n    [style.backgroundColor]=\"barColor\"\n  >\n    <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n  </div>\n}\n@if (indeterminate) {\n  <div class=\"progressbar-track indeterminate\" [style.backgroundColor]=\"barColor\">\n    <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n  </div>\n}\n\n<!-- Workaround for Multiple ng-content tags issue: https://github.com/angular/angular/issues/22972 -->\n<ng-template #content><ng-content></ng-content></ng-template>\n" }]
        }], propDecorators: { value: [{
                type: Input
            }], min: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.aria-valuemin']
            }], max: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.aria-valuemax']
            }], indeterminate: [{
                type: Input
            }], trackColor: [{
                type: Input
            }], barColor: [{
                type: Input
            }], valueNow: [{
                type: HostBinding,
                args: ['attr.aria-valuenow']
            }] } });

class ProgressBarModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarModule, declarations: [ProgressBarComponent], imports: [CommonModule], exports: [ProgressBarComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ProgressBarModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule],
                    exports: [ProgressBarComponent],
                    declarations: [ProgressBarComponent],
                }]
        }] });

const RADIOBUTTON_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => RadioButtonComponent),
    multi: true,
};
let uniqueRadioId = 0;
class RadioButtonComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        this._group = inject(RadioButtonGroupDirective, { optional: true });
        /** Provide a default unique id value for the radiobutton */
        this._radioButtonId = `ux-radio-button-${++uniqueRadioId}`;
        /** Specify a unique Id for this component */
        this.id = this._radioButtonId;
        /** If set to `true` the radio button will not change state when clicked. */
        this.clickable = true;
        /** If this value is set to `true` then the radio button will be disabled */
        this.disabled = false;
        /** If set to `true` the checkbox will be displayed without a border and background. */
        this.simplified = false;
        /** Specify an aria label for the input element */
        this.ariaLabel = '';
        /** Specify an aria labelledby property for the input element */
        this.ariaLabelledby = null;
        /** Specify an aria describedby property for the input element */
        this.ariaDescribedby = null;
        /** Emits when the value has been changed. */
        this.valueChange = new EventEmitter();
        /** Determine if the underlying input component has been focused with the keyboard */
        this._focused = false;
        /** Internally store the current tabindex */
        this._internalTabindex = null;
        /** Used to inform Angular forms that the component has been touched */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        /** Used to inform Angular forms that the component value has changed */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
    }
    ngOnChanges(changes) {
        if (changes.disabled && this._group && !changes.disabled.firstChange) {
            this._group.determineAndSetInternalTabIndex();
        }
    }
    /** Select the current option */
    select() {
        if (this.disabled || !this.clickable) {
            return;
        }
        // toggle the checked state
        this.value = this.option;
        // if there is a group set the selected value
        if (this._group) {
            this._group.value = this.option;
            this._group.emitChange(this.option);
        }
        // emit the value
        this.valueChange.emit(this.value);
        // update the value if used within a form control
        this.onChangeCallback(this.value);
        // mark the component as touched
        this.onTouchedCallback();
    }
    // Functions required to update ng-model
    writeValue(value) {
        if (value !== this.value) {
            this.value = value;
            this._changeDetector.detectChanges();
        }
    }
    /** Allow Angular forms for provide us with a callback for when the input value changes */
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    /** Allow Angular forms for provide us with a callback for when the touched state changes */
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    /** Allow Angular forms to disable the component */
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    /** Set the internal tab index of the radio button */
    setInternalTabindex(tabIndex) {
        this._internalTabindex = tabIndex;
        this._changeDetector.detectChanges();
    }
    /** Focus the input element */
    focus(origin) {
        this._focusIndicator.focus(origin);
    }
    setInputTabIndex(tabindex) {
        this.tabindex = tabindex;
        this._changeDetector.detectChanges();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: RadioButtonComponent, isStandalone: false, selector: "ux-radio-button", inputs: { id: "id", name: "name", inputRole: "inputRole", value: "value", required: "required", tabindex: "tabindex", clickable: "clickable", disabled: "disabled", simplified: "simplified", option: "option", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], ariaDescribedby: ["aria-describedby", "ariaDescribedby"] }, outputs: { valueChange: "valueChange" }, providers: [
            RADIOBUTTON_VALUE_ACCESSOR,
            {
                provide: FocusableItemToken,
                useExisting: RadioButtonComponent,
            },
        ], viewQueries: [{ propertyName: "_focusIndicator", first: true, predicate: FocusIndicatorDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<label\n  [attr.for]=\"(id || _radioButtonId) + '-input'\"\n  class=\"ux-radio-button\"\n  [class.ux-radio-button-checked]=\"value === option\"\n  [class.ux-radio-button-simplified]=\"simplified\"\n  [class.ux-radio-button-disabled]=\"disabled\"\n  [class.ux-radio-button-focused]=\"_focused\"\n>\n  <div class=\"ux-radio-button-container\">\n    <input\n      #input\n      class=\"ux-radio-button-input\"\n      uxFocusIndicator\n      type=\"radio\"\n      [id]=\"(id || _radioButtonId) + '-input'\"\n      [attr.role]=\"inputRole\"\n      [checked]=\"value === option\"\n      [disabled]=\"disabled\"\n      [tabindex]=\"tabindex ?? _internalTabindex\"\n      [attr.name]=\"name\"\n      [required]=\"required\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      [attr.aria-describedby]=\"ariaDescribedby\"\n      [attr.aria-checked]=\"value === option\"\n      (indicator)=\"_focused = $event\"\n      (change)=\"select()\"\n      (click)=\"$event.stopPropagation()\"\n    />\n  </div>\n\n  <span class=\"ux-radio-button-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-radio-button', providers: [
                        RADIOBUTTON_VALUE_ACCESSOR,
                        {
                            provide: FocusableItemToken,
                            useExisting: RadioButtonComponent,
                        },
                    ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<label\n  [attr.for]=\"(id || _radioButtonId) + '-input'\"\n  class=\"ux-radio-button\"\n  [class.ux-radio-button-checked]=\"value === option\"\n  [class.ux-radio-button-simplified]=\"simplified\"\n  [class.ux-radio-button-disabled]=\"disabled\"\n  [class.ux-radio-button-focused]=\"_focused\"\n>\n  <div class=\"ux-radio-button-container\">\n    <input\n      #input\n      class=\"ux-radio-button-input\"\n      uxFocusIndicator\n      type=\"radio\"\n      [id]=\"(id || _radioButtonId) + '-input'\"\n      [attr.role]=\"inputRole\"\n      [checked]=\"value === option\"\n      [disabled]=\"disabled\"\n      [tabindex]=\"tabindex ?? _internalTabindex\"\n      [attr.name]=\"name\"\n      [required]=\"required\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      [attr.aria-describedby]=\"ariaDescribedby\"\n      [attr.aria-checked]=\"value === option\"\n      (indicator)=\"_focused = $event\"\n      (change)=\"select()\"\n      (click)=\"$event.stopPropagation()\"\n    />\n  </div>\n\n  <span class=\"ux-radio-button-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], name: [{
                type: Input
            }], inputRole: [{
                type: Input
            }], value: [{
                type: Input
            }], required: [{
                type: Input
            }], tabindex: [{
                type: Input
            }], clickable: [{
                type: Input
            }], disabled: [{
                type: Input
            }], simplified: [{
                type: Input
            }], option: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], ariaLabelledby: [{
                type: Input,
                args: ['aria-labelledby']
            }], ariaDescribedby: [{
                type: Input,
                args: ['aria-describedby']
            }], valueChange: [{
                type: Output
            }], _focusIndicator: [{
                type: ViewChild,
                args: [FocusIndicatorDirective]
            }] } });

const RADIO_GROUP_CONTROL_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => RadioButtonGroupDirective),
    multi: true,
};
class RadioButtonGroupDirective {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Emit when the currently selected value changes */
        this.valueChange = new EventEmitter();
        /** Used to inform Angular forms that the component has been touched */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouched = () => { };
        /** Used to inform Angular forms that the component value has changed */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChange = () => { };
        /** Unsubscribe from all subscriptions on destroy */
        this._onDestroy$ = new Subject();
        /** Internally store the current value */
        this._value = null;
    }
    /** Define the current selected value within the group */
    set value(value) {
        this._value = value;
        this.updateSelectedRadioButton();
    }
    /** Return the currently selected value */
    get value() {
        return this._value;
    }
    ngAfterContentInit() {
        this.updateSelectedRadioButton();
        // update the selected items any time new ones are added
        this._radioButtons.changes
            .pipe(takeUntil(this._onDestroy$))
            .subscribe(() => this.updateSelectedRadioButton());
    }
    ngOnDestroy() {
        this._onDestroy$.next();
        this._onDestroy$.complete();
    }
    /** Allow Angular forms for provide us with a callback for when the input value changes */
    registerOnChange(fn) {
        this.onChange = fn;
    }
    /** Allow Angular forms for provide us with a callback for when the touched state changes */
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    /** Allow Angular forms to give us the current value */
    writeValue(value) {
        this.value = value;
        this._changeDetector.markForCheck();
    }
    /** Allow Angular forms to disable the component */
    setDisabledState(isDisabled) {
        if (this._radioButtons) {
            this._radioButtons.forEach(radio => radio.setDisabledState(isDisabled));
            this._changeDetector.markForCheck();
        }
    }
    /** Emit the currently selected value */
    emitChange(value) {
        this.valueChange.next(value);
        this.onChange(value);
        this.onTouched();
    }
    /** Determine and set the correct internal tabindex */
    determineAndSetInternalTabIndex() {
        const firstEnabled = this._radioButtons.find(radio => {
            return radio.disabled === false;
        });
        this._radioButtons.forEach(radio => {
            if (this._value !== undefined) {
                radio.setInternalTabindex(radio.option === this._value ? 0 : -1);
            }
            else {
                radio.setInternalTabindex(firstEnabled === radio ? 0 : -1);
            }
        });
    }
    /** Inform all child radio buttons of the latest value */
    updateSelectedRadioButton() {
        // update the selected value in all radio buttons
        if (this._radioButtons) {
            this.determineAndSetInternalTabIndex();
            this._radioButtons.forEach(radio => radio.writeValue(this._value));
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: RadioButtonGroupDirective, isStandalone: false, selector: "ux-radio-button-group, [uxRadioButtonGroup]", inputs: { value: "value" }, outputs: { valueChange: "valueChange" }, host: { attributes: { "role": "radiogroup" } }, providers: [RADIO_GROUP_CONTROL_VALUE_ACCESSOR], queries: [{ propertyName: "_radioButtons", predicate: i0.forwardRef(() => RadioButtonComponent), descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonGroupDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'ux-radio-button-group, [uxRadioButtonGroup]',
                    providers: [RADIO_GROUP_CONTROL_VALUE_ACCESSOR],
                    host: {
                        role: 'radiogroup',
                    },
                    standalone: false,
                }]
        }], propDecorators: { value: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], _radioButtons: [{
                type: ContentChildren,
                args: [forwardRef(() => RadioButtonComponent), { descendants: true }]
            }] } });

class RadioButtonModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonModule, declarations: [RadioButtonComponent, RadioButtonGroupDirective], imports: [AccessibilityModule, FormsModule], exports: [RadioButtonComponent, RadioButtonGroupDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonModule, imports: [AccessibilityModule, FormsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: RadioButtonModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, FormsModule],
                    exports: [RadioButtonComponent, RadioButtonGroupDirective],
                    declarations: [RadioButtonComponent, RadioButtonGroupDirective],
                }]
        }] });

class SankeyChart {
    constructor() {
        /** Define the nodes in the chart */
        this._nodes = [];
        /** Define the links in the chart */
        this._links = [];
        /** Store the node-links */
        this._nodeLinks = [];
        /** Define the minimum width of the nodes */
        this._minWidth = 0;
        /** Define the maximum width of the nodes */
        this._maxWidth = Infinity;
        /** Define the minimum distance from the edge of the chart */
        this._padding = 24;
    }
    /** Define the spacing of the chart */
    spacing(spacing) {
        this._spacing = spacing;
        return this;
    }
    /** Define the width of the chart */
    width(width) {
        this._width = width;
        return this;
    }
    /** Define the height of the chart */
    height(height) {
        this._height = height;
        return this;
    }
    /** Define the nodes */
    nodes(nodes) {
        this._nodes = nodes;
        return this;
    }
    /** Define the links */
    links(links) {
        this._links = links;
        return this;
    }
    /** Define the minimum and maximum size of the nodes */
    size(minWidth, maxWidth, minHeight) {
        this._minWidth = minWidth;
        this._maxWidth = maxWidth;
        this._minHeight = minHeight;
        return this;
    }
    /** Get the sizes of each column */
    columns() {
        // get the number of columns - we use this a lot so avoid multiple function calls
        const columnCount = this.getColumnCount();
        // get the amount of padding there should be on each side of a node
        const padding = this.getColumnPadding();
        const columnWidths = [];
        for (let idx = 0; idx < columnCount; idx++) {
            columnWidths[idx] = this.getNodeWidth() + padding * 2;
            // do no have the default padding on the left of the start node
            // or right of the last node, instead have a default padding
            if (idx === 0 || idx === columnCount - 1) {
                columnWidths[idx] -= padding - this._padding;
            }
        }
        return columnWidths;
    }
    /**
     * Perform the various stages of the layout
     * in the correct order as some steps are dependant
     * on the previous layout stages.
     */
    layout() {
        this.getNodeLinks();
        this.getNodeValues();
        this.getNodeColumns();
        this.getNodeWidths();
        this.getNodeHeights();
        this.getNodePositions();
        this.getLinkPlots();
        return this._nodeLinks;
    }
    /** The curve equation for links */
    link(link) {
        // const dist = chart.blockSpacing / 2;
        const { topLeft, topRight, bottomLeft, bottomRight } = link;
        const dist = (topRight[0] - topLeft[0]) / 2;
        const topLeftCurve = [topLeft[0] + dist, topLeft[1]];
        const topRightCurve = [topRight[0] - dist, topRight[1]];
        const bottomLeftCurve = [bottomLeft[0] + dist, bottomLeft[1]];
        const bottomRightCurve = [bottomRight[0] - dist, bottomRight[1]];
        return ('M' +
            topLeft[0] +
            ',' +
            topLeft[1] +
            'C' +
            topLeftCurve[0] +
            ',' +
            topLeftCurve[1] +
            ' ' +
            topRightCurve[0] +
            ',' +
            topRightCurve[1] +
            ' ' +
            topRight[0] +
            ',' +
            topRight[1] +
            'L' +
            bottomRight[0] +
            ',' +
            bottomRight[1] +
            'C' +
            bottomRightCurve[0] +
            ',' +
            bottomRightCurve[1] +
            ' ' +
            bottomLeftCurve[0] +
            ',' +
            bottomLeftCurve[1] +
            ' ' +
            bottomLeft[0] +
            ',' +
            bottomLeft[1] +
            'L' +
            topLeft[0] +
            ',' +
            topLeft[1]);
    }
    getFalloffPath(nodeLink) {
        const x = nodeLink.x + nodeLink.width;
        const y = nodeLink.outputs.reduce((bottom, output) => Math.max(bottom, output.bottomLeft[1]), 0);
        const width = 20;
        const radius = 6;
        const height = nodeLink.y + nodeLink.height - y + this._spacing / 2;
        return ('M' +
            x +
            ',' +
            y +
            'h ' +
            (width - radius) +
            'a' +
            radius +
            ',' +
            radius +
            ' 0 0,1' +
            radius +
            ',' +
            radius +
            ' ' +
            'v' +
            Math.max(radius, height) +
            'h-' +
            width +
            'Z');
    }
    /**
     * Get a `SankeyNodeLink` object from the id of a node
     */
    getNodeLink(id) {
        return this._nodeLinks.find(nodeLink => nodeLink.node.id === id);
    }
    /** Replace the node ids with actual references */
    getNodeLinks() {
        this._nodeLinks = this._nodes.map(node => {
            // get all the links that input into and output from this node
            const inputs = this._links.filter(link => link.target === node.id);
            const outputs = this._links.filter(link => link.source === node.id);
            return {
                node,
                inputs,
                outputs,
                value: 0,
                column: 0,
                x: 0,
                y: 0,
                width: 0,
                height: 0,
                naturalHeight: 0,
                falloff: 0,
                active: false,
                focus: false,
            };
        });
    }
    /** Get the value for the node based on all its inputs and outputs */
    getNodeValues() {
        for (const node of this._nodeLinks) {
            // the node value can be determined by the total values from all inputs
            // however the first column of nodes have no inputs so must be based of their outputs.
            // We should take the maximum value based on the inputs and outputs as nodes that are
            // not in the first column may not output all of the amount the receive from inputs,
            // for example in the case of falloff etc..
            node.value = Math.max(sum(node.inputs, input => input.value), sum(node.outputs, output => output.value));
        }
    }
    /**
     * We need to determine which column the node should
     * be placed in. This is determined by taking the input
     * and adding one.
     */
    getNodeColumns(nodeLinks = this._nodeLinks.filter(node => node.inputs.length === 0), column = 0) {
        for (const nodeLink of nodeLinks) {
            nodeLink.column = column;
            // call this function to all output links
            this.getNodeColumns(nodeLink.outputs.map(output => this.getNodeLink(output.target)), column + 1);
        }
    }
    /** Get the width of each node */
    getNodeWidths() {
        this._nodeLinks.forEach(node => (node.width = this.getNodeWidth()));
    }
    /**
     * Scale the nodes height based on the value the represent
     */
    getNodeHeights() {
        // get columns by group
        const groups = this.getColumnGroups();
        const groupList = Object.keys(groups).map(group => groups[group]);
        // get the column with the largest total value
        const total = groupList.reduce((count, nodes) => Math.max(count, nodes.reduce((accumulation, node) => accumulation + node.value, 0)), 0);
        // Calculate node heights
        for (const nodeLinks of groupList) {
            // get the proportional size of each node based on the available space
            for (const nodeLink of nodeLinks) {
                nodeLink.naturalHeight = (nodeLink.value / total) * this._height - this._spacing;
                nodeLink.height = Math.max(nodeLink.naturalHeight, this._minHeight);
            }
        }
        // If minHeight is defined, it might cause some columns to exceed the height of the chart following the
        // initial height calculation.
        if (this._minHeight > 0) {
            try {
                // Recalculate node heights until they fit (if possible)
                this.adjustNodeHeightsToFit(groupList);
            }
            catch (error) {
                // If the above recalculation fails, give up and use the naturalHeight (ignore minHeight)
                this.setNodesToNaturalHeight(groupList);
            }
        }
    }
    /**
     * Recalculate node heights within height limits until they fit (if possible).
     * @throws If it is not possible to fit all nodes in the chart due to `minHeight`.
     */
    adjustNodeHeightsToFit(groupList) {
        let largestColumn = this.getLargestColumn(groupList);
        while (largestColumn.height > this._height) {
            // Get the list of nodes whose height cannot be reduced
            const fixedNodes = largestColumn.nodes.filter(nodeLink => nodeLink.height <= this._minHeight);
            // Get the total height in the column which cannot shrink (including spacing)
            const fixedHeight = fixedNodes.length * this._minHeight + largestColumn.nodes.length * this._spacing;
            // If the unshrinkable height is greater than the available height, we can't continue
            if (fixedHeight > this._height) {
                throw new Error(`Cannot fit data into chart with minHeight = ${this._minHeight}px (need ${fixedHeight}px; ${this._height}px available)`);
            }
            // Find the amount of height which can potentially be reduced
            const flexibleHeight = largestColumn.height - fixedHeight;
            // Find the amount of height that the above needs to fit into
            const availableHeight = this._height - fixedHeight;
            // Get the multiplier to reduce the nodes in order to fit the available height
            const ratio = availableHeight / flexibleHeight;
            // Adjust the nodes and reapply the minHeight
            for (const group of groupList) {
                for (const nodeLink of group) {
                    if (nodeLink.height > this._minHeight) {
                        nodeLink.height *= ratio;
                    }
                    if (nodeLink.height < this._minHeight) {
                        nodeLink.height = this._minHeight;
                    }
                }
            }
            largestColumn = this.getLargestColumn(groupList);
        }
    }
    /** Set all nodes height to be the same as the naturalHeight. */
    setNodesToNaturalHeight(groupList) {
        for (const group of groupList) {
            for (const nodeLink of group) {
                nodeLink.height = nodeLink.naturalHeight;
            }
        }
    }
    /**
     * Get all nodes grouped in their corresponding columns
     */
    getColumnGroups() {
        // group nodes by columns
        return this._nodeLinks.reduce((collection, nodeLink) => {
            collection[nodeLink.column] = collection[nodeLink.column] || [];
            collection[nodeLink.column].push(nodeLink);
            return collection;
        }, {});
    }
    /**
     * Get the number of columns
     */
    getColumnCount() {
        return this._nodeLinks.reduce((column, nodeLink) => Math.max(nodeLink.column + 1, column), 0);
    }
    /**
     * Position the nodes in their corresponding x and y positions
     */
    getNodePositions() {
        // get all nodes by group
        const groups = this.getColumnGroups();
        // get the amount of padding required between each item
        const padding = this.getColumnPadding();
        for (const nodeLink of this._nodeLinks) {
            // get the x position based on the column
            nodeLink.x = this.getColumnPosition(nodeLink.column) + padding;
            if (nodeLink.column === 0) {
                nodeLink.x = this._padding;
            }
            // get the y position based on the accumulative height of the nodes above it
            nodeLink.y =
                groups[nodeLink.column]
                    .slice(0, groups[nodeLink.column].indexOf(nodeLink))
                    .reduce((top, _node) => top + _node.height, 0) +
                    this._spacing * groups[nodeLink.column].indexOf(nodeLink);
        }
    }
    getColumnPadding() {
        // get the number of columns - we use this a lot so avoid multiple function calls
        const columnCount = this.getColumnCount();
        // get the chart width minus the width of the nodes
        const width = this._width - columnCount * this.getNodeWidth() - this._padding * 2;
        // get the total amount of places requiring padding (the first and last columns only have padding on one side)
        const paddingCount = Math.max(columnCount * 2 - 2, 0);
        // get the actual size of the padding
        return width / paddingCount;
    }
    getLinkPlots() {
        for (const nodeLink of this._nodeLinks) {
            let inputY = nodeLink.y;
            // process each input link
            for (const link of nodeLink.inputs) {
                link.topRight = [nodeLink.x, inputY];
                inputY += (link.value / nodeLink.value) * nodeLink.height;
                link.bottomRight = [nodeLink.x, inputY];
            }
            let outputValue = 0;
            let outputY = nodeLink.y;
            // process each output link
            for (const link of nodeLink.outputs) {
                link.topLeft = [nodeLink.x + nodeLink.width, outputY];
                outputY += (link.value / nodeLink.value) * nodeLink.height;
                link.bottomLeft = [nodeLink.x + nodeLink.width, outputY];
                outputValue += link.value;
            }
            // determine how much falloff there is
            nodeLink.falloff = nodeLink.value - outputValue;
        }
    }
    /** Determine the position at which a column starts */
    getColumnPosition(column) {
        // the position is the acculation of the widths of all previous columns
        return this.columns()
            .splice(0, column)
            .reduce((total, width) => total + width, 0);
    }
    /** Get the pixel width of a node */
    getNodeWidth() {
        const width = (this._width - this._padding * 2) / (this.getColumnCount() * 2 - 1);
        return Math.min(this._maxWidth, Math.max(this._minWidth, width));
    }
    /** Get the column with the greatest height (along with its height) */
    getLargestColumn(groupList) {
        let largestColumn = null;
        let largestColumnHeight = 0;
        for (const group of groupList) {
            const totalHeight = group.reduce((acc, node) => (acc += node.height), 0) + group.length * this._spacing;
            if (totalHeight > largestColumnHeight) {
                largestColumnHeight = totalHeight;
                largestColumn = group;
            }
        }
        return {
            nodes: largestColumn,
            height: largestColumnHeight,
        };
    }
}

class SankeyFocusManager {
    constructor() {
        /** Store the node that can currently be tabbed to */
        this.active$ = new BehaviorSubject(null);
        /** Emit whenever an item should receive focus */
        this.focused$ = new Subject();
        /** Store the nodes  */
        this._nodes = [];
    }
    /** Get the current active item */
    get _active() {
        return this.active$.value;
    }
    ngOnDestroy() {
        this.active$.complete();
        this.focused$.complete();
    }
    /** Update the list of possible nodes */
    setNodes(nodes) {
        this._nodes = nodes;
        // check if there is currently a tabbable node, if not we should make the first node tabbable
        if (!this.hasActiveNode()) {
            this.setActiveItem(this._nodes[0]);
        }
    }
    /** Set the current active item */
    setActiveItem(node) {
        this.active$.next(node);
    }
    /** Handle keyboard input from nodes */
    onKeydown(event) {
        switch (event.which) {
            case UP_ARROW:
                this.shiftFocusVertically(-1);
                event.preventDefault();
                break;
            case DOWN_ARROW:
                this.shiftFocusVertically(1);
                event.preventDefault();
                break;
            case LEFT_ARROW:
                this.shiftFocusHorizontally(-1);
                event.preventDefault();
                break;
            case RIGHT_ARROW:
                this.shiftFocusHorizontally(1);
                event.preventDefault();
                break;
        }
    }
    setFocusedItem(item) {
        this.setActiveItem(item);
        this.focused$.next(item);
    }
    shiftFocusVertically(delta) {
        const nodes = this.getNodesInColumn(this._active.column);
        // get the node below or above the active node
        const target = nodes[nodes.findIndex(node => node.node.id === this._active.node.id) + delta];
        if (target) {
            this.setFocusedItem(target);
        }
    }
    /** Shift the focus to a node in a sibling column */
    shiftFocusHorizontally(delta) {
        // get nodes in the sibling column in the desired direction
        const nodes = this.getNodesInColumn(this._active.column + delta);
        // if there are no nodes then do nothing as we cannot reduce an empty array
        if (nodes.length === 0) {
            return;
        }
        // get the node with the most similar y position
        const target = nodes.reduce((closest, node) => {
            const closestDiff = Math.max(closest.y, this._active.y) - Math.min(closest.y, this._active.y);
            const currentDiff = Math.max(node.y, this._active.y) - Math.min(node.y, this._active.y);
            return closestDiff < currentDiff ? closest : node;
        });
        if (target) {
            this.setFocusedItem(target);
        }
    }
    /** Get a list of nodes that are in a given column */
    getNodesInColumn(column) {
        return this.getNodesInOrder(this._nodes.filter(node => node.column === column));
    }
    /** Sort the nodes based on the Y position */
    getNodesInOrder(nodes) {
        return [...nodes].sort((nodeOne, nodeTwo) => nodeOne.y - nodeTwo.y);
    }
    /** Determine whether or not there is a not that is tabbable */
    hasActiveNode() {
        return (!!this.active$.value &&
            !!this._nodes.find(node => node.node.id === this.active$.value.node.id));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyFocusManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyFocusManager }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyFocusManager, decorators: [{
            type: Injectable
        }] });

class SankeyNodeDirective {
    constructor() {
        this._focusManager = inject(SankeyFocusManager);
        this._elementRef = inject(ElementRef);
        /** Specify the tab index of the current item */
        this.tabIndex = -1;
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        // Update the tabindex based on the current active item
        this._focusManager.active$
            .pipe(map(item => item && item.node.id === this.node.node.id), takeUntil(this._onDestroy))
            .subscribe(isActive => (this.tabIndex = isActive ? 0 : -1));
        // If this element should be focused perform the focus
        this._focusManager.focused$
            .pipe(filter(node => node.node.id === this.node.node.id), takeUntil(this._onDestroy))
            .subscribe(() => this._elementRef.nativeElement.focus());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    onClick() {
        this._focusManager.setActiveItem(this.node);
    }
    onKeydown(event) {
        this._focusManager.onKeydown(event);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyNodeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SankeyNodeDirective, isStandalone: false, selector: "[uxSankeyNode]", inputs: { node: ["uxSankeyNode", "node"] }, host: { listeners: { "click": "onClick()", "keydown": "onKeydown($event)" }, properties: { "tabIndex": "this.tabIndex" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyNodeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxSankeyNode]',
                    standalone: false,
                }]
        }], propDecorators: { node: [{
                type: Input,
                args: ['uxSankeyNode']
            }], tabIndex: [{
                type: HostBinding
            }], onClick: [{
                type: HostListener,
                args: ['click']
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class SankeyChartComponent {
    constructor() {
        this._focusManager = inject(SankeyFocusManager);
        this._changeDetector = inject(ChangeDetectorRef);
        this._colorService = inject(ColorService);
        /** Define the nodes to display */
        this.nodes = [];
        /** Define the links to display */
        this.links = [];
        /** Define the headers of each column */
        this.columns = [];
        /** Define the minimum width of a node */
        this.minWidth = 0;
        /** Define the maximum width of a node */
        this.maxWidth = Infinity;
        /** The minimum height of a node. */
        this.minHeight = 0;
        /** Define the function to get the contents of a link tooltip */
        this.linkTooltip = this.getLinkTooltip;
        /** Define the function to get the contents of a falloff tooltip */
        this.falloffTooltip = this.getFalloffTooltip;
        /** Define the nodes that should be rendered */
        this._nodes = [];
        /** Define the columns to display */
        this._columns = [];
        /** Determine if the tooltip should be visible or not */
        this._isTooltipOpen = false;
        /** Define the position of the tooltip */
        this._tooltipPosition = { x: 0, y: 0 };
        /** Determine if the component is initialised */
        this._isInitialised = false;
        /** Store the instance of the sankey layout */
        this._sankey = new SankeyChart();
    }
    ngAfterViewInit() {
        // verify we have a node template defined before proceeding
        if (!this.nodeTemplate) {
            throw new Error('Sankey Chart - Node Template has not been defined.');
        }
        // set the initial chart size
        this._width = this.nodeContainer.nativeElement.offsetWidth;
        this._height = this.nodeContainer.nativeElement.offsetHeight;
        // perform the initial render
        this._render();
        // mark the component as initialised
        this._isInitialised = true;
    }
    /**
     * Detect any changes from Inputs. We can skip
     * the first function call as this happens before
     * the initial render so it has no effect.
     */
    ngOnChanges() {
        if (this._isInitialised) {
            this._render();
        }
    }
    /** Re-render the chart */
    _render() {
        this._nodes = this._sankey
            .nodes(this.nodes)
            .links(this.links)
            .spacing(14)
            .size(this.minWidth, this.maxWidth, this.minHeight)
            .width(this._width || this.nodeContainer.nativeElement.offsetWidth)
            .height(this._height || this.nodeContainer.nativeElement.offsetHeight)
            .layout();
        // ensure the focus manager has the latest node data
        this._focusManager.setNodes(this._nodes);
        this._columns = this.getColumns();
        this._changeDetector.detectChanges();
    }
    /** Update the layout whenever the dimensions change changes */
    _onResize(dimensions) {
        this._width = dimensions.width;
        this._height = dimensions.height;
        this._render();
    }
    /**
     * Column count should be based on the data, not the titles
     * as they may not specify titles but the nodes will still be
     * rendered.
     */
    _getColumnCount() {
        return this._nodes.reduce((column, node) => Math.max(column, node.column), 0);
    }
    /**
     * Get the SVG path that defines the shape of the link
     */
    _getPath(link) {
        return this._sankey.link(link);
    }
    /**
     * Set the active state of a node and the inputs and outputs
     * associated with this node.
     */
    _setNodeActive(nodeLink, active) {
        // set the node active state
        nodeLink.active = active;
        // set the active state of each link
        nodeLink.inputs.forEach(link => (link.active = active));
        nodeLink.outputs.forEach(link => (link.active = active));
        // set the active state of all input and output nodes
        nodeLink.inputs
            .map(link => this._sankey.getNodeLink(link.source))
            .forEach(_node => (_node.active = active));
        nodeLink.inputs
            .map(link => this._sankey.getNodeLink(link.target))
            .forEach(_node => (_node.active = active));
        nodeLink.outputs
            .map(link => this._sankey.getNodeLink(link.source))
            .forEach(_node => (_node.active = active));
        nodeLink.outputs
            .map(link => this._sankey.getNodeLink(link.target))
            .forEach(_node => (_node.active = active));
        // ensure we update the view to show highlights
        this._changeDetector.detectChanges();
    }
    /**
     * Set the focused state of a node and the inputs and outputs
     * associated with this node.
     */
    _setNodeFocus(nodeLink, focused, element) {
        // set the node focus state
        nodeLink.focus = focused;
        // set the active state of each link
        nodeLink.inputs.forEach(link => (link.focus = focused));
        nodeLink.outputs.forEach(link => (link.focus = focused));
        // set the active state of all input and output nodes
        nodeLink.inputs
            .map(link => this._sankey.getNodeLink(link.source))
            .forEach(_node => (_node.focus = focused));
        nodeLink.inputs
            .map(link => this._sankey.getNodeLink(link.target))
            .forEach(_node => (_node.focus = focused));
        nodeLink.outputs
            .map(link => this._sankey.getNodeLink(link.source))
            .forEach(_node => (_node.focus = focused));
        nodeLink.outputs
            .map(link => this._sankey.getNodeLink(link.target))
            .forEach(_node => (_node.focus = focused));
        // we need to add the focus indicator here programmatically. The default quantum-ux-aspects focus indicator
        // styling uses `!important` so our inline style needs to also be `!important` to override this, and unfortunately
        // there is a known issue with `NgStyle` and `[style.xyz]` bindings preventing them from adding the `!important`
        // modifier so we must do it manually (not using `Renderer2`).
        if (this.color) {
            element.style.setProperty('box-shadow', this._getFocusIndicator(nodeLink), 'important');
        }
        // ensure we update the view to show highlights
        this._changeDetector.detectChanges();
    }
    /**
     * Set the active state of a link and the source and target
     * nodes associated with the link
     */
    _setLinkActive(link, active) {
        link.active = active;
        if (link.source !== undefined) {
            this._sankey.getNodeLink(link.source).active = active;
        }
        if (link.target !== undefined) {
            this._sankey.getNodeLink(link.target).active = active;
        }
        // update the tooltip visibility
        this._isTooltipOpen = active;
        // update the tooltip content
        this._tooltipContent = active ? this.linkTooltip(link) : '';
        // ensure we update the view to show highlights
        this._changeDetector.detectChanges();
    }
    /**
     * This is required because we want to toggle a class based on the `active`
     * property on a link, however toggling classes using `NgClass` or the class
     * binding syntax `[class.xyz]` does not work in IE when applied to an SVG
     * element. (https://github.com/angular/angular/issues/6327)
     *
     * The alternatice is to bind directly to the `class` attribute and return a
     * string that will toggle the class based on the `active` property.
     */
    _getLinkClass(link) {
        return `ux-sankey-chart-link ${link.active || link.focus ? 'ux-sankey-chart-link-active' : ''}`;
    }
    /**
     * Get the SVG path that defines the shape of the falloff indicator
     */
    _getFalloffPath(node) {
        return this._sankey.getFalloffPath(node);
    }
    /**
     * Falloff represents the amount of data that does not get passed on,
     * for example, if a node gets 1,000,000 items from inputs and only outputs
     * 500,000 then there is falloff of 500,000. However, items in the last column
     * never pass on any information, so tecnhically 100% of their input is falloff
     * so we shouldn't show it in the last column.
     */
    _showFalloff(nodeLink) {
        return nodeLink.column < this._columns.length - 1;
    }
    /** Update the visibility and content of the tooltip on falloff hover */
    _setFalloffTooltip(nodeLink, isVisible) {
        this._isTooltipOpen = isVisible;
        this._tooltipContent = isVisible ? this.falloffTooltip(nodeLink.falloff) : '';
        this._changeDetector.detectChanges();
    }
    /**
     * Update the position of the tooltip
     */
    _setTooltipPosition(event) {
        const { left, top } = this.nodeContainer.nativeElement.getBoundingClientRect();
        const x = event.pageX - left - (window.scrollX || document.documentElement.scrollLeft);
        const y = event.pageY - top - (window.scrollY || document.documentElement.scrollTop);
        this._tooltipPosition = { x, y };
        this._changeDetector.detectChanges();
    }
    /**
     * Correctly track the node changes in `*ngFor` based on
     * the unique node ids to prevent unnecessary re-rendering
     */
    _trackNodeBy(_index, nodeLink) {
        return nodeLink.node.id;
    }
    /**
     * Correctly track the link changes in `*ngFor` based on
     * the source and target to prevent unnecessary re-rendering
     */
    _trackLinkBy(_index, link) {
        return `${link.source}-${link.target}`;
    }
    /**
     * Get the color of node based on whether or not
     * the `color` input has been provided.
     */
    _getColor(item) {
        // if we are not node hovering or focusing or no custom color is defined then return nothing
        if ((!item.active && !item.focus) || !this.color) {
            return;
        }
        // return an rgba value if it is a `ThemeColor` to support transparency
        return this.color instanceof ThemeColor
            ? this.color.toRgba()
            : this._colorService.resolve(this.color);
    }
    /**
     * We want the focus indicator color to match the active color,
     * which if programmatically defined need to be overriden
     */
    _getFocusIndicator(nodeLink) {
        // if the node is not focused or there is no custom color
        // then return null in which case CSS indicator will show
        if (!nodeLink.focus || !this.color) {
            return '';
        }
        // otherwise return the shadow based on the color provided.
        const color = this.color instanceof ThemeColor
            ? this.color
            : ThemeColor.parse(this._colorService.resolve(this.color));
        // generate a box shadow based on the specified color
        return `0 0 0 1px #fff, 0 0 0 3px ${color.setAlpha(0.5).toRgba()}`;
    }
    /**
     * Get columns mapped with their title if they have any
     */
    getColumns() {
        return this._sankey.columns().map((width, index) => ({
            width,
            title: this.columns[index] || '',
            position: this.getColumnPosition(index),
        }));
    }
    /**
     * Get the start position of a column which can be determined
     * by finding a node that is in that column and using its
     * x position as all nodes start at the same position within a column.
     */
    getColumnPosition(column) {
        // find a node in this column and take its x position
        const node = this._nodes.find(_node => _node.column === column);
        return node ? node.x : 0;
    }
    /**
     * Get the default content of a link tooltip
     */
    getLinkTooltip(link) {
        return link.value.toLocaleString('en') + ' items';
    }
    /**
     * Get the default content of a falloff tooltip
     */
    getFalloffTooltip(falloff) {
        return falloff.toLocaleString('en') + ' items';
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SankeyChartComponent, isStandalone: false, selector: "ux-sankey-chart", inputs: { nodes: "nodes", links: "links", columns: "columns", minWidth: "minWidth", maxWidth: "maxWidth", minHeight: "minHeight", linkTooltip: "linkTooltip", falloffTooltip: "falloffTooltip", color: "color" }, queries: [{ propertyName: "nodeTemplate", first: true, predicate: ["sankeyNodeTemplate"], descendants: true }], viewQueries: [{ propertyName: "linkContainer", first: true, predicate: ["linkContainer"], descendants: true, static: true }, { propertyName: "nodeContainer", first: true, predicate: ["nodeContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "@if (columns && columns.length > 0) {\n  <div class=\"ux-sankey-chart-columns\">\n    @for (column of _columns; track column.title) {\n      <div class=\"ux-sankey-chart-column\" [style.width.px]=\"column.width\">\n        <p class=\"ux-sankey-chart-column-title\" [style.left.px]=\"column.position\">\n          {{ column.title }}\n        </p>\n      </div>\n    }\n  </div>\n}\n\n<svg\n  #linkContainer\n  [attr.width]=\"_width\"\n  [attr.height]=\"_height\"\n  class=\"ux-sankey-chart-links\"\n  [style.top.px]=\"!columns || columns.length === 0 ? 8 : null\"\n>\n  <defs>\n    <linearGradient id=\"falloff-gradient\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n      <stop class=\"ux-sankey-chart-falloff-gradient-start\" offset=\"25%\"></stop>\n      <stop class=\"ux-sankey-chart-falloff-gradient-end\" offset=\"100%\"></stop>\n    </linearGradient>\n  </defs>\n  <g>\n    @for (node of _nodes; track _trackNodeBy($index, node)) {\n      <ng-container>\n        @for (link of node.outputs; track _trackLinkBy($index, link)) {\n          <path\n            [attr.class]=\"_getLinkClass(link)\"\n            [attr.d]=\"_getPath(link)\"\n            [style.fill]=\"_getColor(link)\"\n            (mouseenter)=\"_setLinkActive(link, true)\"\n            (mouseleave)=\"_setLinkActive(link, false)\"\n            (mousemove)=\"_setTooltipPosition($event)\"\n          ></path>\n        }\n        @if (node.falloff && _showFalloff(node)) {\n          <path\n            class=\"ux-sankey-chart-falloff-indicator\"\n            [attr.d]=\"_getFalloffPath(node)\"\n            (mouseenter)=\"_setFalloffTooltip(node, true)\"\n            (mouseleave)=\"_setFalloffTooltip(node, false)\"\n            (mousemove)=\"_setTooltipPosition($event)\"\n          ></path>\n        }\n      </ng-container>\n    }\n  </g>\n</svg>\n\n<div\n  #nodeContainer\n  class=\"ux-sankey-chart-nodes\"\n  (uxResize)=\"_onResize($event)\"\n  [style.top.px]=\"!columns || columns.length === 0 ? 8 : null\"\n>\n  @for (node of _nodes; track _trackNodeBy($index, node)) {\n    <div\n      #nodeElement\n      [uxSankeyNode]=\"node\"\n      uxFocusIndicator\n      class=\"ux-sankey-chart-node\"\n      [class.ux-sankey-chart-node-active]=\"node.active || node.focus\"\n      [style.left.px]=\"node.x\"\n      [style.top.px]=\"node.y\"\n      [style.width.px]=\"node.width\"\n      [style.height.px]=\"node.height\"\n      [style.background-color]=\"_getColor(node)\"\n      (mouseenter)=\"_setNodeActive(node, true)\"\n      (mouseleave)=\"_setNodeActive(node, false)\"\n      (indicator)=\"_setNodeFocus(node, $event, nodeElement)\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"nodeTemplate\"\n        [ngTemplateOutletContext]=\"{ node: node.node, active: node.active, focus: node.focus }\"\n      >\n      </ng-container>\n    </div>\n  }\n\n  @if (_isTooltipOpen) {\n    <ux-tooltip\n      class=\"ux-sankey-tooltip\"\n      placement=\"top\"\n      [content]=\"_tooltipContent\"\n      alignment=\"center\"\n      [style.left.px]=\"_tooltipPosition.x\"\n      [style.top.px]=\"_tooltipPosition.y\"\n      [@tooltipAnimation]\n    >\n    </ux-tooltip>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ResizeDirective, selector: "[uxResize]", inputs: ["throttle"], outputs: ["uxResize"] }, { kind: "component", type: TooltipComponent, selector: "ux-tooltip", inputs: ["content", "context", "placement", "alignment"] }, { kind: "directive", type: SankeyNodeDirective, selector: "[uxSankeyNode]", inputs: ["uxSankeyNode"] }], viewProviders: [SankeyFocusManager], animations: [
            trigger('tooltipAnimation', [
                transition(':enter', [style({ opacity: 0 }), animate(160, style({ opacity: 1 }))]),
                transition(':leave', [animate(160, style({ opacity: 0 }))]),
            ]),
        ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-sankey-chart', changeDetection: ChangeDetectionStrategy.OnPush, viewProviders: [SankeyFocusManager], animations: [
                        trigger('tooltipAnimation', [
                            transition(':enter', [style({ opacity: 0 }), animate(160, style({ opacity: 1 }))]),
                            transition(':leave', [animate(160, style({ opacity: 0 }))]),
                        ]),
                    ], standalone: false, template: "@if (columns && columns.length > 0) {\n  <div class=\"ux-sankey-chart-columns\">\n    @for (column of _columns; track column.title) {\n      <div class=\"ux-sankey-chart-column\" [style.width.px]=\"column.width\">\n        <p class=\"ux-sankey-chart-column-title\" [style.left.px]=\"column.position\">\n          {{ column.title }}\n        </p>\n      </div>\n    }\n  </div>\n}\n\n<svg\n  #linkContainer\n  [attr.width]=\"_width\"\n  [attr.height]=\"_height\"\n  class=\"ux-sankey-chart-links\"\n  [style.top.px]=\"!columns || columns.length === 0 ? 8 : null\"\n>\n  <defs>\n    <linearGradient id=\"falloff-gradient\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n      <stop class=\"ux-sankey-chart-falloff-gradient-start\" offset=\"25%\"></stop>\n      <stop class=\"ux-sankey-chart-falloff-gradient-end\" offset=\"100%\"></stop>\n    </linearGradient>\n  </defs>\n  <g>\n    @for (node of _nodes; track _trackNodeBy($index, node)) {\n      <ng-container>\n        @for (link of node.outputs; track _trackLinkBy($index, link)) {\n          <path\n            [attr.class]=\"_getLinkClass(link)\"\n            [attr.d]=\"_getPath(link)\"\n            [style.fill]=\"_getColor(link)\"\n            (mouseenter)=\"_setLinkActive(link, true)\"\n            (mouseleave)=\"_setLinkActive(link, false)\"\n            (mousemove)=\"_setTooltipPosition($event)\"\n          ></path>\n        }\n        @if (node.falloff && _showFalloff(node)) {\n          <path\n            class=\"ux-sankey-chart-falloff-indicator\"\n            [attr.d]=\"_getFalloffPath(node)\"\n            (mouseenter)=\"_setFalloffTooltip(node, true)\"\n            (mouseleave)=\"_setFalloffTooltip(node, false)\"\n            (mousemove)=\"_setTooltipPosition($event)\"\n          ></path>\n        }\n      </ng-container>\n    }\n  </g>\n</svg>\n\n<div\n  #nodeContainer\n  class=\"ux-sankey-chart-nodes\"\n  (uxResize)=\"_onResize($event)\"\n  [style.top.px]=\"!columns || columns.length === 0 ? 8 : null\"\n>\n  @for (node of _nodes; track _trackNodeBy($index, node)) {\n    <div\n      #nodeElement\n      [uxSankeyNode]=\"node\"\n      uxFocusIndicator\n      class=\"ux-sankey-chart-node\"\n      [class.ux-sankey-chart-node-active]=\"node.active || node.focus\"\n      [style.left.px]=\"node.x\"\n      [style.top.px]=\"node.y\"\n      [style.width.px]=\"node.width\"\n      [style.height.px]=\"node.height\"\n      [style.background-color]=\"_getColor(node)\"\n      (mouseenter)=\"_setNodeActive(node, true)\"\n      (mouseleave)=\"_setNodeActive(node, false)\"\n      (indicator)=\"_setNodeFocus(node, $event, nodeElement)\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"nodeTemplate\"\n        [ngTemplateOutletContext]=\"{ node: node.node, active: node.active, focus: node.focus }\"\n      >\n      </ng-container>\n    </div>\n  }\n\n  @if (_isTooltipOpen) {\n    <ux-tooltip\n      class=\"ux-sankey-tooltip\"\n      placement=\"top\"\n      [content]=\"_tooltipContent\"\n      alignment=\"center\"\n      [style.left.px]=\"_tooltipPosition.x\"\n      [style.top.px]=\"_tooltipPosition.y\"\n      [@tooltipAnimation]\n    >\n    </ux-tooltip>\n  }\n</div>\n" }]
        }], propDecorators: { nodes: [{
                type: Input
            }], links: [{
                type: Input
            }], columns: [{
                type: Input
            }], minWidth: [{
                type: Input
            }], maxWidth: [{
                type: Input
            }], minHeight: [{
                type: Input
            }], linkTooltip: [{
                type: Input
            }], falloffTooltip: [{
                type: Input
            }], color: [{
                type: Input
            }], nodeTemplate: [{
                type: ContentChild,
                args: ['sankeyNodeTemplate', { static: false }]
            }], linkContainer: [{
                type: ViewChild,
                args: ['linkContainer', { static: true }]
            }], nodeContainer: [{
                type: ViewChild,
                args: ['nodeContainer', { static: true }]
            }] } });

class SankeyChartModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartModule, declarations: [SankeyChartComponent, SankeyNodeDirective], imports: [AccessibilityModule, CommonModule, ResizeModule, TooltipModule, ColorServiceModule], exports: [SankeyChartComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartModule, imports: [AccessibilityModule, CommonModule, ResizeModule, TooltipModule, ColorServiceModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SankeyChartModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [SankeyChartComponent, SankeyNodeDirective],
                    imports: [AccessibilityModule, CommonModule, ResizeModule, TooltipModule, ColorServiceModule],
                    exports: [SankeyChartComponent],
                }]
        }] });

const UNSET_FOCUS = { groupId: null, index: -1 };
class SearchBuilderFocusService {
    constructor() {
        this.focus$ = new BehaviorSubject(UNSET_FOCUS);
    }
    /**
     * Set focus on a search builder component.
     * @param groupId The `id` of the group containing the component.
     * @param index The (zero-based) index of the component.
     */
    setFocus(groupId, index) {
        this.focus$.next({ groupId, index });
    }
    /**
     * Removes focus from all components. If focus is not on a search builder component, this does nothing.
     */
    clearFocus() {
        this.focus$.next(UNSET_FOCUS);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderFocusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderFocusService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderFocusService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class SearchBuilderService {
    constructor() {
        this.query = {};
        this.queryChange = new Subject();
        this.validationChange = new BehaviorSubject(true);
        this._componentId = 0;
        this._components = [];
        this._validation = {};
    }
    /**
     * Add a component to the internal list of components
     */
    registerComponent(component) {
        // ensure there are no components with a matching name
        if (this._components.find(cmp => cmp.name === component.name)) {
            throw new Error(`Search builder components must have a unique name. The name ${component.name} has already been used.`);
        }
        // if unique then add the component to the list
        this._components.push(component);
    }
    /**
     * Bulk registration of components
     * (Just a helper method)
     */
    registerComponents(components) {
        components.forEach(component => this.registerComponent(component));
    }
    /**
     * Get a registered component class
     */
    getComponent(name) {
        // find the component
        const component = this._components.find(cmp => cmp.name === name);
        // if there is no match throw an exception
        if (!component) {
            throw new Error(`No search build component with the name ${name} exists`);
        }
        // ensure config is defined - at least to an empty object
        component.config = component.config || {};
        return component;
    }
    /**
     * Update the internal search query state
     * note that the query will be immutable
     */
    setQuery(query) {
        this.query = Object.assign({}, query);
    }
    /**
     * Return the current query state
     */
    getQuery() {
        return this.query;
    }
    /**
     * Trigger the observable to indicate the query has been updated
     */
    queryHasChanged() {
        this.queryChange.next(this.query);
    }
    /**
     * Store the validation state of the query
     */
    setValid(id, valid) {
        // store the state for this specific component
        this._validation[id] = valid;
        // evaluate the entire validation state
        this.validationChange.next(!Object.keys(this._validation).some(key => !this._validation[key]));
    }
    /**
     * Generate a unique id for each component
     */
    generateComponentId() {
        return this._componentId++;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderService, decorators: [{
            type: Injectable
        }] });

class SearchBuilderGroupService {
    constructor() {
        this._searchBuilderService = inject(SearchBuilderService);
        this._searchBuilderFocusService = inject(SearchBuilderFocusService);
    }
    /**
     * Initialise the group by defining an id
     */
    init(id) {
        // store the name of the group
        this._id = id;
        // create the entry in the query object if it doesn't exist
        if (!this._searchBuilderService.query[this._id]) {
            // create the section
            this._searchBuilderService.query[this._id] = [];
            // emit the changes after the initial setup
            setTimeout(() => this._searchBuilderService.queryHasChanged());
        }
    }
    /**
     * Remove a field from the search builder query and return focus to the previous field.
     */
    removeAtIndex(index) {
        // get the query for this group
        const query = this.getQuery();
        // remove the field from the array
        query.splice(index, 1);
        // Focus the previous item if available
        this._searchBuilderFocusService.setFocus(this._id, index <= 0 ? 0 : index - 1);
    }
    /**
     * Get the query for this specific search group
     */
    getQuery() {
        return this._searchBuilderService.query[this._id]
            ? this._searchBuilderService.query[this._id]
            : [];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderGroupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderGroupService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderGroupService, decorators: [{
            type: Injectable
        }] });

class SearchBuilderOutletDirective {
    constructor() {
        this._viewContainerRef = inject(ViewContainerRef);
        this._componentFactoryResolver = inject(ComponentFactoryResolver);
        this._searchBuilderService = inject(SearchBuilderService);
        this._searchBuilderFocusService = inject(SearchBuilderFocusService);
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        // get the class from the type
        const componentDefinition = this._searchBuilderService.getComponent(this.outlet);
        // create the component factory
        const componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentDefinition.component);
        // create the component instance
        this._componentRef = this._viewContainerRef.createComponent(componentFactory);
        // combine the predefined config with any dynmaic config
        const config = Object.assign({}, componentDefinition.config, this.context.config || {});
        // set the context and config property on the component instance
        this._componentRef.instance.context = this.context;
        this._componentRef.instance.config = config;
        this._searchBuilderFocusService.focus$
            .pipe(distinctUntilChanged(), delay(0), takeUntil(this._onDestroy))
            .subscribe(focus => {
            this._componentRef.instance.focus =
                focus.groupId === this.groupId && focus.index === this.index;
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderOutletDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SearchBuilderOutletDirective, isStandalone: false, selector: "[uxSearchBuilderOutlet]", inputs: { outlet: ["uxSearchBuilderOutlet", "outlet"], context: ["uxSearchBuilderOutletContext", "context"], groupId: ["uxSearchBuilderOutletGroupId", "groupId"], index: ["uxSearchBuilderOutletIndex", "index"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderOutletDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxSearchBuilderOutlet]',
                    standalone: false,
                }]
        }], propDecorators: { outlet: [{
                type: Input,
                args: ['uxSearchBuilderOutlet']
            }], context: [{
                type: Input,
                args: ['uxSearchBuilderOutletContext']
            }], groupId: [{
                type: Input,
                args: ['uxSearchBuilderOutletGroupId']
            }], index: [{
                type: Input,
                args: ['uxSearchBuilderOutletIndex']
            }] } });

class SearchBuilderGroupComponent {
    constructor() {
        this.searchBuilderGroupService = inject(SearchBuilderGroupService);
        this._searchBuilderFocusService = inject(SearchBuilderFocusService);
        this.operator = 'and';
        this.addText = 'Add a field';
        this.showPlaceholder = false;
        this.removeFieldButtonAriaLabel = 'Remove field';
        this.add = new EventEmitter();
        this.remove = new EventEmitter();
        this.focusIndex = -1;
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        // ensure we have a name otherwise throw an error
        if (!this.id) {
            throw new Error('Search builder group must have an id attribute.');
        }
        // otherwise register the group
        this.searchBuilderGroupService.init(this.id);
        // Track focus for child components
        this._searchBuilderFocusService.focus$.pipe(takeUntil(this._onDestroy)).subscribe(focus => {
            this.focusIndex = focus.groupId === this.id ? focus.index : -1;
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    addField(event) {
        this.add.emit(event);
    }
    removeFieldAtIndex(index, field) {
        this.searchBuilderGroupService.removeAtIndex(index);
        this.remove.emit(field);
    }
    setFocus(index) {
        this._searchBuilderFocusService.setFocus(this.id, index);
    }
    clearFocus() {
        this._searchBuilderFocusService.clearFocus();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SearchBuilderGroupComponent, isStandalone: false, selector: "ux-search-builder-group", inputs: { id: "id", header: "header", operator: "operator", addText: "addText", placeholder: "placeholder", showPlaceholder: "showPlaceholder", removeFieldButtonAriaLabel: "removeFieldButtonAriaLabel" }, outputs: { add: "add", remove: "remove" }, providers: [SearchBuilderGroupService], ngImport: i0, template: "<h4 class=\"search-group-title\">{{ header }}</h4>\n\n<div class=\"search-group-content\">\n  <div\n    class=\"search-group-operator search-group-operator-{{ operator }}\"\n    [class.hidden-operator]=\"searchBuilderGroupService.getQuery().length < 2\"\n  >\n    {{ operator }}\n  </div>\n\n  <div class=\"search-group-items\">\n    @for (field of searchBuilderGroupService.getQuery(); track field; let i = $index) {\n      <div\n        class=\"search-group-item-container\"\n        [class.search-group-item-focus]=\"focusIndex === i\"\n        (uxFocusWithin)=\"setFocus(i)\"\n        (uxBlurWithin)=\"clearFocus()\"\n      >\n        <div class=\"search-group-item\">\n          <ng-container\n            *uxSearchBuilderOutlet=\"field.type; context: field; groupId: id; index: i\"\n          ></ng-container>\n        </div>\n        <button\n          type=\"button\"\n          uxFocusIndicator\n          [attr.aria-label]=\"removeFieldButtonAriaLabel\"\n          class=\"search-group-item-remove\"\n          (click)=\"removeFieldAtIndex(i, field)\"\n        >\n          <ux-icon name=\"close\"></ux-icon>\n        </button>\n      </div>\n    }\n\n    <!-- Placeholder Item -->\n    @if (showPlaceholder) {\n      <!-- The Default Placeholder -->\n      @if (!placeholder) {\n        <div class=\"search-group-item-container placeholder-item\">\n          <div class=\"search-group-item\">\n            <label class=\"form-label\">New field</label>\n            <div class=\"form-control\"></div>\n          </div>\n        </div>\n      }\n      <!-- Allow a custom placeholder -->\n      <ng-container *ngTemplateOutlet=\"placeholder\"></ng-container>\n    }\n  </div>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"search-builder-group-add-field\"\n    (click)=\"addField($event)\"\n  >\n    <ux-icon class=\"search-builder-group-add-field-icon\" name=\"add\"></ux-icon>\n    <span class=\"search-builder-group-add-field-label\">{{ addText }}</span>\n  </button>\n</div>\n\n<hr class=\"search-builder-group-divider\" />\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: FocusWithinDirective, selector: "[uxFocusWithin],[uxBlurWithin]", outputs: ["uxFocusWithin", "uxBlurWithin"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: SearchBuilderOutletDirective, selector: "[uxSearchBuilderOutlet]", inputs: ["uxSearchBuilderOutlet", "uxSearchBuilderOutletContext", "uxSearchBuilderOutletGroupId", "uxSearchBuilderOutletIndex"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderGroupComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-builder-group', providers: [SearchBuilderGroupService], standalone: false, template: "<h4 class=\"search-group-title\">{{ header }}</h4>\n\n<div class=\"search-group-content\">\n  <div\n    class=\"search-group-operator search-group-operator-{{ operator }}\"\n    [class.hidden-operator]=\"searchBuilderGroupService.getQuery().length < 2\"\n  >\n    {{ operator }}\n  </div>\n\n  <div class=\"search-group-items\">\n    @for (field of searchBuilderGroupService.getQuery(); track field; let i = $index) {\n      <div\n        class=\"search-group-item-container\"\n        [class.search-group-item-focus]=\"focusIndex === i\"\n        (uxFocusWithin)=\"setFocus(i)\"\n        (uxBlurWithin)=\"clearFocus()\"\n      >\n        <div class=\"search-group-item\">\n          <ng-container\n            *uxSearchBuilderOutlet=\"field.type; context: field; groupId: id; index: i\"\n          ></ng-container>\n        </div>\n        <button\n          type=\"button\"\n          uxFocusIndicator\n          [attr.aria-label]=\"removeFieldButtonAriaLabel\"\n          class=\"search-group-item-remove\"\n          (click)=\"removeFieldAtIndex(i, field)\"\n        >\n          <ux-icon name=\"close\"></ux-icon>\n        </button>\n      </div>\n    }\n\n    <!-- Placeholder Item -->\n    @if (showPlaceholder) {\n      <!-- The Default Placeholder -->\n      @if (!placeholder) {\n        <div class=\"search-group-item-container placeholder-item\">\n          <div class=\"search-group-item\">\n            <label class=\"form-label\">New field</label>\n            <div class=\"form-control\"></div>\n          </div>\n        </div>\n      }\n      <!-- Allow a custom placeholder -->\n      <ng-container *ngTemplateOutlet=\"placeholder\"></ng-container>\n    }\n  </div>\n\n  <button\n    type=\"button\"\n    uxFocusIndicator\n    class=\"search-builder-group-add-field\"\n    (click)=\"addField($event)\"\n  >\n    <ux-icon class=\"search-builder-group-add-field-icon\" name=\"add\"></ux-icon>\n    <span class=\"search-builder-group-add-field-label\">{{ addText }}</span>\n  </button>\n</div>\n\n<hr class=\"search-builder-group-divider\" />\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], header: [{
                type: Input
            }], operator: [{
                type: Input
            }], addText: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], showPlaceholder: [{
                type: Input
            }], removeFieldButtonAriaLabel: [{
                type: Input
            }], add: [{
                type: Output
            }], remove: [{
                type: Output
            }] } });

class SearchBuilderComponent {
    set components(components) {
        this._searchBuilderService.registerComponents(components);
    }
    set query(value) {
        this._searchBuilderService.setQuery(value);
    }
    get query() {
        return this._searchBuilderService.getQuery();
    }
    /**
     * Register the default search builder components
     */
    constructor() {
        this._searchBuilderService = inject(SearchBuilderService);
        this.queryChange = new EventEmitter();
        this.valid = new EventEmitter(true);
        // watch for any query changes
        this._querySubscription = this._searchBuilderService.queryChange.subscribe(query => this.queryChange.emit(query));
        // watch for any changes to the validation
        this._validSubscription = this._searchBuilderService.validationChange
            .pipe(distinctUntilChanged())
            .subscribe(valid => this.valid.emit(valid));
    }
    /**
     * Remove any subscriptions and cleanup
     */
    ngOnDestroy() {
        this._querySubscription.unsubscribe();
        this._validSubscription.unsubscribe();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: SearchBuilderComponent, isStandalone: false, selector: "ux-search-builder", inputs: { components: "components", query: "query" }, outputs: { queryChange: "queryChange", valid: "valid" }, providers: [SearchBuilderService], ngImport: i0, template: "<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-builder', providers: [SearchBuilderService], standalone: false, template: "<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [], propDecorators: { components: [{
                type: Input
            }], query: [{
                type: Input
            }], queryChange: [{
                type: Output
            }], valid: [{
                type: Output
            }] } });

class TagInputEvent {
    constructor(tag) {
        this.tag = tag;
        this._defaultPrevented = false;
    }
    preventDefault() {
        this._defaultPrevented = true;
    }
    defaultPrevented() {
        return this._defaultPrevented;
    }
}

let uniqueId$3 = 0;
const TAGINPUT_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => TagInputComponent),
    multi: true,
};
const TAGINPUT_VALIDATOR = {
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => TagInputComponent),
    multi: true,
};
class TagInputComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        this._element = inject(ElementRef);
        this._typeaheadKeyService = inject(TypeaheadKeyService);
        this._document = inject(DOCUMENT);
        /** Specify a unique Id for the component */
        this.id = `ux-tag-input-${++uniqueId$3}`;
        /** The editable text appearing in the tag input. */
        this.input = '';
        /** Controls whether pasting text into the text input area automatically converts that text into one or more tags. */
        this.addOnPaste = true;
        /** Controls the disabled state of the tag input. */
        this.disabled = false;
        /**
         * If set to `true`, the tag input will prevent addition and removal of tags to enforce the minTags and maxTags settings.
         * Otherwise, a validation error will be raised.
         */
        this.enforceTagLimits = false;
        /**
         * If `true`, input entered into the text input area can be converted into a tag by pressing enter.
         * Otherwise, tags can only be added from the typeahead list or other external means.
         * (Note that the `maxTags` and `tagPattern` will prevent invalid inputs regardless of this setting.)
         */
        this.freeInput = true;
        /** If `true` the input field will be readonly and selection can only occur by using the dropdown. */
        this.readonlyInput = false;
        /**
         * The maximum number of tags permitted in the tag input. If the number of tags is equal to `maxTags` and
         * `enforceTagLimits` is `true`, addition of tags will be prevented until a tag is removed
         */
        this.maxTags = Number.MAX_VALUE;
        /**
         * The minimum number of tags permitted in the tag input. If the number of tags is equal to `minTags` and `enforceTagLimits` is
         * `true`, removal of tags will be prevented until a new tag is added.
         */
        this.minTags = 0;
        /** The placeholder text which appears in the text input area when it is empty. */
        this.placeholder = '';
        /** Controls whether the typeahead appears when the text input area is clicked. This has no effect if the ux-typeahead component is not configured. */
        this.showTypeaheadOnClick = false;
        /**
         * A string containing the characters which delimit tags.
         * Typing one of the characters in `tagDelimiters` will cause the preceding text to be added as a tag,
         * and the text input area will be cleared. Pasting a string containing one or more of characters in
         * `tagDelimiters` will cause the string to be split into multiple tags.
         * Note that the delimiter character will not be part of the tag text.
         */
        this.tagDelimiters = '';
        /**
         * A function which returns either a string, string[], or Set<string>, compatible with the NgClass directive. The function receives the following parameters:
         * - `tag: any` - the string or custom object representing the tag.
         * - `index: number` - the zero-based index of the tag as it appears in the tag input.
         * - `selected: boolean` - true if the tag is currently selected.
         */
        this.tagClass = () => undefined;
        /**
         * An object which contains details of validation errors. The following properties will be present if there is a related validation error:
         * - `tagRangeError` - present if the number of tags is outside the range specified by minTags and maxTags.
         * - `inputPattern` - present if an input has been submitted which does not match the tagPattern.
         */
        this.validationErrors = {};
        /** Defines the autocomplete property on the input field which can be used to prevent the browser from displaying autocomplete suggestions. */
        this.autocomplete = 'off';
        /** Determine if we should show the clear all button */
        this.clearButton = false;
        /** Determine an aria label for the clear button */
        this.clearButtonAriaLabel = 'Reset selection';
        /** Emits when tags is changed. */
        this.tagsChange = new EventEmitter();
        /** Emits when input is changed. */
        this.inputChange = new EventEmitter();
        /** Raised when a tag is about to be added. The `tag` property of the event contains the tag to be added. Call `preventDefault()` on the event to prevent addition. */
        this.tagAdding = new EventEmitter();
        /** Raised when a tag has been added. The tag property of the event contains the tag. */
        this.tagAdded = new EventEmitter();
        /** Raised when a tag has failed validation according to the `tagPattern`. The tag property of the event contains the string which failed validation. */
        this.tagInvalidated = new EventEmitter();
        /** Raised when a tag is about to be removed. The `tag` property of the event contains the tag to be removed. Call `preventDefault()` on the event to prevent removal. */
        this.tagRemoving = new EventEmitter();
        /** Raised when a tag has been removed. The tag property of the event contains the tag. */
        this.tagRemoved = new EventEmitter();
        /** Raised when a tag has been clicked. The `tag` property of the event contains the clicked tag. Call `preventDefault()` on the event to prevent the default behaviour of selecting the tag. */
        this.tagClick = new EventEmitter();
        // When clicking on the input during multiple mode it will send a on touched event to the parent component
        this.inputFocus = new EventEmitter();
        // Emits when the component loses focus
        this.inputBlur = new EventEmitter();
        this.selectedIndex = -1;
        this.tagApi = {
            getTagDisplay: this.getTagDisplay.bind(this),
            removeTagAt: this.removeTagAt.bind(this),
            canRemoveTagAt: this.canRemoveTagAt.bind(this),
        };
        this.valid = true;
        this.inputValid = true;
        this._tags = [];
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._onChangeHandler = () => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._onTouchedHandler = () => { };
        this._onDestroy = new Subject();
        this._autoCloseDropdown = true;
    }
    /**
     * The list of tags appearing in the tag input. This can be an array of strings or custom objects.
     * See the `displayProperty` property for details of using a custom object.
     */
    get tags() {
        if (!this._tags) {
            this._tags = [];
        }
        return this._tags;
    }
    set tags(value) {
        this._tags = Array.isArray(value) ? value : [];
    }
    /** Determine if the dropdown panel should close on external click.*/
    set autoCloseDropdown(value) {
        this._autoCloseDropdown = coerceBooleanProperty(value);
    }
    get autoCloseDropdown() {
        return this._autoCloseDropdown;
    }
    get _showClearButton() {
        return this.clearButton && this._tags && this._tags.length > 0;
    }
    ngAfterContentInit() {
        // Watch for optional child typeahead control
        this.connectTypeahead(this.typeaheadQuery.first);
        this.typeaheadQuery.changes
            .pipe(takeUntil(this._onDestroy))
            .subscribe(query => this.connectTypeahead(query.first));
    }
    ngOnChanges(changes) {
        if (changes.disabled) {
            if (changes.disabled.currentValue) {
                // Clear selection and close dropdown
                this.selectedIndex = -1;
                if (this.typeahead) {
                    this.typeahead.open = false;
                }
            }
        }
        // Update validation status
        this.validate();
    }
    ngOnDestroy() {
        if (this._subscription) {
            this._subscription.unsubscribe();
        }
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    writeValue(value) {
        if (value) {
            this.tags = value;
            this._changeDetector.markForCheck();
        }
    }
    registerOnChange(fn) {
        this._onChangeHandler = fn;
    }
    registerOnTouched(fn) {
        this._onTouchedHandler = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    /**
     * Set focus on the input field.
     */
    focus() {
        if (this.tagInput) {
            this.tagInput.nativeElement.focus();
        }
    }
    /**
     * Validate the value of the control (tags property).
     */
    validate() {
        this.valid = true;
        let tagRangeError = null;
        if (this._tags && (this._tags.length < this.minTags || this._tags.length > this.maxTags)) {
            tagRangeError = {
                given: this._tags.length,
                min: this.minTags,
                max: this.maxTags,
            };
            this.valid = false;
        }
        this.validationErrors.tagRangeError = tagRangeError;
        // forward any error to the form control
        return tagRangeError;
    }
    keyHandler(event) {
        if (this.disabled) {
            return;
        }
        // Get the input field cursor location
        const inputCursorPos = this.tagInput?.nativeElement.selectionStart ?? 0;
        // Determine if the input field has any text selected
        const hasSelection = this.tagInput?.nativeElement.selectionStart !== this.tagInput?.nativeElement.selectionEnd;
        // Determine if a tag has focus
        const tagSelected = this.isValidTagIndex(this.selectedIndex);
        const inputLength = this.input ? this.input.length : 0;
        // Check whether the arrow keys can move the selection. Otherwise the input field takes the event.
        const canNavigateLeft = tagSelected || (inputCursorPos <= 0 && !hasSelection);
        const canNavigateRight = tagSelected || (inputCursorPos >= inputLength && !hasSelection);
        // Forward key events to the typeahead component.
        this._typeaheadKeyService.handleKey(event, this.typeahead);
        switch (event.which) {
            case ENTER:
                // Check if a typeahead option is highlighted
                if (this.typeahead && this.typeahead.open && this.typeahead.highlighted) {
                    // Add the typeahead option as a tag, clear the input, and close the dropdown
                    this.commitTypeahead(this.typeahead.highlighted);
                    this.typeahead.open = false;
                }
                else if (this.typeahead && !this.typeahead.open && !this.freeInput) {
                    this.typeahead.open = true;
                }
                else {
                    // Validate and add the input text as a tag, if possible
                    this.commitInput();
                }
                event.preventDefault();
                break;
            case BACKSPACE:
                if (canNavigateLeft) {
                    this.backspace();
                    event.stopPropagation();
                    event.preventDefault();
                }
                break;
            case DELETE:
                if (tagSelected) {
                    this.removeTagAt(this.selectedIndex);
                }
                break;
            case LEFT_ARROW:
                if (canNavigateLeft) {
                    this.moveSelection(-1);
                    event.preventDefault();
                }
                break;
            case RIGHT_ARROW:
                if (canNavigateRight) {
                    this.moveSelection(1);
                    event.preventDefault();
                }
                break;
        }
        // Check for keys in the tagDelimiters
        if (this.tagDelimiters && this.tagDelimiters.indexOf(this.getKeyChar(event)) >= 0) {
            // Commit previous text
            this.commitInput();
            event.stopPropagation();
            event.preventDefault();
        }
    }
    focusOutHandler() {
        // If a click on the typeahead is in progress, don't do anything.
        // This works around an issue in IE where clicking a scrollbar drops focus.
        if (this.typeahead?.clicking) {
            return;
        }
        // Close the dropdown on blur
        setTimeout(() => {
            if (!this._element.nativeElement.contains(this._document.activeElement) &&
                this.autoCloseDropdown) {
                this.selectedIndex = -1;
                if (this.typeahead) {
                    this.typeahead.open = false;
                    this._changeDetector.markForCheck();
                }
            }
        }, 200);
    }
    onClick() {
        // Prevent error if you click input when at max tag limit
        if (this.tagInput === undefined) {
            return;
        }
        // focus the input element
        this.tagInput.nativeElement.focus();
        // show the typeahead if we need to
        this.inputClickHandler();
    }
    tagClickHandler(event, tag, index) {
        if (this.disabled) {
            return;
        }
        // Send tagClick event
        const tagClickEvent = new TagInputEvent(tag);
        this.tagClick.emit(tagClickEvent);
        // Prevent focus if preventDefault() was called
        if (tagClickEvent.defaultPrevented()) {
            event.preventDefault();
            return;
        }
        // Select the tag (for IE that doesn't propagate focus)
        this.selectTagAt(index);
    }
    inputClickHandler() {
        if (this.disabled) {
            return;
        }
        if (this.typeahead && this.showTypeaheadOnClick) {
            this.typeahead.open = true;
        }
    }
    inputFocusHandler() {
        if (this.disabled) {
            return;
        }
        this.selectInput();
        // mark form control as touched
        this._onTouchedHandler();
    }
    inputPasteHandler(event) {
        if (this.disabled) {
            return;
        }
        if (this.addOnPaste) {
            // Get text from the clipboard
            let input = null;
            if (event.clipboardData) {
                input = event.clipboardData.getData('text/plain');
            }
            else if (window.clipboardData) {
                // Internet Explorer only
                input = window.clipboardData.getData('Text');
            }
            // Commit the clipboard text directly
            if (this.commit(input)) {
                this.selectInput();
                event.stopPropagation();
                event.preventDefault();
            }
        }
    }
    typeaheadOptionSelectedHandler(event) {
        if (this.disabled) {
            return;
        }
        // When the typeahead sends the optionSelected event, commit the object directly
        this.commitTypeahead(event.option);
    }
    /**
     * Commit the current input value and clear the input field if successful.
     */
    commitInput() {
        if (this.commit(this.input)) {
            this.selectInput();
            this.toggle();
            this.setInputValue('');
        }
    }
    /**
     * Commit the given tag object and clear the input if successful.
     */
    commitTypeahead(tag) {
        if (this.addTag(tag)) {
            this.selectInput();
            this.setInputValue('');
        }
    }
    /**
     * Commit the given string value as one or more tags, if validation passes. Returns true if the tag(s) were created.
     */
    commit(input) {
        if (input && this.freeInput) {
            // Split the tags by the tagDelimiters if configured
            const newTags = this.splitTagInput(input);
            // Check tag validation for all of the individual values
            let allValid = true;
            for (const newTag of newTags) {
                const valid = this.validateTag(newTag);
                if (!valid) {
                    allValid = false;
                }
            }
            // Add the tags if all are valid
            if (allValid) {
                for (const newTag of newTags) {
                    this.addTag(this.createTag(newTag));
                }
                return true;
            }
        }
        return false;
    }
    /**
     * If no tag is selected, select the rightmost tag. If a tag is selected, remove it.
     */
    backspace() {
        if (this.disabled) {
            return;
        }
        if (!this.isValidTagIndex(this.selectedIndex)) {
            this.selectTagAt(this._tags.length - 1);
        }
        else {
            this.removeTagAt(this.selectedIndex);
        }
    }
    /**
     * Move the highlighted option forwards or backwards in the list. Wraps at the limits.
     * @param delta Value to be added to the selected index, i.e. -1 to move backwards, +1 to move forwards.
     */
    moveSelection(delta) {
        if (this.disabled) {
            return;
        }
        if (this.isValidSelectIndex(this.selectedIndex)) {
            this.selectedIndex += delta;
            // Do wrapping of selection when out of bounds
            if (this.selectedIndex < 0) {
                this.selectedIndex = this._tags.length;
            }
            else if (this.selectedIndex > this._tags.length) {
                this.selectedIndex = 0;
            }
        }
    }
    /**
     * Returns a value to display for the given tag. Uses display function/property name if set, otherwise assumes that the tag is a simple string.
     */
    getTagDisplay(tag) {
        if (typeof this.display === 'function') {
            return this.display(tag);
        }
        if (typeof this.display === 'string') {
            return tag[this.display];
        }
        return tag;
    }
    /**
     * Returns true if the given index is selected (tag index or input field).
     */
    isSelected(index) {
        return index === this.selectedIndex;
    }
    /**
     * Select the tag at the given index. Does nothing if disabled is true.
     */
    selectTagAt(tagIndex) {
        if (this.disabled) {
            return;
        }
        if (this.isValidTagIndex(tagIndex)) {
            this.selectedIndex = tagIndex;
        }
    }
    /**
     * Select the input field, giving it focus. Does nothing if disabled is true.
     */
    selectInput() {
        if (this.disabled) {
            return;
        }
        this.selectedIndex = this._tags.length;
    }
    /**
     * Remove the tag at the given index. Does nothing if disabled is true or the minTags property prevents removal.
     */
    removeTagAt(tagIndex) {
        if (this.disabled || !this.canRemoveTagAt()) {
            return;
        }
        // Check that the tagIndex is in range
        if (this.isValidTagIndex(tagIndex)) {
            const tag = this._tags[tagIndex];
            const tagRemovingEvent = new TagInputEvent(tag);
            this.tagRemoving.emit(tagRemovingEvent);
            if (!tagRemovingEvent.defaultPrevented()) {
                // Select input first to avoid issues with dropping focus
                this.selectInput();
                // Remove the tag
                this.tags = this._tags.filter((_tag, index) => index !== tagIndex);
                this.setTagsValue(this._tags);
                // Set focus again since indices have changed
                this.selectInput();
                this.tagRemoved.emit(new TagInputEvent(tag));
                this.validate();
            }
        }
    }
    /**
     * Returns true if the tag at the given index can be removed.
     */
    canRemoveTagAt() {
        return this._tags.length > this.minTags || !this.enforceTagLimits;
    }
    /**
     * Returns true if the input field should be available.
     */
    isInputVisible() {
        return this._tags.length < this.maxTags || !this.enforceTagLimits;
    }
    /**
     * Returns true if any part of the control has focus.
     */
    hasFocus() {
        return this.isValidSelectIndex(this.selectedIndex);
    }
    toggle() {
        this.typeahead && this.typeahead.open
            ? (this.typeahead.open = false)
            : this.inputClickHandler();
    }
    clear() {
        if (this.disabled) {
            return;
        }
        this.tags = [];
        this.setTagsValue(this._tags);
        this.setInputValue('');
        this.focus();
    }
    setInputValue(text) {
        this.input = text;
        this.inputChange.emit(text);
    }
    setTagsValue(tags) {
        this._onChangeHandler(tags);
        this.tagsChange.emit(tags);
    }
    connectTypeahead(typeahead) {
        if (this._subscription) {
            this._subscription.unsubscribe();
            this._subscription = null;
        }
        this.typeahead = typeahead;
        if (this.typeahead) {
            // Set up event handler for selected options
            this._subscription = this.typeahead.optionSelected.subscribe(this.typeaheadOptionSelectedHandler.bind(this));
            // Set up event handler for the highlighted element
            // Added a delay to move it out of the current change detection cycle
            this._subscription.add(this.typeahead.highlightedElementChange
                .pipe(tick())
                .subscribe((element) => (this.highlightedElement = element)));
        }
    }
    /**
     * Validate the given tagValue with the tagPattern, if set. Update validationErrors on validation failure.
     */
    validateTag(tagValue) {
        let inputPattern = null;
        this.inputValid = true;
        if (this.tagPattern && !this.tagPattern.test(tagValue)) {
            inputPattern = {
                given: tagValue,
                pattern: this.tagPattern,
            };
            this.inputValid = false;
        }
        this.validationErrors.inputPattern = inputPattern;
        return this.inputValid;
    }
    /**
     * Create a tag object for the given tagValue. If createTagHandler is specified, use it; otherwise if displayProperty is specified, create an object with the tagValue as the single named property; otherwise return the tagValue itself.
     */
    createTag(tagValue) {
        let tag = null;
        if (this.createTagHandler && typeof this.createTagHandler === 'function') {
            tag = this.createTagHandler(tagValue);
        }
        else if (typeof this.display === 'string') {
            tag = {};
            tag[this.display] = tagValue;
        }
        else {
            tag = tagValue;
        }
        return tag;
    }
    /**
     * Add a tag object, calling the tagAdding and tagAdded events. Returns true if the tag was added to the tags array.
     */
    addTag(tag) {
        if (tag !== null) {
            // Verify that the new tag can be displayed
            const displayValue = this.getTagDisplay(tag);
            if (typeof displayValue === 'string') {
                const tagAddingEvent = new TagInputEvent(tag);
                this.tagAdding.emit(tagAddingEvent);
                if (!tagAddingEvent.defaultPrevented()) {
                    this.tags = [...this._tags, tag];
                    this.setTagsValue(this._tags);
                    this.tagAdded.emit(new TagInputEvent(tag));
                    this.validate();
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * Returns true if the given tagIndex is a valid tag index.
     */
    isValidTagIndex(tagIndex) {
        return tagIndex >= 0 && tagIndex < this._tags.length;
    }
    /**
     * Returns true if the given index is a valid selection index (tags or input field).
     */
    isValidSelectIndex(index) {
        return index >= 0 && index <= this._tags.length;
    }
    /**
     * Returns the character corresponding to the given key event, mainly for IE compatibility.
     */
    getKeyChar(event) {
        switch (event.which) {
            case SPACE:
                return ' ';
        }
        return event.key;
    }
    /**
     * Returns an array of strings corresponding to the input string split by the tagDelimiters characters.
     */
    splitTagInput(input) {
        let tagValues = [input];
        if (this.tagDelimiters && typeof this.tagDelimiters === 'string') {
            // eslint-disable-next-line no-useless-escape
            const escapedDelimiters = this.tagDelimiters.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
            const delimiterRegex = new RegExp(`[${escapedDelimiters}]`, 'g');
            tagValues = input.split(delimiterRegex).filter(s => s.length > 0);
        }
        return tagValues;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TagInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TagInputComponent, isStandalone: false, selector: "ux-tag-input", inputs: { id: "id", tags: "tags", input: "input", display: "display", addOnPaste: "addOnPaste", ariaLabel: "ariaLabel", ariaLabelledby: "ariaLabelledby", disabled: "disabled", required: "required", enforceTagLimits: "enforceTagLimits", freeInput: "freeInput", readonlyInput: "readonlyInput", maxTags: "maxTags", minTags: "minTags", placeholder: "placeholder", showTypeaheadOnClick: "showTypeaheadOnClick", tagDelimiters: "tagDelimiters", tagPattern: "tagPattern", tagTemplate: "tagTemplate", tagClass: "tagClass", validationErrors: "validationErrors", autocomplete: "autocomplete", createTagHandler: ["createTag", "createTagHandler"], icon: "icon", clearButton: "clearButton", clearButtonAriaLabel: "clearButtonAriaLabel", autoCloseDropdown: "autoCloseDropdown" }, outputs: { tagsChange: "tagsChange", inputChange: "inputChange", tagAdding: "tagAdding", tagAdded: "tagAdded", tagInvalidated: "tagInvalidated", tagRemoving: "tagRemoving", tagRemoved: "tagRemoved", tagClick: "tagClick", inputFocus: "inputFocus", inputBlur: "inputBlur" }, host: { listeners: { "keydown": "keyHandler($event)", "focusout": "focusOutHandler()", "click": "onClick()" }, properties: { "class.disabled": "disabled", "class.focus": "hasFocus()", "class.invalid": "!valid || !inputValid", "attr.id": "this.id" } }, providers: [TAGINPUT_VALUE_ACCESSOR, TAGINPUT_VALIDATOR], queries: [{ propertyName: "typeaheadQuery", predicate: TypeaheadComponent }], viewQueries: [{ propertyName: "tagInput", first: true, predicate: ["tagInput"], descendants: true }], exportAs: ["ux-tag-input"], usesOnChanges: true, ngImport: i0, template: "<ol\n  [attr.aria-haspopup]=\"typeahead ? 'listbox' : null\"\n  [attr.aria-expanded]=\"typeahead ? typeahead.open : null\"\n  [attr.aria-controls]=\"typeahead ? typeahead.id : null\"\n  [class.ux-tag-input-clear-inset]=\"_showClearButton\"\n  [class.ux-tag-input-icon-inset]=\"icon\"\n  (click)=\"toggle()\"\n>\n  @for (tag of _tags; track tag; let i = $index) {\n    <li\n      class=\"ux-tag\"\n      [class.disabled]=\"disabled\"\n      [ngClass]=\"tagClass(tag, i, isSelected(i))\"\n      [attr.tabindex]=\"disabled ? null : 0\"\n      [focusIf]=\"isSelected(i)\"\n      (click)=\"tagClickHandler($event, tag, i); $event.stopPropagation()\"\n      (focus)=\"selectTagAt(i)\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"tagTemplate || defaultTagTemplate\"\n        [ngTemplateOutletContext]=\"{ tag: tag, index: i, disabled: disabled, api: tagApi }\"\n      >\n      </ng-container>\n    </li>\n  }\n  @if (isInputVisible()) {\n    <li class=\"ux-tag-input\">\n      <input\n        #tagInput\n        type=\"text\"\n        attr.id=\"{{ id }}-input\"\n        class=\"ux-tag-input\"\n        [ngModel]=\"input\"\n        (ngModelChange)=\"setInputValue($event)\"\n        [autocomplete]=\"autocomplete\"\n        [class.invalid]=\"!inputValid\"\n        [required]=\"required\"\n        [attr.aria-activedescendant]=\"highlightedElement?.id\"\n        [attr.aria-autocomplete]=\"typeahead ? 'list' : 'none'\"\n        [attr.aria-controls]=\"typeahead?.id\"\n        [attr.aria-label]=\"ariaLabel\"\n        [attr.aria-labelledby]=\"ariaLabelledby\"\n        aria-multiline=\"false\"\n        [placeholder]=\"disabled ? '' : placeholder || ''\"\n        [disabled]=\"disabled\"\n        [focusIf]=\"isSelected(_tags.length)\"\n        (click)=\"toggle(); $event.stopPropagation()\"\n        (focus)=\"inputFocusHandler(); inputFocus.emit($event)\"\n        (blur)=\"inputBlur.emit($event)\"\n        (paste)=\"inputPasteHandler($event)\"\n        [readonly]=\"readonlyInput\"\n      />\n    </li>\n  }\n</ol>\n\n<!-- Insert the custom icon if provided -->\n@if (icon || _showClearButton) {\n  <div class=\"ux-tag-icons\" (click)=\"toggle(); $event.stopPropagation()\">\n    <!-- Clear All Button -->\n    @if (_showClearButton) {\n      <i\n        uxFocusIndicator\n        class=\"ux-tag-icon ux-icon ux-icon-close ux-select-clear-icon\"\n        [attr.tabindex]=\"disabled ? -1 : 0\"\n        [attr.aria-label]=\"clearButtonAriaLabel\"\n        (click)=\"clear(); $event.stopPropagation()\"\n        (keydown.enter)=\"clear(); $event.stopPropagation()\"\n      >\n      </i>\n    }\n    <!-- Custom Icon -->\n    @if (icon) {\n      <div class=\"ux-custom-icon\">\n        <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n      </div>\n    }\n  </div>\n}\n\n<ng-content #typeahead></ng-content>\n\n<ng-template\n  #defaultTagTemplate\n  let-tag=\"tag\"\n  let-index=\"index\"\n  let-disabled=\"disabled\"\n  let-api=\"api\"\n>\n  <span class=\"ux-tag-text\">{{ api.getTagDisplay(tag) }}</span>\n  @if (api.canRemoveTagAt(index)) {\n    <button\n      uxFocusIndicator\n      type=\"button\"\n      class=\"ux-tag-remove\"\n      aria-label=\"Remove Item\"\n      [disabled]=\"disabled\"\n      (click)=\"api.removeTagAt(index); $event.stopPropagation()\"\n    >\n      <ux-icon name=\"close\"></ux-icon>\n    </button>\n  }\n</ng-template>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TagInputComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-tag-input', exportAs: 'ux-tag-input', providers: [TAGINPUT_VALUE_ACCESSOR, TAGINPUT_VALIDATOR], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.disabled]': 'disabled',
                        '[class.focus]': 'hasFocus()',
                        '[class.invalid]': '!valid || !inputValid',
                    }, standalone: false, template: "<ol\n  [attr.aria-haspopup]=\"typeahead ? 'listbox' : null\"\n  [attr.aria-expanded]=\"typeahead ? typeahead.open : null\"\n  [attr.aria-controls]=\"typeahead ? typeahead.id : null\"\n  [class.ux-tag-input-clear-inset]=\"_showClearButton\"\n  [class.ux-tag-input-icon-inset]=\"icon\"\n  (click)=\"toggle()\"\n>\n  @for (tag of _tags; track tag; let i = $index) {\n    <li\n      class=\"ux-tag\"\n      [class.disabled]=\"disabled\"\n      [ngClass]=\"tagClass(tag, i, isSelected(i))\"\n      [attr.tabindex]=\"disabled ? null : 0\"\n      [focusIf]=\"isSelected(i)\"\n      (click)=\"tagClickHandler($event, tag, i); $event.stopPropagation()\"\n      (focus)=\"selectTagAt(i)\"\n    >\n      <ng-container\n        [ngTemplateOutlet]=\"tagTemplate || defaultTagTemplate\"\n        [ngTemplateOutletContext]=\"{ tag: tag, index: i, disabled: disabled, api: tagApi }\"\n      >\n      </ng-container>\n    </li>\n  }\n  @if (isInputVisible()) {\n    <li class=\"ux-tag-input\">\n      <input\n        #tagInput\n        type=\"text\"\n        attr.id=\"{{ id }}-input\"\n        class=\"ux-tag-input\"\n        [ngModel]=\"input\"\n        (ngModelChange)=\"setInputValue($event)\"\n        [autocomplete]=\"autocomplete\"\n        [class.invalid]=\"!inputValid\"\n        [required]=\"required\"\n        [attr.aria-activedescendant]=\"highlightedElement?.id\"\n        [attr.aria-autocomplete]=\"typeahead ? 'list' : 'none'\"\n        [attr.aria-controls]=\"typeahead?.id\"\n        [attr.aria-label]=\"ariaLabel\"\n        [attr.aria-labelledby]=\"ariaLabelledby\"\n        aria-multiline=\"false\"\n        [placeholder]=\"disabled ? '' : placeholder || ''\"\n        [disabled]=\"disabled\"\n        [focusIf]=\"isSelected(_tags.length)\"\n        (click)=\"toggle(); $event.stopPropagation()\"\n        (focus)=\"inputFocusHandler(); inputFocus.emit($event)\"\n        (blur)=\"inputBlur.emit($event)\"\n        (paste)=\"inputPasteHandler($event)\"\n        [readonly]=\"readonlyInput\"\n      />\n    </li>\n  }\n</ol>\n\n<!-- Insert the custom icon if provided -->\n@if (icon || _showClearButton) {\n  <div class=\"ux-tag-icons\" (click)=\"toggle(); $event.stopPropagation()\">\n    <!-- Clear All Button -->\n    @if (_showClearButton) {\n      <i\n        uxFocusIndicator\n        class=\"ux-tag-icon ux-icon ux-icon-close ux-select-clear-icon\"\n        [attr.tabindex]=\"disabled ? -1 : 0\"\n        [attr.aria-label]=\"clearButtonAriaLabel\"\n        (click)=\"clear(); $event.stopPropagation()\"\n        (keydown.enter)=\"clear(); $event.stopPropagation()\"\n      >\n      </i>\n    }\n    <!-- Custom Icon -->\n    @if (icon) {\n      <div class=\"ux-custom-icon\">\n        <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n      </div>\n    }\n  </div>\n}\n\n<ng-content #typeahead></ng-content>\n\n<ng-template\n  #defaultTagTemplate\n  let-tag=\"tag\"\n  let-index=\"index\"\n  let-disabled=\"disabled\"\n  let-api=\"api\"\n>\n  <span class=\"ux-tag-text\">{{ api.getTagDisplay(tag) }}</span>\n  @if (api.canRemoveTagAt(index)) {\n    <button\n      uxFocusIndicator\n      type=\"button\"\n      class=\"ux-tag-remove\"\n      aria-label=\"Remove Item\"\n      [disabled]=\"disabled\"\n      (click)=\"api.removeTagAt(index); $event.stopPropagation()\"\n    >\n      <ux-icon name=\"close\"></ux-icon>\n    </button>\n  }\n</ng-template>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.id']
            }], tags: [{
                type: Input
            }], input: [{
                type: Input
            }], display: [{
                type: Input
            }], addOnPaste: [{
                type: Input
            }], ariaLabel: [{
                type: Input
            }], ariaLabelledby: [{
                type: Input
            }], disabled: [{
                type: Input
            }], required: [{
                type: Input
            }], enforceTagLimits: [{
                type: Input
            }], freeInput: [{
                type: Input
            }], readonlyInput: [{
                type: Input
            }], maxTags: [{
                type: Input
            }], minTags: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], showTypeaheadOnClick: [{
                type: Input
            }], tagDelimiters: [{
                type: Input
            }], tagPattern: [{
                type: Input
            }], tagTemplate: [{
                type: Input
            }], tagClass: [{
                type: Input
            }], validationErrors: [{
                type: Input
            }], autocomplete: [{
                type: Input
            }], createTagHandler: [{
                type: Input,
                args: ['createTag']
            }], icon: [{
                type: Input
            }], clearButton: [{
                type: Input
            }], clearButtonAriaLabel: [{
                type: Input
            }], autoCloseDropdown: [{
                type: Input
            }], tagsChange: [{
                type: Output
            }], inputChange: [{
                type: Output
            }], tagAdding: [{
                type: Output
            }], tagAdded: [{
                type: Output
            }], tagInvalidated: [{
                type: Output
            }], tagRemoving: [{
                type: Output
            }], tagRemoved: [{
                type: Output
            }], tagClick: [{
                type: Output
            }], inputFocus: [{
                type: Output
            }], inputBlur: [{
                type: Output
            }], typeaheadQuery: [{
                type: ContentChildren,
                args: [TypeaheadComponent]
            }], tagInput: [{
                type: ViewChild,
                args: ['tagInput', { static: false }]
            }], keyHandler: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }], focusOutHandler: [{
                type: HostListener,
                args: ['focusout']
            }], onClick: [{
                type: HostListener,
                args: ['click']
            }] } });

let uniqueId$2 = 0;
const SELECT_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SelectComponent),
    multi: true,
};
class SelectComponent {
    constructor() {
        this._element = inject(ElementRef);
        this._platform = inject(Platform);
        this._typeaheadKeyService = inject(TypeaheadKeyService);
        this._changeDetector = inject(ChangeDetectorRef);
        this._document = inject(DOCUMENT);
        /** A unique id for the component. */
        this.id = `ux-select-${++uniqueId$2}`;
        /**
         * Controls whether the value of the single select control can be cleared by deleting the selected value in the
         * input field. This does not affect the initial state of the control, so specify a value for `value` if null should
         * never be allowed.
         */
        this.allowNull = false;
        /** Controls the disabled state of the tag input. */
        this.disabled = false;
        /** The positioning of the typeahead dropdown in relation to its parent. */
        this.dropDirection = 'down';
        /** The maximum height of the typeahead dropdown, as a CSS value. */
        this.maxHeight = '250px';
        /**
         * Controls whether the user can select more than one option in the select control. If set to true, selected
         * options will appear as tags in the input area. If set to false, the selected value will appear as editable text
         * in the input area.
         */
        this.multiple = false;
        /**
         * The number of options to request in a page. This should ideally be more than twice the number of items which
         * fit into the height of the dropdown, but this is not required.
         */
        this.pageSize = 20;
        /** The placeholder text which appears in the text input area when it is empty. */
        this.placeholder = '';
        /**
         * Defines the `autocomplete` property on the `input` element which can be used to prevent the browser from
         * displaying autocomplete suggestions.
         */
        this.autocomplete = 'off';
        /** If `true` the input field will be readonly and selection can only occur by using the dropdown. */
        this.readonlyInput = false;
        /** Determine if we should show the clear all button */
        this.clearButton = false;
        /** Determine an aria label for the clear button */
        this.clearButtonAriaLabel = 'Reset selection';
        /** Emits when `value` changes. */
        this.valueChange = new EventEmitter();
        /** Emits when `input` changes. */
        this.inputChange = new EventEmitter();
        /** Emits when `dropdownOpen` changes. */
        this.dropdownOpenChange = new EventEmitter();
        /** Emits when recently selected options change. */
        this.recentOptionsChange = new EventEmitter();
        this._value$ = new ReplaySubject(1);
        this._hasValue = false;
        this._input$ = new BehaviorSubject({ userInteraction: false, value: '' });
        this._dropdownOpen = false;
        this._userInput = false;
        this._filterDebounceTime = 200;
        this._autoCloseDropdown = true;
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._onChange = (_) => { };
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this._onTouched = () => { };
        this._onDestroy = new Subject();
    }
    /** The selected option (for single select) or array of options (for multiple select). */
    set value(value) {
        this._value$.next(value);
    }
    get value() {
        return this._value;
    }
    /** The text in the input area. This is used to filter the options dropdown. */
    set input(value) {
        this._input$.next({ ...this._input$.value, value });
    }
    get input() {
        return this._input$.value.value;
    }
    /** The status of the typeahead dropdown. */
    set dropdownOpen(value) {
        this._dropdownOpen = value;
        this.dropdownOpenChange.emit(value);
    }
    get dropdownOpen() {
        return this._dropdownOpen;
    }
    /** Determine if the dropdown panel should close on external click.*/
    set autoCloseDropdown(value) {
        this._autoCloseDropdown = coerceBooleanProperty(value);
    }
    get autoCloseDropdown() {
        return this._autoCloseDropdown;
    }
    /** Specify the debounceTime value for the select filter */
    get filterDebounceTime() {
        return this._filterDebounceTime;
    }
    set filterDebounceTime(filterDebounceTime) {
        this._filterDebounceTime = coerceNumberProperty(filterDebounceTime);
    }
    ngOnInit() {
        // Emit change events
        this._value$.pipe(takeUntil(this._onDestroy), distinctUntilChanged()).subscribe(value => {
            this._value = value;
            this._hasValue = !!value;
        });
        // Changes to the input field
        this._input$
            .pipe(skip(1), filter(() => this.allowNull), filter(value => !this.multiple && value.value !== this.getDisplay(this.value)), takeUntil(this._onDestroy))
            .subscribe(input => {
            if (input.userInteraction && input.value === '') {
                this.value = null;
                this._onChange(null);
                this.valueChange.next(null);
            }
        });
        // open the dropdown once the filter debounce has elapsed
        this.filter$
            .pipe(filter(() => this._userInput), take(1), takeUntil(this._onDestroy))
            .subscribe(() => {
            this.dropdownOpen = true;
            this._userInput = false;
        });
        // Update the single-select input when the model changes
        this._value$
            .pipe(distinctUntilChanged(), delay(0), filter(value => value !== null && !this.multiple), takeUntil(this._onDestroy))
            .subscribe(value => {
            const inputValue = this.getDisplay(value);
            // check if the input value has changed and if so the emit
            if (inputValue !== this.input) {
                this.input = inputValue;
                this.inputChange.emit(this.input);
            }
            this._changeDetector.markForCheck();
        });
    }
    ngOnChanges(changes) {
        if (changes.multiple &&
            !changes.multiple.firstChange &&
            changes.multiple.currentValue !== changes.multiple.previousValue) {
            this.input = '';
        }
        // Set up filter from input
        this.filter$ = this._input$.pipe(map(input => !this.multiple && input.value === this.getDisplay(this.value) ? '' : input.value), debounceTime(this.filterDebounceTime));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    writeValue(obj) {
        if (obj !== undefined && obj !== this.value) {
            this.value = obj;
            this._changeDetector.markForCheck();
        }
    }
    registerOnChange(fn) {
        this._onChange = fn;
    }
    registerOnTouched(fn) {
        this._onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    inputClickHandler() {
        this.selectInputText();
        this.dropdownOpen = true;
    }
    inputBlurHandler() {
        // If a click on the typeahead is in progress, just refocus the input.
        // This works around an issue in IE where clicking a scrollbar drops focus.
        if (this.singleTypeahead && this.singleTypeahead.clicking) {
            this.singleInput.nativeElement.focus();
            return;
        }
        // Close dropdown and reset text input if focus is lost
        setTimeout(() => {
            if (!this._element.nativeElement.contains(this._document.activeElement) &&
                this._autoCloseDropdown) {
                this.dropdownOpen = false;
                if (!this.multiple) {
                    this.input = this.getDisplay(this.value);
                }
            }
        }, 200);
    }
    /**
     * Key handler for single select only. Multiple select key handling is in TagInputComponent.
     */
    inputKeyHandler(event) {
        // Standard keys for typeahead (up/down/esc)
        this._typeaheadKeyService.handleKey(event, this.singleTypeahead);
        if (event.keyCode === ENTER) {
            if (this._dropdownOpen) {
                // Set the highlighted option as the value and close
                this.singleTypeahead.selectHighlighted();
            }
            else {
                this.dropdownOpen = true;
            }
            // Update the input field. If dropdown isn't open then reset it to the previous value.
            this.input = this.getDisplay(this.value);
            event.preventDefault();
        }
        // when the user types and the value is not empty then we should open the dropdown except for non printable keys.
        if (event.key.length === 1) {
            this._userInput = true;
            this._dropdownOpen = true;
        }
    }
    /** This gets called whenever the user types in the input */
    onInputChange(input) {
        this._input$.next({
            value: input,
            userInteraction: true,
        });
        this.inputChange.emit(this.input);
    }
    /** Whenever a single select item is selected emit the values */
    _singleOptionSelected(event) {
        if (event.option !== null && event.option !== this.value) {
            this.value = event.option;
            this.dropdownOpen = false;
            this.valueChange.emit(this.value);
            this._onChange(this.value);
        }
    }
    /** Whenever a multi-select item is selected emit the values */
    _multipleOptionSelected(selection) {
        // update the internal selection
        this._value$.next(selection);
        this.valueChange.emit(this.value);
        this._onChange(this.value);
    }
    /**
     * Returns the display value of the given option.
     */
    getDisplay(option) {
        if (option === null || option === undefined) {
            return '';
        }
        if (typeof this.display === 'function') {
            return this.display(option);
        }
        if (typeof this.display === 'string' &&
            typeof option === 'object' &&
            // eslint-disable-next-line no-prototype-builtins
            option.hasOwnProperty(this.display)) {
            return option[this.display];
        }
        return option;
    }
    /** Toggle the dropdown open state */
    toggle() {
        // if the select is disabled then do not show the dropdown
        if (this.disabled) {
            return;
        }
        if (this.dropdownOpen) {
            this.dropdownOpen = false;
        }
        else {
            this.inputClickHandler();
        }
    }
    /** Handle input focus events */
    onFocus() {
        // mark form control as touched
        this._onTouched();
        // if the input is readonly we do not want to select the text on focus
        if (this.readonlyInput) {
            // cast the select input element
            const element = this.singleInput.nativeElement;
            // firefox requires a delay before clearing the selection (other browsers don't)
            this._platform.FIREFOX
                ? requestAnimationFrame(() => element.setSelectionRange(0, 0))
                : element.setSelectionRange(0, 0);
        }
    }
    clear() {
        if (this.disabled) {
            return;
        }
        // clear the value and input text
        this.value = null;
        this.input = null;
        this.selectInputText();
        // emit the latest values
        this.valueChange.emit(this.value);
        this._onChange(this.value);
        this.inputChange.emit(this.input);
    }
    selectInputText() {
        if (!this.readonlyInput) {
            this.singleInput.nativeElement.select();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SelectComponent, isStandalone: false, selector: "ux-select, ux-combobox, ux-dropdown", inputs: { id: "id", value: "value", input: "input", dropdownOpen: "dropdownOpen", options: "options", display: "display", key: "key", allowNull: "allowNull", ariaLabel: "ariaLabel", ariaLabelledby: "ariaLabelledby", listboxAriaLabel: "listboxAriaLabel", disabled: "disabled", dropDirection: "dropDirection", maxHeight: "maxHeight", multiple: "multiple", pageSize: "pageSize", placeholder: "placeholder", tagTemplate: "tagTemplate", optionsHeadingTemplate: "optionsHeadingTemplate", recentOptionsHeadingTemplate: "recentOptionsHeadingTemplate", autocomplete: "autocomplete", loadingTemplate: "loadingTemplate", noOptionsTemplate: "noOptionsTemplate", readonlyInput: "readonlyInput", clearButton: "clearButton", clearButtonAriaLabel: "clearButtonAriaLabel", autoCloseDropdown: "autoCloseDropdown", optionTemplate: "optionTemplate", recentOptions: "recentOptions", recentOptionsMaxCount: "recentOptionsMaxCount", required: "required", filterDebounceTime: "filterDebounceTime" }, outputs: { valueChange: "valueChange", inputChange: "inputChange", dropdownOpenChange: "dropdownOpenChange", recentOptionsChange: "recentOptionsChange" }, host: { properties: { "class.ux-select-custom-icon": "!!icon", "class.ux-select-disabled": "disabled", "attr.id": "this.id" } }, providers: [SELECT_VALUE_ACCESSOR], queries: [{ propertyName: "icon", first: true, predicate: ["icon"], descendants: true }], viewQueries: [{ propertyName: "singleInput", first: true, predicate: ["singleInput"], descendants: true }, { propertyName: "tagInput", first: true, predicate: ["tagInput"], descendants: true }, { propertyName: "multipleTypeahead", first: true, predicate: ["multipleTypeahead"], descendants: true }, { propertyName: "singleTypeahead", first: true, predicate: ["singleTypeahead"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (multiple) {\n  <ux-tag-input\n    #tagInput=\"ux-tag-input\"\n    [id]=\"id + '-input'\"\n    [tags]=\"_value$ | async\"\n    (tagsChange)=\"_multipleOptionSelected($event)\"\n    [ariaLabelledby]=\"ariaLabelledby\"\n    [(input)]=\"input\"\n    [required]=\"required\"\n    (inputChange)=\"onInputChange($event)\"\n    [ariaLabel]=\"ariaLabel\"\n    [autocomplete]=\"autocomplete\"\n    [addOnPaste]=\"false\"\n    [disabled]=\"disabled\"\n    [display]=\"display\"\n    [freeInput]=\"false\"\n    [placeholder]=\"placeholder || ''\"\n    [tagTemplate]=\"tagTemplate\"\n    (inputFocus)=\"onFocus()\"\n    [showTypeaheadOnClick]=\"true\"\n    [readonlyInput]=\"readonlyInput\"\n    [icon]=\"icon\"\n    [clearButton]=\"clearButton\"\n    [autoCloseDropdown]=\"autoCloseDropdown\"\n    [clearButtonAriaLabel]=\"clearButtonAriaLabel\"\n  >\n    <ux-typeahead\n      #multipleTypeahead\n      [ariaLabel]=\"listboxAriaLabel\"\n      [id]=\"id + '-typeahead'\"\n      [options]=\"options\"\n      [filter]=\"filter$ | async\"\n      [(open)]=\"dropdownOpen\"\n      [display]=\"display\"\n      [key]=\"key\"\n      [disabledOptions]=\"_value$ | async\"\n      [dropDirection]=\"dropDirection\"\n      [maxHeight]=\"maxHeight\"\n      [multiselectable]=\"true\"\n      [pageSize]=\"pageSize\"\n      [selectFirst]=\"true\"\n      [loadingTemplate]=\"loadingTemplate\"\n      [optionTemplate]=\"optionTemplate\"\n      [noOptionsTemplate]=\"noOptionsTemplate\"\n      [recentOptions]=\"recentOptions\"\n      [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n      (recentOptionsChange)=\"recentOptionsChange.emit($event)\"\n      [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n      [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n    >\n    </ux-typeahead>\n  </ux-tag-input>\n}\n\n@if (!multiple) {\n  <div class=\"ux-select-container\" [class.disabled]=\"disabled\" aria-haspopup=\"listbox\">\n    <input\n      #singleInput\n      type=\"text\"\n      [attr.id]=\"id + '-input'\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      class=\"form-control\"\n      [required]=\"required\"\n      [class.ux-tag-input-clear-inset]=\"clearButton && allowNull && _hasValue\"\n      [attr.aria-activedescendant]=\"highlightedElement?.id\"\n      aria-autocomplete=\"list\"\n      role=\"combobox\"\n      [attr.aria-controls]=\"id + '-typeahead'\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-expanded]=\"dropdownOpen\"\n      [autocomplete]=\"autocomplete\"\n      [(ngModel)]=\"input\"\n      (ngModelChange)=\"onInputChange($event)\"\n      [placeholder]=\"placeholder || ''\"\n      [disabled]=\"disabled\"\n      (click)=\"toggle()\"\n      (focus)=\"onFocus()\"\n      (blur)=\"inputBlurHandler()\"\n      (keydown)=\"inputKeyHandler($event)\"\n      [readonly]=\"readonlyInput\"\n    />\n    <div class=\"ux-select-icons\">\n      @if (clearButton && allowNull && _hasValue) {\n        <i\n          uxFocusIndicator\n          [attr.tabindex]=\"disabled ? -1 : 0\"\n          [attr.aria-label]=\"clearButtonAriaLabel\"\n          class=\"ux-select-icon ux-icon ux-icon-close ux-select-clear-icon\"\n          (click)=\"clear(); $event.stopPropagation()\"\n          (keydown.enter)=\"clear(); $event.stopPropagation()\"\n        >\n        </i>\n      }\n      @if (!icon) {\n        <i\n          class=\"ux-select-icon ux-icon ux-select-chevron-icon\"\n          [class.ux-icon-up]=\"dropDirection === 'up'\"\n          [class.ux-icon-down]=\"dropDirection === 'down'\"\n          (click)=\"toggle(); $event.stopPropagation(); singleInput.focus()\"\n        >\n        </i>\n      }\n      @if (icon) {\n        <div class=\"ux-custom-icon\">\n          <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n        </div>\n      }\n    </div>\n    <ux-typeahead\n      #singleTypeahead\n      [ariaLabel]=\"listboxAriaLabel\"\n      [id]=\"id + '-typeahead'\"\n      [active]=\"_value$ | async\"\n      [options]=\"options\"\n      [filter]=\"filter$ | async\"\n      [(open)]=\"dropdownOpen\"\n      [display]=\"display\"\n      [key]=\"key\"\n      [dropDirection]=\"dropDirection\"\n      [maxHeight]=\"maxHeight\"\n      [multiselectable]=\"false\"\n      [openOnFilterChange]=\"false\"\n      [pageSize]=\"pageSize\"\n      [selectFirst]=\"true\"\n      [loadingTemplate]=\"loadingTemplate\"\n      [optionTemplate]=\"optionTemplate\"\n      [noOptionsTemplate]=\"noOptionsTemplate\"\n      [recentOptions]=\"recentOptions\"\n      [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n      [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n      [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n      (optionSelected)=\"_singleOptionSelected($event)\"\n      (highlightedElementChange)=\"highlightedElement = $event\"\n      (recentOptionsChange)=\"recentOptionsChange.emit($any($event))\"\n    >\n    </ux-typeahead>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TagInputComponent, selector: "ux-tag-input", inputs: ["id", "tags", "input", "display", "addOnPaste", "ariaLabel", "ariaLabelledby", "disabled", "required", "enforceTagLimits", "freeInput", "readonlyInput", "maxTags", "minTags", "placeholder", "showTypeaheadOnClick", "tagDelimiters", "tagPattern", "tagTemplate", "tagClass", "validationErrors", "autocomplete", "createTag", "icon", "clearButton", "clearButtonAriaLabel", "autoCloseDropdown"], outputs: ["tagsChange", "inputChange", "tagAdding", "tagAdded", "tagInvalidated", "tagRemoving", "tagRemoved", "tagClick", "inputFocus", "inputBlur"], exportAs: ["ux-tag-input"] }, { kind: "component", type: TypeaheadComponent, selector: "ux-typeahead", inputs: ["id", "options", "filter", "open", "display", "key", "disabledOptions", "dropDirection", "maxHeight", "multiselectable", "ariaLabel", "openOnFilterChange", "pageSize", "selectFirst", "selectOnEnter", "loading", "loadingTemplate", "optionTemplate", "noOptionsTemplate", "active", "recentOptions", "recentOptionsMaxCount", "recentOptionsHeadingTemplate", "optionsHeadingTemplate"], outputs: ["openChange", "optionSelected", "highlightedChange", "highlightedElementChange", "recentOptionsChange"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-select, ux-combobox, ux-dropdown', providers: [SELECT_VALUE_ACCESSOR], host: {
                        '[class.ux-select-custom-icon]': '!!icon',
                        '[class.ux-select-disabled]': 'disabled',
                    }, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@if (multiple) {\n  <ux-tag-input\n    #tagInput=\"ux-tag-input\"\n    [id]=\"id + '-input'\"\n    [tags]=\"_value$ | async\"\n    (tagsChange)=\"_multipleOptionSelected($event)\"\n    [ariaLabelledby]=\"ariaLabelledby\"\n    [(input)]=\"input\"\n    [required]=\"required\"\n    (inputChange)=\"onInputChange($event)\"\n    [ariaLabel]=\"ariaLabel\"\n    [autocomplete]=\"autocomplete\"\n    [addOnPaste]=\"false\"\n    [disabled]=\"disabled\"\n    [display]=\"display\"\n    [freeInput]=\"false\"\n    [placeholder]=\"placeholder || ''\"\n    [tagTemplate]=\"tagTemplate\"\n    (inputFocus)=\"onFocus()\"\n    [showTypeaheadOnClick]=\"true\"\n    [readonlyInput]=\"readonlyInput\"\n    [icon]=\"icon\"\n    [clearButton]=\"clearButton\"\n    [autoCloseDropdown]=\"autoCloseDropdown\"\n    [clearButtonAriaLabel]=\"clearButtonAriaLabel\"\n  >\n    <ux-typeahead\n      #multipleTypeahead\n      [ariaLabel]=\"listboxAriaLabel\"\n      [id]=\"id + '-typeahead'\"\n      [options]=\"options\"\n      [filter]=\"filter$ | async\"\n      [(open)]=\"dropdownOpen\"\n      [display]=\"display\"\n      [key]=\"key\"\n      [disabledOptions]=\"_value$ | async\"\n      [dropDirection]=\"dropDirection\"\n      [maxHeight]=\"maxHeight\"\n      [multiselectable]=\"true\"\n      [pageSize]=\"pageSize\"\n      [selectFirst]=\"true\"\n      [loadingTemplate]=\"loadingTemplate\"\n      [optionTemplate]=\"optionTemplate\"\n      [noOptionsTemplate]=\"noOptionsTemplate\"\n      [recentOptions]=\"recentOptions\"\n      [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n      (recentOptionsChange)=\"recentOptionsChange.emit($event)\"\n      [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n      [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n    >\n    </ux-typeahead>\n  </ux-tag-input>\n}\n\n@if (!multiple) {\n  <div class=\"ux-select-container\" [class.disabled]=\"disabled\" aria-haspopup=\"listbox\">\n    <input\n      #singleInput\n      type=\"text\"\n      [attr.id]=\"id + '-input'\"\n      [attr.aria-labelledby]=\"ariaLabelledby\"\n      class=\"form-control\"\n      [required]=\"required\"\n      [class.ux-tag-input-clear-inset]=\"clearButton && allowNull && _hasValue\"\n      [attr.aria-activedescendant]=\"highlightedElement?.id\"\n      aria-autocomplete=\"list\"\n      role=\"combobox\"\n      [attr.aria-controls]=\"id + '-typeahead'\"\n      [attr.aria-label]=\"ariaLabel\"\n      [attr.aria-expanded]=\"dropdownOpen\"\n      [autocomplete]=\"autocomplete\"\n      [(ngModel)]=\"input\"\n      (ngModelChange)=\"onInputChange($event)\"\n      [placeholder]=\"placeholder || ''\"\n      [disabled]=\"disabled\"\n      (click)=\"toggle()\"\n      (focus)=\"onFocus()\"\n      (blur)=\"inputBlurHandler()\"\n      (keydown)=\"inputKeyHandler($event)\"\n      [readonly]=\"readonlyInput\"\n    />\n    <div class=\"ux-select-icons\">\n      @if (clearButton && allowNull && _hasValue) {\n        <i\n          uxFocusIndicator\n          [attr.tabindex]=\"disabled ? -1 : 0\"\n          [attr.aria-label]=\"clearButtonAriaLabel\"\n          class=\"ux-select-icon ux-icon ux-icon-close ux-select-clear-icon\"\n          (click)=\"clear(); $event.stopPropagation()\"\n          (keydown.enter)=\"clear(); $event.stopPropagation()\"\n        >\n        </i>\n      }\n      @if (!icon) {\n        <i\n          class=\"ux-select-icon ux-icon ux-select-chevron-icon\"\n          [class.ux-icon-up]=\"dropDirection === 'up'\"\n          [class.ux-icon-down]=\"dropDirection === 'down'\"\n          (click)=\"toggle(); $event.stopPropagation(); singleInput.focus()\"\n        >\n        </i>\n      }\n      @if (icon) {\n        <div class=\"ux-custom-icon\">\n          <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n        </div>\n      }\n    </div>\n    <ux-typeahead\n      #singleTypeahead\n      [ariaLabel]=\"listboxAriaLabel\"\n      [id]=\"id + '-typeahead'\"\n      [active]=\"_value$ | async\"\n      [options]=\"options\"\n      [filter]=\"filter$ | async\"\n      [(open)]=\"dropdownOpen\"\n      [display]=\"display\"\n      [key]=\"key\"\n      [dropDirection]=\"dropDirection\"\n      [maxHeight]=\"maxHeight\"\n      [multiselectable]=\"false\"\n      [openOnFilterChange]=\"false\"\n      [pageSize]=\"pageSize\"\n      [selectFirst]=\"true\"\n      [loadingTemplate]=\"loadingTemplate\"\n      [optionTemplate]=\"optionTemplate\"\n      [noOptionsTemplate]=\"noOptionsTemplate\"\n      [recentOptions]=\"recentOptions\"\n      [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n      [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n      [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n      (optionSelected)=\"_singleOptionSelected($event)\"\n      (highlightedElementChange)=\"highlightedElement = $event\"\n      (recentOptionsChange)=\"recentOptionsChange.emit($any($event))\"\n    >\n    </ux-typeahead>\n  </div>\n}\n" }]
        }], propDecorators: { id: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.id']
            }], value: [{
                type: Input
            }], input: [{
                type: Input
            }], dropdownOpen: [{
                type: Input
            }], options: [{
                type: Input
            }], display: [{
                type: Input
            }], key: [{
                type: Input
            }], allowNull: [{
                type: Input
            }], ariaLabel: [{
                type: Input
            }], ariaLabelledby: [{
                type: Input
            }], listboxAriaLabel: [{
                type: Input
            }], disabled: [{
                type: Input
            }], dropDirection: [{
                type: Input
            }], maxHeight: [{
                type: Input
            }], multiple: [{
                type: Input
            }], pageSize: [{
                type: Input
            }], placeholder: [{
                type: Input
            }], tagTemplate: [{
                type: Input
            }], optionsHeadingTemplate: [{
                type: Input
            }], recentOptionsHeadingTemplate: [{
                type: Input
            }], autocomplete: [{
                type: Input
            }], loadingTemplate: [{
                type: Input
            }], noOptionsTemplate: [{
                type: Input
            }], readonlyInput: [{
                type: Input
            }], clearButton: [{
                type: Input
            }], clearButtonAriaLabel: [{
                type: Input
            }], autoCloseDropdown: [{
                type: Input
            }], optionTemplate: [{
                type: Input
            }], recentOptions: [{
                type: Input
            }], recentOptionsMaxCount: [{
                type: Input
            }], required: [{
                type: Input
            }], filterDebounceTime: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], inputChange: [{
                type: Output
            }], dropdownOpenChange: [{
                type: Output
            }], recentOptionsChange: [{
                type: Output
            }], icon: [{
                type: ContentChild,
                args: ['icon', { static: false }]
            }], singleInput: [{
                type: ViewChild,
                args: ['singleInput', { static: false }]
            }], tagInput: [{
                type: ViewChild,
                args: ['tagInput', { static: false }]
            }], multipleTypeahead: [{
                type: ViewChild,
                args: ['multipleTypeahead', { static: false }]
            }], singleTypeahead: [{
                type: ViewChild,
                args: ['singleTypeahead', { static: false }]
            }] } });

class TagInputModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TagInputModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TagInputModule, declarations: [TagInputComponent], imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            FocusIfModule,
            IconModule,
            TypeaheadModule], exports: [TagInputComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TagInputModule, imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            FocusIfModule,
            IconModule,
            TypeaheadModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TagInputModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        FormsModule,
                        FocusIfModule,
                        IconModule,
                        TypeaheadModule,
                    ],
                    exports: [TagInputComponent],
                    declarations: [TagInputComponent],
                }]
        }] });

class SelectModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SelectModule, declarations: [SelectComponent], imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            InfiniteScrollModule,
            TagInputModule,
            TypeaheadModule,
            PlatformModule], exports: [SelectComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectModule, imports: [AccessibilityModule,
            CommonModule,
            FormsModule,
            InfiniteScrollModule,
            TagInputModule,
            TypeaheadModule,
            PlatformModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        FormsModule,
                        InfiniteScrollModule,
                        TagInputModule,
                        TypeaheadModule,
                        PlatformModule,
                    ],
                    exports: [SelectComponent],
                    declarations: [SelectComponent],
                }]
        }] });

class BaseSearchComponent {
    constructor() {
        this._searchBuilderService = inject(SearchBuilderService);
        this._id = this._searchBuilderService.generateComponentId();
        this._valid = true;
    }
    get id() {
        return `ux-search-builder-search-component-${this._id}`;
    }
    /**
     * Get the current value of the component
     */
    get value() {
        return this.context.value;
    }
    /**
     * Set the current value of the component
     */
    set value(value) {
        this.context.value = value;
        this._searchBuilderService.queryHasChanged();
        // if value has been set perform validation
        this.validate();
    }
    get valid() {
        return this._valid;
    }
    set valid(valid) {
        this._valid = valid;
        this._searchBuilderService.setValid(this._id, valid);
    }
    /**
     * Make sure we clean up after ourselves
     */
    ngOnDestroy() {
        this.valid = true;
    }
    /**
     * Perform any required validation on the value
     */
    validate() {
        // if a custom validation function has been provided then use it
        this.valid = this.config.validation ? this.config.validation(this, this.value) : true;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: BaseSearchComponent, isStandalone: false, selector: "ux-base-search", ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseSearchComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'ux-base-search',
                    template: '',
                    standalone: false,
                }]
        }] });

class SearchDateRangeComponent extends BaseSearchComponent {
    constructor() {
        super(...arguments);
        this.type = 'date-range';
    }
    get label() {
        return this.config.label;
    }
    get from() {
        // if value does not exist the set it
        if (!this.value || !this.value.from) {
            this.from = new Date();
        }
        // ensure that the from value is a date object
        if (this.value.from instanceof Date === false) {
            this.value.from = new Date(this.value.from);
        }
        return this.value.from;
    }
    set from(fromValue) {
        // create new object based on the current value
        const value = Object.assign({}, this.value);
        // ensure that the from value is a date
        if (fromValue instanceof Date === false) {
            fromValue = new Date(fromValue);
        }
        // set the latest value
        value.from = fromValue;
        // update the value object while ensuring immutability
        this.value = value;
    }
    get to() {
        // if value does not exist the set it
        if (!this.value || !this.value.to) {
            this.to = new Date();
        }
        // ensure that the to value is a date object
        if (this.value.to instanceof Date === false) {
            this.value.to = new Date(this.value.to);
        }
        return this.value.to;
    }
    set to(toValue) {
        // create new object based on the current value
        const value = Object.assign({}, this.value);
        // ensure that the to value is a date
        if (toValue instanceof Date === false) {
            toValue = new Date(toValue);
        }
        // set the latest value
        value.to = toValue;
        // update the value object while ensuring immutability
        this.value = value;
    }
    get fromLabel() {
        return this.config.fromLabel || 'From';
    }
    get toLabel() {
        return this.config.toLabel || 'To';
    }
    get fromPlaceholder() {
        return this.config.fromPlaceholder;
    }
    get toPlaceholder() {
        return this.config.toPlaceholder;
    }
    get toDateInputAriaLabel() {
        return this.config.toDateInputAriaLabel || 'Selected date';
    }
    get fromDateInputAriaLabel() {
        return this.config.fromDateInputAriaLabel || 'Selected date';
    }
    /**
     * Override the default validation
     */
    validate() {
        // check if there is a config validation function
        if (this.config.validation) {
            return super.validate();
        }
        // create copies of the dates so we can modify time value (to ignore it)
        const from = new Date(this.value.from);
        const to = new Date(this.value.to);
        // set the time to the same so we dont compare it
        from.setHours(0, 0, 0, 0);
        to.setHours(0, 0, 0, 0);
        // valid if the from date is less than or equal to the to date
        this.valid = from <= to;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchDateRangeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SearchDateRangeComponent, isStandalone: false, selector: "ux-search-date-range", usesInheritance: true, ngImport: i0, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<div class=\"row\">\n  <div class=\"col-sm-12\">\n    <div class=\"form-inline\" [class.has-error]=\"!valid\">\n      <div class=\"form-group p-r-md\">\n        <label class=\"form-label m-r-xs\">{{ fromLabel }}</label>\n\n        <div class=\"input-group date m-nil\">\n          <span class=\"input-group-addon p-r-xs\" tabindex=\"1\" (click)=\"fromPopover.show()\">\n            <ux-icon name=\"calendar\"></ux-icon>\n          </span>\n          <input\n            type=\"text\"\n            #fromPopover=\"ux-popover\"\n            [ngModel]=\"from | date: 'dd MMMM yyyy'\"\n            [uxPopover]=\"fromPopoverTemplate\"\n            placement=\"bottom\"\n            popoverClass=\"date-time-picker-popover\"\n            class=\"form-control\"\n            [attr.aria-label]=\"fromDateInputAriaLabel\"\n            [placeholder]=\"fromPlaceholder\"\n            [focusIf]=\"focus\"\n          />\n        </div>\n      </div>\n\n      <div class=\"form-group p-r-xs\">\n        <label class=\"form-label m-r-xs\">{{ toLabel }}</label>\n\n        <div class=\"input-group date m-nil\">\n          <span class=\"input-group-addon\" tabindex=\"1\" (click)=\"toPopover.show()\">\n            <ux-icon name=\"calendar\"></ux-icon>\n          </span>\n          <input\n            type=\"text\"\n            #toPopover=\"ux-popover\"\n            [ngModel]=\"to | date: 'dd MMMM yyyy'\"\n            [uxPopover]=\"toPopoverTemplate\"\n            placement=\"bottom\"\n            popoverClass=\"date-time-picker-popover\"\n            class=\"form-control\"\n            [attr.aria-label]=\"toDateInputAriaLabel\"\n            [placeholder]=\"toPlaceholder\"\n          />\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<ng-template #fromPopoverTemplate>\n  <ux-date-time-picker [(date)]=\"from\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n\n<ng-template #toPopoverTemplate>\n  <ux-date-time-picker [(date)]=\"to\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n", dependencies: [{ kind: "component", type: DateTimePickerComponent, selector: "ux-date-time-picker", inputs: ["showDate", "showTime", "showTimezone", "showSeconds", "showMeridian", "showSpinners", "weekdays", "months", "monthsShort", "meridians", "nowBtnText", "showNowBtn", "timezones", "startOfWeek", "nowBtnAriaLabel", "date", "timezone", "min", "max"], outputs: ["dateChange", "timezoneChange"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchDateRangeComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-date-range', standalone: false, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<div class=\"row\">\n  <div class=\"col-sm-12\">\n    <div class=\"form-inline\" [class.has-error]=\"!valid\">\n      <div class=\"form-group p-r-md\">\n        <label class=\"form-label m-r-xs\">{{ fromLabel }}</label>\n\n        <div class=\"input-group date m-nil\">\n          <span class=\"input-group-addon p-r-xs\" tabindex=\"1\" (click)=\"fromPopover.show()\">\n            <ux-icon name=\"calendar\"></ux-icon>\n          </span>\n          <input\n            type=\"text\"\n            #fromPopover=\"ux-popover\"\n            [ngModel]=\"from | date: 'dd MMMM yyyy'\"\n            [uxPopover]=\"fromPopoverTemplate\"\n            placement=\"bottom\"\n            popoverClass=\"date-time-picker-popover\"\n            class=\"form-control\"\n            [attr.aria-label]=\"fromDateInputAriaLabel\"\n            [placeholder]=\"fromPlaceholder\"\n            [focusIf]=\"focus\"\n          />\n        </div>\n      </div>\n\n      <div class=\"form-group p-r-xs\">\n        <label class=\"form-label m-r-xs\">{{ toLabel }}</label>\n\n        <div class=\"input-group date m-nil\">\n          <span class=\"input-group-addon\" tabindex=\"1\" (click)=\"toPopover.show()\">\n            <ux-icon name=\"calendar\"></ux-icon>\n          </span>\n          <input\n            type=\"text\"\n            #toPopover=\"ux-popover\"\n            [ngModel]=\"to | date: 'dd MMMM yyyy'\"\n            [uxPopover]=\"toPopoverTemplate\"\n            placement=\"bottom\"\n            popoverClass=\"date-time-picker-popover\"\n            class=\"form-control\"\n            [attr.aria-label]=\"toDateInputAriaLabel\"\n            [placeholder]=\"toPlaceholder\"\n          />\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<ng-template #fromPopoverTemplate>\n  <ux-date-time-picker [(date)]=\"from\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n\n<ng-template #toPopoverTemplate>\n  <ux-date-time-picker [(date)]=\"to\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n" }]
        }] });

class SearchDateComponent extends BaseSearchComponent {
    constructor() {
        super(...arguments);
        this.type = 'date';
    }
    get label() {
        return this.config.label;
    }
    get placeholder() {
        return this.config.placeholder || 'Enter date';
    }
    get dateInputAriaLabel() {
        return this.config.dateInputAriaLabel || 'Selected date';
    }
    ngOnInit() {
        // by default set to the current date if not specified
        if (!this.value) {
            this.value = new Date();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchDateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SearchDateComponent, isStandalone: false, selector: "ux-search-date", usesInheritance: true, ngImport: i0, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<div class=\"input-group date m-nil\">\n  <span class=\"input-group-addon\" tabindex=\"1\" (click)=\"popover.show()\">\n    <ux-icon name=\"calendar\"></ux-icon>\n  </span>\n  <input\n    type=\"text\"\n    class=\"form-control\"\n    [attr.aria-label]=\"dateInputAriaLabel\"\n    [placeholder]=\"placeholder\"\n    #popover=\"ux-popover\"\n    [ngModel]=\"value | date: 'dd MMMM yyyy'\"\n    [uxPopover]=\"popoverTemplate\"\n    placement=\"bottom\"\n    popoverClass=\"date-time-picker-popover\"\n    [focusIf]=\"focus\"\n  />\n</div>\n\n<ng-template #popoverTemplate>\n  <ux-date-time-picker [(date)]=\"value\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n", dependencies: [{ kind: "component", type: DateTimePickerComponent, selector: "ux-date-time-picker", inputs: ["showDate", "showTime", "showTimezone", "showSeconds", "showMeridian", "showSpinners", "weekdays", "months", "monthsShort", "meridians", "nowBtnText", "showNowBtn", "timezones", "startOfWeek", "nowBtnAriaLabel", "date", "timezone", "min", "max"], outputs: ["dateChange", "timezoneChange"] }, { kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: PopoverDirective, selector: "[uxPopover]", inputs: ["uxPopover", "popoverTitle", "popoverDisabled", "popoverClass", "popoverRole", "popoverContext", "popoverDelay", "showTriggers", "hideTriggers"], exportAs: ["ux-popover"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchDateComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-date', standalone: false, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<div class=\"input-group date m-nil\">\n  <span class=\"input-group-addon\" tabindex=\"1\" (click)=\"popover.show()\">\n    <ux-icon name=\"calendar\"></ux-icon>\n  </span>\n  <input\n    type=\"text\"\n    class=\"form-control\"\n    [attr.aria-label]=\"dateInputAriaLabel\"\n    [placeholder]=\"placeholder\"\n    #popover=\"ux-popover\"\n    [ngModel]=\"value | date: 'dd MMMM yyyy'\"\n    [uxPopover]=\"popoverTemplate\"\n    placement=\"bottom\"\n    popoverClass=\"date-time-picker-popover\"\n    [focusIf]=\"focus\"\n  />\n</div>\n\n<ng-template #popoverTemplate>\n  <ux-date-time-picker [(date)]=\"value\" [showTime]=\"false\"></ux-date-time-picker>\n</ng-template>\n" }]
        }] });

class SearchSelectComponent extends BaseSearchComponent {
    constructor() {
        super(...arguments);
        this.type = 'select';
    }
    /**
     * Provide defaults for undefined properties
     */
    get label() {
        return this.config.label;
    }
    get options() {
        return this.config.options || [];
    }
    get multiple() {
        return this.config.multiple || false;
    }
    get placeholder() {
        return this.config.placeholder || 'Select item';
    }
    get dropDirection() {
        return this.config.dropDirection || 'down';
    }
    get allowNull() {
        return this.config.allowNull || false;
    }
    get disabled() {
        return this.config.disabled || false;
    }
    get maxHeight() {
        return this.config.maxHeight || '250px';
    }
    get pageSize() {
        return this.config.pageSize || 20;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SearchSelectComponent, isStandalone: false, selector: "ux-search-select", usesInheritance: true, ngImport: i0, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<ux-select\n  [(value)]=\"value\"\n  [options]=\"options\"\n  [multiple]=\"multiple\"\n  [placeholder]=\"placeholder\"\n  [dropDirection]=\"dropDirection\"\n  [pageSize]=\"pageSize\"\n  [allowNull]=\"allowNull\"\n  [disabled]=\"disabled\"\n  [maxHeight]=\"maxHeight\"\n  [key]=\"config.key\"\n  [display]=\"config.display\"\n  [loadingTemplate]=\"config.loadingTemplate\"\n  [optionTemplate]=\"config.optionTemplate\"\n  [noOptionsTemplate]=\"config.noOptionsTemplate\"\n  [focusIf]=\"focus\"\n>\n</ux-select>\n", dependencies: [{ kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: SelectComponent, selector: "ux-select, ux-combobox, ux-dropdown", inputs: ["id", "value", "input", "dropdownOpen", "options", "display", "key", "allowNull", "ariaLabel", "ariaLabelledby", "listboxAriaLabel", "disabled", "dropDirection", "maxHeight", "multiple", "pageSize", "placeholder", "tagTemplate", "optionsHeadingTemplate", "recentOptionsHeadingTemplate", "autocomplete", "loadingTemplate", "noOptionsTemplate", "readonlyInput", "clearButton", "clearButtonAriaLabel", "autoCloseDropdown", "optionTemplate", "recentOptions", "recentOptionsMaxCount", "required", "filterDebounceTime"], outputs: ["valueChange", "inputChange", "dropdownOpenChange", "recentOptionsChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchSelectComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-select', standalone: false, template: "@if (label) {\n  <label class=\"form-label\">{{ label }}</label>\n}\n\n<ux-select\n  [(value)]=\"value\"\n  [options]=\"options\"\n  [multiple]=\"multiple\"\n  [placeholder]=\"placeholder\"\n  [dropDirection]=\"dropDirection\"\n  [pageSize]=\"pageSize\"\n  [allowNull]=\"allowNull\"\n  [disabled]=\"disabled\"\n  [maxHeight]=\"maxHeight\"\n  [key]=\"config.key\"\n  [display]=\"config.display\"\n  [loadingTemplate]=\"config.loadingTemplate\"\n  [optionTemplate]=\"config.optionTemplate\"\n  [noOptionsTemplate]=\"config.noOptionsTemplate\"\n  [focusIf]=\"focus\"\n>\n</ux-select>\n" }]
        }] });

class SearchTextComponent extends BaseSearchComponent {
    constructor() {
        super(...arguments);
        this.type = 'text';
    }
    get label() {
        return this.config.label;
    }
    get placeholder() {
        return this.config.placeholder || 'Enter text';
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchTextComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SearchTextComponent, isStandalone: false, selector: "ux-search-text", usesInheritance: true, ngImport: i0, template: "@if (label) {\n  <label class=\"form-label\" [attr.for]=\"id\">{{ label }}</label>\n}\n<input\n  type=\"text\"\n  [attr.id]=\"id\"\n  class=\"form-control\"\n  [placeholder]=\"placeholder\"\n  [(ngModel)]=\"value\"\n  [focusIf]=\"focus\"\n/>\n", dependencies: [{ kind: "directive", type: FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchTextComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-search-text', standalone: false, template: "@if (label) {\n  <label class=\"form-label\" [attr.for]=\"id\">{{ label }}</label>\n}\n<input\n  type=\"text\"\n  [attr.id]=\"id\"\n  class=\"form-control\"\n  [placeholder]=\"placeholder\"\n  [(ngModel)]=\"value\"\n  [focusIf]=\"focus\"\n/>\n" }]
        }] });

class SearchBuilderModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderModule, declarations: [SearchBuilderComponent,
            SearchBuilderGroupComponent,
            SearchTextComponent,
            SearchDateComponent,
            SearchDateRangeComponent,
            SearchBuilderOutletDirective,
            SearchSelectComponent,
            BaseSearchComponent], imports: [AccessibilityModule,
            CommonModule,
            DateTimePickerModule,
            FocusIfModule,
            FormsModule,
            IconModule,
            PopoverModule,
            SelectModule], exports: [SearchBuilderComponent, SearchBuilderGroupComponent, BaseSearchComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderModule, imports: [AccessibilityModule,
            CommonModule,
            DateTimePickerModule,
            FocusIfModule,
            FormsModule,
            IconModule,
            PopoverModule,
            SelectModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SearchBuilderModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        AccessibilityModule,
                        CommonModule,
                        DateTimePickerModule,
                        FocusIfModule,
                        FormsModule,
                        IconModule,
                        PopoverModule,
                        SelectModule,
                    ],
                    exports: [SearchBuilderComponent, SearchBuilderGroupComponent, BaseSearchComponent],
                    declarations: [
                        SearchBuilderComponent,
                        SearchBuilderGroupComponent,
                        SearchTextComponent,
                        SearchDateComponent,
                        SearchDateRangeComponent,
                        SearchBuilderOutletDirective,
                        SearchSelectComponent,
                        BaseSearchComponent,
                    ],
                }]
        }] });

class SelectionStrategy {
    constructor(selectionService) {
        this.selectionService = selectionService;
    }
    setSelectionService(selectionService) {
        this.selectionService = selectionService;
    }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    mousedown(event, data) { }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    click(event, data) { }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    keydown(event, data) { }
    /**
     * Select the item - default behavior
     */
    select(...data) {
        this.selectionService.select(...data);
    }
    /**
     * Replace the current selection with the list of items specified
     */
    selectOnly(...data) {
        this.selectionService.selectOnly(...data);
    }
    /**
     * Toggle the item's selected state - default behavior
     */
    toggle(...data) {
        this.selectionService.toggle(...data);
    }
    /**
     * Deselect the item - default behavior
     */
    deselect(...data) {
        this.selectionService.deselect(...data);
    }
    /**
     * Select all items - default behavior
     */
    selectAll() {
        this.select(...this.selectionService.dataset);
    }
    /**
     * Deselect all items - default behavior
     */
    deselectAll() {
        // call deselect on all items in the dataset
        this.selectionService.deselectAll();
    }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    destroy() { }
}

class RowSelectionStrategy extends SelectionStrategy {
    constructor() {
        super(...arguments);
        // store the most recently selected row
        this._selection = { start: null, end: null };
    }
    /**
     * By default on shift click the browser will highlight
     * text. This looks bad and we don't want this to occur
     */
    mousedown(event) {
        event.preventDefault();
    }
    /**
     * When a row is clicked we want to handle selection
     */
    click(event, data) {
        // determine which modifier keys are pressed
        const { ctrlKey, shiftKey } = event;
        // if the shift key is pressed we want to perform a multiple selection
        if (shiftKey) {
            return this.multipleSelect(data);
        }
        // if the control key is pressed we want to perform an additive toggle selection
        if (ctrlKey) {
            return this.toggle(data);
        }
        // perform a single selection where all other rows are deselected
        this.singleSelect(data);
    }
    /**
     * To support full keyboard control we need to support the following:
     * 1. Arrow keys to navigate up and down
     * 2. Spacebar to toggle selection
     * 3. Shift + Arrow keys to multiple select
     * 4. Ctrl + Arrow keys to allow retained selection and navigation
     */
    keydown(event, data) {
        switch (event.which) {
            case UP_ARROW:
            case DOWN_ARROW:
                event.preventDefault();
                this.navigate(event, data);
                break;
            case SPACE:
                event.preventDefault();
                this.selectionService.strategy.toggle(data);
                // also activate the item
                this.selectionService.activate(data);
                break;
        }
    }
    /**
     * Override the standard toggle function to store or clear the
     * most recently selected item
     */
    toggle(data) {
        super.toggle(data);
        // store or clear the selection
        this.selectionService.isSelected(data) ? this.setSelectionStart(data) : this.clearSelection();
    }
    /**
     * Clear all other selected items and select only
     * the most recently selected item
     */
    singleSelect(data) {
        // deselect all other rows if neither modifier key is pressed
        this.deselectAll();
        // select the current row
        this.select(data);
        // store the current item as the selection start
        this.setSelectionStart(data);
    }
    /**
     * Handle multiple selection:
     * 1. If no start item selected - select it
     * 2. If a start item has been selected - select all in between
     * 3. If a start and end item have been selected clear the range and then select the new range
     */
    multipleSelect(data) {
        // if no selection currently exists then perform initial selection
        if (!this._selection.start) {
            // select the row
            this.select(data);
            // store the starting point
            return this.setSelectionStart(data);
        }
        // if a multiple selection already took place - clear the previous selection
        if (this._selection.start && this._selection.end) {
            this.deselect(...this.getSelectedItems());
        }
        // set the new selection end point
        this.setSelectionEnd(data);
        // select all the items in the range
        this.select(...this.getSelectedItems());
    }
    /**
     * Set the selection start point. If there was previously a
     * selection end point then clear it as this is a new selection
     */
    setSelectionStart(data) {
        this._selection.start = data;
        this._selection.end = null;
        // activate the item
        this.selectionService.activate(data);
    }
    /**
     * Set the selection end point
     */
    setSelectionEnd(data) {
        this._selection.end = data;
        // activate the item
        this.selectionService.activate(data);
    }
    /**
     * Clear both start and end selection points
     */
    clearSelection(deactivate = true) {
        // reset the selected item
        this._selection = { start: null, end: null };
        // remove the current active item
        if (deactivate) {
            this.selectionService.deactivate();
        }
    }
    /**
     * Determine all the items affected by the current selection.
     * Note that the end point may be above the start point so
     * we need to account for this.
     */
    getSelectedItems() {
        // get the latest dataset
        const { dataset } = this.selectionService;
        // get the indexes of the start and end point
        const startIdx = dataset.indexOf(this._selection.start);
        const endIdx = dataset.indexOf(this._selection.end);
        // get the region of the array that is selected - note the endIdx may be before the startIdx so account for this
        return dataset.slice(Math.min(startIdx, endIdx), Math.max(startIdx, endIdx) + 1);
    }
    /**
     * Activate the sibling item when arrow keys are pressed
     */
    navigate(event, data) {
        // determine which modifier keys are pressed
        const { ctrlKey, shiftKey } = event;
        // if no modifier keys are pressed then deselect all and clear the selection
        if (!ctrlKey && !shiftKey) {
            this.deselectAll();
            this.clearSelection(false);
        }
        // activate the sibling - if the up arrow is pressed then navigate to the previous sibling
        const sibling = this.selectionService.activateSibling(event.which === UP_ARROW);
        // if the shift key is pressed then we also want to toggle the state if the item
        if (shiftKey && sibling) {
            // if there is no current selection start then select the current row
            if (!this._selection.start) {
                this.multipleSelect(data);
            }
            this.multipleSelect(sibling);
        }
    }
}

class RowAltSelectionStrategy extends RowSelectionStrategy {
    keydown(event, data) {
        switch (event.which) {
            case UP_ARROW:
            case DOWN_ARROW:
                event.preventDefault();
                this.handleCursorKey(event, data);
                break;
            case SPACE:
                event.preventDefault();
                this.selectionService.strategy.toggle(data);
                break;
        }
    }
    /**
     * Select the sibling item when arrow keys are pressed
     */
    handleCursorKey(event, data) {
        // determine which modifier keys are pressed
        const { ctrlKey, shiftKey } = event;
        // if no modifier keys are pressed then deselect all and clear the selection
        if (!ctrlKey && !shiftKey) {
            this.deselectAll();
            this.clearSelection(false);
        }
        if (ctrlKey) {
            this.selectionService.activateSibling(event.which === UP_ARROW);
        }
        else {
            const sibling = this.selectionService.getSibling(event.which === UP_ARROW);
            this.multipleSelect(sibling ? sibling : data);
        }
    }
}

class SimpleSelectionStrategy extends SelectionStrategy {
    /**
     * When the item is clicked simply toggle the current selected state
     */
    click(_event, data) {
        this.toggle(data);
    }
    /**
     * Add basic keyboard support for navigating
     * and selecting/deselecting items
     */
    keydown(event, data) {
        switch (event.which) {
            case UP_ARROW:
                event.preventDefault();
                this.selectionService.activateSibling(true);
                return;
            case DOWN_ARROW:
                event.preventDefault();
                this.selectionService.activateSibling(false);
                return;
            case SPACE:
                event.preventDefault();
                return this.toggle(data);
        }
    }
    /**
     * Override the standard toggle function to always activate the item
     */
    toggle(data) {
        super.toggle(data);
        this.selectionService.activate(data);
    }
}

class SelectionService {
    constructor() {
        /** The active selection strategy that defines how selections can be made */
        this.strategy = new SimpleSelectionStrategy(this);
        /** Define if selections can be performed on any items */
        this.isEnabled = true;
        /** Define if the mouse can be used to perform selections */
        this.isClickEnabled = true;
        /** Define if the keyboard can be used to perform selections */
        this.isKeyboardEnabled = true;
        /** Define the currently focused item */
        this.focus$ = new BehaviorSubject(null);
        /** Define the currently active item */
        this.active$ = new BehaviorSubject(null);
        /** Store the current list of selected items as an array */
        this.selection$ = new BehaviorSubject([]);
        /** Store the current set of selectable items */
        this._dataset = [];
        /** Store the selection strategy that should be destroyed */
        this._strategyToDestroy = this.strategy;
        /** Store the current selection in a set */
        this._selection = new Set();
        /** Store the current disabled items in a set */
        this._disabled = new Set();
    }
    /** Store the current set of selectable items and ensure an item can be focused */
    set dataset(dataset) {
        this._dataset = dataset;
        if (this._dataset.indexOf(this._active) === -1) {
            this.setFirstItemFocusable();
        }
    }
    /** Get the current set of selectable items */
    get dataset() {
        return this._dataset;
    }
    ngOnDestroy() {
        // destroy the active strategy
        if (this._strategyToDestroy) {
            this._strategyToDestroy.destroy();
        }
        // complete all observables
        this.focus$.complete();
        this.active$.complete();
        this.selection$.complete();
    }
    /**
     * If the item is not currently selected then add it
     * to the list of selected items
     */
    select(...selections) {
        // filter out any disabled items
        selections = selections.filter(item => !this._disabled.has(item));
        // add each selection to the set
        selections.forEach(selection => this._selection.add(selection));
        // propagate the changes
        this.selectionHasMutated();
    }
    /**
     * Deselect all currently selected items and replace with a new selection
     */
    selectOnly(...selection) {
        // filter out any disabled items
        selection = selection.filter(item => !this._disabled.has(item));
        // remove all currently selected items
        this._selection.clear();
        // select only the specified item
        selection.forEach(item => this._selection.add(item));
        // emit the changes
        this.selectionHasMutated();
    }
    /**
     * Remove an item from the list of selected items
     */
    deselect(...selections) {
        // remove each item from the set
        selections.forEach(selection => this._selection.delete(selection));
        // propagate the changes
        this.selectionHasMutated();
    }
    /**
     * Remove all items from the list of selected items
     */
    deselectAll() {
        // remove all items in the array
        this.deselect(...this._dataset);
        // clear the set in case any items have been removed from the DOM but are still selected
        this._selection.clear();
    }
    /**
     * Toggle the selected state of any specified items
     */
    toggle(...selections) {
        selections.forEach(selection => this.isSelected(selection) ? this.deselect(selection) : this.select(selection));
    }
    /**
     * Determine whether or not a specific item is currently selected
     */
    isSelected(data) {
        return this._selection.has(data);
    }
    /**
     * Return an observable specifically for notifying the subscriber
     * only when the selection state of a specific object has changed
     */
    getSelectionState(data) {
        return this.selection$.pipe(map(() => this.isSelected(data)), distinctUntilChanged());
    }
    /**
     * Define how selections should be performed.
     * This allows us to use an strategy pattern to handle the various keyboard
     * and mouse interactions while keeping each mode separated and
     * easily extensible if we want to add more modes in future!
     */
    setStrategy(mode) {
        if (this._strategyToDestroy) {
            // Destroy previous strategy if it was created internally
            this._strategyToDestroy.destroy();
            this._strategyToDestroy = null;
        }
        if (mode instanceof SelectionStrategy) {
            // Custom strategy - pass in the service instance
            this.strategy = mode;
            this.strategy.setSelectionService(this);
        }
        else {
            switch (mode.toLowerCase().trim()) {
                case 'simple':
                    this.strategy = this._strategyToDestroy = new SimpleSelectionStrategy(this);
                    break;
                case 'row':
                    this.strategy = this._strategyToDestroy = new RowSelectionStrategy(this);
                    break;
                case 'row-alt':
                    this.strategy = this._strategyToDestroy = new RowAltSelectionStrategy(this);
                    break;
                default:
                    throw new Error(`The selection mode '${mode}' does not exist. Valid modes are 'simple', 'row', or 'row-alt'.`);
            }
        }
    }
    /**
     * Set the current active item
     */
    activate(data) {
        this._active = data;
        this.active$.next(this._active);
    }
    /**
     * Deactive all items
     */
    deactivate() {
        this._active = null;
        this.active$.next(this._active);
    }
    /**
     * Return the next or previous sibling of the current active item.
     * @param previous If true, the previous sibling will be returned.
     */
    getSibling(previous = false) {
        // check if there is a current active item
        if (!this._active) {
            return;
        }
        // get the index of the current item
        const idx = this.dataset.indexOf(this._active);
        const target = this.dataset[previous ? idx - 1 : idx + 1];
        return target;
    }
    /**
     * Activate the sibling of the current active item.
     * If previous is set to true the previous sibling will be activated
     * rather than the next sibling. This function will also return the
     * data of the newly activated sibling
     */
    activateSibling(previous = false) {
        const target = this.getSibling(previous);
        // check if the target exists
        if (target) {
            this.activate(target);
        }
        return target;
    }
    setDisabled(disabled) {
        // store the current disabled state
        this.isEnabled = !disabled;
        // clear any stateful data
        this._active = null;
        this.active$.next(this._active);
        this._selection.clear();
        // emit the selection change information
        this.selectionHasMutated();
    }
    /** Store the disabled state of an item */
    setItemDisabled(item, isDisabled) {
        // update the internal list of disabled items
        if (isDisabled && !this._disabled.has(item)) {
            this._disabled.add(item);
        }
        else if (!isDisabled) {
            this._disabled.delete(item);
        }
    }
    selectionHasMutated() {
        this.selection$.next(Array.from(this._selection));
    }
    setFirstItemFocusable() {
        if (this._dataset.length > 0) {
            this.focus$.next(this._dataset[0]);
            this._active = this._dataset[0];
        }
        else {
            this._active = null;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionService, decorators: [{
            type: Injectable
        }] });

class SelectListItemComponent {
    set selected(isSelected) {
        isSelected ? this._selection.select(this.data) : this._selection.deselect(this.data);
    }
    get selected() {
        return this._selection.isSelected(this.data);
    }
    constructor() {
        this._selection = inject(SelectionService);
        this.elementRef = inject(ElementRef);
        this.focusIndicatorService = inject(FocusIndicatorService);
        this.tabindex = -1;
        /** Unsubscribe from all subscriptions on destroy */
        this._onDestroy = new Subject();
        // create the focus indicator
        this._focusIndicator = this.focusIndicatorService.monitor(this.elementRef.nativeElement);
        this._selection.active$
            .pipe(takeUntil(this._onDestroy), filter(data => data === this.data))
            .subscribe(active => {
            this._selection.focus$.next(active);
            this.elementRef.nativeElement.focus();
        });
        // make this item tabbable or not based on the focused element
        this._selection.focus$
            .pipe(takeUntil(this._onDestroy), tick())
            .subscribe(focused => (this.tabindex = focused === this.data ? 0 : -1));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this._focusIndicator.destroy();
    }
    onMouseDown(event) {
        this._selection.strategy.mousedown(event, this.data);
    }
    onClick(event) {
        this._selection.strategy.click(event, this.data);
    }
    onKeydown(event) {
        this._selection.strategy.keydown(event, this.data);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: SelectListItemComponent, isStandalone: false, selector: "ux-select-list-item", inputs: { data: "data" }, host: { attributes: { "role": "listitem" }, listeners: { "mousedown": "onMouseDown($event)", "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "tabindex": "this.tabindex", "class.selected": "this.selected", "attr.aria-selected": "this.selected" } }, ngImport: i0, template: "<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-select-list-item', host: {
                        role: 'listitem',
                    }, standalone: false, template: "<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [], propDecorators: { data: [{
                type: Input
            }], tabindex: [{
                type: HostBinding,
                args: ['tabindex']
            }], selected: [{
                type: HostBinding,
                args: ['class.selected']
            }, {
                type: HostBinding,
                args: ['attr.aria-selected']
            }], onMouseDown: [{
                type: HostListener,
                args: ['mousedown', ['$event']]
            }], onClick: [{
                type: HostListener,
                args: ['click', ['$event']]
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class SelectionItemDirective {
    /** Defines whether or not this item is currently selected. */
    set selected(selected) {
        selected ? this.select() : this.deselect();
    }
    get selected() {
        return this._selected;
    }
    /** Determine whether or not this item can be selected */
    set uxSelectionDisabled(isDisabled) {
        // if this item was selected then deselect it
        if (this._selected && isDisabled) {
            this.deselect();
        }
        // inform the selection service of the disabled state
        this._selectionService.setItemDisabled(this.uxSelectionItem, isDisabled);
        // store the current disabled state
        this._isDisabled = isDisabled;
    }
    /** Whether aria-selected is added to the host element */
    set addAriaAttributes(value) {
        this._addAriaAttributes = coerceBooleanProperty(value);
    }
    get addAriaAttributes() {
        return this._addAriaAttributes;
    }
    get attrTabIndex() {
        return this.tabindex !== null ? this.tabindex : this._managedTabIndex;
    }
    constructor() {
        this.focusIndicatorService = inject(FocusIndicatorService);
        this._selectionService = inject(SelectionService);
        this._elementRef = inject(ElementRef);
        this._managedFocusContainerService = inject(ManagedFocusContainerService);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Defines the tab index of the row */
        this.tabindex = null;
        /** Defines whether or not this item is currently selected. */
        this.selectedChange = new EventEmitter();
        /** Store whether this item is the focusable item */
        this.active = false;
        /** Store the focused state of the element */
        this.isFocused = false;
        /** Store the current selected state */
        this._selected = false;
        /** Store the disabled state */
        this._isDisabled = false;
        /** Store the tab indexed if using the managed focus container */
        this._managedTabIndex = -1;
        /** Determine if there is a pending state change as we debounce before emitting */
        this._hasPendingStateChange = false;
        /** Store value for _addAriaAttributes */
        this._addAriaAttributes = true;
        /** Automatically unsubscribe when the component is destroyed */
        this._onDestroy = new Subject();
        this._focusIndicator = this.focusIndicatorService.monitor(this._elementRef.nativeElement);
    }
    ngOnInit() {
        // if there is no associated data then throw an error
        if (!this.uxSelectionItem) {
            throw new Error('The uxSelectionItem directive must have data associated with it.');
        }
        // subscribe to changes to the active state
        this._selectionService.active$
            .pipe(takeUntil(this._onDestroy), map(active => active === this.uxSelectionItem))
            .subscribe(active => {
            // store the focus state
            this.active = active;
            // if it is active then focus the element
            if (active === true) {
                this._selectionService.focus$.next(this.uxSelectionItem);
                this._elementRef.nativeElement.focus();
            }
        });
        // Subscribe to changes to the focus target
        // This is mostly the same as active$, except that it has an initial value of the first item in the collection.
        this._selectionService.focus$.pipe(takeUntil(this._onDestroy)).subscribe(focusTarget => {
            this._managedTabIndex = focusTarget === this.uxSelectionItem ? 0 : -1;
        });
        // Watch for focus within the container element and manage tabindex of descendants
        this._managedFocusContainerService.register(this._elementRef.nativeElement, this);
        // Listen for changes to the focus state and apply the appropriate class
        this._focusIndicator.origin$
            .pipe(map(origin => origin === 'keyboard'), takeUntil(this._onDestroy))
            .subscribe(isFocused => {
            this.isFocused = isFocused;
            this._changeDetector.markForCheck();
        });
    }
    ngOnChanges(changes) {
        if (changes.uxSelectionItem) {
            this.updateSelectionItem();
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this._focusIndicator.destroy();
        this._managedFocusContainerService.unregister(this._elementRef.nativeElement, this);
    }
    click(event) {
        if (!this._isDisabled &&
            !this._hasPendingStateChange &&
            this._selectionService.isEnabled &&
            this._selectionService.isClickEnabled) {
            this._selectionService.strategy.click(event, this.uxSelectionItem);
        }
    }
    mousedown(event) {
        if (!this._isDisabled &&
            this._selectionService.isEnabled &&
            this._selectionService.isClickEnabled) {
            this._selectionService.strategy.mousedown(event, this.uxSelectionItem);
        }
    }
    keydown(event) {
        // if the space key (selection key) is pressed and we are disabled then we should block
        // the event from propagating. However if this is a key such as arrow presses then we do
        // still want this to propagate to allow keyboard navigation for accessibility purposes.
        const isDisabled = this._isDisabled && event.keyCode === SPACE;
        if (!isDisabled &&
            this._selectionService.isEnabled &&
            this._selectionService.isKeyboardEnabled) {
            this._selectionService.strategy.keydown(event, this.uxSelectionItem);
        }
    }
    focus() {
        // If tabbed to from outside the component, activate.
        if (this._selectionService.active$.getValue() !== this.uxSelectionItem) {
            this._selectionService.activate(this.uxSelectionItem);
        }
    }
    /**
     * Select this item using the current strategy
     */
    select() {
        if (!this._isDisabled && this._selectionService.isEnabled) {
            this._selectionService.strategy.select(this.uxSelectionItem);
        }
    }
    /**
     * Deselect this item using the current strategy
     */
    deselect() {
        if (!this._isDisabled && this._selectionService.isEnabled) {
            this._selectionService.strategy.deselect(this.uxSelectionItem);
        }
    }
    updateSelectionItem() {
        if (this._selectionStateSubscription) {
            this._selectionStateSubscription.unsubscribe();
        }
        // subscribe to selection changes on this item (don't emit the initial value)
        this._selectionStateSubscription = this._selectionService
            .getSelectionState(this.uxSelectionItem)
            .pipe(skip(1), tap(() => (this._hasPendingStateChange = true)), debounceTime(0), takeUntil(this._onDestroy))
            .subscribe(selected => {
            this._hasPendingStateChange = false;
            if (this._selected === selected) {
                return;
            }
            // store the selected state
            this._selected = selected;
            // emit the selected state
            this.selectedChange.emit(selected);
            this._changeDetector.markForCheck();
        });
        this._selected = this._selectionService.isSelected(this.uxSelectionItem);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SelectionItemDirective, isStandalone: false, selector: "[uxSelectionItem]", inputs: { uxSelectionItem: "uxSelectionItem", selected: "selected", tabindex: "tabindex", uxSelectionDisabled: "uxSelectionDisabled", addAriaAttributes: "addAriaAttributes" }, outputs: { selectedChange: "selectedChange" }, host: { listeners: { "click": "click($event)", "mousedown": "mousedown($event)", "keydown": "keydown($event)", "focus": "focus()" }, properties: { "attr.aria-selected": "addAriaAttributes ? selected : null", "class.ux-selection-selected": "this.selected", "class.ux-selection-focused": "this.isFocused", "attr.tabindex": "this.attrTabIndex" } }, exportAs: ["ux-selection-item"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxSelectionItem]',
                    exportAs: 'ux-selection-item',
                    host: {
                        '[attr.aria-selected]': 'addAriaAttributes ? selected : null',
                    },
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxSelectionItem: [{
                type: Input
            }], selected: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.ux-selection-selected']
            }], tabindex: [{
                type: Input
            }], uxSelectionDisabled: [{
                type: Input
            }], addAriaAttributes: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], isFocused: [{
                type: HostBinding,
                args: ['class.ux-selection-focused']
            }], attrTabIndex: [{
                type: HostBinding,
                args: ['attr.tabindex']
            }], click: [{
                type: HostListener,
                args: ['click', ['$event']]
            }], mousedown: [{
                type: HostListener,
                args: ['mousedown', ['$event']]
            }], keydown: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }], focus: [{
                type: HostListener,
                args: ['focus']
            }] } });

class SelectionDirective {
    /** Defines the items that should be selected. */
    set uxSelection(items) {
        this._lastSelection = items;
        this._selectionService.selectOnly(...items);
    }
    /** Can be used to enabled/disable selection behavior. */
    set disabled(disabled) {
        this._selectionService.setDisabled(disabled);
    }
    /**
     * Defines the selection behavior. Alternatively, custom selection behavior can be defined by defining a
     * class which extends SelectionStrategy, and providing an instance of the custom class to this property.
     * See below for details of the SelectionStrategy class.
     */
    set mode(mode) {
        this._selectionService.setStrategy(mode);
    }
    /**
     * Can be used to enable/disable click selection on items. This can be used to manually control the selection of an item,
     * for example, binding the selection state to a checkbox.
     */
    set clickSelection(isClickEnabled) {
        this._selectionService.isClickEnabled = isClickEnabled;
    }
    /** Can be used to enable/disable keyboard navigation on items. Use this if you wish to provide custom keyboard controls for selection. */
    set keyboardSelection(isKeyboardEnabled) {
        this._selectionService.isKeyboardEnabled = isKeyboardEnabled;
    }
    /**
     * The full set of selection items.
     * Only needed if the full set of `uxSelectionItem`s is not available, e.g. within a virtual scroll container.
     */
    set selectionItems(value) {
        this._hasExplicitDataset = !!value;
        if (value) {
            this._selectionService.dataset = value;
        }
    }
    constructor() {
        this._selectionService = inject(SelectionService);
        this._cdRef = inject(ChangeDetectorRef);
        /** The tabstop of the selection outer element */
        this.tabindex = null;
        /** This event will be triggered when there is a change to the selected items. It will contain an array of the currently selected items. */
        this.uxSelectionChange = new EventEmitter();
        /** Unsubscribe from all observables on component destroy */
        this._onDestroy = new Subject();
        /** Store the previous selection so we don't emit more than we have to */
        this._lastSelection = [];
        /** Whether a value has been provided to the `selectionItems` input. */
        this._hasExplicitDataset = false;
        this._selectionService.selection$
            .pipe(debounceTime(0), takeUntil(this._onDestroy))
            .subscribe(items => {
            if (this.isSelectionChanged(items)) {
                this.uxSelectionChange.emit(items);
            }
            // store the most recent selection
            this._lastSelection = [...items];
        });
    }
    ngAfterContentInit() {
        // provide the initial list of selection items
        this.update();
        // if the list changes then inform the service
        this.items.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => this.update());
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Update the dataset to reflect the latest selection items
     */
    update() {
        // Capture the set of data items from the ContentChildren, unless an explicit value has been provided.
        if (!this._hasExplicitDataset) {
            this._selectionService.dataset = this.items.map(item => item.uxSelectionItem);
        }
        // Make sure that a tab target has been defined so that the component can be tabbed to.
        if (this._selectionService.focus$.getValue() === null &&
            this._selectionService.dataset.length > 0) {
            this._selectionService.focus$.next(this._selectionService.dataset[0]);
        }
        // The above could trigger a change in the computed tabindex for selection items
        this._cdRef.detectChanges();
    }
    /**
     * Select all the items in the list
     */
    selectAll() {
        if (this._selectionService.isEnabled) {
            this._selectionService.strategy.selectAll();
        }
    }
    /**
     * Deselect all currently selected items
     */
    deselectAll() {
        if (this._selectionService.isEnabled) {
            this._selectionService.strategy.deselectAll();
        }
    }
    /**
     * Determine if the previous selection is the same as the current selection
     */
    isSelectionChanged(selection) {
        // fast, efficient check, if length is different they must have changed
        if ((!this._lastSelection && selection) || this._lastSelection.length !== selection.length) {
            return true;
        }
        // if both arrays have 0 items then they have not changed
        if (this._lastSelection.length === 0 && selection.length === 0) {
            return false;
        }
        // otherwise do a check on each item
        return !this._lastSelection.every(item => selection.indexOf(item) !== -1);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SelectionDirective, isStandalone: false, selector: "[uxSelection]", inputs: { uxSelection: "uxSelection", disabled: "disabled", mode: "mode", clickSelection: "clickSelection", keyboardSelection: "keyboardSelection", selectionItems: "selectionItems", tabindex: "tabindex" }, outputs: { uxSelectionChange: "uxSelectionChange" }, host: { properties: { "attr.tabindex": "this.tabindex" } }, providers: [SelectionService], queries: [{ propertyName: "items", predicate: SelectionItemDirective }], exportAs: ["ux-selection"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxSelection]',
                    exportAs: 'ux-selection',
                    providers: [SelectionService],
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxSelection: [{
                type: Input
            }], disabled: [{
                type: Input
            }], mode: [{
                type: Input
            }], clickSelection: [{
                type: Input
            }], keyboardSelection: [{
                type: Input
            }], selectionItems: [{
                type: Input
            }], tabindex: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['attr.tabindex']
            }], uxSelectionChange: [{
                type: Output
            }], items: [{
                type: ContentChildren,
                args: [SelectionItemDirective]
            }] } });

class SelectionModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SelectionModule, declarations: [SelectionDirective, SelectionItemDirective], imports: [CommonModule], exports: [SelectionDirective, SelectionItemDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectionModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule],
                    declarations: [SelectionDirective, SelectionItemDirective],
                    exports: [SelectionDirective, SelectionItemDirective],
                }]
        }] });

class MultipleSelectListStrategy extends SelectionStrategy {
    /** Prevent the browser from highlighting text on shift click */
    mousedown(event) {
        event.preventDefault();
    }
    click(event, data) {
        // activate the clicked item
        this.selectionService.activate(data);
        // if the shift key is pressed we want to perform a multiple selection
        if (event.shiftKey) {
            return this.multipleSelect(data);
        }
        // otherwise perform a single toggle selection
        if (this.selectionService.isSelected(data)) {
            this.deselect(data);
            this._lastSelection = null;
        }
        else {
            this.select(data);
            this._lastSelection = data;
        }
    }
    keydown(event, data) {
        switch (event.which) {
            case UP_ARROW: {
                event.preventDefault();
                const sibling = this.selectionService.activateSibling(true);
                if (event.shiftKey) {
                    this.select(data, sibling);
                    this._lastSelection = sibling;
                }
                break;
            }
            case DOWN_ARROW: {
                event.preventDefault();
                const sibling = this.selectionService.activateSibling(false);
                if (event.shiftKey) {
                    this.select(data, sibling);
                    this._lastSelection = sibling;
                }
                break;
            }
            case SPACE:
            case ENTER:
                event.preventDefault();
                this.toggle(data);
                this._lastSelection = this.selectionService.isSelected(data) ? data : null;
                break;
        }
    }
    multipleSelect(data) {
        // if there is no start item selected
        if (!this._lastSelection) {
            this.select(data);
            this._lastSelection = data;
            return;
        }
        // if there already is a start item then find the items in the range
        this.select(...this.getSelectedItems(this._lastSelection, data));
        // store the selection end point
        this._lastSelection = data;
    }
    getSelectedItems(start, end) {
        // get the latest dataset
        const { dataset } = this.selectionService;
        // get the indexes of the start and end point
        const startIdx = dataset.indexOf(start);
        const endIdx = dataset.indexOf(end);
        // get the region of the array that is selected - note the endIdx may be before the startIdx so account for this
        return dataset.slice(Math.min(startIdx, endIdx), Math.max(startIdx, endIdx) + 1);
    }
}

class SingleSelectListStrategy extends SelectionStrategy {
    click(_event, data) {
        // activate the clicked item
        this.selectionService.activate(data);
        // toggle the selected state of the item
        if (!this.selectionService.isSelected(data)) {
            this.selectOnly(data);
        }
        else {
            this.deselect(data);
        }
    }
    keydown(event, data) {
        switch (event.which) {
            case UP_ARROW: {
                event.preventDefault();
                this.selectionService.activateSibling(true);
                break;
            }
            case DOWN_ARROW: {
                event.preventDefault();
                this.selectionService.activateSibling(false);
                break;
            }
            case SPACE:
            case ENTER:
                event.preventDefault();
                this.click(null, data);
                break;
        }
    }
}

class SelectListComponent {
    /** Determine if we allow multiple items to be selected */
    set multiple(multiple) {
        this._selection.strategy.deselectAll();
        this._selection.setStrategy(multiple ? new MultipleSelectListStrategy() : new SingleSelectListStrategy());
    }
    /** Set the selected items */
    set selected(selected) {
        // if the selection entered is the same as the current selection then do nothing
        if (this._selection.selection$.value === selected) {
            return;
        }
        // if selected is an array and has not items and there are no items currently selected also do nothing
        if (Array.isArray(selected) &&
            selected.length === 0 &&
            this._selection.selection$.value.length === 0) {
            return;
        }
        // select only the specified items
        if (Array.isArray(selected)) {
            this._selection.selectOnly(...selected);
        }
        else {
            this._selection.selectOnly(selected);
        }
    }
    constructor() {
        this._selection = inject(SelectionService);
        /** Emit when the selection changes */
        this.selectedChange = new EventEmitter();
        /** Automatically unsubscribe all observables */
        this._onDestroy = new Subject();
        // set the selection strategy to single by default
        this._selection.setStrategy(new SingleSelectListStrategy());
        // emit the selection changes when they occur
        this._selection.selection$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(selection => this.selectedChange.emit(selection));
    }
    ngAfterContentInit() {
        // supply the initial item set
        this._selection.dataset = this.items.map(item => item.data);
        // if the item set changes update the list
        this.items.changes
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => (this._selection.dataset = this.items.map(item => item.data)));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: SelectListComponent, isStandalone: false, selector: "ux-select-list", inputs: { multiple: "multiple", selected: "selected" }, outputs: { selectedChange: "selectedChange" }, host: { attributes: { "role": "list" } }, providers: [SelectionService], queries: [{ propertyName: "items", predicate: SelectListItemComponent }], ngImport: i0, template: "<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-select-list', providers: [SelectionService], host: {
                        role: 'list',
                    }, standalone: false, template: "<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [], propDecorators: { multiple: [{
                type: Input
            }], selected: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], items: [{
                type: ContentChildren,
                args: [SelectListItemComponent]
            }] } });

class SelectListModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SelectListModule, declarations: [SelectListComponent, SelectListItemComponent], imports: [AccessibilityModule], exports: [SelectListComponent, SelectListItemComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListModule, imports: [AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectListModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule],
                    declarations: [SelectListComponent, SelectListItemComponent],
                    exports: [SelectListComponent, SelectListItemComponent],
                }]
        }] });

class SidePanelCloseDirective {
    constructor() {
        this._service = inject(SidePanelService);
        this._focusOrigin = inject(FocusIndicatorOriginService);
    }
    onClick(event) {
        // determine the correct origin for the trigger event
        this._focusOrigin.setOrigin(isKeyboardTrigger(event) ? 'keyboard' : 'mouse');
        // close the side panel menu
        this._service.close();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelCloseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SidePanelCloseDirective, isStandalone: false, selector: "[uxSidePanelClose]", host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelCloseDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxSidePanelClose]',
                    standalone: false,
                }]
        }], propDecorators: { onClick: [{
                type: HostListener,
                args: ['click', ['$event']]
            }] } });

const EXPORTS$1 = [SidePanelComponent, SidePanelCloseDirective];
class SidePanelModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SidePanelModule, declarations: [SidePanelComponent, SidePanelCloseDirective], imports: [AccessibilityModule, CommonModule, A11yModule, FocusIfModule], exports: [SidePanelComponent, SidePanelCloseDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelModule, imports: [AccessibilityModule, CommonModule, A11yModule, FocusIfModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidePanelModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, A11yModule, FocusIfModule],
                    exports: EXPORTS$1,
                    declarations: EXPORTS$1,
                }]
        }] });

class SparkComponent {
    constructor() {
        this._colorService = inject(ColorService);
        this.values = [];
        this.barHeight = 10;
        this._theme = 'primary';
        this._barColor = [];
    }
    set theme(value) {
        this._theme = this._colorService.resolveColorName(value);
    }
    get theme() {
        return this._theme;
    }
    set trackColor(value) {
        this._trackColor = this._colorService.resolve(value);
    }
    get trackColor() {
        return this._trackColor;
    }
    set barColor(value) {
        if (Array.isArray(value)) {
            this._barColor = value.map(color => this._colorService.resolve(color));
        }
        else {
            this._barColor = [this._colorService.resolve(value)];
        }
    }
    get barColor() {
        return this._barColor;
    }
    set value(value) {
        // ensure 'value' is an array at this point
        const values = Array.isArray(value) ? value : [value];
        // get the total value of all lines
        const total = Math.max(values.reduce((previous, current) => previous + current, 0), 100);
        // figure out the percentages for each spark line
        this.values = values.map(val => (val / total) * 100);
    }
    get value() {
        return this.values;
    }
    /**
     * Get the aria label for the spark chart
     */
    getAriaLabel() {
        if (!Array.isArray(this.ariaLabel)) {
            return this.ariaLabel || this.tooltip;
        }
        else {
            return this.ariaLabel.join(', ');
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SparkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SparkComponent, isStandalone: false, selector: "ux-spark", inputs: { barHeight: "barHeight", inlineLabel: "inlineLabel", topLeftLabel: "topLeftLabel", topRightLabel: "topRightLabel", bottomLeftLabel: "bottomLeftLabel", bottomRightLabel: "bottomRightLabel", tooltip: "tooltip", ariaLabel: ["aria-label", "ariaLabel"], ariaDescription: ["aria-description", "ariaDescription"], theme: "theme", trackColor: "trackColor", barColor: "barColor", value: "value" }, ngImport: i0, template: "<!-- Inline Spark Chart -->\n@if (inlineLabel) {\n  <div class=\"ux-spark-inline-label-container\">\n    <div class=\"ux-spark-inline-label-left\" [innerHtml]=\"inlineLabel\"></div>\n    <div class=\"ux-spark-line\">\n      @if (topLeftLabel || topRightLabel) {\n        <div class=\"ux-spark-top-container\">\n          @if (topLeftLabel) {\n            <div class=\"ux-spark-label-top-left\" [innerHtml]=\"topLeftLabel\"></div>\n          }\n          @if (topRightLabel) {\n            <div class=\"ux-spark-label-top-right\" [innerHtml]=\"topRightLabel\"></div>\n          }\n        </div>\n      }\n      <div\n        role=\"progressbar\"\n        [attr.aria-label]=\"getAriaLabel()\"\n        [attr.aria-description]=\"ariaDescription\"\n        aria-valuemin=\"0\"\n        aria-valuemax=\"100\"\n        [attr.aria-valuenow]=\"values.length === 1 ? values[0] : undefined\"\n        class=\"ux-spark ux-inline ux-spark-theme-{{ theme }}\"\n        [style.height.px]=\"barHeight\"\n        [style.backgroundColor]=\"trackColor\"\n        [uxTooltip]=\"tooltip\"\n      >\n        @for (line of values; track line; let idx = $index) {\n          <div\n            class=\"ux-spark-bar\"\n            [style.width.%]=\"line\"\n            [style.backgroundColor]=\"barColor[idx]\"\n          ></div>\n        }\n      </div>\n      @if (bottomLeftLabel || bottomRightLabel) {\n        <div class=\"ux-spark-bottom-container\">\n          @if (bottomLeftLabel) {\n            <div class=\"ux-spark-label-bottom-left\" [innerHtml]=\"bottomLeftLabel\"></div>\n          }\n          @if (bottomRightLabel) {\n            <div class=\"ux-spark-label-bottom-right\" [innerHtml]=\"bottomRightLabel\"></div>\n          }\n        </div>\n      }\n    </div>\n  </div>\n}\n\n<!-- End Inline Spark Chart -->\n\n<!-- Non Inline Spark Chart -->\n@if (!inlineLabel) {\n  <div>\n    @if (topLeftLabel || topRightLabel) {\n      <div class=\"ux-spark-top-container\">\n        @if (topLeftLabel) {\n          <div class=\"ux-spark-label-top-left\" [innerHtml]=\"topLeftLabel\"></div>\n        }\n        @if (topRightLabel) {\n          <div class=\"ux-spark-label-top-right\" [innerHtml]=\"topRightLabel\"></div>\n        }\n      </div>\n    }\n    <div\n      role=\"progressbar\"\n      [attr.aria-label]=\"getAriaLabel()\"\n      [attr.aria-description]=\"ariaDescription\"\n      aria-valuemin=\"0\"\n      aria-valuemax=\"100\"\n      [attr.aria-valuenow]=\"values.length === 1 ? values[0] : undefined\"\n      class=\"ux-spark ux-spark-theme-{{ theme }}\"\n      [class.ux-spark-multi-value]=\"values.length > 1\"\n      [style.height.px]=\"barHeight\"\n      [style.backgroundColor]=\"trackColor\"\n      [uxTooltip]=\"tooltip\"\n    >\n      @for (line of values; track line; let idx = $index) {\n        <div\n          class=\"ux-spark-bar\"\n          [style.width.%]=\"line\"\n          [style.backgroundColor]=\"barColor[idx]\"\n        ></div>\n      }\n    </div>\n    @if (bottomLeftLabel || bottomRightLabel) {\n      <div class=\"ux-spark-bottom-container\">\n        @if (bottomLeftLabel) {\n          <div class=\"ux-spark-label-bottom-left\" [innerHtml]=\"bottomLeftLabel\"></div>\n        }\n        @if (bottomRightLabel) {\n          <div class=\"ux-spark-label-bottom-right\" [innerHtml]=\"bottomRightLabel\"></div>\n        }\n      </div>\n    }\n  </div>\n}\n\n<!-- End Non Inline Spark Chart -->\n", dependencies: [{ kind: "directive", type: TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SparkComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-spark', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- Inline Spark Chart -->\n@if (inlineLabel) {\n  <div class=\"ux-spark-inline-label-container\">\n    <div class=\"ux-spark-inline-label-left\" [innerHtml]=\"inlineLabel\"></div>\n    <div class=\"ux-spark-line\">\n      @if (topLeftLabel || topRightLabel) {\n        <div class=\"ux-spark-top-container\">\n          @if (topLeftLabel) {\n            <div class=\"ux-spark-label-top-left\" [innerHtml]=\"topLeftLabel\"></div>\n          }\n          @if (topRightLabel) {\n            <div class=\"ux-spark-label-top-right\" [innerHtml]=\"topRightLabel\"></div>\n          }\n        </div>\n      }\n      <div\n        role=\"progressbar\"\n        [attr.aria-label]=\"getAriaLabel()\"\n        [attr.aria-description]=\"ariaDescription\"\n        aria-valuemin=\"0\"\n        aria-valuemax=\"100\"\n        [attr.aria-valuenow]=\"values.length === 1 ? values[0] : undefined\"\n        class=\"ux-spark ux-inline ux-spark-theme-{{ theme }}\"\n        [style.height.px]=\"barHeight\"\n        [style.backgroundColor]=\"trackColor\"\n        [uxTooltip]=\"tooltip\"\n      >\n        @for (line of values; track line; let idx = $index) {\n          <div\n            class=\"ux-spark-bar\"\n            [style.width.%]=\"line\"\n            [style.backgroundColor]=\"barColor[idx]\"\n          ></div>\n        }\n      </div>\n      @if (bottomLeftLabel || bottomRightLabel) {\n        <div class=\"ux-spark-bottom-container\">\n          @if (bottomLeftLabel) {\n            <div class=\"ux-spark-label-bottom-left\" [innerHtml]=\"bottomLeftLabel\"></div>\n          }\n          @if (bottomRightLabel) {\n            <div class=\"ux-spark-label-bottom-right\" [innerHtml]=\"bottomRightLabel\"></div>\n          }\n        </div>\n      }\n    </div>\n  </div>\n}\n\n<!-- End Inline Spark Chart -->\n\n<!-- Non Inline Spark Chart -->\n@if (!inlineLabel) {\n  <div>\n    @if (topLeftLabel || topRightLabel) {\n      <div class=\"ux-spark-top-container\">\n        @if (topLeftLabel) {\n          <div class=\"ux-spark-label-top-left\" [innerHtml]=\"topLeftLabel\"></div>\n        }\n        @if (topRightLabel) {\n          <div class=\"ux-spark-label-top-right\" [innerHtml]=\"topRightLabel\"></div>\n        }\n      </div>\n    }\n    <div\n      role=\"progressbar\"\n      [attr.aria-label]=\"getAriaLabel()\"\n      [attr.aria-description]=\"ariaDescription\"\n      aria-valuemin=\"0\"\n      aria-valuemax=\"100\"\n      [attr.aria-valuenow]=\"values.length === 1 ? values[0] : undefined\"\n      class=\"ux-spark ux-spark-theme-{{ theme }}\"\n      [class.ux-spark-multi-value]=\"values.length > 1\"\n      [style.height.px]=\"barHeight\"\n      [style.backgroundColor]=\"trackColor\"\n      [uxTooltip]=\"tooltip\"\n    >\n      @for (line of values; track line; let idx = $index) {\n        <div\n          class=\"ux-spark-bar\"\n          [style.width.%]=\"line\"\n          [style.backgroundColor]=\"barColor[idx]\"\n        ></div>\n      }\n    </div>\n    @if (bottomLeftLabel || bottomRightLabel) {\n      <div class=\"ux-spark-bottom-container\">\n        @if (bottomLeftLabel) {\n          <div class=\"ux-spark-label-bottom-left\" [innerHtml]=\"bottomLeftLabel\"></div>\n        }\n        @if (bottomRightLabel) {\n          <div class=\"ux-spark-label-bottom-right\" [innerHtml]=\"bottomRightLabel\"></div>\n        }\n      </div>\n    }\n  </div>\n}\n\n<!-- End Non Inline Spark Chart -->\n" }]
        }], propDecorators: { barHeight: [{
                type: Input
            }], inlineLabel: [{
                type: Input
            }], topLeftLabel: [{
                type: Input
            }], topRightLabel: [{
                type: Input
            }], bottomLeftLabel: [{
                type: Input
            }], bottomRightLabel: [{
                type: Input
            }], tooltip: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], ariaDescription: [{
                type: Input,
                args: ['aria-description']
            }], theme: [{
                type: Input
            }], trackColor: [{
                type: Input
            }], barColor: [{
                type: Input
            }], value: [{
                type: Input
            }] } });

class SparkModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SparkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: SparkModule, declarations: [SparkComponent], imports: [CommonModule, ColorServiceModule, TooltipModule], exports: [SparkComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SparkModule, imports: [CommonModule, ColorServiceModule, TooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SparkModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, ColorServiceModule, TooltipModule],
                    exports: [SparkComponent],
                    declarations: [SparkComponent],
                }]
        }] });

function isColumnPickerGroupItem(column) {
    return column.name !== undefined;
}

class ColumnPickerService {
    getDeselectedColumnsInPresentationOrder(deselected, sort) {
        let columns;
        if (sort) {
            const normalizedColumns = this.normalizeColumns(deselected);
            normalizedColumns.sort(sort);
            columns = this.denormalizeColumns(normalizedColumns, deselected);
        }
        else {
            const grouped = deselected.filter(column => isColumnPickerGroupItem(column) && column.group !== undefined);
            columns = [...grouped, ...deselected.filter(column => grouped.indexOf(column) === -1)];
        }
        return columns;
    }
    createTreeData(columns) {
        const treeData = [];
        const groupedColumns = columns.filter(column => isColumnPickerGroupItem(column) && column.group !== undefined);
        columns.forEach((column) => {
            if (groupedColumns.indexOf(column) !== -1 && isColumnPickerGroupItem(column)) {
                const groupNode = this.createOrFindGroupNode(column, treeData);
                groupNode.children.push(column.name);
                this.createColumnTreeNode(column, treeData, 1);
            }
            else if (!isColumnPickerGroupItem(column)) {
                this.createColumnTreeNode(column, treeData, 0);
            }
        });
        return treeData;
    }
    normalizeColumns(columns) {
        return columns.map(column => ({
            name: isColumnPickerGroupItem(column) ? column.name : column,
            group: isColumnPickerGroupItem(column) ? column.group : undefined,
        }));
    }
    denormalizeColumns(normalizedColumns, originalColumns) {
        return normalizedColumns.map(normalized => {
            const original = originalColumns.find(originalColumn => {
                if (isColumnPickerGroupItem(originalColumn)) {
                    return (originalColumn.group === normalized.group && originalColumn.name === normalized.name);
                }
                return false;
            });
            // Anything not found in the original array must have been normalized from a string
            return original ? original : normalized.name;
        });
    }
    createOrFindGroupNode(column, treeData) {
        let groupNode = treeData.find(node => node.name === column.group && node.expandable);
        if (!groupNode) {
            groupNode = this.createGroupTreeNode(column, treeData);
        }
        return groupNode;
    }
    createGroupTreeNode(column, treeData) {
        treeData.push({
            name: column.group,
            level: 0,
            expandable: true,
            isExpanded: this.isGroupTreeNodeExpanded(column.group),
            column,
            children: [],
        });
        return treeData[treeData.length - 1];
    }
    createColumnTreeNode(column, treeData, level) {
        treeData.push({
            name: isColumnPickerGroupItem(column) ? column.name : column,
            level,
            expandable: false,
            column,
        });
    }
    isGroupTreeNodeExpanded(groupName) {
        const groupTreeNode = this.groups.find(group => group.name === groupName);
        return groupTreeNode ? groupTreeNode.expanded : false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnPickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnPickerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnPickerService, decorators: [{
            type: Injectable
        }] });

let uniqueId$1 = 0;
class ColumnPickerComponent {
    constructor() {
        this._columnPicker = inject(ColumnPickerService);
        /** Access the LiveAnnounce to provide accessibility on reordering */
        this._liveAnnouncer = inject(LiveAnnouncer);
        /** We are using OnPush change detection so we must manually trigger CD */
        this._changeDetectorRef = inject(ChangeDetectorRef);
        /** Sets the id of the column picker. */
        this.id = `ux-number-picker-${uniqueId$1++}`;
        /** Define a list of all selected columns. */
        this.selected = [];
        /** Define a list of columns that are always selected. The columns cannot be moved or reordered. */
        this.locked = [];
        /** Define a list of columns that are not selected or locked. All columns must have unique names, including columns in different groups. */
        this.deselected = [];
        /** Define a function to get the aria label of reorderable items in the selected column. */
        this.selectedAriaLabel = this.getDefaultSelectedAriaLabel;
        /** Define a function to get the aria label of a group in the deselected list. */
        this.deselectedGroupAriaLabel = this.getDefaultDeselectedGroupAriaLabel;
        /** Define a function that returns a column move announcement. */
        this.columnMovedAnnouncement = this.getColumnMovedAnnouncement;
        /** Define the aria label for the add column button */
        this.addColumnAriaLabel = 'Add selected item';
        /** Define the aria label for the remove column button */
        this.removeColumnAriaLabel = 'Remove selected item';
        /** Define the aria label for the add all column button */
        this.addAllColumnsAriaLabel = 'Add all items';
        /** Define the aria label for the remove all column button */
        this.removeAllColumnsAriaLabel = 'Remove all items';
        /** Emits when the selected items change or the order of the selected items change. */
        this.selectedChange = new EventEmitter();
        /** Emits when the deselected items change. */
        this.deselectedChange = new EventEmitter();
        /** The Nested tree control used for the deselect tree */
        this._treeControl = new FlatTreeControl(node => node.level, node => node.expandable);
        /** The remaining selectable columns in the deselected list */
        this._availableDeselectedColumns = 0;
        /** An array of items that are currently selected in the left column. */
        this._deselectedSelection = [];
        /** An array of items that are currently selected in the right column. */
        this._selectedSelection = [];
        /** Cache selection during reordering */
        this._selection = [];
        /** Shallow copy of selection for reordering directive */
        this._storedSelection = [];
    }
    /** Define settings for the grouped deselected items. */
    set groups(groups) {
        this._columnPicker.groups = groups;
    }
    get groups() {
        return this._columnPicker.groups;
    }
    ngOnChanges(changes) {
        // recreate tree when deselected changes
        if (changes.deselected &&
            changes.deselected.currentValue !== changes.deselected.previousValue) {
            this.rebuildDeselectTree();
        }
    }
    /** Parse data into suitable format for the FlatTreeComponent to understand and initialize deselect tree */
    rebuildDeselectTree() {
        const columns = this._columnPicker.getDeselectedColumnsInPresentationOrder(this.deselected.slice(), this.sort);
        const treeData = this._columnPicker.createTreeData(columns);
        this._treeData = treeData;
        this._treeDataSource = new ArrayDataSource(treeData);
        // set initial count for deselected values
        this.updateAvailableDeselectedColumns();
    }
    /** A function that can be called to add columns. If no columns are passed to the function, the items that are selected in the left column will be added. */
    addColumns(columns = this._deselectedSelection) {
        const deselectedSelection = columns.filter(column => this.selected.indexOf(column) === -1);
        // add each item to the selected columns list
        this.selected = [...this.selected, ...deselectedSelection];
        this.deselected = this.deselected.filter(column => deselectedSelection.indexOf(column) === -1);
        // emit the selection changes
        this.selectedChange.emit(this.selected);
        this.deselectedChange.emit(this.deselected);
        // store the available deselected items
        this.updateAvailableDeselectedColumns();
        // clear the current selection
        this._deselectedSelection = [];
    }
    /** A function that can be called to remove columns. If no columns are passed to the function, the items that are selected in the right column will be removed. */
    removeColumns(columns = this._selectedSelection) {
        // remove each item from the selected columns list
        this.selected = this.selected.filter(column => columns.indexOf(column) === -1);
        this.deselected = [...this.deselected, ...columns];
        // emit the selection changes
        this.selectedChange.emit(this.selected);
        this.deselectedChange.emit(this.deselected);
        // store the available deselected items
        this.updateAvailableDeselectedColumns();
        // clear the current selection
        this._selectedSelection = [];
    }
    /** A function that can be called to add all columns. */
    addAllColumns() {
        this.addColumns(this.deselected);
    }
    /** A function that can be called to remove all columns. */
    removeAllColumns() {
        this.removeColumns(this.selected);
    }
    /** Ensure we don't select while dragging */
    storeSelection() {
        this._selection = [...this._selectedSelection];
        this._storedSelection = [...this.selected];
    }
    /** Restore the selection once dragging ends */
    restoreSelection() {
        this._selectedSelection = [...this._selection];
    }
    /** Update when reordering has occurred */
    onReorder() {
        this.selectedChange.emit(this.selected);
    }
    /** Get an aria label for deselected list groups */
    getDefaultDeselectedGroupAriaLabel(column) {
        return `Toggle ${column}`;
    }
    /** Get an aria label for reorderable items */
    getDefaultSelectedAriaLabel(column) {
        return `${column}. Press Alt up and alt down to reorder.`;
    }
    /** Get the announcement to read when a selected column is moved */
    getColumnMovedAnnouncement(column, delta) {
        return `${column} column moved ${delta > 0 ? 'down' : 'up'}`;
    }
    /** Perform a reorder with the keyboard */
    move(column, delta) {
        // perform the move
        const index = this.selected.indexOf(column);
        this.swap(index, index + delta);
        // Announce the move if the order has changed
        if (this.selected.indexOf(column) !== index) {
            this._liveAnnouncer.announce(`Column moved ${delta > 0 ? 'down' : 'up'}`);
        }
        // emit the changes
        this.selectedChange.emit(this.selected);
        // perform change detection
        this._changeDetectorRef.detectChanges();
        // after the UI has updated focus the element again (ngFor creates new DOM elements)
        setTimeout(() => {
            const columnIndex = this.selected.indexOf(column);
            const target = this.selectedElements.toArray()[columnIndex];
            if (target) {
                // focus the element
                target.nativeElement.focus();
            }
        });
    }
    /** Provide a trackBy function for the reorderable options */
    selectedTrackBy(index, column) {
        return index + getColumnPickerGroupItemName(column);
    }
    /** Swap two elements in the selected columns array */
    swap(source, target) {
        // perform boundary checks
        if (target < 0 || target > this.selected.length - 1) {
            return;
        }
        // create a copy of the array to manipulate
        const selected = [...this.selected];
        // swap the array elements
        [selected[target], selected[source]] = [selected[source], selected[target]];
        // update the original array
        this.selected = [...selected];
    }
    /** Get the column name based on type */
    _getColumnName(item) {
        return isColumnPickerGroupItem(item) ? item.name : item;
    }
    /** Check if tree group has visible children */
    _nodeHasChildren(_, node) {
        return node.expandable;
    }
    _nodeHasAvailableChildren(node) {
        return node.children.filter(column => this.selected.indexOf(column) === -1).length > 0;
    }
    /** Check to see if current item should display in deselect tree */
    _shouldRenderNode(node) {
        const parent = this.getTreeParent(node);
        return ((!parent || parent.isExpanded) &&
            !this.selected.find(column => this._getColumnName(column) === node.name));
    }
    /** Work backwards from the index of the current node to find the parent node  */
    getTreeParent(node) {
        const nodeIndex = this._treeData.indexOf(node);
        if (node.level > 0) {
            for (let i = nodeIndex - 1; i >= 0; i--) {
                if (this._treeData[i].level === 0) {
                    return this._treeData[i];
                }
            }
        }
        return null;
    }
    /** Store the current count of nodes that are available for selection from the deselected list */
    updateAvailableDeselectedColumns() {
        this._availableDeselectedColumns = this.deselected.length;
    }
    /** Update the order of the items when reordering has changed */
    onReorderChange(model) {
        this.selected = [...model];
        this.onReorder();
    }
    /** Get the action context, ensuring that functions have a pre-bound context */
    _getActionContext() {
        return {
            addSelection: this._deselectedSelection,
            removeSelection: this._selectedSelection,
            addColumns: this.addColumns.bind(this),
            removeColumns: this.removeColumns.bind(this),
            addAllColumns: this.addAllColumns.bind(this),
            removeAllColumns: this.removeAllColumns.bind(this),
        };
    }
    /** Change the expanded state of a node */
    _setNodeExpanded(node, isExpanded) {
        node.isExpanded = isExpanded;
        // the first change detection cycle will hide the elements but we need to trigger
        // a second change detection cycle on the next tick to ensure the ContentChildren
        // QueryList gets updated in the uxTabbableList directive
        requestAnimationFrame(() => this._changeDetectorRef.detectChanges());
    }
    _isNodeSelected(name) {
        const filtered = this._deselectedSelection.filter(selection => isColumnPickerGroupItem(selection) ? selection.name === name : selection === name);
        return filtered.length > 0;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ColumnPickerComponent, isStandalone: false, selector: "ux-column-picker", inputs: { id: "id", selected: "selected", locked: "locked", deselected: "deselected", selectedTitleTemplate: "selectedTitleTemplate", deselectedTitleTemplate: "deselectedTitleTemplate", deselectedTemplate: "deselectedTemplate", selectedTemplate: "selectedTemplate", lockedTemplate: "lockedTemplate", actionsTemplate: "actionsTemplate", selectedAriaLabel: "selectedAriaLabel", deselectedGroupAriaLabel: "deselectedGroupAriaLabel", columnMovedAnnouncement: "columnMovedAnnouncement", groups: "groups", sort: "sort", addColumnAriaLabel: "addColumnAriaLabel", removeColumnAriaLabel: "removeColumnAriaLabel", addAllColumnsAriaLabel: "addAllColumnsAriaLabel", removeAllColumnsAriaLabel: "removeAllColumnsAriaLabel" }, outputs: { selectedChange: "selectedChange", deselectedChange: "deselectedChange" }, providers: [ColumnPickerService], viewQueries: [{ propertyName: "selectedElements", predicate: ["selectedColumn"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"column-picker-column\">\n  <div class=\"column-picker-stats\" [id]=\"id + '-stats-left'\">\n    @if (!deselectedTitleTemplate) {\n      {{ _deselectedSelection.length }} of {{ _availableDeselectedColumns }} selected\n    }\n\n    @if (deselectedTitleTemplate) {\n      <ng-container [ngTemplateOutlet]=\"deselectedTitleTemplate\"> </ng-container>\n    }\n  </div>\n\n  <cdk-tree\n    class=\"column-picker-list\"\n    [dataSource]=\"_treeDataSource\"\n    [treeControl]=\"_treeControl\"\n    [(uxSelection)]=\"_deselectedSelection\"\n    [attr.aria-labelledby]=\"id + '-stats-left'\"\n    tabindex=\"-1\"\n    uxTabbableList\n  >\n    <!-- Create item for not expandable node -->\n    <cdk-tree-node\n      *cdkTreeNodeDef=\"let node\"\n      [attr.aria-hidden]=\"selected && selected.indexOf(node.name) > -1\"\n      [attr.aria-selected]=\"_isNodeSelected(node.name)\"\n    >\n      @if (_shouldRenderNode(node)) {\n        <div\n          uxTabbableListItem\n          [uxSelectionItem]=\"node.column\"\n          addAriaAttributes=\"false\"\n          class=\"column-picker-list-item\"\n          [ngClass]=\"'column-picker-tree-node-level-' + node.level\"\n        >\n          @if (!deselectedTemplate) {\n            {{ node.name }}\n          }\n          @if (deselectedTemplate) {\n            <ng-container\n              [ngTemplateOutlet]=\"deselectedTemplate\"\n              [ngTemplateOutletContext]=\"{ $implicit: node.name }\"\n            >\n            </ng-container>\n          }\n        </div>\n      }\n    </cdk-tree-node>\n\n    <!-- Create item for expandable node -->\n    <cdk-tree-node\n      *cdkTreeNodeDef=\"let node; when: _nodeHasChildren\"\n      [attr.aria-expanded]=\"node.isExpanded\"\n    >\n      @if (_nodeHasAvailableChildren(node)) {\n        <div class=\"column-picker-tree-group-node\">\n          <button\n            uxTabbableListItem\n            (click)=\"_setNodeExpanded(node, !node.isExpanded)\"\n            (keydown.arrowright)=\"_setNodeExpanded(node, true)\"\n            (keydown.arrowleft)=\"_setNodeExpanded(node, false)\"\n            [style.visibility]=\"node.expandable ? 'visible' : 'hidden'\"\n            [attr.aria-label]=\"deselectedGroupAriaLabel(node.name, node.isExpanded)\"\n            class=\"column-picker-group-toggle-btn\"\n          >\n            <ux-icon [name]=\"node.isExpanded ? 'chevron-down' : 'chevron-right'\"></ux-icon>\n            @if (!deselectedTemplate) {\n              {{ node.name }}\n            }\n            @if (deselectedTemplate) {\n              <ng-container\n                [ngTemplateOutlet]=\"deselectedTemplate\"\n                [ngTemplateOutletContext]=\"{ $implicit: node.name }\"\n              >\n              </ng-container>\n            }\n          </button>\n        </div>\n      }\n    </cdk-tree-node>\n  </cdk-tree>\n</div>\n\n<div class=\"column-picker-actions-column\">\n  <!-- Show the default action buttons -->\n  @if (!actionsTemplate) {\n    <button\n      class=\"btn button-primary btn-block\"\n      [disabled]=\"_deselectedSelection.length === 0\"\n      [attr.aria-label]=\"addColumnAriaLabel\"\n      (click)=\"addColumns()\"\n    >\n      <ux-icon name=\"chevron-right\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-primary btn-block m-b-md\"\n      [disabled]=\"_selectedSelection.length === 0\"\n      [attr.aria-label]=\"removeColumnAriaLabel\"\n      (click)=\"removeColumns()\"\n    >\n      <ux-icon name=\"chevron-left\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-secondary btn-block\"\n      [disabled]=\"_availableDeselectedColumns === 0\"\n      [attr.aria-label]=\"addAllColumnsAriaLabel\"\n      (click)=\"addAllColumns()\"\n    >\n      <ux-icon name=\"chevron-right-double\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-secondary btn-block\"\n      [disabled]=\"selected.length === 0\"\n      [attr.aria-label]=\"removeAllColumnsAriaLabel\"\n      (click)=\"removeAllColumns()\"\n    >\n      <ux-icon name=\"chevron-left-double\"></ux-icon>\n    </button>\n  }\n\n  <!-- Allow custom actions template -->\n  @if (actionsTemplate) {\n    <ng-container\n      [ngTemplateOutlet]=\"actionsTemplate\"\n      [ngTemplateOutletContext]=\"_getActionContext()\"\n    >\n    </ng-container>\n  }\n</div>\n\n<div class=\"column-picker-column\">\n  <div class=\"column-picker-stats\" [id]=\"id + '-stats-right'\">\n    @if (!selectedTitleTemplate) {\n      {{ selected.length + locked.length }} columns added\n    }\n\n    @if (selectedTitleTemplate) {\n      <ng-container [ngTemplateOutlet]=\"selectedTitleTemplate\"> </ng-container>\n    }\n  </div>\n\n  <div class=\"column-picker-list\" role=\"listbox\" [attr.aria-labelledby]=\"id + '-stats-right'\">\n    @for (column of locked; track column) {\n      <div class=\"column-picker-list-item column-picker-list-item-locked\">\n        @if (!lockedTemplate) {\n          {{ column }} <ux-icon name=\"lock\"></ux-icon>\n        }\n        @if (lockedTemplate) {\n          <ng-container\n            [ngTemplateOutlet]=\"lockedTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: column }\"\n          >\n          </ng-container>\n        }\n      </div>\n    }\n\n    <div\n      [(uxSelection)]=\"_selectedSelection\"\n      uxReorderable\n      [reorderableModel]=\"_storedSelection\"\n      (reorderableModelChange)=\"onReorderChange($event)\"\n      (reorderStart)=\"storeSelection()\"\n      (reorderEnd)=\"restoreSelection()\"\n    >\n      @for (column of selected; track selectedTrackBy(index, column); let index = $index) {\n        <div\n          #selectedColumn\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          class=\"column-picker-list-item column-picker-list-item-selected\"\n          [uxSelectionItem]=\"column\"\n          [uxReorderableModel]=\"column\"\n          [attr.aria-label]=\"selectedAriaLabel(column, selected.indexOf(column))\"\n          (keydown.alt.arrowup)=\"move(column, -1)\"\n          (keydown.alt.arrowdown)=\"move(column, 1)\"\n          role=\"option\"\n        >\n          @if (!selectedTemplate) {\n            <ux-icon uxReorderableHandle name=\"drag\" class=\"drag-handle-icon\"></ux-icon>\n            {{ _getColumnName(column) }}\n          }\n          @if (selectedTemplate) {\n            <ng-container\n              [ngTemplateOutlet]=\"selectedTemplate\"\n              [ngTemplateOutletContext]=\"{ $implicit: column }\"\n            >\n            </ng-container>\n          }\n        </div>\n      }\n    </div>\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i5.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "component", type: i5.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "levelAccessor", "childrenAccessor", "trackBy", "expansionKey"], exportAs: ["cdkTree"] }, { kind: "directive", type: i5.CdkTreeNode, selector: "cdk-tree-node", inputs: ["role", "isExpandable", "isExpanded", "isDisabled", "cdkTreeNodeTypeaheadLabel"], outputs: ["activation", "expandedChange"], exportAs: ["cdkTreeNode"] }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: ReorderableDirective, selector: "[uxReorderable]", inputs: ["reorderableModel", "reorderableGroup", "reorderingDisabled"], outputs: ["reorderableModelChange", "reorderStart", "reorderCancel", "reorderEnd"] }, { kind: "directive", type: ReorderableHandleDirective, selector: "[uxReorderableHandle]" }, { kind: "directive", type: ReorderableModelDirective, selector: "[uxReorderableModel]", inputs: ["uxReorderableModel"] }, { kind: "directive", type: SelectionDirective, selector: "[uxSelection]", inputs: ["uxSelection", "disabled", "mode", "clickSelection", "keyboardSelection", "selectionItems", "tabindex"], outputs: ["uxSelectionChange"], exportAs: ["ux-selection"] }, { kind: "directive", type: SelectionItemDirective, selector: "[uxSelectionItem]", inputs: ["uxSelectionItem", "selected", "tabindex", "uxSelectionDisabled", "addAriaAttributes"], outputs: ["selectedChange"], exportAs: ["ux-selection-item"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ColumnPickerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-column-picker', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ColumnPickerService], standalone: false, template: "<div class=\"column-picker-column\">\n  <div class=\"column-picker-stats\" [id]=\"id + '-stats-left'\">\n    @if (!deselectedTitleTemplate) {\n      {{ _deselectedSelection.length }} of {{ _availableDeselectedColumns }} selected\n    }\n\n    @if (deselectedTitleTemplate) {\n      <ng-container [ngTemplateOutlet]=\"deselectedTitleTemplate\"> </ng-container>\n    }\n  </div>\n\n  <cdk-tree\n    class=\"column-picker-list\"\n    [dataSource]=\"_treeDataSource\"\n    [treeControl]=\"_treeControl\"\n    [(uxSelection)]=\"_deselectedSelection\"\n    [attr.aria-labelledby]=\"id + '-stats-left'\"\n    tabindex=\"-1\"\n    uxTabbableList\n  >\n    <!-- Create item for not expandable node -->\n    <cdk-tree-node\n      *cdkTreeNodeDef=\"let node\"\n      [attr.aria-hidden]=\"selected && selected.indexOf(node.name) > -1\"\n      [attr.aria-selected]=\"_isNodeSelected(node.name)\"\n    >\n      @if (_shouldRenderNode(node)) {\n        <div\n          uxTabbableListItem\n          [uxSelectionItem]=\"node.column\"\n          addAriaAttributes=\"false\"\n          class=\"column-picker-list-item\"\n          [ngClass]=\"'column-picker-tree-node-level-' + node.level\"\n        >\n          @if (!deselectedTemplate) {\n            {{ node.name }}\n          }\n          @if (deselectedTemplate) {\n            <ng-container\n              [ngTemplateOutlet]=\"deselectedTemplate\"\n              [ngTemplateOutletContext]=\"{ $implicit: node.name }\"\n            >\n            </ng-container>\n          }\n        </div>\n      }\n    </cdk-tree-node>\n\n    <!-- Create item for expandable node -->\n    <cdk-tree-node\n      *cdkTreeNodeDef=\"let node; when: _nodeHasChildren\"\n      [attr.aria-expanded]=\"node.isExpanded\"\n    >\n      @if (_nodeHasAvailableChildren(node)) {\n        <div class=\"column-picker-tree-group-node\">\n          <button\n            uxTabbableListItem\n            (click)=\"_setNodeExpanded(node, !node.isExpanded)\"\n            (keydown.arrowright)=\"_setNodeExpanded(node, true)\"\n            (keydown.arrowleft)=\"_setNodeExpanded(node, false)\"\n            [style.visibility]=\"node.expandable ? 'visible' : 'hidden'\"\n            [attr.aria-label]=\"deselectedGroupAriaLabel(node.name, node.isExpanded)\"\n            class=\"column-picker-group-toggle-btn\"\n          >\n            <ux-icon [name]=\"node.isExpanded ? 'chevron-down' : 'chevron-right'\"></ux-icon>\n            @if (!deselectedTemplate) {\n              {{ node.name }}\n            }\n            @if (deselectedTemplate) {\n              <ng-container\n                [ngTemplateOutlet]=\"deselectedTemplate\"\n                [ngTemplateOutletContext]=\"{ $implicit: node.name }\"\n              >\n              </ng-container>\n            }\n          </button>\n        </div>\n      }\n    </cdk-tree-node>\n  </cdk-tree>\n</div>\n\n<div class=\"column-picker-actions-column\">\n  <!-- Show the default action buttons -->\n  @if (!actionsTemplate) {\n    <button\n      class=\"btn button-primary btn-block\"\n      [disabled]=\"_deselectedSelection.length === 0\"\n      [attr.aria-label]=\"addColumnAriaLabel\"\n      (click)=\"addColumns()\"\n    >\n      <ux-icon name=\"chevron-right\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-primary btn-block m-b-md\"\n      [disabled]=\"_selectedSelection.length === 0\"\n      [attr.aria-label]=\"removeColumnAriaLabel\"\n      (click)=\"removeColumns()\"\n    >\n      <ux-icon name=\"chevron-left\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-secondary btn-block\"\n      [disabled]=\"_availableDeselectedColumns === 0\"\n      [attr.aria-label]=\"addAllColumnsAriaLabel\"\n      (click)=\"addAllColumns()\"\n    >\n      <ux-icon name=\"chevron-right-double\"></ux-icon>\n    </button>\n    <button\n      class=\"btn button-secondary btn-block\"\n      [disabled]=\"selected.length === 0\"\n      [attr.aria-label]=\"removeAllColumnsAriaLabel\"\n      (click)=\"removeAllColumns()\"\n    >\n      <ux-icon name=\"chevron-left-double\"></ux-icon>\n    </button>\n  }\n\n  <!-- Allow custom actions template -->\n  @if (actionsTemplate) {\n    <ng-container\n      [ngTemplateOutlet]=\"actionsTemplate\"\n      [ngTemplateOutletContext]=\"_getActionContext()\"\n    >\n    </ng-container>\n  }\n</div>\n\n<div class=\"column-picker-column\">\n  <div class=\"column-picker-stats\" [id]=\"id + '-stats-right'\">\n    @if (!selectedTitleTemplate) {\n      {{ selected.length + locked.length }} columns added\n    }\n\n    @if (selectedTitleTemplate) {\n      <ng-container [ngTemplateOutlet]=\"selectedTitleTemplate\"> </ng-container>\n    }\n  </div>\n\n  <div class=\"column-picker-list\" role=\"listbox\" [attr.aria-labelledby]=\"id + '-stats-right'\">\n    @for (column of locked; track column) {\n      <div class=\"column-picker-list-item column-picker-list-item-locked\">\n        @if (!lockedTemplate) {\n          {{ column }} <ux-icon name=\"lock\"></ux-icon>\n        }\n        @if (lockedTemplate) {\n          <ng-container\n            [ngTemplateOutlet]=\"lockedTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: column }\"\n          >\n          </ng-container>\n        }\n      </div>\n    }\n\n    <div\n      [(uxSelection)]=\"_selectedSelection\"\n      uxReorderable\n      [reorderableModel]=\"_storedSelection\"\n      (reorderableModelChange)=\"onReorderChange($event)\"\n      (reorderStart)=\"storeSelection()\"\n      (reorderEnd)=\"restoreSelection()\"\n    >\n      @for (column of selected; track selectedTrackBy(index, column); let index = $index) {\n        <div\n          #selectedColumn\n          uxFocusIndicator\n          [programmaticFocusIndicator]=\"true\"\n          class=\"column-picker-list-item column-picker-list-item-selected\"\n          [uxSelectionItem]=\"column\"\n          [uxReorderableModel]=\"column\"\n          [attr.aria-label]=\"selectedAriaLabel(column, selected.indexOf(column))\"\n          (keydown.alt.arrowup)=\"move(column, -1)\"\n          (keydown.alt.arrowdown)=\"move(column, 1)\"\n          role=\"option\"\n        >\n          @if (!selectedTemplate) {\n            <ux-icon uxReorderableHandle name=\"drag\" class=\"drag-handle-icon\"></ux-icon>\n            {{ _getColumnName(column) }}\n          }\n          @if (selectedTemplate) {\n            <ng-container\n              [ngTemplateOutlet]=\"selectedTemplate\"\n              [ngTemplateOutletContext]=\"{ $implicit: column }\"\n            >\n            </ng-container>\n          }\n        </div>\n      }\n    </div>\n  </div>\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], selected: [{
                type: Input
            }], locked: [{
                type: Input
            }], deselected: [{
                type: Input
            }], selectedTitleTemplate: [{
                type: Input
            }], deselectedTitleTemplate: [{
                type: Input
            }], deselectedTemplate: [{
                type: Input
            }], selectedTemplate: [{
                type: Input
            }], lockedTemplate: [{
                type: Input
            }], actionsTemplate: [{
                type: Input
            }], selectedAriaLabel: [{
                type: Input
            }], deselectedGroupAriaLabel: [{
                type: Input
            }], columnMovedAnnouncement: [{
                type: Input
            }], groups: [{
                type: Input
            }], sort: [{
                type: Input
            }], addColumnAriaLabel: [{
                type: Input
            }], removeColumnAriaLabel: [{
                type: Input
            }], addAllColumnsAriaLabel: [{
                type: Input
            }], removeAllColumnsAriaLabel: [{
                type: Input
            }], selectedChange: [{
                type: Output
            }], deselectedChange: [{
                type: Output
            }], selectedElements: [{
                type: ViewChildren,
                args: ['selectedColumn']
            }] } });
function getColumnPickerGroupItemName(column) {
    return isColumnPickerGroupItem(column) ? column.name : column;
}

class BaseResizableTableService {
    constructor() {
        /** Emit an event whenever a column is resized */
        this.onResize$ = new Subject();
        /** Store the current width of the table */
        this.tableWidth = 0;
        /** Determine if we are currently resizing */
        this.isResizing$ = new BehaviorSubject(false);
        /** Indicate when the columns are ready */
        this.isInitialised$ = new BehaviorSubject(false);
        /** Store the percentage widths of each column */
        this.columns = [];
    }
    /** Cleanup when service is disposed */
    ngOnDestroy() {
        this.onResize$.complete();
    }
    /** Update the resizing state */
    setResizing(isResizing) {
        this.isResizing$.next(isResizing);
    }
    /** Get the width of a column in a specific unit */
    getColumnWidth(index, unit, columns = this.columns) {
        switch (unit) {
            case ColumnUnit$2.Percentage:
                return columns[index];
            case ColumnUnit$2.Pixel:
                return (this.tableWidth / 100) * columns[index];
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseResizableTableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseResizableTableService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseResizableTableService, decorators: [{
            type: Injectable
        }] });
var ColumnUnit$2;
(function (ColumnUnit) {
    ColumnUnit[ColumnUnit["Pixel"] = 0] = "Pixel";
    ColumnUnit[ColumnUnit["Percentage"] = 1] = "Percentage";
})(ColumnUnit$2 || (ColumnUnit$2 = {}));
var ResizableTableType;
(function (ResizableTableType) {
    ResizableTableType[ResizableTableType["Standard"] = 0] = "Standard";
    ResizableTableType[ResizableTableType["Expand"] = 1] = "Expand";
})(ResizableTableType || (ResizableTableType = {}));

const RESIZABLE_TABLE_SERVICE_TOKEN = new InjectionToken('RESIZABLE_TABLE_SERVICE_TOKEN');

class ResizableTableService extends BaseResizableTableService {
    constructor() {
        super(...arguments);
        /** Define the type of resizing we should use */
        this.type = ResizableTableType.Standard;
    }
    /** Store the size of each column */
    setColumns(columns) {
        // store the current columns
        this._columns = columns;
        // store the sizes
        this.columns = columns.map(column => (column.getNaturalWidth() / this.tableWidth) * 100);
        // check if there is any overflow
        this.columns = this.ensureNoOverflow(this.columns);
        // ensure all the columns fit
        this._columns.forEach((column, idx) => {
            if (!column.disabled) {
                this.resizeColumn(idx, 0, false);
            }
        });
        // indicate we are now initialised
        if (this.isInitialised$.value === false) {
            this.isInitialised$.next(true);
        }
    }
    /** Set all resizable columns to the same width */
    setUniformWidths() {
        // set any disabled columns to their specified width
        this.columns = this._columns.map(column => column.disabled ? (column.getNaturalWidth() / this.tableWidth) * 100 : 0);
        // check to see if we've reached 100% of the table width
        const totalWidth = this.columns.reduce((partial, columnWidth) => partial + columnWidth);
        if (totalWidth > 100) {
            // remove overflow
            this.columns = this.ensureNoOverflow(this.columns);
        }
        else {
            // get the list of resizable columns
            const resizableColumns = this._columns.toArray().filter(column => !column.disabled);
            // work out what we need to add to each column to make up the full width
            const newWidth = (100 - totalWidth) / resizableColumns.length;
            // set the non-disabled columns to the new width
            this.columns = this._columns.map((column, idx) => column.disabled ? this.columns[idx] : newWidth);
        }
        // do the resizing
        this._columns.forEach((column, idx) => {
            if (!column.disabled) {
                this.resizeColumn(idx, 0, false);
            }
        });
    }
    ensureNoOverflow(columns) {
        // get the total width
        const total = columns.reduce((width, column) => width + column);
        // if we have no overflow then we don't need to do anything
        if (total <= 100) {
            return columns;
        }
        // if there is overflow identify which columns can be resized
        const variableColumns = this._columns.filter(column => !column.disabled &&
            this.getColumnWidth(column.getCellIndex(), ColumnUnit$1.Pixel, columns) > column.minWidth);
        // if there are no columns that can be resized then stop here
        if (variableColumns.length === 0) {
            return columns;
        }
        // determine the total width of the variable columns
        const totalWidth = this._columns.reduce((width, column) => width + this.getColumnWidth(column.getCellIndex(), ColumnUnit$1.Pixel, columns), 0);
        // determine to the width of all the variable columns
        const variableColumnsWidth = variableColumns.reduce((width, column) => width + this.getColumnWidth(column.getCellIndex(), ColumnUnit$1.Pixel, columns), 0);
        // determine how much the columns are currently too large (ignoring fixed columns)
        const targetWidth = this.tableWidth - (totalWidth - variableColumnsWidth);
        // determine how much we need to reduce a column by
        const difference = variableColumnsWidth - targetWidth;
        // find the column with the largest size
        const target = variableColumns.reduce((widest, column) => {
            const columnWidth = this.getColumnWidth(column.getCellIndex(), ColumnUnit$1.Pixel, columns);
            const widestWidth = this.getColumnWidth(widest.getCellIndex(), ColumnUnit$1.Pixel, columns);
            return columnWidth > widestWidth ? column : widest;
        });
        // perform the resize
        columns = this.setColumnWidth(target.getCellIndex(), this.getColumnWidth(target.getCellIndex(), ColumnUnit$1.Pixel, columns) - difference, ColumnUnit$1.Pixel, columns);
        // check if we are still over the limit (allow some variance for javascript double precision)
        if (columns.reduce((width, column) => width + column) > 100.01) {
            return this.ensureNoOverflow(columns);
        }
        return columns;
    }
    /** Allow setting the column size in any unit */
    setColumnWidth(index, value, unit, columns = this.columns) {
        // create a new array so we keep the instance array immutable
        const sizes = [...columns];
        switch (unit) {
            case ColumnUnit$1.Percentage:
                sizes[index] = value;
                break;
            case ColumnUnit$1.Pixel:
                sizes[index] = (value / this.tableWidth) * 100;
                break;
        }
        // update the instance variable
        return sizes;
    }
    /** Resize a column by a specific pixel amount */
    resizeColumn(index, delta, isDragging = true) {
        // get the sibling column that will also be resized
        const sibling = this.getSiblingColumn(index);
        // if there is no sibling that can be resized then stop here
        if (!sibling) {
            return;
        }
        // create a new array for the sizes
        let columns = [...this.columns];
        // resize the column to the desired size
        columns = this.setColumnWidth(index, Math.round(this.getColumnWidth(index, ColumnUnit$1.Pixel) + delta), ColumnUnit$1.Pixel, columns);
        columns = this.setColumnWidth(sibling, Math.round(this.getColumnWidth(sibling, ColumnUnit$1.Pixel) - delta), ColumnUnit$1.Pixel, columns);
        // if the move is not possible then stop here
        if (!this.isWidthValid(index, this.getColumnWidth(index, ColumnUnit$1.Pixel, columns)) ||
            !this.isWidthValid(sibling, this.getColumnWidth(sibling, ColumnUnit$1.Pixel, columns))) {
            return;
        }
        // check that we add up to exactly 100%
        const total = columns.reduce((count, column) => column + count, 0);
        // if the columns to not add to 100 ensure we make them
        if (total !== 100) {
            // get the column with a variable width
            const target = this.getVariableColumn(100 - total);
            if (target && !isDragging) {
                columns[this._columns.toArray().indexOf(target)] += 100 - total;
            }
            else {
                columns[index] += 100 - total;
            }
        }
        // store the new sizes
        this.columns = columns;
        // emit the resize event for each column
        this.onResize$.next();
    }
    getVariableColumn(delta) {
        // get all variable width columns that are not disabled
        const variableColumns = this._columns.filter(column => !column.isFixedWidth && !column.disabled);
        // find one that is greater than its min width by enough
        return variableColumns
            .reverse()
            .find(column => this.getColumnWidth(column.getCellIndex(), ColumnUnit$1.Pixel) >= column.minWidth + delta);
    }
    getColumn(index) {
        return this._columns ? this._columns.toArray()[index] : null;
    }
    getColumnDisabled(index) {
        return this.getColumn(index) ? this.getColumn(index).disabled : false;
    }
    /** Determine whether a column is above or below its minimum width */
    isWidthValid(index, width) {
        // get the column at a given position
        const column = this.getColumnInstance(index);
        // determine if the specified width is greater than the min width
        return column && width >= column.minWidth;
    }
    /** Get the next column in the sequence of columns */
    getSiblingColumn(index) {
        // find the first sibling that is not disabled
        for (let idx = index + 1; idx < this.columns.length; idx++) {
            const sibling = this.getColumnInstance(idx);
            if (!sibling || !sibling.disabled) {
                return idx;
            }
        }
        return null;
    }
    /** Get the column class from our query list */
    getColumnInstance(index) {
        return this._columns ? this._columns.toArray()[index] : null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableService, decorators: [{
            type: Injectable
        }] });
var ColumnUnit$1;
(function (ColumnUnit) {
    ColumnUnit[ColumnUnit["Pixel"] = 0] = "Pixel";
    ColumnUnit[ColumnUnit["Percentage"] = 1] = "Percentage";
})(ColumnUnit$1 || (ColumnUnit$1 = {}));

class ResizableTableCellComponent {
    constructor() {
        this._table = inject(RESIZABLE_TABLE_SERVICE_TOKEN);
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        /** Unsubscribe from all subscriptions on destroy */
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        this._minWidth = parseFloat(getComputedStyle(this._elementRef.nativeElement).minWidth);
        // if the table has already been initialised then we should set the initial size
        if (this._table.isInitialised$.value) {
            this.setColumnWidth();
            this.setColumnFlex();
        }
        // update the sizes when columns are resized
        combineLatest([this._table.onResize$, this._table.isResizing$])
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => {
            this.setColumnWidth();
            this.setColumnFlex();
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Get the column index this cell is part of */
    getCellIndex() {
        return this._elementRef.nativeElement.cellIndex;
    }
    /** Set the width of the column */
    setColumnWidth() {
        const width = this._table.isResizing$.value || this._table.getColumnDisabled(this.getCellIndex())
            ? `${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel)}px`
            : `${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Percentage)}%`;
        if (this._table.type === ResizableTableType.Expand) {
            const minWidth = Math.max(this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel), this._minWidth);
            this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', `${minWidth}px`);
        }
        this._renderer.setStyle(this._elementRef.nativeElement, 'width', width);
    }
    /** Set the flex value of the column */
    setColumnFlex() {
        // if we are resizing then always return 'none' to allow free movement
        if (this._table.isResizing$.value || this._table.getColumnDisabled(this.getCellIndex())) {
            this._renderer.setStyle(this._elementRef.nativeElement, 'flex', 'none');
            return;
        }
        const flex = this._table.isInitialised$.value
            ? `0 1 ${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Percentage)}%`
            : '';
        this._renderer.setStyle(this._elementRef.nativeElement, 'flex', flex);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ResizableTableCellComponent, isStandalone: false, selector: "[uxResizableTableCell]", ngImport: i0, template: "<div class=\"ux-resizable-table-cell-content\">\n  <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableCellComponent, decorators: [{
            type: Component,
            args: [{ selector: '[uxResizableTableCell]', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ux-resizable-table-cell-content\">\n  <ng-content></ng-content>\n</div>\n" }]
        }] });

class ResizableTableColumnComponent {
    constructor() {
        this._table = inject(RESIZABLE_TABLE_SERVICE_TOKEN);
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        /** Show/Hide column resizable handle */
        this._handleVisible = true;
        /** Disabled the column resizing */
        this.disabled = false;
        /** Emit the current column width */
        this.widthChange = new EventEmitter();
        /** Determine if this column is a variable width column */
        this.isFixedWidth = false;
        /** Emit when all observables should be unsubscribed */
        this._onDestroy = new Subject();
    }
    get handleVisible() {
        return this._handleVisible;
    }
    set handleVisible(value) {
        this._handleVisible = coerceBooleanProperty(value);
    }
    /** Define the width of a column */
    set width(width) {
        // there may be cases where columns are created with an `*ngFor` and a width
        // may be specified on *some* columns and not others. This this setter will
        // still be called whenever the value is empty and this will mark this column
        // as having a fixed width, even though it doesn't. So we should only proceed
        // whenever there is an actual numeric value passed in.
        if (width === null || width === undefined) {
            return;
        }
        // ensure width is a valid number
        this._width = coerceNumberProperty(width);
        // note that this column has a fixed width
        this.isFixedWidth = true;
        // if we have not initialised then set the element width
        if (!this._table.isInitialised$.value) {
            this._renderer.setStyle(this._elementRef.nativeElement, 'width', `${this._width}px`);
        }
        else {
            // if it is initialised then resize the column
            const currentWidth = this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel);
            // resize the column by the difference in size
            if (isNaN(currentWidth)) {
                this._table.resizeColumn(this.getCellIndex(), this._width, false);
            }
            else {
                this._table.resizeColumn(this.getCellIndex(), this._width - currentWidth, false);
            }
        }
    }
    get width() {
        return this._width;
    }
    /** Get the minimum width allowed by the column */
    get minWidth() {
        // determine the minimum width of the column based on its computed CSS value
        const computed = parseFloat(getComputedStyle(this._elementRef.nativeElement).minWidth);
        // if it is disabled use its current width - otherwise use its CSS min width if it is valid
        return this.disabled
            ? this._elementRef.nativeElement.offsetWidth
            : isNaN(computed)
                ? 0
                : computed;
    }
    ngAfterViewInit() {
        // initially emit the size when we have initialised
        this._table.isInitialised$
            .pipe(takeUntil(this._onDestroy), filter(isInitialised => isInitialised))
            .subscribe(() => {
            // get the current min-width
            this._minWidth = parseFloat(getComputedStyle(this._elementRef.nativeElement).minWidth);
            const width = this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel);
            if (!isNaN(width)) {
                this.widthChange.emit(width);
            }
        });
        // ensure the correct width gets emitted on column size change
        this._table.onResize$.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            this.setColumnWidth();
            this.setColumnFlex();
            // get the current table width
            const width = this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel);
            // check if the width actually changed - otherwise don't emit
            if (!isNaN(width) &&
                (this._width === undefined ||
                    Math.max(width, this._width) - Math.min(width, this._width) >= 1)) {
                this.widthChange.emit(width);
            }
        });
    }
    /** Cleanup when component is destroyed */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Get the natural pixel width of the column */
    getNaturalWidth() {
        return this._width || this._elementRef.nativeElement.offsetWidth;
    }
    /** When the dragging starts */
    onDragStart(event) {
        // determine the mouse position within the handle
        this._offset = event.clientX - event.target.getBoundingClientRect().left;
    }
    /** When the mouse is moved */
    onDragMove(event, handle) {
        // get the current mouse position
        const mouseX = event.pageX - pageXOffset;
        // position of the drag handle
        const { left } = handle.getBoundingClientRect();
        // determine how much the mouse has moved since the last update
        const delta = mouseX - (left + this._offset);
        // perform resizing
        this._table.resizeColumn(this.getCellIndex(), delta);
        // set the resizing state
        this._table.setResizing(true);
    }
    /** When the dragging ends */
    onDragEnd() {
        this._table.setResizing(false);
    }
    /** Shrink the column when the left arrow key is pressed */
    onMoveLeft() {
        this._table.resizeColumn(this.getCellIndex(), -10);
    }
    /** Grow the column when the right arrow key is pressed */
    onMoveRight() {
        this._table.resizeColumn(this.getCellIndex(), 10);
    }
    /** Get the column index this cell is part of */
    getCellIndex() {
        return this._elementRef.nativeElement.cellIndex;
    }
    /** The percentage width of the column */
    setColumnWidth() {
        if (this.disabled && this._width !== undefined) {
            this._renderer.setStyle(this._elementRef.nativeElement, 'width', `${this._width}px`);
            this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', `${this._width}px`);
            return;
        }
        if (!this._table.isInitialised$.value) {
            return;
        }
        const width = this._table.isResizing$.value
            ? `${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel)}px`
            : `${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Percentage)}%`;
        this._renderer.setStyle(this._elementRef.nativeElement, 'width', width);
        this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', null);
    }
    /** The flex width of the column */
    setColumnFlex() {
        let flex;
        // if we are resizing then always return 'none' to allow free movement
        if (this._table.isResizing$.value || this.disabled) {
            this._renderer.setStyle(this._elementRef.nativeElement, 'flex', 'none');
        }
        if (this._table.type === ResizableTableType.Expand) {
            flex = this._table.isInitialised$.value
                ? `0 0 ${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Pixel)}px`
                : '';
        }
        else {
            flex = this._table.isInitialised$.value
                ? `0 1 ${this._table.getColumnWidth(this.getCellIndex(), ColumnUnit$1.Percentage)}%`
                : '';
        }
        this._renderer.setStyle(this._elementRef.nativeElement, 'flex', flex);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ResizableTableColumnComponent, isStandalone: false, selector: "[uxResizableTableColumn]", inputs: { disabled: "disabled", handleVisible: "handleVisible", width: "width" }, outputs: { widthChange: "widthChange" }, host: { properties: { "class.ux-resizable-table-hide-handle": "!handleVisible", "class.ux-resizable-table-column-disabled": "this.disabled" }, classAttribute: "ux-resizable-table-column" }, ngImport: i0, template: "<div class=\"ux-resizable-table-column-content\">\n  <ng-content></ng-content>\n</div>\n\n<div class=\"ux-resizable-table-column-actions\">\n  <ng-content select=\"ux-column-sorting\"></ng-content>\n</div>\n\n@if (!disabled && handleVisible) {\n  <div\n    #handle\n    uxDrag\n    uxFocusIndicator\n    tabindex=\"0\"\n    role=\"separator\"\n    aria-label=\"Column resize handle. Use arrow keys to change the column width.\"\n    class=\"ux-resizable-table-column-handle\"\n    [attr.aria-valuemin]=\"minWidth\"\n    [attr.aria-valuenow]=\"width\"\n    (onDragStart)=\"onDragStart($event)\"\n    (onDrag)=\"onDragMove($event, handle)\"\n    (onDragEnd)=\"onDragEnd()\"\n    (keydown.ArrowLeft)=\"onMoveLeft()\"\n    (keydown.ArrowRight)=\"onMoveRight()\"\n    (click)=\"$event.stopPropagation()\"\n  >\n    <div class=\"ux-resizable-table-column-handle-icon\"></div>\n  </div>\n}\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: DragDirective, selector: "[uxDrag]", inputs: ["clone", "group", "model", "draggable"], outputs: ["onDragStart", "onDrag", "onDragScroll", "onDragEnd", "onDrop", "onDropEnter", "onDropLeave"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableColumnComponent, decorators: [{
            type: Component,
            args: [{ selector: '[uxResizableTableColumn]', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        class: 'ux-resizable-table-column',
                        '[class.ux-resizable-table-hide-handle]': '!handleVisible',
                    }, standalone: false, template: "<div class=\"ux-resizable-table-column-content\">\n  <ng-content></ng-content>\n</div>\n\n<div class=\"ux-resizable-table-column-actions\">\n  <ng-content select=\"ux-column-sorting\"></ng-content>\n</div>\n\n@if (!disabled && handleVisible) {\n  <div\n    #handle\n    uxDrag\n    uxFocusIndicator\n    tabindex=\"0\"\n    role=\"separator\"\n    aria-label=\"Column resize handle. Use arrow keys to change the column width.\"\n    class=\"ux-resizable-table-column-handle\"\n    [attr.aria-valuemin]=\"minWidth\"\n    [attr.aria-valuenow]=\"width\"\n    (onDragStart)=\"onDragStart($event)\"\n    (onDrag)=\"onDragMove($event, handle)\"\n    (onDragEnd)=\"onDragEnd()\"\n    (keydown.ArrowLeft)=\"onMoveLeft()\"\n    (keydown.ArrowRight)=\"onMoveRight()\"\n    (click)=\"$event.stopPropagation()\"\n  >\n    <div class=\"ux-resizable-table-column-handle-icon\"></div>\n  </div>\n}\n" }]
        }], propDecorators: { disabled: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.ux-resizable-table-column-disabled']
            }], handleVisible: [{
                type: Input
            }], width: [{
                type: Input
            }], widthChange: [{
                type: Output
            }] } });

class BaseResizableTableDirective {
    constructor() {
        this._table = inject(RESIZABLE_TABLE_SERVICE_TOKEN);
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this.resize = inject(ResizeService);
        /** Unsubscribe from the observables */
        this._onDestroy = new Subject();
        /** Store the initialised state of the table */
        this._initialised = false;
        // watch for the table being resized
        this.resize
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => {
            // store the latest table size
            this._table.tableWidth = this.getScrollWidth();
            // run the initial logic if the table is fully visible
            this.onTableReady();
        });
    }
    /** Cleanup after the component is destroyed */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Set all resizable columns to the same width */
    setUniformWidths() {
        this._table.setUniformWidths();
    }
    /** Get the smallest tbody width taking into account scrollbars (uxFixedHeaderTable) */
    getScrollWidth() {
        return Array.from(this._elementRef.nativeElement.tBodies).reduce((width, tbody) => Math.min(width, tbody.scrollWidth), this._elementRef.nativeElement.offsetWidth);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseResizableTableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: BaseResizableTableDirective, isStandalone: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BaseResizableTableDirective, decorators: [{
            type: Directive
        }], ctorParameters: () => [] });

class ResizableExpandingTableService extends BaseResizableTableService {
    constructor() {
        super(...arguments);
        /** Define the type of resizing we should use */
        this.type = ResizableTableType.Expand;
    }
    /** Store the size of each column */
    setColumns(columns) {
        // store the current columns
        this._columns = columns;
        // store the sizes
        this.columns = columns.map(column => (column.getNaturalWidth() / this.tableWidth) * 100);
        // ensure all the columns fit
        this._columns.forEach((column, idx) => {
            if (!column.disabled) {
                this.columns = this.setColumnWidth(idx, this.columns[idx], ColumnUnit.Percentage, this.columns);
            }
        });
        // indicate we are now initialised
        if (this.isInitialised$.value === false) {
            this.isInitialised$.next(true);
        }
    }
    /** Set all resizable columns to the same width */
    setUniformWidths() {
        // set any disabled columns to their specified width
        this.columns = this._columns.map(column => column.disabled ? (column.getNaturalWidth() / this.tableWidth) * 100 : 0);
        // check to see if we've reached 100% of the table width
        const totalWidth = this.columns.reduce((partial, columnWidth) => partial + columnWidth);
        if (totalWidth > 98) {
            // remove overflow
            this.columns = this.ensureNoOverflow(this.columns);
        }
        else {
            // get the list of resizable columns
            const resizableColumns = this._columns.toArray().filter(column => !column.disabled);
            // work out what we need to add to each column to make up the full width
            const newWidth = (98 - totalWidth) / resizableColumns.length;
            // set the non-disabled columns to the new width
            this.columns = this._columns.map((column, idx) => column.disabled ? this.columns[idx] : newWidth);
        }
        // do the resizing
        this._columns.forEach((column, idx) => {
            if (!column.disabled) {
                this.resizeColumn(idx, 0);
            }
        });
    }
    ensureNoOverflow(columns) {
        // get the total width
        const total = columns.reduce((width, column) => width + column);
        // if we have no overflow then we don't need to do anything
        if (total <= 100) {
            return columns;
        }
        // if there is overflow identify which columns can be resized
        const variableColumns = this._columns.filter(column => !column.disabled &&
            this.getColumnWidth(column.getCellIndex(), ColumnUnit.Pixel, columns) > column.minWidth);
        // if there are no columns that can be resized then stop here
        if (variableColumns.length === 0) {
            return columns;
        }
        // determine the total width of the variable columns
        const totalWidth = this._columns.reduce((width, column) => width + this.getColumnWidth(column.getCellIndex(), ColumnUnit.Pixel, columns), 0);
        // determine to the width of all the variable columns
        const variableColumnsWidth = variableColumns.reduce((width, column) => width + this.getColumnWidth(column.getCellIndex(), ColumnUnit.Pixel, columns), 0);
        // determine how much the columns are currently too large (ignoring fixed columns)
        const targetWidth = this.tableWidth - (totalWidth - variableColumnsWidth);
        // determine how much we need to reduce a column by
        const difference = variableColumnsWidth - targetWidth;
        // find the column with the largest size
        const target = variableColumns.reduce((widest, column) => {
            const columnWidth = this.getColumnWidth(column.getCellIndex(), ColumnUnit.Pixel, columns);
            const widestWidth = this.getColumnWidth(widest.getCellIndex(), ColumnUnit.Pixel, columns);
            return columnWidth > widestWidth ? column : widest;
        });
        // perform the resize
        columns = this.setColumnWidth(target.getCellIndex(), this.getColumnWidth(target.getCellIndex(), ColumnUnit.Pixel, columns) - difference, ColumnUnit.Pixel, columns);
        // check if we are still over the limit (allow some variance for javascript double precision)
        if (columns.reduce((width, column) => width + column) > 100.01) {
            return this.ensureNoOverflow(columns);
        }
        return columns;
    }
    /** Allow setting the column size in any unit */
    setColumnWidth(index, value, unit, columns = this.columns) {
        // create a new array so we keep the instance array immutable
        const sizes = [...columns];
        switch (unit) {
            case ColumnUnit.Percentage:
                sizes[index] = value;
                break;
            case ColumnUnit.Pixel:
                sizes[index] = (value / this.tableWidth) * 100;
                break;
        }
        // update the instance variable
        return sizes;
    }
    /** Resize a column by a specific pixel amount */
    resizeColumn(index, delta) {
        // get the sibling column that will also be resized
        const sibling = this.getSiblingColumn(index);
        // create a new array for the sizes
        let columns = [...this.columns];
        // resize the column to the desired size
        columns = this.setColumnWidth(index, Math.round(this.getColumnWidth(index, ColumnUnit.Pixel) + delta), ColumnUnit.Pixel);
        columns = this.setColumnWidth(sibling, Math.round(this.getColumnWidth(sibling, ColumnUnit.Pixel)), ColumnUnit.Pixel, columns);
        // if the move is not possible then stop here
        if (!this.isWidthValid(index, this.getColumnWidth(index, ColumnUnit.Pixel, columns))) {
            return;
        }
        // store the new sizes
        this.columns = columns;
        // emit the resize event for each column
        this.onResize$.next();
    }
    /** Get the next column in the sequence of columns */
    getSiblingColumn(index) {
        // find the first sibling that is not disabled
        for (let idx = index + 1; idx < this.columns.length; idx++) {
            const sibling = this.getColumn(idx);
            if (!sibling || !sibling.disabled) {
                return idx;
            }
        }
        return null;
    }
    /** Return true if this column is above the minimum width */
    isWidthValid(index, width) {
        // get the column at a given position
        const column = this.getColumn(index);
        // determine if the specified width is greater than the min width
        return column && width >= column.minWidth;
    }
    getColumn(index) {
        return this._columns ? this._columns.toArray()[index] : null;
    }
    getColumnDisabled(index) {
        return this.getColumn(index) ? this.getColumn(index).disabled : false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableExpandingTableService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableExpandingTableService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableExpandingTableService, decorators: [{
            type: Injectable
        }] });
var ColumnUnit;
(function (ColumnUnit) {
    ColumnUnit[ColumnUnit["Pixel"] = 0] = "Pixel";
    ColumnUnit[ColumnUnit["Percentage"] = 1] = "Percentage";
})(ColumnUnit || (ColumnUnit = {}));

class ResizableExpandingTableDirective extends BaseResizableTableDirective {
    constructor() {
        super();
        this._platformId = inject(PLATFORM_ID);
        /** Has horizontal overflow */
        this._overflowX = false;
    }
    ngAfterViewInit() {
        if (isPlatformBrowser(this._platformId)) {
            const tableHeaders = this._elementRef.nativeElement.querySelectorAll('thead > tr');
            for (const body of Array.from(this._elementRef.nativeElement.tBodies)) {
                fromEvent(body, 'scroll')
                    .pipe(takeUntil(this._onDestroy))
                    .subscribe(() => {
                    Array.from(tableHeaders).forEach(thead => this._renderer.setStyle(thead, 'margin-left', `-${body.scrollLeft}px`));
                });
            }
            /** checks if the table is resizing and allows for a class to be added for when moving from
                   overflow to no overflow */
            merge(this._table.onResize$, this.columns.changes)
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => {
                this._overflowX =
                    this._elementRef.nativeElement.tBodies[0].scrollWidth >
                        this._elementRef.nativeElement.tBodies[0].offsetWidth;
            });
        }
    }
    /**
     * If this is being used within a modal the table width may initially be zero. This can cause some issues when it does actually appear
     * visibily on screen. We should only setup the table once we actually have a width/
     */
    onTableReady() {
        // if we have already initialised or the table width is currently 0 then do nothing
        if (this._initialised || this.getScrollWidth() === 0) {
            // if the table has been initialized but the width is now 0
            // for example, due to the element being hidden (eg. in a collapsed accordion)
            // we would need to re-run this logic whenever the width is back over 0
            // to do this we can mark the table as not having been initialized
            if (this._initialised && this.getScrollWidth() === 0) {
                this._initialised = false;
            }
            return;
        }
        // ensure we initially set the table width
        this._table.tableWidth = this.getScrollWidth();
        // set the columns - prevent expression changed error
        Promise.resolve().then(() => {
            // initially set the columns
            this._table.setColumns(this.columns);
            // force relayout to occur to ensure the UI is consistent with the internal state
            this.updateLayout();
        });
        // watch for any future changes to the columns
        this.columns.changes
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => Promise.resolve().then(() => this._table.setColumns(this.columns)));
        this._initialised = true;
    }
    /** Force the layout to recalculate */
    updateLayout() {
        Promise.resolve().then(() => this.columns.forEach((_column, index) => this._table.resizeColumn(index, 0)));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableExpandingTableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ResizableExpandingTableDirective, isStandalone: false, selector: "[uxResizableExpandingTable]", host: { properties: { "class.ux-resizable-expanding-table-overflow": "_overflowX" }, classAttribute: "ux-resizable-expanding-table" }, providers: [
            {
                provide: RESIZABLE_TABLE_SERVICE_TOKEN,
                useClass: ResizableExpandingTableService,
            },
        ], queries: [{ propertyName: "columns", predicate: ResizableTableColumnComponent, descendants: true }], exportAs: ["ux-resizable-expanding-table"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableExpandingTableDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxResizableExpandingTable]',
                    exportAs: 'ux-resizable-expanding-table',
                    providers: [
                        {
                            provide: RESIZABLE_TABLE_SERVICE_TOKEN,
                            useClass: ResizableExpandingTableService,
                        },
                    ],
                    host: {
                        class: 'ux-resizable-expanding-table',
                        '[class.ux-resizable-expanding-table-overflow]': '_overflowX',
                    },
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { columns: [{
                type: ContentChildren,
                args: [ResizableTableColumnComponent, { descendants: true }]
            }] } });

class ResizableTableDirective extends BaseResizableTableDirective {
    constructor() {
        super();
        // we should hide any horizontal overflow when we are resizing
        this._table.isResizing$.pipe(takeUntil(this._onDestroy)).subscribe(this.setOverflow.bind(this));
    }
    /**
     * If this is being used within a modal the table width may initially be zero. This can cause some issues when it does actually appear
     * visibily on screen. We should only setup the table once we actually have a width/
     */
    onTableReady() {
        // if we have already initialised or the table width is currently 0 then do nothing
        if (this._initialised || this.getScrollWidth() === 0) {
            return;
        }
        // ensure we initially set the table width
        this._table.tableWidth = this.getScrollWidth();
        // set the columns - prevent expression changed error
        Promise.resolve().then(() => {
            // initially set the columns
            this._table.setColumns(this.columns);
            // force relayout to occur to ensure the UI is consistent with the internal state
            this.updateLayout();
        });
        // watch for any future changes to the columns
        this.columns.changes
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => Promise.resolve().then(() => this._table.setColumns(this.columns)));
        this._initialised = true;
    }
    /** Force the layout to recalculate */
    updateLayout() {
        Promise.resolve().then(() => this.columns.forEach((_column, index) => this._table.resizeColumn(index, 0)));
    }
    /**
     * We should hide any horizontal overflow whenever we are resizing, this is because when we are dragging a column
     * we must set the column widths in pixel values as percentages cause some jankiness when moving them. However pixel
     * values are less precise and can in some cases cause overflow, so we should hide overflow when we are resizing
     */
    setOverflow(isResizing) {
        Array.from(this._elementRef.nativeElement.tBodies).forEach(tbody => this._renderer.setStyle(tbody, 'overflow-x', isResizing ? 'hidden' : null));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ResizableTableDirective, isStandalone: false, selector: "[uxResizableTable]", host: { classAttribute: "ux-resizable-table" }, providers: [
            {
                provide: RESIZABLE_TABLE_SERVICE_TOKEN,
                useClass: ResizableTableService,
            },
        ], queries: [{ propertyName: "columns", predicate: ResizableTableColumnComponent, descendants: true }], exportAs: ["ux-resizable-table"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ResizableTableDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxResizableTable]',
                    exportAs: 'ux-resizable-table',
                    providers: [
                        {
                            provide: RESIZABLE_TABLE_SERVICE_TOKEN,
                            useClass: ResizableTableService,
                        },
                    ],
                    host: {
                        class: 'ux-resizable-table',
                    },
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { columns: [{
                type: ContentChildren,
                args: [ResizableTableColumnComponent, { descendants: true }]
            }] } });

class TableModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TableModule, declarations: [ColumnPickerComponent,
            ResizableTableDirective,
            ResizableExpandingTableDirective,
            ResizableTableColumnComponent,
            ResizableTableCellComponent], imports: [A11yModule,
            AccessibilityModule,
            CdkTreeModule,
            CommonModule,
            DragModule,
            IconModule,
            ResizeModule,
            ReorderableModule,
            SelectionModule], exports: [ColumnPickerComponent,
            ResizableTableDirective,
            ResizableExpandingTableDirective,
            ResizableTableColumnComponent,
            ResizableTableCellComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TableModule, imports: [A11yModule,
            AccessibilityModule,
            CdkTreeModule,
            CommonModule,
            DragModule,
            IconModule,
            ResizeModule,
            ReorderableModule,
            SelectionModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TableModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        A11yModule,
                        AccessibilityModule,
                        CdkTreeModule,
                        CommonModule,
                        DragModule,
                        IconModule,
                        ResizeModule,
                        ReorderableModule,
                        SelectionModule,
                    ],
                    declarations: [
                        ColumnPickerComponent,
                        ResizableTableDirective,
                        ResizableExpandingTableDirective,
                        ResizableTableColumnComponent,
                        ResizableTableCellComponent,
                    ],
                    exports: [
                        ColumnPickerComponent,
                        ResizableTableDirective,
                        ResizableExpandingTableDirective,
                        ResizableTableColumnComponent,
                        ResizableTableCellComponent,
                    ],
                }]
        }] });

let uniqueId = 0;
class TimelineEventComponent {
    constructor() {
        /** Define the id for the event */
        this.id = `ux-timeline-event-${uniqueId++}`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineEventComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TimelineEventComponent, isStandalone: false, selector: "ux-timeline-event", inputs: { id: "id", badgeColor: "badgeColor", badgeTitle: "badgeTitle" }, ngImport: i0, template: "<div class=\"timeline-badge\" [ngClass]=\"badgeColor\" [attr.aria-describedby]=\"id\">\n  <span>{{ badgeTitle }}</span>\n</div>\n\n<div class=\"timeline-panel\" [id]=\"id\">\n  <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineEventComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-timeline-event', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"timeline-badge\" [ngClass]=\"badgeColor\" [attr.aria-describedby]=\"id\">\n  <span>{{ badgeTitle }}</span>\n</div>\n\n<div class=\"timeline-panel\" [id]=\"id\">\n  <ng-content></ng-content>\n</div>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], badgeColor: [{
                type: Input
            }], badgeTitle: [{
                type: Input
            }] } });

class TimelineComponent {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TimelineComponent, isStandalone: false, selector: "ux-timeline", ngImport: i0, template: "<div class=\"timeline\">\n  <div class=\"timeline-connector\"></div>\n  <ux-icon class=\"timeline-arrow\" name=\"chevron-down\"></ux-icon>\n  <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "component", type: IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-timeline', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"timeline\">\n  <div class=\"timeline-connector\"></div>\n  <ux-icon class=\"timeline-arrow\" name=\"chevron-down\"></ux-icon>\n  <ng-content></ng-content>\n</div>\n" }]
        }] });

class TimelineModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TimelineModule, declarations: [TimelineComponent, TimelineEventComponent], imports: [CommonModule, IconModule], exports: [TimelineComponent, TimelineEventComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineModule, imports: [CommonModule, IconModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule, IconModule],
                    exports: [TimelineComponent, TimelineEventComponent],
                    declarations: [TimelineComponent, TimelineEventComponent],
                }]
        }] });

const TOGGLESWITCH_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => ToggleSwitchComponent),
    multi: true,
};
let uniqueToggleSwitchId = 0;
class ToggleSwitchComponent {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        /** Provide a default unique id value for the toggle switch */
        this._toggleSwitchId = `ux-toggleswitch-${++uniqueToggleSwitchId}`;
        /** Specify a unique id for the element. */
        this.id = this._toggleSwitchId;
        /** Binding for the state of the switch; `true` for "on" and `false` for "off." */
        this.value = false;
        /** Specify a tabindex. */
        this.tabindex = 0;
        /** If set to `false` the switch will not be updated when clicking on it, can be used if something else is updating the state of the switch. */
        this.clickable = true;
        /** If this value is set to `true` then the toggle switch will be disabled. */
        this.disabled = false;
        /** Specify an aria label for the input element */
        this.ariaLabel = '';
        /** Specify an aria labelledby property for the input element */
        this.ariaLabelledby = null;
        /** Emits when `value` has been changed. */
        this.valueChange = new EventEmitter();
        /** Determine if the underlying input component has been focused with the keyboard */
        this._focused = false;
        /** Used to inform Angular forms that the component has been touched */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        /** Used to inform Angular forms that the component value has changed */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
    }
    toggle() {
        if (!this.disabled && this.clickable) {
            this.value = !this.value;
            // emit the value
            this.valueChange.emit(this.value);
            // update the value if used within a form control
            this.onChangeCallback(this.value);
            // mark the component as touched
            this.onTouchedCallback();
        }
    }
    writeValue(value) {
        this.value = !!value;
        this._changeDetector.markForCheck();
    }
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this._changeDetector.markForCheck();
    }
    /** Focus the input element */
    focus(origin) {
        this._focusIndicator.focus(origin);
    }
    setInputTabIndex(tabindex) {
        this.tabindex = tabindex;
        this._changeDetector.markForCheck();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ToggleSwitchComponent, isStandalone: false, selector: "ux-toggleswitch", inputs: { id: "id", name: "name", value: "value", tabindex: "tabindex", clickable: "clickable", disabled: "disabled", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], required: "required" }, outputs: { valueChange: "valueChange" }, providers: [
            TOGGLESWITCH_VALUE_ACCESSOR,
            {
                provide: FocusableItemToken,
                useExisting: ToggleSwitchComponent,
            },
        ], viewQueries: [{ propertyName: "_focusIndicator", first: true, predicate: FocusIndicatorDirective, descendants: true }], ngImport: i0, template: "<label\n  [attr.for]=\"(id || _toggleSwitchId) + '-input'\"\n  class=\"ux-toggleswitch\"\n  [class.ux-toggleswitch-checked]=\"value\"\n  [class.ux-toggleswitch-disabled]=\"disabled\"\n  [class.ux-toggleswitch-focused]=\"_focused\"\n>\n  <input\n    #input\n    class=\"ux-toggleswitch-input\"\n    uxFocusIndicator\n    type=\"checkbox\"\n    [id]=\"(id || _toggleSwitchId) + '-input'\"\n    [checked]=\"value\"\n    [disabled]=\"disabled\"\n    [required]=\"required\"\n    [attr.name]=\"name\"\n    [tabindex]=\"tabindex\"\n    [attr.aria-label]=\"ariaLabel\"\n    [attr.aria-labelledby]=\"ariaLabelledby\"\n    [attr.aria-checked]=\"value\"\n    (indicator)=\"_focused = $event\"\n    (change)=\"toggle()\"\n    (click)=\"$event.stopPropagation()\"\n  />\n\n  <div class=\"ux-toggleswitch-container\">\n    <div class=\"ux-toggleswitch-bg\"></div>\n    <div class=\"ux-toggleswitch-nub\"></div>\n  </div>\n\n  <span class=\"ux-toggleswitch-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-toggleswitch', providers: [
                        TOGGLESWITCH_VALUE_ACCESSOR,
                        {
                            provide: FocusableItemToken,
                            useExisting: ToggleSwitchComponent,
                        },
                    ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<label\n  [attr.for]=\"(id || _toggleSwitchId) + '-input'\"\n  class=\"ux-toggleswitch\"\n  [class.ux-toggleswitch-checked]=\"value\"\n  [class.ux-toggleswitch-disabled]=\"disabled\"\n  [class.ux-toggleswitch-focused]=\"_focused\"\n>\n  <input\n    #input\n    class=\"ux-toggleswitch-input\"\n    uxFocusIndicator\n    type=\"checkbox\"\n    [id]=\"(id || _toggleSwitchId) + '-input'\"\n    [checked]=\"value\"\n    [disabled]=\"disabled\"\n    [required]=\"required\"\n    [attr.name]=\"name\"\n    [tabindex]=\"tabindex\"\n    [attr.aria-label]=\"ariaLabel\"\n    [attr.aria-labelledby]=\"ariaLabelledby\"\n    [attr.aria-checked]=\"value\"\n    (indicator)=\"_focused = $event\"\n    (change)=\"toggle()\"\n    (click)=\"$event.stopPropagation()\"\n  />\n\n  <div class=\"ux-toggleswitch-container\">\n    <div class=\"ux-toggleswitch-bg\"></div>\n    <div class=\"ux-toggleswitch-nub\"></div>\n  </div>\n\n  <span class=\"ux-toggleswitch-label\">\n    <ng-content></ng-content>\n  </span>\n</label>\n" }]
        }], propDecorators: { id: [{
                type: Input
            }], name: [{
                type: Input
            }], value: [{
                type: Input
            }], tabindex: [{
                type: Input
            }], clickable: [{
                type: Input
            }], disabled: [{
                type: Input
            }], ariaLabel: [{
                type: Input,
                args: ['aria-label']
            }], ariaLabelledby: [{
                type: Input,
                args: ['aria-labelledby']
            }], required: [{
                type: Input
            }], valueChange: [{
                type: Output
            }], _focusIndicator: [{
                type: ViewChild,
                args: [FocusIndicatorDirective]
            }] } });

class ToggleSwitchModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchModule, declarations: [ToggleSwitchComponent], imports: [AccessibilityModule, FormsModule], exports: [ToggleSwitchComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchModule, imports: [AccessibilityModule, FormsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToggleSwitchModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, FormsModule],
                    exports: [ToggleSwitchComponent],
                    declarations: [ToggleSwitchComponent],
                }]
        }] });

class ToolbarSearchButtonDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        /** Emit whenever the button is clicked */
        this.clicked = new EventEmitter();
    }
    /** Get the width of the button element */
    get width() {
        return this._elementRef.nativeElement.offsetWidth;
    }
    clickHandler() {
        this.clicked.emit();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ToolbarSearchButtonDirective, isStandalone: false, selector: "[uxToolbarSearchButton]", outputs: { clicked: "clicked" }, host: { listeners: { "click": "clickHandler()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchButtonDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxToolbarSearchButton]',
                    standalone: false,
                }]
        }], propDecorators: { clicked: [{
                type: Output
            }], clickHandler: [{
                type: HostListener,
                args: ['click']
            }] } });

/* eslint-disable @angular-eslint/no-output-native */
const TOOLBAR_SEARCH_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => ToolbarSearchFieldDirective),
    multi: true,
};
class ToolbarSearchFieldDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._changeDetector = inject(ChangeDetectorRef);
        /** Emit whenever the escape key is pressed */
        this.cancel = new EventEmitter();
        /** Emit whenever the enter key is pressed */
        this.submitted = new EventEmitter();
        /** For use with the Forms and ReactiveForms */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onTouchedCallback = () => { };
        /** Call this function with the latest value to update ngModel or formControl name */
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        this.onChangeCallback = () => { };
    }
    /** Get the current value of the input control */
    get text() {
        return this._elementRef.nativeElement.value;
    }
    focus() {
        // mark the control as dirty
        this.onTouchedCallback();
        // focus the input control after a delay to ensure the element is present
        requestAnimationFrame(() => this._elementRef.nativeElement.focus());
    }
    blur() {
        // blur the input control after a delay to ensure the element is present
        requestAnimationFrame(() => this._elementRef.nativeElement.blur());
    }
    /** Clear the input, if we have an ngModel reset its value otherwise just set the input value to empty */
    clear() {
        this.setValue('');
    }
    onEnter() {
        this.submitted.emit(this.text);
    }
    onEscape() {
        this._elementRef.nativeElement.blur();
        this.cancel.emit();
    }
    onInput() {
        this.setValue(this.text);
    }
    /** Update the input value based on ngModel or formControl */
    writeValue(value) {
        this.setValue(value);
        this._changeDetector.markForCheck();
    }
    /** Register a function to update form control */
    registerOnChange(fn) {
        this.onChangeCallback = fn;
    }
    /** Register a function to mark form control as touched */
    registerOnTouched(fn) {
        this.onTouchedCallback = fn;
    }
    /** Update the value in all required places */
    setValue(value) {
        // ngModel/form control can set the default value to null or undefined, which can show in the input. Replace with empty string
        if (!value) {
            value = '';
        }
        // update the form value if there is one in use
        this.onChangeCallback(value);
        // update the content of the input control
        this._elementRef.nativeElement.value = value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchFieldDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: ToolbarSearchFieldDirective, isStandalone: false, selector: "[uxToolbarSearchField]", outputs: { cancel: "cancel", submitted: "submitted" }, host: { listeners: { "keydown.enter": "onEnter()", "keydown.escape": "onEscape()", "input": "onInput()" } }, providers: [TOOLBAR_SEARCH_VALUE_ACCESSOR], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchFieldDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxToolbarSearchField]',
                    providers: [TOOLBAR_SEARCH_VALUE_ACCESSOR],
                    standalone: false,
                }]
        }], propDecorators: { cancel: [{
                type: Output
            }], submitted: [{
                type: Output
            }], onEnter: [{
                type: HostListener,
                args: ['keydown.enter']
            }], onEscape: [{
                type: HostListener,
                args: ['keydown.escape']
            }], onInput: [{
                type: HostListener,
                args: ['input']
            }] } });

class ToolbarSearchComponent {
    constructor() {
        this._platformId = inject(PLATFORM_ID);
        this._elementRef = inject(ElementRef);
        this._colorService = inject(ColorService);
        this._renderer = inject(Renderer2);
        /** The direction in which the search box will expand. If the search button is aligned to the right edge of the container, specify left. */
        this.direction = 'right';
        /** Whether the color scheme is inverted. For use when the component is hosted on a dark background, e.g. the masthead. */
        this.inverse = false;
        /** Indicate whether or not the search field should always be expanded */
        this.alwaysExpanded = false;
        /** Emitted when the expanded state changes */
        this.expandedChange = new EventEmitter();
        /**
         * Emitted when a search query has been submitted, either by pressing enter when the search field has focus, or by clicking the search button
         * when the search field contains text. The event contains the search text.
         */
        // eslint-disable-next-line @angular-eslint/no-output-native
        this.search = new EventEmitter();
        /** Store the CSS position value as this may change to absolute */
        this._position = 'relative';
        /** Store the active background color */
        this._backgroundColor = 'transparent';
        /** Store the expanded state */
        this._expanded = false;
        /** Unsubscribe from all subscriptions on component destroy */
        this._onDestroy = new Subject();
    }
    /** Whether the input field is visible. Use this to collapse or expand the control in response to other events. */
    set expanded(value) {
        this._expanded = value;
        this.expandedChange.emit(this.expanded);
        if (this.expanded) {
            // Set focus on the input when expanded
            this.field.focus();
        }
        else {
            // Clear text when contracted
            this.field.clear();
            // Remove focus (works around an IE issue where the caret remains visible)
            this.field.blur();
        }
    }
    get expanded() {
        return this.alwaysExpanded || this._expanded;
    }
    /*
     * The background color of the component. Color names from the Color Palette can be used here.
     * Specify this when a transparent background would cause display issues, such as background items showing through the search field.
     */
    set background(value) {
        this._backgroundColor = this._colorService.resolve(value) || 'transparent';
    }
    /** Return the correct animation based on the expanded state */
    get _expandedAnimation() {
        return {
            value: this.expanded ? 'expanded' : 'collapsed',
            params: {
                initialWidth: this.button.width + 'px',
            },
        };
    }
    ngAfterContentInit() {
        // Subscribe to the submitted event on the input field, triggering the search event
        this.field.submitted
            .pipe(takeUntil(this._onDestroy))
            .subscribe((text) => this.search.emit(text));
        // Subscribe to cancel events coming from the input field
        this.field.cancel.pipe(takeUntil(this._onDestroy)).subscribe(() => (this.expanded = false));
        // Subscribe to the button click event
        this.button.clicked.pipe(takeUntil(this._onDestroy)).subscribe(() => {
            this.expanded && this.field.text
                ? this.search.emit(this.field.text)
                : (this.expanded = !this.expanded);
        });
        // Create placeholder element to avoid changing layout when switching to position: absolute
        // If the platform is a server we dont want to do this as we can't access getComputedStyle
        if (!isPlatformServer(this._platformId)) {
            this.createPlaceholder();
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        /**
         * We programmatically created the placeholder node so Angular is not aware of its existence
         * so we must manually destroy it otherwise the reference will be retained.
         * Note, the `destroyNode` function may be null or undefined as mentioned in the
         * Angular API docs (https://angular.io/api/core/Renderer2#destroyNode) so
         * we must check that the function is available before attempting to call it
         */
        if (this._placeholder && this._renderer && this._renderer.destroyNode) {
            this._renderer.destroyNode(this._placeholder);
        }
    }
    animationStart(event) {
        if (event.toState === 'expanded') {
            this._position = 'absolute';
            this.setPlaceholderVisible(true);
        }
    }
    animationDone(event) {
        if (event.toState === 'collapsed') {
            this._position = 'relative';
            this.setPlaceholderVisible(false);
        }
    }
    /** Programmatically create a placeholder element */
    createPlaceholder() {
        // Create invisible div with the same dimensions
        this._placeholder = this._renderer.createElement('div');
        this._renderer.setStyle(this._placeholder, 'display', 'none');
        this._renderer.setStyle(this._placeholder, 'width', this.button.width + 'px');
        this._renderer.setStyle(this._placeholder, 'visibility', 'hidden');
        this.setPlaceholderHeight();
        // Add as a sibling
        this._renderer.insertBefore(this._elementRef.nativeElement.parentNode, this._placeholder, this._elementRef.nativeElement);
    }
    /** Update the display state of the placeholder node */
    setPlaceholderVisible(isVisible) {
        if (!this._placeholder) {
            return;
        }
        // Recalculate the height since the layout might not be complete when initially created.
        if (isVisible) {
            this.setPlaceholderHeight();
        }
        this._renderer.setStyle(this._placeholder, 'display', isVisible ? 'inline-block' : 'none');
    }
    /** Set the placeholder height to match the height of this component. */
    setPlaceholderHeight() {
        const { height } = getComputedStyle(this._elementRef.nativeElement);
        this._renderer.setStyle(this._placeholder, 'height', height);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ToolbarSearchComponent, isStandalone: false, selector: "ux-toolbar-search", inputs: { direction: "direction", inverse: "inverse", alwaysExpanded: "alwaysExpanded", expanded: "expanded", background: "background" }, outputs: { expandedChange: "expandedChange", search: "search" }, host: { listeners: { "@expanded.start": "animationStart($event)", "@expanded.done": "animationDone($event)" }, properties: { "class.expanded": "expanded", "class.left": "direction === \"left\"", "class.right": "direction === \"right\"", "class.inverse": "inverse", "style.position": "_position", "style.background-color": "_backgroundColor", "@expanded": "_expandedAnimation" } }, queries: [{ propertyName: "field", first: true, predicate: ToolbarSearchFieldDirective, descendants: true, static: true }, { propertyName: "button", first: true, predicate: ToolbarSearchButtonDirective, descendants: true }], ngImport: i0, template: '<ng-content></ng-content>', isInline: true, animations: [
            trigger('expanded', [
                state('collapsed', style({ width: '{{initialWidth}}' }), {
                    params: { initialWidth: '30px' },
                }),
                state('expanded', style({ width: '100%' })),
                transition('collapsed <=> expanded', [animate('0.3s ease-out')]),
            ]),
        ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'ux-toolbar-search',
                    template: '<ng-content></ng-content>',
                    changeDetection: ChangeDetectionStrategy.OnPush,
                    animations: [
                        trigger('expanded', [
                            state('collapsed', style({ width: '{{initialWidth}}' }), {
                                params: { initialWidth: '30px' },
                            }),
                            state('expanded', style({ width: '100%' })),
                            transition('collapsed <=> expanded', [animate('0.3s ease-out')]),
                        ]),
                    ],
                    host: {
                        '[class.expanded]': 'expanded',
                        '[class.left]': 'direction === "left"',
                        '[class.right]': 'direction === "right"',
                        '[class.inverse]': 'inverse',
                        '[style.position]': '_position',
                        '[style.background-color]': '_backgroundColor',
                        '[@expanded]': '_expandedAnimation',
                    },
                    standalone: false,
                }]
        }], propDecorators: { direction: [{
                type: Input
            }], inverse: [{
                type: Input
            }], alwaysExpanded: [{
                type: Input
            }], expanded: [{
                type: Input
            }], background: [{
                type: Input
            }], expandedChange: [{
                type: Output
            }], search: [{
                type: Output
            }], field: [{
                type: ContentChild,
                args: [ToolbarSearchFieldDirective, { static: true }]
            }], button: [{
                type: ContentChild,
                args: [ToolbarSearchButtonDirective, { static: false }]
            }], animationStart: [{
                type: HostListener,
                args: ['@expanded.start', ['$event']]
            }], animationDone: [{
                type: HostListener,
                args: ['@expanded.done', ['$event']]
            }] } });

const DECLARATIONS$3 = [
    ToolbarSearchComponent,
    ToolbarSearchFieldDirective,
    ToolbarSearchButtonDirective,
];
class ToolbarSearchModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchModule, declarations: [ToolbarSearchComponent,
            ToolbarSearchFieldDirective,
            ToolbarSearchButtonDirective], imports: [CommonModule], exports: [ToolbarSearchComponent,
            ToolbarSearchFieldDirective,
            ToolbarSearchButtonDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ToolbarSearchModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule],
                    exports: DECLARATIONS$3,
                    declarations: DECLARATIONS$3,
                    providers: [],
                }]
        }] });

class VirtualForService {
    constructor() {
        /** Store the size of each item */
        this.itemSize = 0;
        /** Emit the current dataset */
        this.dataset = new ReplaySubject(1);
        /** Emit the visible range */
        this.range = new ReplaySubject(1);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForService, decorators: [{
            type: Injectable
        }] });

/**
 * This implementation is inspired by the CDK virtual for:
 * https://github.com/angular/material2/blob/master/src/cdk/scrolling/virtual-for-of.ts
 * However the CDK requires a container component which limits use in places such
 * as fixed header tables, so this is a more generic implementation that does not
 * require a parent element but instead uses an attribute on the parent container instead
 */
class VirtualForDirective {
    /** Store the list of items to display */
    set uxVirtualForOf(dataset) {
        // emit the latest dataset
        this._virtualScroll.dataset.next(dataset);
        // store a local version of the dataset
        this._dataset = dataset;
        // if this is an update and not the initial dataset then we should
        // forcibly redraw the list of items. In cases where the length of
        // the dataset change would trigger a re-renderer as the scroll position
        // would change, however if we are performing sorting then it would not
        // so we must ensure we update everytime the dataset changes.
        if (this._renderedRange) {
            this.onRangeChange();
        }
    }
    constructor() {
        /** A reference to the container element where we will insert elements. */
        this._viewContainerRef = inject(ViewContainerRef);
        /** The template for all items */
        this._templateRef = inject(TemplateRef);
        /** Gets the set of Angular differs for detecting changes. */
        this._differs = inject(IterableDiffers);
        /** Get the renderer to perform DOM manipulation */
        this._renderer = inject(Renderer2);
        this._changeDetector = inject(ChangeDetectorRef);
        /** A service to share values between the container and child elements */
        this._virtualScroll = inject(VirtualForService, {
            optional: true,
        });
        /** Provide a trackBy function to optimize rendering */
        this.uxVirtualForTrackBy = this.defaultTrackBy;
        /** Indicate whether we need to perform a view update */
        this._isDirty = false;
        /** Store a cache of recently disposed views for reuse */
        this._templateCache = [];
        /** Limit the size of the cache as it can use a lot of memory */
        this._cacheSize = 20;
        /** Unsubscribe from all observables */
        this._onDestroy = new Subject();
        // While marked as optional, it isn't. We do this so we can provide a more helpful error message
        if (!this._virtualScroll) {
            throw new Error('The "uxVirtualFor" directive requires the "uxVirtualForContainer" directive to be added to the parent element.');
        }
    }
    ngOnInit() {
        // update the UI whenever the range changes
        this._virtualScroll.range
            .pipe(distinctUntilChanged(this.isRangeSame), takeUntil(this._onDestroy))
            .subscribe(range => {
            this._renderedRange = range;
            this.onRangeChange();
            this._changeDetector.detectChanges();
        });
    }
    ngDoCheck() {
        if (this._isDirty && this._differ) {
            // check if there area any changes
            const changes = this.getChanges();
            if (changes) {
                this.applyChanges(changes);
            }
            else {
                this.updateContexts();
            }
            // now that we have rendered any change we should store this so we don't perform unneeded updates
            this._isDirty = false;
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** If an itemSize is not specified we need to calculate it */
    getHeight(context, length) {
        // create a temporary view
        const view = this.createView(0);
        // set the implicit value to the item value
        view.context.$implicit = context;
        view.context.count = length;
        view.context.even = true;
        view.context.odd = false;
        view.context.first = true;
        view.context.last = length === 1;
        // run change detection
        view.detectChanges();
        // get the size of the view
        const height = view.rootNodes[0].offsetHeight;
        // destroy the view
        this._viewContainerRef.remove(0);
        view.destroy();
        return height;
    }
    /** Determine if the range has changed (performance optimization) */
    isRangeSame(previous, current) {
        return previous.start === current.start && previous.end === current.end;
    }
    onRangeChange() {
        // store the visible range
        this._renderedItems = this._dataset.slice(this._renderedRange.start, this._renderedRange.end);
        // create the Angular differ if we haven't previously done so
        if (!this._differ) {
            this._differ = this._differs.find(this._renderedItems).create(this.uxVirtualForTrackBy);
        }
        // mark the view for re-render
        this._isDirty = true;
    }
    /** Determine which items have changed */
    getChanges() {
        return this._differ.diff(this._renderedItems);
    }
    /** Insert, move and remove any items within the view */
    applyChanges(changes) {
        // Go through each changes and either add or rearrange accordingly
        changes.forEachOperation((record, previousIndex, currentIndex) => {
            // check if a new item was added
            if (previousIndex === null) {
                // create the new embedded view
                const view = this.createView(currentIndex);
                // set the implicit value to the item value
                view.context.$implicit = record.item;
            }
            else if (currentIndex === null) {
                // check if the item should be removed
                const view = this._viewContainerRef.detach(currentIndex);
                const index = this._viewContainerRef.indexOf(view);
                // if there is space in the cache then store the detached view
                if (this._templateCache.length < this._cacheSize) {
                    this._templateCache.push(view);
                }
                else {
                    index === -1 ? view.destroy() : this._viewContainerRef.remove(index);
                }
            }
            else {
                // the position of the item has changed
                // get the view from its current position
                const view = this._viewContainerRef.get(previousIndex);
                // move it to the new position
                this._viewContainerRef.move(view, currentIndex);
                // update the implicit value (the rest will stay the same)
                view.context.$implicit = record.item;
            }
        });
        // Ensure the implicit value is correct for any items whose identity changed
        changes.forEachIdentityChange((record) => {
            const view = this._viewContainerRef.get(record.currentIndex);
            if (view) {
                view.context.$implicit = record.item;
            }
        });
        this.updateContexts();
    }
    updateContexts() {
        // update all the other context properties
        for (let idx = 0; idx < this._viewContainerRef.length; idx++) {
            // get the view at a given position
            const view = this._viewContainerRef.get(idx);
            // update the properties
            view.context.index = this._renderedRange.start + idx;
            view.context.count = this._dataset.length;
            view.context.first = view.context.index === 0;
            view.context.last = view.context.index === view.context.count - 1;
            view.context.even = view.context.index % 2 === 0;
            view.context.odd = !view.context.even;
            // update the position in the DOM
            view.rootNodes.forEach((node) => {
                this._renderer.setStyle(node, 'position', 'absolute');
                this._renderer.setStyle(node, 'width', '100%');
                this._renderer.setStyle(node, 'top', '0');
                this._renderer.setStyle(node, 'transform', `translateY(${view.context.index * this._virtualScroll.itemSize}px`);
            });
            view.detectChanges();
        }
    }
    createView(index) {
        // get a checked EmbeddedViewRef is there is one
        const cachedTemplate = this._templateCache.pop();
        if (cachedTemplate) {
            // replace existing context with the defaults
            cachedTemplate.context.$implicit = null;
            cachedTemplate.context.index = -1;
            cachedTemplate.context.count = -1;
            cachedTemplate.context.first = false;
            cachedTemplate.context.last = false;
            cachedTemplate.context.even = false;
            cachedTemplate.context.odd = false;
            // insert the view
            this._viewContainerRef.insert(cachedTemplate, index);
            // return the cached EmbeddedViewRef
            return cachedTemplate;
        }
        // otherwise create a new view and insert it
        return this._viewContainerRef.createEmbeddedView(this._templateRef, {
            $implicit: null,
            index: -1,
            count: -1,
            first: false,
            last: false,
            even: false,
            odd: false,
        }, index);
    }
    defaultTrackBy(index) {
        return index;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: VirtualForDirective, isStandalone: false, selector: "[uxVirtualFor][uxVirtualForOf]", inputs: { uxVirtualForOf: "uxVirtualForOf", uxVirtualForTrackBy: "uxVirtualForTrackBy" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxVirtualFor][uxVirtualForOf]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { uxVirtualForOf: [{
                type: Input
            }], uxVirtualForTrackBy: [{
                type: Input
            }] } });

class VirtualForContainerComponent {
    constructor() {
        /** Get the ElementRef of the container element */
        this._elementRef = inject(ElementRef);
        /** A service to share values between the container and child elements */
        this._virtualScroll = inject(VirtualForService);
        /** Handle key presses if there is a tabbable list */
        this._tabbableList = inject(TabbableListService, { optional: true, self: true });
        /** Keep a local reference of the dataset */
        this._dataset = [];
        /** Indicate if the component has finished initialising */
        this._initialized = false;
        /** Unsubscribe from all observables */
        this._onDestroy = new Subject();
    }
    /** Define the height of each virtual item */
    set itemSize(itemSize) {
        this._virtualScroll.itemSize = itemSize;
        if (this._initialized) {
            requestAnimationFrame(() => {
                this.updateContainer();
                this.virtualFor.updateContexts();
            });
        }
    }
    get itemSize() {
        return this._virtualScroll.itemSize;
    }
    /** Determine if this is a table */
    get _isTable() {
        return (this._elementRef.nativeElement.tagName === 'TABLE' ||
            this._elementRef.nativeElement.tagName === 'TBODY');
    }
    /** Determine if this is a list */
    get _isList() {
        return (this._elementRef.nativeElement.tagName === 'OL' ||
            this._elementRef.nativeElement.tagName === 'UL');
    }
    ngAfterViewInit() {
        // subscribe to changes to the dataset
        this._virtualScroll.dataset.pipe(takeUntil(this._onDestroy)).subscribe(dataset => {
            // store the latest dataset
            this._dataset = dataset;
            // update the container properties
            requestAnimationFrame(() => {
                this.updateContainer();
                // mark the component as ready
                this._initialized = true;
            });
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    updateContainer() {
        if (this.itemSize === 0 && this._dataset.length > 0) {
            this.itemSize = this.virtualFor.getHeight(this._dataset[0], this._dataset.length);
        }
        // calculate the total height of all the items
        this._totalHeight = this._dataset.length * this.itemSize;
        // get the actual height of the container element
        const height = this.getContainerHeight();
        // determine the number of items it takes to fill the container height (multiply by 2 to give us some buffer items)
        const itemCount = Math.ceil((height / this.itemSize) * 2);
        /** Determine the number of items we have as a top buffer */
        const topBufferCount = Math.ceil((height / this.itemSize) * 0.5);
        // get the scroll offset
        const scrollOffset = this.getScrollOffset();
        // determine the start index based on the scroll offset
        const startIdx = Math.max(Math.floor(scrollOffset / this.itemSize) - Math.floor(topBufferCount), 0);
        // determine the end index based on the start and the number of items to display
        const endIdx = Math.min(startIdx + itemCount, this._dataset.length);
        // update the range
        this._range = { start: startIdx, end: endIdx };
        // emit the new visible range
        this._virtualScroll.range.next(this._range);
    }
    /** If cells are automatically getting their height detected you may want to update the size */
    recalculateCellSize() {
        this.itemSize = 0;
    }
    onKeydown(event, keyCode) {
        if (!this._tabbableList) {
            return;
        }
        switch (keyCode) {
            case PAGE_UP:
                this._tabbableList.focusKeyManager.setFirstItemActive();
                event.preventDefault();
                break;
            case PAGE_DOWN:
                this._tabbableList.focusKeyManager.setLastItemActive();
                event.preventDefault();
                break;
            case HOME:
                // ensure the QueryList doesn't do any updates until we have finished
                this._tabbableList.shouldFocusOnChange = false;
                // scroll to the top of the container
                this._elementRef.nativeElement.scrollTop = 0;
                // after the update the activate the first item
                requestAnimationFrame(() => {
                    this._tabbableList.focusKeyManager.setFirstItemActive();
                    this._tabbableList.shouldFocusOnChange = true;
                });
                event.preventDefault();
                break;
            case END:
                // ensure the QueryList doesn't do any updates until we have finished
                this._tabbableList.shouldFocusOnChange = false;
                // scroll to the bottom of the container
                this._elementRef.nativeElement.scrollTop = this._elementRef.nativeElement.scrollHeight;
                // after the update the activate the last item
                requestAnimationFrame(() => {
                    this._tabbableList.focusKeyManager.setLastItemActive();
                    this._tabbableList.shouldFocusOnChange = true;
                });
                event.preventDefault();
                break;
        }
    }
    getScrollOffset() {
        return this._elementRef.nativeElement.scrollTop;
    }
    getContainerHeight() {
        return this._elementRef.nativeElement.clientHeight;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: VirtualForContainerComponent, isStandalone: false, selector: "[uxVirtualForContainer]", inputs: { itemSize: "itemSize" }, host: { listeners: { "scroll": "updateContainer()", "keydown": "onKeydown($event,$event.keyCode)" }, properties: { "style.position": "\"relative\"" } }, providers: [VirtualForService], queries: [{ propertyName: "virtualFor", first: true, predicate: VirtualForDirective, descendants: true }], ngImport: i0, template: "<!-- Display the appropriate top spacer -->\n@if (_isTable) {\n  <tr\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></tr>\n}\n\n@if (_isList) {\n  <li\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></li>\n}\n\n@if (!_isTable && !_isList) {\n  <div\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></div>\n}\n\n<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualForContainerComponent, decorators: [{
            type: Component,
            args: [{ selector: '[uxVirtualForContainer]', providers: [VirtualForService], host: {
                        '[style.position]': '"relative"',
                    }, standalone: false, template: "<!-- Display the appropriate top spacer -->\n@if (_isTable) {\n  <tr\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></tr>\n}\n\n@if (_isList) {\n  <li\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></li>\n}\n\n@if (!_isTable && !_isList) {\n  <div\n    class=\"ux-virtual-scroll-spacer\"\n    [style.height.px]=\"_totalHeight\"\n    [style.padding.px]=\"0\"\n    [style.margin.px]=\"0\"\n    [style.border]=\"'none'\"\n  ></div>\n}\n\n<ng-content></ng-content>\n" }]
        }], propDecorators: { itemSize: [{
                type: Input
            }], virtualFor: [{
                type: ContentChild,
                args: [VirtualForDirective, { static: false }]
            }], updateContainer: [{
                type: HostListener,
                args: ['scroll']
            }], onKeydown: [{
                type: HostListener,
                args: ['keydown', ['$event', '$event.keyCode']]
            }] } });

class VirtualScrollCellDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: VirtualScrollCellDirective, isStandalone: false, selector: "[uxVirtualScrollCell]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollCellDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxVirtualScrollCell]',
                    standalone: false,
                }]
        }] });

class VirtualScrollLoadButtonDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollLoadButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: VirtualScrollLoadButtonDirective, isStandalone: false, selector: "[uxVirtualScrollLoadButton]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollLoadButtonDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxVirtualScrollLoadButton]',
                    standalone: false,
                }]
        }] });

class VirtualScrollLoadingDirective {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollLoadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: VirtualScrollLoadingDirective, isStandalone: false, selector: "[uxVirtualScrollLoading]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollLoadingDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxVirtualScrollLoading]',
                    standalone: false,
                }]
        }] });

class VirtualScrollComponent {
    constructor() {
        this.resizeService = inject(ResizeService);
        this._elementRef = inject(ElementRef);
        /** Provide the collection of items to display */
        this.collection = Observable.create();
        /** Indicate whether pages should be loaded on scroll or button click */
        this.loadOnScroll = true;
        /** Emit when we need to load another page */
        this.loading = new EventEmitter();
        this.cells = new BehaviorSubject([]);
        this.scrollTop = 0;
        this.isLoading = false;
        this.pageNumber = 0;
        this.data = [];
        this.loadingComplete = false;
        this._buffer = 5;
        this._onDestroy = new Subject();
        // watch for any future changes to size
        this.resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(event => (this._height = event.height));
    }
    ngOnInit() {
        if (!this.cellHeight) {
            throw new Error('Virtual Scroll Component requires "cellHeight" property to be defined.');
        }
        // subscribe to the collection
        this.setupObservable();
        // load the first page of data
        this.loadNextPage();
    }
    ngAfterContentInit() {
        // re-render cells now that we can display any loading indicator or loading button
        this.renderCells();
    }
    ngOnChanges(changes) {
        if (changes.collection &&
            changes.collection.currentValue !== changes.collection.previousValue &&
            !changes.collection.isFirstChange()) {
            this.setupObservable();
            this.reset();
        }
    }
    ngOnDestroy() {
        this._subscription.unsubscribe();
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    setupObservable() {
        // if there is a current subscription, unsubscribe
        if (this._subscription && this._subscription.unsubscribe) {
            this._subscription.unsubscribe();
        }
        this._subscription = this.collection.subscribe(collection => {
            this.data = [...this.data, ...collection];
            this.renderCells();
            this.isLoading = false;
        }, null, () => {
            this.loadingComplete = true;
        });
    }
    renderCells() {
        this.cells.next(this.getVisibleCells());
        if (this.loadOnScroll && !this.isLoading && !this.loadingComplete) {
            const remainingScroll = this._elementRef.nativeElement.scrollHeight -
                (this._elementRef.nativeElement.scrollTop + this._elementRef.nativeElement.clientHeight);
            // if the current cells take up less than the height of the component then load the next page
            if (remainingScroll <= this._elementRef.nativeElement.clientHeight) {
                this.loadNextPage();
            }
        }
    }
    getVisibleCells() {
        // store the initial element height
        if (!this._height) {
            this._height = this._elementRef.nativeElement.offsetHeight;
        }
        // perform some calculations
        const scrollTop = this._elementRef.nativeElement.scrollTop;
        const startCell = Math.floor(scrollTop / this.cellHeight);
        const endCell = Math.ceil(this._height / this.cellHeight);
        // we want to add some buffer cells on both the top and bottom of the visible list
        const startBuffer = Math.max(0, startCell - this._buffer);
        const endBuffer = startCell + (startCell - startBuffer) + Math.min(this.data.length, endCell + this._buffer);
        // update the scroll position
        this.scrollTop =
            scrollTop - (scrollTop % this.cellHeight) - (startCell - startBuffer) * this.cellHeight;
        // return a sublist of items visible on the screen
        const cells = this.data.slice(startBuffer, endBuffer);
        // now map these cells to a virtual cell interface
        return cells.map((cell, index) => ({ data: cell, index: startBuffer + index }));
    }
    getTotalHeight() {
        return this.cellHeight * this.data.length;
    }
    loadNextPage() {
        this.isLoading = true;
        this.loading.next(this.pageNumber);
        this.pageNumber++;
    }
    reset() {
        // reset all values
        this.scrollTop = 0;
        this.data = [];
        this._height = undefined;
        this.pageNumber = 0;
        this.loadingComplete = false;
        // set scroll position
        this._elementRef.nativeElement.scrollTop = 0;
        // clear the current cells
        this.renderCells();
        // reload first page
        this.loadNextPage();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: VirtualScrollComponent, isStandalone: false, selector: "ux-virtual-scroll", inputs: { collection: "collection", cellHeight: "cellHeight", loadOnScroll: "loadOnScroll" }, outputs: { loading: "loading" }, host: { listeners: { "scroll": "renderCells()" } }, queries: [{ propertyName: "cellTemplate", first: true, predicate: VirtualScrollCellDirective, descendants: true, read: TemplateRef }, { propertyName: "loadingIndicatorTemplate", first: true, predicate: VirtualScrollLoadingDirective, descendants: true, read: TemplateRef }, { propertyName: "loadButtonTemplate", first: true, predicate: VirtualScrollLoadButtonDirective, descendants: true, read: TemplateRef }], usesOnChanges: true, ngImport: i0, template: "<div class=\"virtual-scroll-content-height\" [style.height.px]=\"getTotalHeight()\"></div>\n<div class=\"virtual-scroll-content\" [style.transform]=\"'translateY(' + scrollTop + 'px)'\">\n  <!-- Virtually Render Cells -->\n  @for (cell of cells | async; track cell) {\n    <ng-container\n      *ngTemplateOutlet=\"cellTemplate; context: { cell: cell.data, index: cell.index }\"\n    ></ng-container>\n  }\n\n  <!-- Loading Indicator -->\n  @if (loadingIndicatorTemplate && isLoading) {\n    <ng-container [ngTemplateOutlet]=\"loadingIndicatorTemplate\"></ng-container>\n  }\n\n  <!-- Loading Button -->\n  @if (loadButtonTemplate && !loadOnScroll && !loadingComplete && !isLoading) {\n    <div class=\"virtual-scroll-load-button\" (click)=\"loadNextPage()\">\n      <ng-container *ngTemplateOutlet=\"loadButtonTemplate\"></ng-container>\n    </div>\n  }\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ux-virtual-scroll', standalone: false, template: "<div class=\"virtual-scroll-content-height\" [style.height.px]=\"getTotalHeight()\"></div>\n<div class=\"virtual-scroll-content\" [style.transform]=\"'translateY(' + scrollTop + 'px)'\">\n  <!-- Virtually Render Cells -->\n  @for (cell of cells | async; track cell) {\n    <ng-container\n      *ngTemplateOutlet=\"cellTemplate; context: { cell: cell.data, index: cell.index }\"\n    ></ng-container>\n  }\n\n  <!-- Loading Indicator -->\n  @if (loadingIndicatorTemplate && isLoading) {\n    <ng-container [ngTemplateOutlet]=\"loadingIndicatorTemplate\"></ng-container>\n  }\n\n  <!-- Loading Button -->\n  @if (loadButtonTemplate && !loadOnScroll && !loadingComplete && !isLoading) {\n    <div class=\"virtual-scroll-load-button\" (click)=\"loadNextPage()\">\n      <ng-container *ngTemplateOutlet=\"loadButtonTemplate\"></ng-container>\n    </div>\n  }\n</div>\n" }]
        }], ctorParameters: () => [], propDecorators: { collection: [{
                type: Input
            }], cellHeight: [{
                type: Input
            }], loadOnScroll: [{
                type: Input
            }], loading: [{
                type: Output
            }], cellTemplate: [{
                type: ContentChild,
                args: [VirtualScrollCellDirective, { read: TemplateRef, static: false }]
            }], loadingIndicatorTemplate: [{
                type: ContentChild,
                args: [VirtualScrollLoadingDirective, { read: TemplateRef, static: false }]
            }], loadButtonTemplate: [{
                type: ContentChild,
                args: [VirtualScrollLoadButtonDirective, { read: TemplateRef, static: false }]
            }], renderCells: [{
                type: HostListener,
                args: ['scroll']
            }] } });

const DECLARATIONS$2 = [
    VirtualScrollComponent,
    VirtualScrollLoadingDirective,
    VirtualScrollLoadButtonDirective,
    VirtualScrollCellDirective,
    VirtualForContainerComponent,
    VirtualForDirective,
];
class VirtualScrollModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollModule, declarations: [VirtualScrollComponent,
            VirtualScrollLoadingDirective,
            VirtualScrollLoadButtonDirective,
            VirtualScrollCellDirective,
            VirtualForContainerComponent,
            VirtualForDirective], imports: [AccessibilityModule, CommonModule, ResizeModule], exports: [VirtualScrollComponent,
            VirtualScrollLoadingDirective,
            VirtualScrollLoadButtonDirective,
            VirtualScrollCellDirective,
            VirtualForContainerComponent,
            VirtualForDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollModule, imports: [AccessibilityModule, CommonModule, ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VirtualScrollModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule, CommonModule, ResizeModule],
                    exports: DECLARATIONS$2,
                    declarations: DECLARATIONS$2,
                }]
        }] });

class AutoGrowDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        // ensure this is a textarea or else throw error
        if (this._elementRef.nativeElement.tagName.toLowerCase() !== 'textarea') {
            throw new Error('uxAutoGrow directive can only be used on <textarea> elements.');
        }
    }
    ngAfterViewInit() {
        this.update();
    }
    update() {
        // perform sizing
        this._renderer.setStyle(this._elementRef.nativeElement, 'overflowY', 'hidden');
        this._renderer.setStyle(this._elementRef.nativeElement, 'height', 'auto');
        // get the new total height and element height
        const { scrollHeight } = this._elementRef.nativeElement;
        const { maxHeight } = getComputedStyle(this._elementRef.nativeElement);
        // determine what the maximum allowed height is
        const maximum = !isNaN(parseFloat(maxHeight)) ? parseFloat(maxHeight) : Infinity;
        // if there is a max height specifed we want to show the scrollbars
        if (maximum < scrollHeight) {
            this._renderer.setStyle(this._elementRef.nativeElement, 'overflowY', 'auto');
            this._renderer.setStyle(this._elementRef.nativeElement, 'height', maximum + 'px');
        }
        else {
            this._renderer.setStyle(this._elementRef.nativeElement, 'height', scrollHeight + 'px');
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: AutoGrowDirective, isStandalone: false, selector: "[uxAutoGrow]", host: { listeners: { "input": "update()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxAutoGrow]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { update: [{
                type: HostListener,
                args: ['input']
            }] } });

class AutoGrowModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowModule, declarations: [AutoGrowDirective], exports: [AutoGrowDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AutoGrowModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [AutoGrowDirective],
                    declarations: [AutoGrowDirective],
                }]
        }] });

class BadgeDirective {
    constructor() {
        this._element = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._colorService = inject(ColorService);
        this._contrastService = inject(ContrastService);
        this._className = 'ux-badge';
        this._darkColor = ThemeColor.parse('#000');
        this._lightColor = ThemeColor.parse('#FFF');
        this._badgeContent = null;
        this._badgeColor = this._darkColor;
        /**
         * Set the badge vertical position in relation to the parent element
         */
        this.badgeVerticalPosition = 'above';
        /**
         * Set the badge horizontal position in relation to the parent element
         */
        this.badgeHorizontalPosition = 'after';
        /**
         * Set if the badge overlaps parent content or flows after parent
         */
        this.badgeOverlap = false;
        /**
         * Badge size (based on CSS styles)
         */
        this.badgeSize = 'medium';
        /**
         * Hide badge from view
         */
        this.badgeHidden = false;
    }
    get badgeContent() {
        return this._badgeContent;
    }
    set badgeContent(badge) {
        if (typeof badge === 'number') {
            this._badgeContent = badge;
            this._isNumber = true;
        }
        else if (typeof badge === 'string' && badge.replace(/ /g, '').length > 0) {
            const subject = badge.trim();
            this._isNumber = /^\d+$/.test(subject);
            this._badgeContent = subject;
        }
        else {
            this._badgeContent = null;
        }
    }
    /**
     * Define the badge background color
     */
    get badgeColor() {
        return this._badgeColor.toRgba();
    }
    set badgeColor(color) {
        this._badgeColor = this.parseThemeColor(color);
    }
    /**
     * Define the badge border color - if unset there is no border
     */
    get badgeBorderColor() {
        return this._badgeBorderColor.toRgba();
    }
    set badgeBorderColor(color) {
        this._badgeBorderColor = this.parseThemeColor(color);
    }
    ngAfterViewInit() {
        this._badgeElement = this._renderer.createElement('span');
        this._renderer.addClass(this._badgeElement, this._className);
        this._renderer.setStyle(this._badgeElement, 'display', 'none');
        this.setBadgeColor();
        this.setBadgeBorderColor();
        this.setBadgeSize();
        this.setContent(this._badgeContent, this.badgeMaxValue);
        this._renderer.appendChild(this._element.nativeElement, this._badgeElement);
        this._renderer.removeStyle(this._badgeElement, 'display');
    }
    ngOnChanges(changes) {
        // if the badge is visible set changed values
        if (!this._badgeElement) {
            return;
        }
        // set badge content and get display friendly version of text based on max length and type of val
        if (changes.badgeContent || changes.badgeMaxValue) {
            const finalText = (changes.badgeContent && changes.badgeContent.currentValue) || this.badgeContent || null;
            const maxValue = (changes.badgeMaxValue && changes.badgeMaxValue.currentValue) || this.badgeMaxValue || null;
            this.setContent(finalText, maxValue);
        }
        // set the badge color
        if (changes.badgeColor &&
            changes.badgeColor.currentValue !== changes.badgeColor.previousValue) {
            this.setBadgeColor();
        }
        // set the badge border color
        if (changes.badgeBorderColor &&
            changes.badgeBorderColor.currentValue !== changes.badgeBorderColor.previousValue) {
            this.setBadgeBorderColor();
        }
        // set badge size
        if (changes.badgeSize && changes.badgeSize.currentValue !== changes.badgeSize.previousValue) {
            this.setBadgeSize(changes.badgeSize.previousValue);
        }
    }
    ngOnDestroy() {
        if (this._renderer.destroyNode) {
            this._renderer.destroyNode(this._badgeElement);
        }
    }
    setContent(content, maxValue) {
        if (content && maxValue && maxValue > 0) {
            if (this._isNumber) {
                const numericValue = typeof content === 'number' ? content : parseInt(content);
                if (numericValue > maxValue) {
                    content = `${maxValue}+`;
                }
            }
            else if (typeof content === 'string' && content.length > maxValue) {
                content = `${content.substr(0, maxValue)}…`;
            }
        }
        this._badgeDisplayContent = content;
        this._badgeElement.textContent = this._badgeDisplayContent?.toString();
    }
    setBadgeColor() {
        if (this._badgeColor) {
            this._renderer.setStyle(this._badgeElement, 'background-color', this._badgeColor.toRgba());
        }
        else {
            this._renderer.removeStyle(this._badgeElement, 'background-color');
        }
        this._renderer.setStyle(this._badgeElement, 'color', this.determineContentTextColor().toRgba());
    }
    setBadgeBorderColor() {
        if (this._badgeBorderColor) {
            this._renderer.setStyle(this._badgeElement, 'border-color', this._badgeBorderColor.toRgba());
        }
        else {
            this._renderer.removeStyle(this._badgeElement, 'border-color');
        }
        this._renderer.setStyle(this._badgeElement, 'background-clip', this._badgeBorderColor ? 'padding-box' : 'border-box');
    }
    setBadgeSize(previousSize) {
        if (previousSize) {
            this._renderer.removeClass(this._badgeElement, `ux-badge-${previousSize}`);
        }
        this._renderer.addClass(this._badgeElement, `ux-badge-${this.badgeSize}`);
    }
    determineContentTextColor() {
        return this._badgeColor
            ? ThemeColor.parse(this._contrastService
                .getContrastColor(this._badgeColor, this._lightColor, this._darkColor)
                .toRgba())
            : this._lightColor;
    }
    parseThemeColor(color) {
        if (!color) {
            return null;
        }
        return this._colorService.colorExists(color)
            ? ThemeColor.parse(this._colorService.resolve(color))
            : ThemeColor.parse(color);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BadgeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: BadgeDirective, isStandalone: false, selector: "[uxBadge]", inputs: { badgeContent: ["uxBadge", "badgeContent"], badgeColor: "badgeColor", badgeBorderColor: "badgeBorderColor", badgeVerticalPosition: "badgeVerticalPosition", badgeHorizontalPosition: "badgeHorizontalPosition", badgeOverlap: "badgeOverlap", badgeMaxValue: "badgeMaxValue", badgeSize: "badgeSize", badgeHidden: "badgeHidden" }, host: { properties: { "class.ux-badge-above": "badgeVerticalPosition === \"above\"", "class.ux-badge-below": "badgeVerticalPosition === \"below\"", "class.ux-badge-after": "badgeHorizontalPosition === \"after\"", "class.ux-badge-before": "badgeHorizontalPosition === \"before\"", "class.ux-badge-overlap": "this.badgeOverlap", "class.ux-badge-hidden": "this.badgeHidden" }, classAttribute: "ux-badge-container" }, exportAs: ["ux-badge"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BadgeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxBadge]',
                    exportAs: 'ux-badge',
                    host: {
                        class: 'ux-badge-container',
                        '[class.ux-badge-above]': 'badgeVerticalPosition === "above"',
                        '[class.ux-badge-below]': 'badgeVerticalPosition === "below"',
                        '[class.ux-badge-after]': 'badgeHorizontalPosition === "after"',
                        '[class.ux-badge-before]': 'badgeHorizontalPosition === "before"',
                    },
                    standalone: false,
                }]
        }], propDecorators: { badgeContent: [{
                type: Input,
                args: ['uxBadge']
            }], badgeColor: [{
                type: Input
            }], badgeBorderColor: [{
                type: Input
            }], badgeVerticalPosition: [{
                type: Input
            }], badgeHorizontalPosition: [{
                type: Input
            }], badgeOverlap: [{
                type: HostBinding,
                args: ['class.ux-badge-overlap']
            }, {
                type: Input
            }], badgeMaxValue: [{
                type: Input
            }], badgeSize: [{
                type: Input
            }], badgeHidden: [{
                type: HostBinding,
                args: ['class.ux-badge-hidden']
            }, {
                type: Input
            }] } });

class BadgeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BadgeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: BadgeModule, declarations: [BadgeDirective], imports: [ColorServiceModule, AccessibilityModule], exports: [BadgeDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BadgeModule, imports: [ColorServiceModule, AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BadgeModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [ColorServiceModule, AccessibilityModule],
                    exports: [BadgeDirective],
                    declarations: [BadgeDirective],
                }]
        }] });

class FixedHeaderTableDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._resizeService = inject(ResizeService);
        /** Emit when the table tries to load more data */
        this.tablePaging = new EventEmitter();
        /** Apply a class whenever the table has scrolled */
        this._hasScrolled = false;
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
    }
    /** Allow dataset changes to trigger re-layout */
    set dataset(_dataset) {
        requestAnimationFrame(() => this.setLayout());
    }
    ngOnInit() {
        // add class to the table
        this._renderer.addClass(this._elementRef.nativeElement, 'ux-fixed-header-table');
        // locate the important elements
        this._tableHead = this._elementRef.nativeElement.querySelector('thead');
        this._tableBody = this._elementRef.nativeElement.querySelector('tbody');
        // bind to scroll events on the table body
        this._renderer.listen(this._tableBody, 'scroll', this.onScroll.bind(this));
        // resize the table header to account for scrollbar
        this.setLayout();
        // if a resize occurs perform a relayout (this can be useful when displaying tables in modals)
        this._resizeService
            .addResizeListener(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(() => this.setLayout());
        // trigger the loading of the first page
        this.tablePaging.emit();
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /**
     * Get the table element
     * Primarily used by column width directive
     */
    getTable() {
        return this._elementRef.nativeElement;
    }
    /**
     * Update the size of the table header to account for the scrollbar.
     * This is important to keep the columns aligned
     */
    setLayout() {
        if (!this._tableBody || !this._tableHead) {
            return;
        }
        // calculate the size of the scrollbar
        const scrollbar = this._tableBody.offsetWidth - this._tableBody.clientWidth;
        // add padding to the header to account for this
        this._renderer.setStyle(this._tableHead, 'padding-right', scrollbar + 'px');
        // set the desired height of the table body
        this._renderer.setStyle(this._tableBody, 'height', typeof this.tableHeight === 'number' ? `${this.tableHeight}px` : this.tableHeight);
    }
    /**
     * Handle scroll events
     */
    onScroll() {
        // determine if we are scrolled to the bottom and if so load the next page
        const scrollTop = this._tableBody.scrollTop;
        const scrollHeight = this._tableBody.scrollHeight - this._tableBody.offsetHeight;
        const delta = Math.max(scrollTop, scrollHeight) - Math.min(scrollTop, scrollHeight);
        // its possible for the difference to be a value < 1 when we are at the bottom. Account for this:
        if (delta < 1) {
            this.tablePaging.emit();
        }
        // update the class based on the scroll position
        this._hasScrolled = scrollTop > 0;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FixedHeaderTableDirective, isStandalone: false, selector: "[uxFixedHeaderTable]", inputs: { dataset: "dataset", tableHeight: "tableHeight" }, outputs: { tablePaging: "tablePaging" }, host: { properties: { "class.ux-fixed-header-table-scrolled": "this._hasScrolled" } }, exportAs: ["ux-fixed-header-table"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFixedHeaderTable]',
                    exportAs: 'ux-fixed-header-table',
                    standalone: false,
                }]
        }], propDecorators: { dataset: [{
                type: Input
            }], tableHeight: [{
                type: Input
            }], tablePaging: [{
                type: Output
            }], _hasScrolled: [{
                type: HostBinding,
                args: ['class.ux-fixed-header-table-scrolled']
            }] } });

class FixedHeaderTableModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableModule, declarations: [FixedHeaderTableDirective], imports: [ResizeModule], exports: [FixedHeaderTableDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableModule, imports: [ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FixedHeaderTableModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [ResizeModule],
                    exports: [FixedHeaderTableDirective],
                    declarations: [FixedHeaderTableDirective],
                }]
        }] });

class FloatLabelDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._renderer = inject(Renderer2);
        this._autofillMonitor = inject(AutofillMonitor);
        this.mode = 'focus';
        this.raised = false;
        this._focused = false;
        this._eventHandles = [];
        this._subscription = new Subscription();
    }
    set input(input) {
        // remove any previous autofill subscriptions
        if (this._input) {
            this._autofillMonitor.stopMonitoring(this._input);
        }
        this._subscription.unsubscribe();
        this._input = input;
        // if the input is null then don't need to subscribe to autofillMonitor
        if (!input) {
            return;
        }
        // create a new autofillMonitor subscription
        this._subscription = this._autofillMonitor.monitor(input).subscribe(event => {
            if (!this.raised && event.isAutofilled) {
                this.raised = true;
            }
            if (this.raised && !event.isAutofilled && !this.hasText()) {
                this.raised = false;
            }
        });
    }
    get input() {
        return this._input;
    }
    ngOnInit() {
        this._eventHandles.push(this._renderer.listen(this.input, 'focus', this.inputFocus.bind(this)), this._renderer.listen(this.input, 'blur', this.inputBlur.bind(this)), this._renderer.listen(this.input, 'input', this.inputChange.bind(this)));
        // Check initial input value
        this.raised = this.hasText();
        // Ensure that the `for` attribute is set
        if (!this._elementRef.nativeElement.getAttribute('for') && this.input.getAttribute('id')) {
            this._renderer.setAttribute(this._elementRef.nativeElement, 'for', this.input.getAttribute('id'));
        }
    }
    ngOnChanges() {
        if (!(this.mode === 'focus' && this._focused)) {
            this.raised = this.hasText();
        }
    }
    ngOnDestroy() {
        // Unsubscribe event handles
        this._eventHandles.forEach(eventHandle => eventHandle());
        this._autofillMonitor.stopMonitoring(this._input);
        this._subscription.unsubscribe();
    }
    hasText() {
        if (this.value === undefined) {
            return !!this.input.value;
        }
        return !!this.value;
    }
    inputFocus() {
        if (this.mode === 'focus') {
            this._focused = true;
            this.raised = true;
        }
    }
    inputBlur() {
        if (this.mode === 'focus') {
            this._focused = false;
            this.raised = this.hasText();
        }
    }
    inputChange() {
        if (this.mode === 'input') {
            this.raised = this.hasText();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: FloatLabelDirective, isStandalone: false, selector: "[uxFloatLabel]", inputs: { input: ["uxFloatLabel", "input"], value: "value", mode: "mode" }, host: { properties: { "class.ux-float-label-raised": "this.raised" }, classAttribute: "ux-float-label" }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxFloatLabel]',
                    host: {
                        class: 'ux-float-label',
                    },
                    standalone: false,
                }]
        }], propDecorators: { input: [{
                type: Input,
                args: ['uxFloatLabel']
            }], value: [{
                type: Input
            }], mode: [{
                type: Input
            }], raised: [{
                type: HostBinding,
                args: ['class.ux-float-label-raised']
            }] } });

class FloatLabelModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelModule, declarations: [FloatLabelDirective], exports: [FloatLabelDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FloatLabelModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [],
                    exports: [FloatLabelDirective],
                    declarations: [FloatLabelDirective],
                    providers: [],
                }]
        }] });

class HelpCenterService {
    constructor() {
        this.items = new BehaviorSubject([]);
    }
    registerItem(item) {
        // get the current items
        const items = this.items.getValue();
        // add the new item to the list
        items.push(item);
        // update the observable
        this.items.next(items);
    }
    unregisterItem(item) {
        // get the current items
        let items = this.items.getValue();
        // remove the item being unregistered
        items = items.filter(itm => itm !== item);
        // update the observable
        this.items.next(items);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class HelpCenterItemDirective {
    constructor() {
        this._helpCenterService = inject(HelpCenterService);
    }
    ngOnInit() {
        // register the item in the service
        this._helpCenterService.registerItem(this.uxHelpCenterItem);
    }
    ngOnDestroy() {
        // remove this item when it is destroyed
        this._helpCenterService.unregisterItem(this.uxHelpCenterItem);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: HelpCenterItemDirective, isStandalone: false, selector: "[uxHelpCenterItem]", inputs: { uxHelpCenterItem: "uxHelpCenterItem" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxHelpCenterItem]',
                    standalone: false,
                }]
        }], propDecorators: { uxHelpCenterItem: [{
                type: Input
            }] } });

class HelpCenterModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterModule, declarations: [HelpCenterItemDirective], exports: [HelpCenterItemDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HelpCenterModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [HelpCenterItemDirective],
                    declarations: [HelpCenterItemDirective],
                }]
        }] });

class HoverActionService {
    constructor() {
        this.active = new BehaviorSubject(false);
        this._focused = false;
        this._hovered = false;
        this._actions = [];
    }
    register(action) {
        this._actions.push(action);
    }
    unregister(action) {
        this._actions = this._actions.filter(actn => actn !== action);
    }
    setFocusState(focus) {
        this._focused = focus;
        this.updateVisibility();
    }
    setHoverState(hover) {
        this._hovered = hover;
        this.updateVisibility();
    }
    updateVisibility() {
        this.active.next(this._focused || this._hovered || this.actionHasFocus());
    }
    actionHasFocus() {
        return !!this.getFocusedAction();
    }
    getFocusedAction() {
        return this._actions.find(action => action.focused);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionService, decorators: [{
            type: Injectable
        }] });

class HoverActionContainerDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._managedFocusContainerService = inject(ManagedFocusContainerService);
        this._hoverActionService = inject(HoverActionService);
        this.tabindex = 0;
        this.active = false;
        this._onDestroy = new Subject();
    }
    ngOnInit() {
        // Watch for focus within the container element and manage tabindex of descendants
        this._managedFocusContainerService.register(this._elementRef.nativeElement, this);
        // Track focus and update state for the child directives
        this._managedFocusContainerService
            .hasFocus(this._elementRef.nativeElement)
            .pipe(takeUntil(this._onDestroy))
            .subscribe(active => {
            this.active = active;
            this._hoverActionService.setFocusState(active);
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this._managedFocusContainerService.unregister(this._elementRef.nativeElement, this);
    }
    onHover() {
        this._hoverActionService.setHoverState(true);
    }
    onLeave() {
        this._hoverActionService.setHoverState(false);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: HoverActionContainerDirective, isStandalone: false, selector: "[uxHoverActionContainer]", inputs: { tabindex: "tabindex" }, host: { listeners: { "mouseenter": "onHover()", "mouseleave": "onLeave()" }, properties: { "tabindex": "this.tabindex", "class.hover-action-container-active": "this.active" } }, providers: [HoverActionService], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionContainerDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxHoverActionContainer]',
                    providers: [HoverActionService],
                    standalone: false,
                }]
        }], propDecorators: { tabindex: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['tabindex']
            }], active: [{
                type: HostBinding,
                args: ['class.hover-action-container-active']
            }], onHover: [{
                type: HostListener,
                args: ['mouseenter']
            }], onLeave: [{
                type: HostListener,
                args: ['mouseleave']
            }] } });

class HoverActionDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        this._hoverActionService = inject(HoverActionService);
        this.focusIndicatorService = inject(FocusIndicatorService);
        this.tabindex = 0;
        this.active = false;
        this.focused = false;
        this._onDestroy = new Subject();
        // create the focus indicator
        this._focusIndicator = this.focusIndicatorService.monitor(this._elementRef.nativeElement);
        // register the action
        this._hoverActionService.register(this);
        // watch for changes to the activeness of the container
        this._hoverActionService.active
            .pipe(takeUntil(this._onDestroy))
            .subscribe(active => (this.active = active));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
        this._hoverActionService.unregister(this);
        this._focusIndicator.destroy();
    }
    focus() {
        this._elementRef.nativeElement.focus();
    }
    onFocus() {
        this.focused = true;
        this._hoverActionService.updateVisibility();
    }
    onBlur() {
        this.focused = false;
        this._hoverActionService.updateVisibility();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: HoverActionDirective, isStandalone: false, selector: "[uxHoverAction]", inputs: { tabindex: "tabindex" }, host: { listeners: { "focus": "onFocus()", "blur": "onBlur()" }, properties: { "tabindex": "this.tabindex", "class.hover-action-active": "this.active", "class.hover-action-focused": "this.focused" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxHoverAction]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { tabindex: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['tabindex']
            }], active: [{
                type: HostBinding,
                args: ['class.hover-action-active']
            }], focused: [{
                type: HostBinding,
                args: ['class.hover-action-focused']
            }], onFocus: [{
                type: HostListener,
                args: ['focus']
            }], onBlur: [{
                type: HostListener,
                args: ['blur']
            }] } });

const DECLARATIONS$1 = [HoverActionDirective, HoverActionContainerDirective];
class HoverActionModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: HoverActionModule, declarations: [HoverActionDirective, HoverActionContainerDirective], imports: [AccessibilityModule], exports: [HoverActionDirective, HoverActionContainerDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionModule, imports: [AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: HoverActionModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule],
                    exports: DECLARATIONS$1,
                    declarations: DECLARATIONS$1,
                }]
        }] });

class LayoutSwitcherItemDirective {
    constructor() {
        this._templateRef = inject(TemplateRef);
        this._viewContainerRef = inject(ViewContainerRef);
    }
    getLayout() {
        return this._templateRef;
    }
    getConfig() {
        return this._config;
    }
    activate() {
        this._embeddedView = this._viewContainerRef.createEmbeddedView(this._templateRef);
    }
    deactivate() {
        const index = this._viewContainerRef.indexOf(this._embeddedView);
        this._viewContainerRef.remove(index);
        this._embeddedView = null;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: LayoutSwitcherItemDirective, isStandalone: false, selector: "[uxLayoutSwitcherItem]", inputs: { _config: ["uxLayoutSwitcherItem", "_config"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxLayoutSwitcherItem]',
                    standalone: false,
                }]
        }], propDecorators: { _config: [{
                type: Input,
                args: ['uxLayoutSwitcherItem']
            }] } });

class LayoutSwitcherDirective {
    constructor() {
        this.resizeService = inject(ResizeService);
        this._elementRef = inject(ElementRef);
        this._viewContainerRef = inject(ViewContainerRef);
        // watch for changes to the container size
        this.resizeService.addResizeListener(this._elementRef.nativeElement).subscribe(event => {
            this._width = event.width;
            // render the appropriate layout
            this.updateActiveLayout();
        });
    }
    ngOnChanges(changes) {
        // if the active group has changed then render the appropriate layout
        if (changes.group.currentValue !== changes.group.previousValue) {
            this.updateActiveLayout();
        }
    }
    getActiveLayout() {
        // if there are currently no layouts then do nothing
        if (!this._layouts) {
            return null;
        }
        // otherwise find layouts that match the active group and that meet the constraints
        return this._layouts
            .filter(layout => this.group === layout.getConfig().group)
            .find(layout => {
            const minWidth = layout.getConfig().minWidth || 0;
            const maxWidth = layout.getConfig().maxWidth || Infinity;
            return this._width >= minWidth && this._width < maxWidth;
        });
    }
    updateActiveLayout() {
        // get the layout that should be shown
        const layout = this.getActiveLayout();
        // check if we are currently showing the layout
        if (this._activeLayout === layout) {
            return;
        }
        // remove the current layout
        if (this._activeLayout) {
            this._activeLayout.deactivate();
        }
        // store the new active layout
        this._activeLayout = layout;
        // if there is an active layout then activate
        if (this._activeLayout) {
            this._activeLayout.activate();
        }
    }
    ngAfterContentInit() {
        // store the initial current element width
        this._width = this._elementRef.nativeElement.offsetWidth;
        // render the appropriate layout - need a delay as Angular doesn't like changes like this in these lifecycle hooks
        requestAnimationFrame(this.updateActiveLayout.bind(this));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: LayoutSwitcherDirective, isStandalone: false, selector: "[uxLayoutSwitcher]", inputs: { group: "group" }, queries: [{ propertyName: "_layouts", predicate: LayoutSwitcherItemDirective }], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxLayoutSwitcher]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { group: [{
                type: Input
            }], _layouts: [{
                type: ContentChildren,
                args: [LayoutSwitcherItemDirective]
            }] } });

const DECLARATIONS = [LayoutSwitcherDirective, LayoutSwitcherItemDirective];
class LayoutSwitcherModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherModule, declarations: [LayoutSwitcherDirective, LayoutSwitcherItemDirective], imports: [ResizeModule], exports: [LayoutSwitcherDirective, LayoutSwitcherItemDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherModule, imports: [ResizeModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LayoutSwitcherModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [ResizeModule],
                    exports: DECLARATIONS,
                    declarations: DECLARATIONS,
                    providers: [],
                }]
        }] });

class MenuNavigationService {
    constructor() {
        /** Store a list of items that belong to this menu */
        this.menuItems = [];
        /** Store the current active menu item */
        this.active$ = new BehaviorSubject(null);
    }
    /** Add an item to this menu */
    register(menuItem) {
        this.menuItems = [...this.menuItems, menuItem];
    }
    /** Remove an item from the list of menu items */
    unregister(menuItem) {
        this.menuItems = this.menuItems.filter(_menuItem => _menuItem !== menuItem);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationService, decorators: [{
            type: Injectable
        }] });

class MenuNavigationItemDirective {
    constructor() {
        this.focusIndicatorService = inject(FocusIndicatorService);
        this._elementRef = inject(ElementRef);
        this._menuNavigationService = inject(MenuNavigationService);
        /** Emit when this menu is activated */
        this.activated = new EventEmitter();
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
        // register this item with the menu - this allows for nested menus as we each uxMenuNavigation will create its own service
        this._menuNavigationService.register(this);
        // create the focus indicator
        this._focusIndicator = this.focusIndicatorService.monitor(this._elementRef.nativeElement, {
            programmaticFocusIndicator: true,
            checkChildren: false,
        });
        /** Subscribe to the current active index */
        this._menuNavigationService.active$
            .pipe(takeUntil(this._onDestroy), filter(item => item === this))
            .subscribe(() => this.setActive());
    }
    ngOnDestroy() {
        this._menuNavigationService.unregister(this);
        this._onDestroy.unsubscribe();
        this._focusIndicator.destroy();
    }
    setActive() {
        this._elementRef.nativeElement.focus();
        this.activated.emit();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuNavigationItemDirective, isStandalone: false, selector: "[uxMenuNavigationItem]", outputs: { activated: "activated" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationItemDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuNavigationItem]',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { activated: [{
                type: Output
            }] } });

class MenuNavigationToggleDirective {
    /** Define if the menu is open */
    get menuOpen() {
        return this._menuOpen;
    }
    set menuOpen(value) {
        this._menuOpen = value;
        this.menuOpenChange.emit(value);
    }
    constructor() {
        this.elementRef = inject(ElementRef);
        this.focusIndicatorService = inject(FocusIndicatorService);
        /** Define the position the menu appears relative to the button */
        this.menuPosition = 'bottom';
        /** Emit when the menu open state changes */
        this.menuOpenChange = new EventEmitter();
        /** Emits whenever a key that opens the menu is pressed */
        this.keyEnter = new EventEmitter();
        this._focusIndicator = this.focusIndicatorService.monitor(this.elementRef.nativeElement);
    }
    ngOnDestroy() {
        this._focusIndicator.destroy();
    }
    focus(origin) {
        this._focusIndicator.focus(origin);
    }
    keydownHandler(event) {
        if (this.isKeyMatch(event.which)) {
            // Open the menu
            this.menuOpen = true;
            // Allow the menu to init, then send the event to give it focus
            setTimeout(() => this.keyEnter.emit());
            event.preventDefault();
            event.stopPropagation();
        }
    }
    isKeyMatch(key) {
        switch (key) {
            case ENTER:
            case SPACE:
                return true;
            case UP_ARROW:
                return this.menuPosition === 'top';
            case DOWN_ARROW:
                return this.menuPosition === 'bottom';
            case LEFT_ARROW:
                return this.menuPosition === 'left';
            case RIGHT_ARROW:
                return this.menuPosition === 'right';
        }
        return false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationToggleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuNavigationToggleDirective, isStandalone: false, selector: "[uxMenuNavigationToggle]", inputs: { menuOpen: "menuOpen", menuPosition: "menuPosition" }, outputs: { menuOpenChange: "menuOpenChange", keyEnter: "keyEnter" }, host: { listeners: { "keydown": "keydownHandler($event)" } }, exportAs: ["uxMenuNavigationToggle"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationToggleDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuNavigationToggle]',
                    exportAs: 'uxMenuNavigationToggle',
                    standalone: false,
                }]
        }], ctorParameters: () => [], propDecorators: { menuOpen: [{
                type: Input
            }], menuPosition: [{
                type: Input
            }], menuOpenChange: [{
                type: Output
            }], keyEnter: [{
                type: Output
            }], keydownHandler: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

class MenuNavigationDirective {
    constructor() {
        this._menuNavigationService = inject(MenuNavigationService);
        /** Define the position of the toggle button relative to the menu */
        this.toggleButtonPosition = 'top';
        /** Emit when the menu is no longer focused */
        this.navigatedOut = new EventEmitter();
        /** Determine if the menu currently has focus */
        this._isFocused = false;
        /** Unsubscribe from all observables on destroy */
        this._onDestroy = new Subject();
    }
    /** Get the index of the currently active item */
    get activeIndex() {
        return this.menuItems.indexOf(this._menuNavigationService.active$.value);
    }
    // get the list of menu items
    get menuItems() {
        return this._menuNavigationService.menuItems;
    }
    ngOnInit() {
        if (this.toggleButton) {
            this.toggleButton.keyEnter
                .pipe(takeUntil(this._onDestroy))
                .subscribe(() => this.focusFirst());
        }
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    focusFirst() {
        this.moveFirst();
    }
    onFocusIn() {
        this._isFocused = true;
    }
    onFocusOut() {
        this._isFocused = false;
    }
    keydownHandler(event) {
        // Only handle events when focus in within the list of menu items
        if (this._isFocused === false) {
            return;
        }
        let handled = false;
        switch (event.which) {
            case UP_ARROW:
                this.movePrevious(event);
                handled = true;
                break;
            case DOWN_ARROW:
                this.moveNext(event);
                handled = true;
                break;
            case LEFT_ARROW:
                if (this.toggleButtonPosition === 'left') {
                    this.moveToToggleButton(event);
                    handled = true;
                }
                break;
            case RIGHT_ARROW:
                if (this.toggleButtonPosition === 'right') {
                    this.moveToToggleButton(event);
                    handled = true;
                }
                break;
            case HOME:
                this.moveFirst();
                handled = true;
                break;
            case END:
                this.moveLast();
                handled = true;
                break;
            case ESCAPE:
                this.moveToToggleButton(event);
                handled = true;
                break;
        }
        if (handled) {
            event.preventDefault();
            event.stopPropagation();
        }
    }
    moveNext(event) {
        // Do nothing if there's no active menu item registered
        if (this.activeIndex < 0) {
            return;
        }
        const nextIndex = this.activeIndex + 1;
        if (nextIndex < this.menuItems.length) {
            // Activate the next menu item
            // (uxMenuNavigationItem subscribes to this and applies focus if it matches)
            this._menuNavigationService.active$.next(this.menuItems[nextIndex]);
        }
        else {
            // Check if focus went out of bounds in the direction of the origin toggle button
            if (this.toggleButtonPosition === 'bottom') {
                this.moveToToggleButton(event);
            }
        }
    }
    movePrevious(event) {
        // Do nothing if there's no active menu item registered
        if (this.activeIndex < 0) {
            return;
        }
        const nextIndex = this.activeIndex - 1;
        if (nextIndex >= 0) {
            // Activate the previous menu item
            // (uxMenuNavigationItem subscribes to this and applies focus if it matches)
            this._menuNavigationService.active$.next(this.menuItems[nextIndex]);
        }
        else {
            // Check if focus went out of bounds in the direction of the origin toggle button
            if (this.toggleButtonPosition === 'top') {
                this.moveToToggleButton(event);
            }
        }
    }
    moveFirst() {
        if (this.menuItems.length > 0) {
            this._menuNavigationService.active$.next(this.menuItems[0]);
        }
    }
    moveLast() {
        if (this.menuItems.length > 0) {
            this._menuNavigationService.active$.next(this.menuItems[this.menuItems.length - 1]);
        }
    }
    moveToToggleButton(event) {
        if (this.toggleButton) {
            this.toggleButton.focus('keyboard');
            this.toggleButton.menuOpen = false;
        }
        this.navigatedOut.emit(event);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: MenuNavigationDirective, isStandalone: false, selector: "[uxMenuNavigation]", inputs: { toggleButton: "toggleButton", toggleButtonPosition: "toggleButtonPosition" }, outputs: { navigatedOut: "navigatedOut" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusOut()", "keydown": "keydownHandler($event)" } }, providers: [MenuNavigationService], exportAs: ["uxMenuNavigation"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxMenuNavigation]',
                    exportAs: 'uxMenuNavigation',
                    providers: [MenuNavigationService],
                    standalone: false,
                }]
        }], propDecorators: { toggleButton: [{
                type: Input
            }], toggleButtonPosition: [{
                type: Input
            }], navigatedOut: [{
                type: Output
            }], onFocusIn: [{
                type: HostListener,
                args: ['focusin']
            }], onFocusOut: [{
                type: HostListener,
                args: ['focusout']
            }], keydownHandler: [{
                type: HostListener,
                args: ['keydown', ['$event']]
            }] } });

const EXPORTS = [
    MenuNavigationDirective,
    MenuNavigationItemDirective,
    MenuNavigationToggleDirective,
];
class MenuNavigationModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationModule, declarations: [MenuNavigationDirective,
            MenuNavigationItemDirective,
            MenuNavigationToggleDirective], imports: [AccessibilityModule], exports: [MenuNavigationDirective,
            MenuNavigationItemDirective,
            MenuNavigationToggleDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationModule, imports: [AccessibilityModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MenuNavigationModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [AccessibilityModule],
                    exports: EXPORTS,
                    declarations: EXPORTS,
                }]
        }] });

class OverflowDirective {
    constructor() {
        this._elementRef = inject(ElementRef);
        /** Allow overflow to be within a range before emitting */
        this.tolerance = 0;
        /** Emit when there is a change to the overflow state - horizontal or vertical */
        this.uxOverflowObserver = new EventEmitter();
        /** Emit when there is a change to overflow on the horizontal axis */
        this.uxOverflowHorizontalObserver = new EventEmitter();
        /** Emit when there is a change to overflow on the vertical axis */
        this.uxOverflowVerticalObserver = new EventEmitter();
        /** Store the overflow state on both axis */
        this._state = { horizontalOverflow: false, verticalOverflow: false };
        /** Unsubscribe from all the observables */
        this._onDestroy = new Subject();
    }
    /** Set up the trigger if specified */
    ngOnInit() {
        if (this.trigger) {
            this.trigger.pipe(takeUntil(this._onDestroy)).subscribe(() => this.checkForOverflow());
        }
    }
    /** Perform an intial check for overflow */
    ngAfterViewInit() {
        requestAnimationFrame(() => this.checkForOverflow());
    }
    /** Unsubscribe from the trigger */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Programmatically trigger check for overflow */
    checkForOverflow() {
        const { offsetWidth, offsetHeight, scrollWidth, scrollHeight } = this._elementRef.nativeElement;
        const horizontalOverflow = scrollWidth - offsetWidth > this.tolerance;
        const verticalOverflow = scrollHeight - offsetHeight > this.tolerance;
        if (horizontalOverflow !== this._state.horizontalOverflow) {
            this.uxOverflowHorizontalObserver.emit(horizontalOverflow);
        }
        if (verticalOverflow !== this._state.verticalOverflow) {
            this.uxOverflowVerticalObserver.emit(verticalOverflow);
        }
        if (horizontalOverflow !== this._state.horizontalOverflow ||
            verticalOverflow !== this._state.verticalOverflow) {
            this.uxOverflowObserver.emit(horizontalOverflow || verticalOverflow);
        }
        // store the state
        this._state = { horizontalOverflow, verticalOverflow };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OverflowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: OverflowDirective, isStandalone: false, selector: "[uxOverflowObserver], [uxOverflowHorizontalObserver], [uxOverflowVerticalObserver]", inputs: { trigger: "trigger", tolerance: "tolerance" }, outputs: { uxOverflowObserver: "uxOverflowObserver", uxOverflowHorizontalObserver: "uxOverflowHorizontalObserver", uxOverflowVerticalObserver: "uxOverflowVerticalObserver" }, exportAs: ["ux-overflow-observer"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OverflowDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxOverflowObserver], [uxOverflowHorizontalObserver], [uxOverflowVerticalObserver]',
                    exportAs: 'ux-overflow-observer',
                    standalone: false,
                }]
        }], propDecorators: { trigger: [{
                type: Input
            }], tolerance: [{
                type: Input
            }], uxOverflowObserver: [{
                type: Output
            }], uxOverflowHorizontalObserver: [{
                type: Output
            }], uxOverflowVerticalObserver: [{
                type: Output
            }] } });

class ObserversModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ObserversModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ObserversModule, declarations: [OverflowDirective], exports: [OverflowDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ObserversModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ObserversModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [OverflowDirective],
                    declarations: [OverflowDirective],
                }]
        }] });

class TreeGridState {
    constructor(level, setSize, positionInSet) {
        this.level = level;
        this.setSize = setSize;
        this.positionInSet = positionInSet;
        this.loading$ = new BehaviorSubject(false);
    }
}

class TreeGridService {
    constructor() {
        /** The raw table data */
        this.data$ = new BehaviorSubject([]);
        /** The flattened table data */
        this.rows$ = new BehaviorSubject([]);
        /** Ensure we destroy all observables correctly */
        this._onDestroy = new Subject();
        this.data$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(data => this.rows$.next(this.getFlattenedTree(data)));
    }
    /** Unsubscribe from all observables */
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    /** Set the expanded state of a row */
    async setExpanded(item, expanded) {
        if (expanded) {
            await this.getChildren(item);
            this.insertChildren(item);
        }
        else {
            this.removeChildren(item);
        }
    }
    /** A function to flatten tree data */
    getFlattenedTree(data, parent) {
        // flatten the nodes at this level
        return data.reduce((previous, item, index) => {
            item.state = new TreeGridState(parent ? parent.state.level + 1 : 0, data.length, index + 1);
            // Convert any child nodes
            const children = item.children && item.expanded ? this.getFlattenedTree(item.children, item) : [];
            // return the nodes in a flattened array
            return [...previous, item, ...children];
        }, []);
    }
    /** Load any children dynamically */
    async getChildren(item) {
        if (!item.children && this.loadChildren) {
            item.state.loading$.next(true);
            try {
                item.children = await this.getNormalizedChildren(this.loadChildren(item));
            }
            finally {
                item.state.loading$.next(false);
            }
        }
    }
    /** We want to support an array, a promise and an observable. This will return all types as a promise */
    async getNormalizedChildren(response) {
        // if it is already an observable do nothing
        if (isObservable(response)) {
            return await response.toPromise();
        }
        // if it is a promise wrap it as an observable
        if (response instanceof Promise) {
            return await response;
        }
        // if it is an array then make it an observable
        return response;
    }
    /** Insert the children into the flattened tree at the correct location */
    insertChildren(parent) {
        if (!parent.children) {
            return;
        }
        const rows = [...this.rows$.getValue()];
        const index = rows.indexOf(parent);
        if (index < 0) {
            return;
        }
        // Skip duplicates - this could happen if an already expanded child has been inserted
        const uniqueChildren = parent.children.filter(child => rows.indexOf(child) === -1);
        const childRows = this.getFlattenedTree(uniqueChildren, parent);
        rows.splice(index + 1, 0, ...childRows);
        this.rows$.next(rows);
    }
    /** Remove all rows from the flattened tree */
    removeChildren(parent) {
        const rows = [...this.rows$.getValue()];
        const index = rows.indexOf(parent);
        if (index < 0) {
            return;
        }
        while (index + 1 < rows.length && rows[index + 1].state.level > parent.state.level) {
            rows.splice(index + 1, 1);
        }
        this.rows$.next(rows);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

class TreeGridRowDirective {
    constructor() {
        this._treeGridService = inject(TreeGridService);
        this.expandedChange = new EventEmitter();
        this.loading = false;
        this._expanded = false;
        this._onDestroy = new Subject();
    }
    set expanded(value) {
        const expanded = coerceBooleanProperty(value);
        if (expanded !== this._expanded) {
            this._expanded = expanded;
            this._treeGridService.setExpanded(this.item, expanded);
        }
    }
    get expanded() {
        return this._expanded;
    }
    ngOnInit() {
        if (!this.item || !this.item.state) {
            throw new Error('uxTreeGridRow should be configured with an object emitted by uxTreeGrid.rows.');
        }
        this.item.state.loading$
            .pipe(takeUntil(this._onDestroy))
            .subscribe(loading => (this.loading = loading));
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    collapse(event) {
        this.expanded = false;
        this.expandedChange.emit(false);
        if (event) {
            event.preventDefault();
        }
    }
    expand(event) {
        // take into account whether or not the item can expanded
        if (!this.canExpand) {
            return;
        }
        this.expanded = true;
        this.expandedChange.emit(true);
        if (event) {
            event.preventDefault();
        }
    }
    toggle() {
        this.expanded ? this.collapse() : this.expand();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TreeGridRowDirective, isStandalone: false, selector: "[uxTreeGridRow]", inputs: { item: ["uxTreeGridRow", "item"], canExpand: "canExpand", expanded: "expanded" }, outputs: { expandedChange: "expandedChange" }, host: { listeners: { "keydown.ArrowLeft": "collapse($event)", "keydown.ArrowRight": "expand($event)" }, properties: { "class.treegrid-row": "true", "class.treegrid-row-expanded": "this.expanded", "class.treegrid-row-loading": "this.loading" } }, exportAs: ["uxTreeGridRow"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridRowDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTreeGridRow]',
                    exportAs: 'uxTreeGridRow',
                    host: {
                        '[class.treegrid-row]': 'true',
                    },
                    standalone: false,
                }]
        }], propDecorators: { item: [{
                type: Input,
                args: ['uxTreeGridRow']
            }], canExpand: [{
                type: Input
            }], expanded: [{
                type: Input
            }, {
                type: HostBinding,
                args: ['class.treegrid-row-expanded']
            }], expandedChange: [{
                type: Output
            }], loading: [{
                type: HostBinding,
                args: ['class.treegrid-row-loading']
            }], collapse: [{
                type: HostListener,
                args: ['keydown.ArrowLeft', ['$event']]
            }], expand: [{
                type: HostListener,
                args: ['keydown.ArrowRight', ['$event']]
            }] } });

class TreeGridIndentDirective {
    constructor() {
        this._row = inject(TreeGridRowDirective);
    }
    /** The amount each level should be indented by */
    set uxTreeGridIndent(value) {
        this._indent = coerceNumberProperty(value, 25);
    }
    get uxTreeGridIndent() {
        return this._indent;
    }
    /** The padding value applied to each level */
    get indentation() {
        return this._row && this._row.item ? 7 + this._row.item.state.level * this._indent : 7;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridIndentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TreeGridIndentDirective, isStandalone: false, selector: "[uxTreeGridIndent]", inputs: { uxTreeGridIndent: "uxTreeGridIndent" }, host: { properties: { "style.padding-left.px": "this.indentation" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridIndentDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTreeGridIndent]',
                    standalone: false,
                }]
        }], propDecorators: { uxTreeGridIndent: [{
                type: Input
            }], indentation: [{
                type: HostBinding,
                args: ['style.padding-left.px']
            }] } });

class TreeGridDirective {
    constructor() {
        this._changeDetector = inject(ChangeDetectorRef);
        this._treeGridService = inject(TreeGridService);
        this.rowsChange = new EventEmitter();
        this._onDestroy = new Subject();
    }
    set data(data) {
        this._treeGridService.data$.next(data);
    }
    set loadChildren(loadChildren) {
        this._treeGridService.loadChildren = loadChildren;
    }
    ngOnInit() {
        this._treeGridService.rows$.pipe(takeUntil(this._onDestroy)).subscribe(rows => {
            this.rowsChange.emit(rows);
            this._changeDetector.detectChanges();
        });
    }
    ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TreeGridDirective, isStandalone: false, selector: "[uxTreeGrid]", inputs: { data: ["uxTreeGrid", "data"], loadChildren: "loadChildren" }, outputs: { rowsChange: "rowsChange" }, host: { classAttribute: "treegrid" }, providers: [TreeGridService], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[uxTreeGrid]',
                    providers: [TreeGridService],
                    host: {
                        class: 'treegrid',
                    },
                    standalone: false,
                }]
        }], propDecorators: { data: [{
                type: Input,
                args: ['uxTreeGrid']
            }], loadChildren: [{
                type: Input
            }], rowsChange: [{
                type: Output
            }] } });

class TreeGridModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TreeGridModule, declarations: [TreeGridDirective, TreeGridRowDirective, TreeGridIndentDirective], exports: [TreeGridDirective, TreeGridRowDirective, TreeGridIndentDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TreeGridModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [TreeGridDirective, TreeGridRowDirective, TreeGridIndentDirective],
                    exports: [TreeGridDirective, TreeGridRowDirective, TreeGridIndentDirective],
                }]
        }] });

class StringFilterPipe {
    transform(items, value) {
        if (!items) {
            return [];
        }
        return items.filter(it => it.toLowerCase().indexOf(value.toLowerCase()) >= 0);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: StringFilterPipe, isStandalone: false, name: "stringFilter" }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterPipe }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'stringFilter',
                    standalone: false,
                }]
        }, {
            type: Injectable
        }] });

class StringFilterModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: StringFilterModule, declarations: [StringFilterPipe], exports: [StringFilterPipe] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: StringFilterModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [StringFilterPipe],
                    declarations: [StringFilterPipe],
                }]
        }] });

const timelineDefaultOptions = {
    timeline: {
        backgroundColor: '#f1f2f3',
        selectionColor: 'rgba(198, 23, 157, 0.15)',
        // eslint-disable-next-line @typescript-eslint/no-empty-function
        onChange: function onChange() { },
        keyboard: {
            step: 2_592_000_000, // 30 days
        },
        handles: {
            backgroundColor: '#000',
            foregroundColor: '#dcdedf',
            focusIndicatorColor: 'rgba(0, 115, 231, 0.5)',
        },
        range: {
            lower: null,
            upper: null,
            minimum: 0,
            maximum: Infinity,
        },
        state: {
            lowerHandleFocus: false,
            upperHandleFocus: false,
            rangeHandleFocus: false,
        },
    },
};
class TimelineChartPlugin {
    constructor() {
        this.id = 'timeline-chart-plugin';
    }
    /** We only want to register the plugin once per application */
    static { this._isRegistered = false; }
    /** Register this plugin */
    static register() {
        /**
         * We have to register this plugin globally because
         * ng2-charts doesn't support plugins on an invidual
         * basis. We must check in all lifecycle hooks that
         * it is an timeline chart before performing any actions.
         *
         * We also need to have it inside the class otherwise it
         * will be included in every application by default.
         * Having it here allows it to be tree-shaken.
         */
        if (!this._isRegistered) {
            // if pluginService exists then we are in v2
            if (window.Chart?.pluginService) {
                window.Chart.pluginService.register(new TimelineChartPlugin());
            }
            else {
                import('chart.js').then(({ Chart }) => {
                    Chart.register(new TimelineChartPlugin());
                });
            }
            this._isRegistered = true;
        }
    }
    isVersion3() {
        return window.Chart?.pluginService ? false : true;
    }
    /**
     * When chart is initialised store the chart instance and context
     * for use outside lifecycle hooks.
     *
     * We should also supply default options for any options that have
     * not been specified by the consuming application.
     *
     * We also need to add some event listeners for events that Chart.js
     * does not inform us of.
     */
    beforeInit(chart) {
        // provide the default options for any missing properties
        if (this.getEnabled(chart)) {
            // chart.config.options.timeline = { ...timelineDefaultOptions.timeline, ...this.getOptions(chart) };
            chart.config.options.timeline = this.getOptionsWithDefaults(this.getOptions(chart));
            // get the range
            const { lower, upper } = this.getRange(chart);
            // ensure we have an initial range set
            if (lower === null || upper === null) {
                throw new Error('Timeline Chart - Ensure that both an upper and lower range are initially provided.');
            }
            // setup the function
            chart.config.options.timeline.state.onMouseDown = () => this.onMouseDown(chart);
            chart.config.options.timeline.state.onMouseUp = () => this.onMouseUp(chart);
            // add mouse down and mouseup event listeners
            chart.canvas.addEventListener('mousedown', chart.config.options.timeline.state.onMouseDown);
            document.addEventListener('mouseup', chart.config.options.timeline.state.onMouseUp);
        }
    }
    /**
     * We want to setup some additional functionality
     * after the chart has initialized.
     */
    afterInit(chart) {
        if (this.getEnabled(chart)) {
            // add accessibility attributes and elements to the chart
            this.setupAccessibility(chart);
            // intially call the onChange function
            this.triggerOnChange(chart);
        }
    }
    /**
     * The timeline chart should have a subtle background
     * color behind the main chart area (excluding the axis area).
     * Suprisingly Chart.js does not support this out of the box
     * so we need to add this functionality but it should be behind
     * all chart elements.
     */
    beforeDraw(chart) {
        if (this.getEnabled(chart)) {
            this.drawBackgroundColor(chart);
        }
    }
    /**
     * Once the Chart elements have been drawn we want to draw the drag
     * handles and the overlay showing the selected region
     */
    afterDraw(chart) {
        if (this.getEnabled(chart)) {
            this.drawSelection(chart);
            this.drawHandles(chart);
        }
    }
    /**
     * We want to update the cursor whenever the mouse is over
     * one of the drag handles. We have do calculate this manually
     * as there are no DOM element to add CSS to.
     */
    afterEvent(chart, parentEvent) {
        const event = this.isVersion3() ? parentEvent.event : parentEvent;
        if (parentEvent.replay === true) {
            return;
        }
        // skip this if timeline is not enabled
        if (!this.getEnabled(chart)) {
            return;
        }
        switch (event.type) {
            case 'mousemove':
                this.setCursor(chart, event);
                this.setRangeOnDrag(chart, event);
                this.handleMouseMove(chart, event);
                // store the latest mouse position
                this.setState(chart, { mouseX: event.x });
                break;
            case 'mouseout':
                this.resetCursor(chart);
                this.hideTooltip(chart);
                break;
        }
    }
    /**
     * Unbind from the event listeners we manually set up
     */
    destroy(chart) {
        if (this.getEnabled(chart)) {
            document.removeEventListener('mouseup', chart.config.options.timeline.state.onMouseUp, true);
        }
    }
    /** Get the timeline options from the chart instance */
    getOptions(chart) {
        return chart.config.options.timeline;
    }
    /** Determine if this chart is using the timeline */
    getEnabled(chart) {
        return !!this.getOptions(chart);
    }
    /** Get the timeline range from the chart instance */
    getRange(chart) {
        return this.getOptions(chart).range;
    }
    /** Get the chart area but include any padding */
    getChartArea(chart) {
        const { top, right, bottom, left } = chart.chartArea;
        const padding = chart.config.options.layout && chart.config.options.layout.padding
            ? chart.config.options.layout.padding
            : 0;
        if (typeof padding === 'number') {
            return {
                top: top - padding,
                right: right - padding,
                left: left - padding,
                bottom: bottom - padding,
            };
        }
        else if (typeof padding === 'object') {
            return {
                top: top - padding.top,
                right: right - padding.right,
                left: left - padding.left,
                bottom: bottom - padding.bottom,
            };
        }
        return chart.chartArea;
    }
    /** Get stored state inside the chart options */
    getState(chart) {
        return this.getOptions(chart).state;
    }
    /** Store state inside the chart options */
    setState(chart, state) {
        // store the latest state
        chart.config.options.timeline.state = { ...chart.config.options.timeline.state, ...state };
        // trigger a chart re-render
        chart.update();
    }
    /** Call the callback with the latest range */
    triggerOnChange(chart) {
        // get the current date range
        const { lower, upper } = this.getRange(chart);
        // get the callback function
        const { onChange } = this.getOptions(chart);
        // call the callback with the lower and upper values
        requestAnimationFrame(() => onChange(lower, upper));
        // get the handle elements
        const { lowerHandleElement, upperHandleElement } = this.getState(chart);
        // update the aria properties
        lowerHandleElement.setAttribute('aria-valuemin', new Date(this.getHandleMinimum(chart, TimelineHandle.Lower)).toDateString());
        lowerHandleElement.setAttribute('aria-valuenow', lower.toDateString());
        lowerHandleElement.setAttribute('aria-valuemax', new Date(this.getHandleMaximum(chart, TimelineHandle.Lower)).toDateString());
        upperHandleElement.setAttribute('aria-valuemin', new Date(this.getHandleMinimum(chart, TimelineHandle.Upper)).toDateString());
        upperHandleElement.setAttribute('aria-valuenow', upper.toDateString());
        upperHandleElement.setAttribute('aria-valuemax', new Date(this.getHandleMaximum(chart, TimelineHandle.Upper)).toDateString());
    }
    /** To make the chart accessible add some internal elements that can be focused */
    setupAccessibility(chart) {
        // create the invisible elements
        const lowerHandle = document.createElement('div');
        const upperHandle = document.createElement('div');
        const rangeHandle = document.createElement('div');
        // make the items focusable
        lowerHandle.setAttribute('tabindex', '0');
        upperHandle.setAttribute('tabindex', '0');
        rangeHandle.setAttribute('tabindex', '0');
        // insert the elements
        chart.canvas.appendChild(lowerHandle);
        chart.canvas.appendChild(upperHandle);
        chart.canvas.appendChild(rangeHandle);
        // add the event handlers
        lowerHandle.addEventListener('focus', () => this.setState(chart, { lowerHandleFocus: true }));
        lowerHandle.addEventListener('blur', () => this.setState(chart, { lowerHandleFocus: false }));
        lowerHandle.addEventListener('keydown', (event) => this.onKeydown(chart, event, TimelineHandle.Lower));
        upperHandle.addEventListener('focus', () => this.setState(chart, { upperHandleFocus: true }));
        upperHandle.addEventListener('blur', () => this.setState(chart, { upperHandleFocus: false }));
        upperHandle.addEventListener('keydown', (event) => this.onKeydown(chart, event, TimelineHandle.Upper));
        rangeHandle.addEventListener('focus', () => this.setState(chart, { rangeHandleFocus: true }));
        rangeHandle.addEventListener('blur', () => this.setState(chart, { rangeHandleFocus: false }));
        rangeHandle.addEventListener('keydown', (event) => this.onRangeKeydown(chart, event));
        // store the items in the state object
        this.setState(chart, {
            lowerHandleElement: lowerHandle,
            upperHandleElement: upperHandle,
            rangeHandleElement: rangeHandle,
        });
    }
    /** Handle keyboard accessibility events */
    onKeydown(chart, event, handle) {
        // get the current value for the given handle
        const value = this.getHandleValue(chart, handle).getTime();
        const step = this.getOptions(chart).keyboard.step;
        const [minimum, maximum] = this.getChartRange(chart);
        switch (event.keyCode) {
            case LEFT_ARROW:
                this.setHandleValue(chart, handle, new Date(value - step));
                event.preventDefault();
                break;
            case HOME:
                this.setHandleValue(chart, handle, new Date(minimum));
                event.preventDefault();
                break;
            case RIGHT_ARROW:
                this.setHandleValue(chart, handle, new Date(value + step));
                event.preventDefault();
                break;
            case END:
                this.setHandleValue(chart, handle, new Date(maximum));
                event.preventDefault();
                break;
        }
    }
    /**
     * Handle range changes made with the keyboard as these are exempt from
     * many of the validation checks that are required when dragging only one
     * handle at a time.
     */
    onRangeKeydown(chart, event) {
        // get the current handle values
        let lowerValue = this.getHandleValue(chart, TimelineHandle.Lower).getTime();
        let upperValue = this.getHandleValue(chart, TimelineHandle.Upper).getTime();
        const step = this.getOptions(chart).keyboard.step;
        const difference = upperValue - lowerValue;
        // get the chart boundaries
        const [minimum, maximum] = this.getChartRange(chart);
        switch (event.keyCode) {
            case LEFT_ARROW:
                lowerValue = Math.max(lowerValue - step, minimum);
                upperValue = lowerValue + difference;
                event.preventDefault();
                break;
            case RIGHT_ARROW:
                upperValue = Math.min(upperValue + step, maximum);
                lowerValue = upperValue - difference;
                event.preventDefault();
                break;
            case HOME:
                lowerValue = minimum;
                upperValue = lowerValue + difference;
                event.preventDefault();
                break;
            case END:
                upperValue = maximum;
                lowerValue = upperValue - difference;
                event.preventDefault();
                break;
        }
        // store the new values
        chart.config.options.timeline.range[TimelineHandle.Lower] = new Date(lowerValue);
        chart.config.options.timeline.range[TimelineHandle.Upper] = new Date(upperValue);
        // update the chart
        chart.update();
        // emit the latest range
        this.triggerOnChange(chart);
    }
    /**
     * When the mouse is first pressed within a chart we should see if we are
     * currently over a drag handle to start the dragging
     */
    onMouseDown(chart) {
        // ensure we only proceed when we have a chart context
        if (!chart.ctx) {
            return;
        }
        // get the position from the chart area
        const { top } = this.getChartArea(chart);
        // get the properties from the state
        const { mouseX } = this.getState(chart);
        // check if the event started within a drag handle
        const handle = this.isWithinHandle(chart, { x: mouseX, y: top });
        // if it did then we are now dragging the handle and should store it
        this.setState(chart, { handle: handle !== null ? handle : null });
    }
    /** When the mouse is released we are no longer dragging */
    onMouseUp(chart) {
        if (chart.canvas) {
            this.setState(chart, { handle: null });
        }
    }
    handleMouseMove(chart, event) {
        const mousePosition = this.isWithinHandle(chart, event);
        const timelineOptions = (this.isVersion3() ? chart.config.options : chart.options);
        // eslint-disable-next-line no-prototype-builtins
        const hasTooltipOnRange = timelineOptions.timeline.range.hasOwnProperty('tooltip');
        // eslint-disable-next-line no-prototype-builtins
        const hasTooltipOnHandles = timelineOptions.timeline.handles.hasOwnProperty('tooltip');
        let timelineTooltipText;
        let handleTooltipText;
        if (hasTooltipOnRange) {
            timelineTooltipText = timelineOptions.timeline.range.tooltip.label();
        }
        if (hasTooltipOnHandles) {
            handleTooltipText = timelineOptions.timeline.handles.tooltip.label();
        }
        if (mousePosition === TimelineHandle.Range && hasTooltipOnRange) {
            this.externalTooltipHandler(chart, TimelineHandle.Range, timelineTooltipText);
        }
        else if (mousePosition === TimelineHandle.Lower && hasTooltipOnHandles) {
            this.externalTooltipHandler(chart, TimelineHandle.Lower, handleTooltipText.rangeLower);
        }
        else if (mousePosition === TimelineHandle.Upper && hasTooltipOnHandles) {
            this.externalTooltipHandler(chart, TimelineHandle.Upper, handleTooltipText.rangeUpper);
        }
        else {
            this.hideTooltip(chart);
        }
    }
    hideTooltip(chart) {
        const tooltipEl = this.getOrCreateTooltip(chart);
        tooltipEl.style.opacity = '0';
    }
    getOrCreateTooltip(chart) {
        let tooltipEl = chart.canvas.parentNode.querySelector('.timeline-tooltip');
        if (!tooltipEl) {
            tooltipEl = document.createElement('div');
            tooltipEl.classList.add('timeline-tooltip');
            tooltipEl.classList.add('tooltip');
            const caret = document.createElement('div');
            caret.classList.add('tooltip-caret');
            const span = document.createElement('span');
            tooltipEl.appendChild(span);
            tooltipEl.appendChild(caret);
            chart.canvas.parentNode.appendChild(tooltipEl);
        }
        return tooltipEl;
    }
    externalTooltipHandler(chart, position, tooltipText) {
        // Tooltip Element
        const tooltipEl = this.getOrCreateTooltip(chart);
        const span = tooltipEl.querySelector('span');
        span.innerText = tooltipText;
        const { x, y } = this.tooltipPositioner(chart, position);
        tooltipEl.style.left = x + 'px';
        tooltipEl.style.top = y + 'px';
        tooltipEl.style.opacity = '1';
    }
    tooltipPositioner(chart, position) {
        const lower = this.getHandleArea(chart, TimelineHandle.Lower).left;
        const upper = this.getHandleArea(chart, TimelineHandle.Upper).left;
        const tooltipEl = this.getOrCreateTooltip(chart);
        const width = tooltipEl.getBoundingClientRect().width;
        const caret = tooltipEl.querySelector('.tooltip-caret');
        if (position === TimelineHandle.Range) {
            caret.style.top = null;
            caret.style.right = null;
            caret.style.left = '50%';
            caret.style.transform = 'rotate(0deg)';
            const middle = (lower + upper) / 2;
            return {
                x: middle + 2,
                y: -14,
            };
        }
        else if (position === TimelineHandle.Lower) {
            caret.style.top = '40%';
            caret.style.right = 'auto';
            caret.style.left = '-2px';
            caret.style.transform = 'rotate(90deg)';
            return {
                x: lower + (width / 2 + 20),
                y: 10,
            };
        }
        else if (position === TimelineHandle.Upper) {
            caret.style.top = '40%';
            caret.style.right = '-7px';
            caret.style.left = 'auto';
            caret.style.transform = 'rotate(-90deg)';
            return {
                x: upper - (width / 2 + 20),
                y: 10,
            };
        }
    }
    /** Update the range when dragged */
    setRangeOnDrag(chart, event) {
        const { handle, mouseX } = this.getState(chart);
        // if we are not dragging then do nothing
        if (!handle) {
            return;
        }
        // get the chart area
        const { left, right } = this.getChartArea(chart);
        // get the current range
        const { lower, upper } = this.getRange(chart);
        // get the difference in x position since the last mouse position
        const delta = event.x - mouseX;
        // get the width of the chart area
        const width = right - left;
        // get the time range on the x-axis
        const [minimum, maximum] = this.getChartRange(chart);
        // determine how much of the time range was spanned in the move
        const percentageDiff = (delta / width) * 100;
        // calculate the time difference in the movement
        const valueDiff = ((maximum - minimum) / 100) * percentageDiff;
        if (handle === TimelineHandle.Lower) {
            this.setHandleValue(chart, TimelineHandle.Lower, new Date(lower.getTime() + valueDiff));
        }
        if (handle === TimelineHandle.Upper) {
            this.setHandleValue(chart, TimelineHandle.Upper, new Date(upper.getTime() + valueDiff));
        }
        if (handle === TimelineHandle.Range) {
            // get the current range
            const range = upper.getTime() - lower.getTime();
            // update the values
            if (valueDiff < 0) {
                this.setHandleValue(chart, TimelineHandle.Upper, new Date(upper.getTime() + valueDiff));
                this.setHandleValue(chart, TimelineHandle.Lower, new Date(lower.getTime() + valueDiff));
            }
            else {
                this.setHandleValue(chart, TimelineHandle.Lower, new Date(lower.getTime() + valueDiff));
                this.setHandleValue(chart, TimelineHandle.Upper, new Date(upper.getTime() + valueDiff));
            }
            // calculate the new range
            const currentRange = chart.config.options.timeline.range.upper.getTime() -
                chart.config.options.timeline.range.lower.getTime();
            // ensure the range is still the same
            if (currentRange !== range) {
                if (valueDiff < 0) {
                    this.setHandleValue(chart, TimelineHandle.Upper, new Date(chart.config.options.timeline.range.upper.getTime() + (range - currentRange)));
                }
                else {
                    this.setHandleValue(chart, TimelineHandle.Lower, new Date(chart.config.options.timeline.range.lower.getTime() + (currentRange - range)));
                }
            }
        }
    }
    /**
     * Draw the background color in the region that sits behind all the chart content
     */
    drawBackgroundColor(chart) {
        // get the region that the chart is drawn on (excluding axis)
        const { top, right, bottom, left } = this.getChartArea(chart);
        // fill the background color
        chart.ctx.save();
        chart.ctx.fillStyle = this.getOptions(chart).backgroundColor;
        chart.ctx.fillRect(left, top, right - left, bottom - top);
        chart.ctx.restore();
    }
    /** Draw the overlay that indicates the selected region */
    drawSelection(chart) {
        // get the region that the chart is drawn on (excluding axis)
        const { top, bottom } = this.getChartArea(chart);
        // get the fill color
        const selectionColor = this.getOptions(chart).selectionColor;
        // get the focus indicator color
        const { focusIndicatorColor } = this.getOptions(chart).handles;
        // get the lower and upper handle render regions
        const lower = this.getHandleArea(chart, TimelineHandle.Lower);
        const upper = this.getHandleArea(chart, TimelineHandle.Upper);
        // draw selection region
        chart.ctx.save();
        chart.ctx.fillStyle = selectionColor;
        chart.ctx.fillRect(lower.left, 0, upper.right - lower.left, bottom - top);
        // check if we are focused on the range handle
        if (this.isHandleFocused(chart, TimelineHandle.Range)) {
            chart.ctx.strokeStyle = focusIndicatorColor;
            const handleWidth = 4;
            const lineWidth = 2;
            chart.ctx.lineWidth = lineWidth;
            chart.ctx.strokeRect(lower.left + handleWidth + lineWidth, lineWidth / 2, upper.right - lower.left - (handleWidth + lineWidth) * 2, bottom - top - lineWidth);
        }
        chart.ctx.restore();
    }
    /** Darw the drag handles */
    drawHandles(chart) {
        // get the region that the chart is drawn on (excluding axis)
        const { top, bottom } = this.getChartArea(chart);
        // get the handle colors
        const { backgroundColor, foregroundColor, focusIndicatorColor } = this.getOptions(chart).handles;
        // draw each handle
        [TimelineHandle.Lower, TimelineHandle.Upper].forEach(handle => {
            // get the area of the handle
            const area = this.getHandleArea(chart, handle);
            const handleWidth = 5;
            const chartHeight = bottom - top;
            chart.ctx.save();
            // if the handle is focused draw an outline
            if (this.isHandleFocused(chart, handle)) {
                chart.ctx.fillStyle = focusIndicatorColor;
                chart.ctx.fillRect(area.left - 2, 0, handleWidth + 4, chartHeight);
            }
            // draw the handle
            chart.ctx.fillStyle = backgroundColor;
            chart.ctx.fillRect(area.left, 0, handleWidth, chartHeight);
            // draw the 3 drag handles within the drag handle
            chart.ctx.fillStyle = foregroundColor;
            // calculate size and position
            const width = 3;
            const height = 3;
            const x = area.left + (handleWidth - width) / 2;
            const midpoint = area.top + chartHeight / 2;
            const topY = midpoint - height * 2.5;
            const middleY = midpoint - height / 2;
            const bottomY = midpoint + height * 1.5;
            chart.ctx.fillRect(x, topY, width, height);
            chart.ctx.fillRect(x, middleY, width, height);
            chart.ctx.fillRect(x, bottomY, width, height);
            chart.ctx.restore();
        });
    }
    /**
     * Update the CSS cursor on the canvas element if we are hovering over a drag handle
     */
    setCursor(chart, event) {
        // get the handle if we are hovering over one
        const handle = this.getState(chart).handle || this.isWithinHandle(chart, event);
        if (handle === TimelineHandle.Lower || handle === TimelineHandle.Upper) {
            chart.canvas.style.cursor = 'ew-resize';
        }
        else if (handle === TimelineHandle.Range) {
            chart.canvas.style.cursor = 'move';
        }
        else {
            this.resetCursor(chart);
        }
    }
    // restore the cursor to the default
    resetCursor(chart) {
        if (chart.canvas.style.cursor !== '') {
            chart.canvas.style.cursor = '';
        }
    }
    isHandleFocused(chart, handle) {
        if (handle === TimelineHandle.Lower) {
            return this.getState(chart).lowerHandleFocus;
        }
        if (handle === TimelineHandle.Upper) {
            return this.getState(chart).upperHandleFocus;
        }
        if (handle === TimelineHandle.Range) {
            return this.getState(chart).rangeHandleFocus;
        }
        return false;
    }
    /** Determine if a position is within one of the drag handles */
    isWithinHandle(chart, event) {
        // get the lower and upper handle render regions
        const lower = this.getHandleArea(chart, TimelineHandle.Lower);
        const upper = this.getHandleArea(chart, TimelineHandle.Upper);
        // get the position co-ordinates
        const { x, y } = event;
        if (x >= lower.left && x <= lower.right && y >= lower.top && y <= lower.bottom) {
            return TimelineHandle.Lower;
        }
        if (x >= upper.left && x <= upper.right && y >= upper.top && y <= upper.bottom) {
            return TimelineHandle.Upper;
        }
        if (x > lower.right && x < upper.left && y >= lower.top && y <= lower.bottom) {
            return TimelineHandle.Range;
        }
        return null;
    }
    /** Get the area a specific handle covers within the chart */
    getHandleArea(chart, handle) {
        // get the region that the chart is drawn on (excluding axis)
        const { left, top, right, bottom } = this.getChartArea(chart);
        // perform some calculations on the chart area
        const width = right - left;
        // get the minimum and maximum ticks on the chart
        const [minimum, maximum] = this.getChartRange(chart);
        // get the lower and upper range values
        const { lower, upper } = this.getOptions(chart).range;
        if (handle === TimelineHandle.Lower) {
            const percentage = ((lower.getTime() - minimum) / (maximum - minimum)) * 100;
            const position = left + (width / 100) * percentage;
            return { top, left: position, right: position + 5, bottom };
        }
        if (handle === TimelineHandle.Upper) {
            const percentage = ((upper.getTime() - minimum) / (maximum - minimum)) * 100;
            const position = left + (width / 100) * percentage;
            return { top, left: position - 5, right: position, bottom };
        }
    }
    /**
     * Get the minimum and maximum values on the x-axis
     */
    getChartRange(chart) {
        let minimum;
        let maximum;
        if (this.isVersion3()) {
            // get the current data
            const data = chart.scales;
            // get the range on the x-axis
            minimum = data.x.min;
            maximum = data.x.max;
        }
        else {
            // get the current data
            const { data } = chart.getDatasetMeta(0);
            // get the range on the x-axis
            minimum = data[0]._xScale.min;
            maximum = data[0]._xScale.max;
        }
        return [minimum, maximum];
    }
    /** Get the value for a given handle */
    getHandleValue(chart, handle) {
        const { lower, upper } = this.getOptions(chart).range;
        return handle === TimelineHandle.Lower ? lower : upper;
    }
    setHandleValue(chart, handle, value) {
        // perform lower handle validation
        if (handle === TimelineHandle.Lower) {
            value = new Date(Math.min(Math.max(this.getHandleMinimum(chart, handle), value.getTime()), this.getHandleMaximum(chart, handle)));
        }
        // perform upper handle validation
        if (handle === TimelineHandle.Upper) {
            value = new Date(Math.max(Math.min(this.getHandleMaximum(chart, handle), value.getTime()), this.getHandleMinimum(chart, handle)));
        }
        // store the new value
        chart.config.options.timeline.range[handle] = value;
        // update the chart
        chart.update();
        // emit the latest range
        this.triggerOnChange(chart);
    }
    getHandleMinimum(chart, handle) {
        // get the minimum distance
        const minDistance = this.getOptions(chart).range.minimum || 0;
        const maxDistance = this.getOptions(chart).range.maximum || Infinity;
        // get the chart boundaries
        const [minimum] = this.getChartRange(chart);
        // get the current date range
        const { lower, upper } = this.getRange(chart);
        if (handle === TimelineHandle.Lower) {
            return Math.max(upper.getTime() - maxDistance, minimum);
        }
        if (handle === TimelineHandle.Upper) {
            return lower.getTime() + minDistance;
        }
    }
    getHandleMaximum(chart, handle) {
        // get the minimum distance
        const minDistance = this.getOptions(chart).range.minimum || 0;
        const maxDistance = this.getOptions(chart).range.maximum || Infinity;
        // get the chart boundaries
        const [, maximum] = this.getChartRange(chart);
        // get the current date range
        const { lower, upper } = this.getRange(chart);
        if (handle === TimelineHandle.Lower) {
            return upper.getTime() - minDistance;
        }
        if (handle === TimelineHandle.Upper) {
            return Math.min(lower.getTime() + maxDistance, maximum);
        }
    }
    getOptionsWithDefaults(options) {
        const merge = (target, source) => {
            for (const key of Object.keys(source)) {
                if (source[key] instanceof Object &&
                    !(source[key] instanceof Date) &&
                    typeof source[key] !== 'function') {
                    Object.assign(source[key], merge(target[key], source[key]));
                }
            }
            return Object.assign(target || {}, source);
        };
        return merge({ ...timelineDefaultOptions.timeline }, options);
    }
}
/**
 * Directly exporting a file that is not an Angular component, module, etc..
 * can cause build issues. We can use a module that instantiates the plugin
 * instead of directly exporting the Chart.js plugin.
 */
class TimelineChartModule {
    constructor() {
        TimelineChartPlugin.register();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineChartModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TimelineChartModule }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineChartModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TimelineChartModule, decorators: [{
            type: NgModule,
            args: [{}]
        }], ctorParameters: () => [] });
var TimelineHandle;
(function (TimelineHandle) {
    TimelineHandle["Lower"] = "lower";
    TimelineHandle["Upper"] = "upper";
    TimelineHandle["Range"] = "range";
})(TimelineHandle || (TimelineHandle = {}));

class CookieAdapter {
    getItem(key) {
        if (document.cookie) {
            // get all the cookies for this site
            const cookies = document.cookie.split(';');
            // process the cookies into a from we can easily manage
            const match = cookies
                .map(cookie => ({ key: cookie.split('=')[0].trim(), value: cookie.split('=')[1].trim() }))
                .find(cookie => cookie.key === key);
            return match ? match.value : null;
        }
        return null;
    }
    setItem(key, value) {
        document.cookie = `${key}=${value}; path=/`;
    }
    removeItem(key) {
        document.cookie.split(';').forEach(cookie => {
            const eqPos = cookie.indexOf('=');
            const name = eqPos > -1 ? cookie.substr(0, eqPos).trim() : cookie;
            if (name === key) {
                document.cookie = cookie
                    .trim()
                    .replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`);
            }
        });
    }
    clear() {
        // call remove item on each cookie
        document.cookie
            .split(';')
            .map(cookie => cookie.split('=')[0].trim())
            .forEach(cookie => this.removeItem(cookie));
    }
    getSupported() {
        // cookies are supported in all browsers
        return this;
    }
}

class LocalStorageAdapter {
    getItem(key) {
        return localStorage.getItem(key);
    }
    setItem(key, value) {
        localStorage.setItem(key, value);
    }
    removeItem(key) {
        localStorage.removeItem(key);
    }
    clear() {
        localStorage.clear();
    }
    getSupported() {
        // if local storage variable does not exist fall back to cookies
        if (!localStorage) {
            return new CookieAdapter();
        }
        // try to make a test save to local storage to see if there are any exceptions
        try {
            localStorage.setItem('ux-persistent-data-service', 'ux-persistent-data-service');
            localStorage.removeItem('ux-persistent-data-service');
            return this;
        }
        catch (err) {
            return new CookieAdapter();
        }
    }
}

class SessionStorageAdapter {
    getItem(key) {
        return sessionStorage.getItem(key);
    }
    setItem(key, value) {
        sessionStorage.setItem(key, value);
    }
    removeItem(key) {
        sessionStorage.removeItem(key);
    }
    clear() {
        sessionStorage.clear();
    }
    getSupported() {
        // if local storage variable does not exist fall back to cookies
        if (!sessionStorage) {
            return new CookieAdapter();
        }
        // try to make a test save to local storage to see if there are any exceptions
        try {
            sessionStorage.setItem('ux-persistent-data-service', 'ux-persistent-data-service');
            sessionStorage.removeItem('ux-persistent-data-service');
            return this;
        }
        catch (err) {
            return new CookieAdapter();
        }
    }
}

class PersistentDataService {
    /**
     * Save the item in some form of persistent storage
     */
    setItem(key, value, type = PersistentDataStorageType.LocalStorage) {
        this.getAdapter(type).setItem(key, value);
    }
    /**
     * Get a stored value from persistent storage
     */
    getItem(key, type = PersistentDataStorageType.LocalStorage) {
        return this.getAdapter(type).getItem(key);
    }
    /**
     * Remove a stored value from persistent storage
     */
    removeItem(key, type = PersistentDataStorageType.LocalStorage) {
        this.getAdapter(type).removeItem(key);
    }
    /**
     * Remove a stored value from persistent storage
     */
    clear(type = PersistentDataStorageType.LocalStorage) {
        this.getAdapter(type).clear();
    }
    /**
     * Return the appropriate adapter based on the type requested
     */
    getAdapter(type) {
        switch (type) {
            case PersistentDataStorageType.Cookie:
                return new CookieAdapter();
            case PersistentDataStorageType.LocalStorage:
                // eslint-disable-next-line no-case-declarations
                const localStorageAdapter = new LocalStorageAdapter();
                return localStorageAdapter.getSupported();
            case PersistentDataStorageType.SessionStorage:
                // eslint-disable-next-line no-case-declarations
                const sessionStorageAdapter = new SessionStorageAdapter();
                return sessionStorageAdapter.getSupported();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataService, decorators: [{
            type: Injectable
        }] });
var PersistentDataStorageType;
(function (PersistentDataStorageType) {
    PersistentDataStorageType[PersistentDataStorageType["LocalStorage"] = 0] = "LocalStorage";
    PersistentDataStorageType[PersistentDataStorageType["Cookie"] = 1] = "Cookie";
    PersistentDataStorageType[PersistentDataStorageType["SessionStorage"] = 2] = "SessionStorage";
})(PersistentDataStorageType || (PersistentDataStorageType = {}));

class PersistentDataModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataModule }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataModule, providers: [PersistentDataService] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PersistentDataModule, decorators: [{
            type: NgModule,
            args: [{
                    providers: [PersistentDataService],
                }]
        }] });

class StorageAdapter {
}

/**
 * Export Common Functionality
 */

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

export { ACCESSIBILITY_OPTIONS_TOKEN, AccessibilityModule, AccessibilityOptionsService, AccordionComponent, AccordionModule, AccordionPanelComponent, AccordionPanelHeadingDirective, AccordionService, ActionDirection, AlertComponent, AlertIconDirective, AlertModule, AudioService, AudioServiceModule, AutoGrowDirective, AutoGrowModule, BadgeDirective, BadgeModule, BaseSearchComponent, BreadcrumbsComponent, BreadcrumbsModule, CHECKBOX_VALUE_ACCESSOR, COLOR_SET_TOKEN, CONDUITS, CardTabComponent, CardTabContentDirective, CardTabsModule, CardTabsService, CardTabsetComponent, CheckboxComponent, CheckboxModule, ClickOutsideDirective, ClickOutsideModule, Color, ColorContrastDirective, ColorPickerColor, ColorPickerComponent, ColorPickerModule, ColorService, ColorServiceModule, ColumnPickerComponent, ColumnSortingComponent, ColumnSortingDirective, ColumnSortingModule, ColumnSortingState, Conduit, ConduitComponent, ConduitModule, ConduitSubject, ConduitZone, ConduitZoneComponent, ContrastService, CookieAdapter, DashboardComponent, DashboardDragHandleDirective, DashboardGrabHandleDirective, DashboardModule, DashboardService, DashboardWidgetComponent, DateFormatterPipe, DateFormatterPipeModule, DatePickerHeaderEvent, DatePickerMode, DateRangeOptions, DateRangePicker, DateRangePickerComponent, DateRangePickerDirective, DateRangePickerModule, DateRangeService, DateTimePickerComponent, DateTimePickerConfig, DateTimePickerModule, DateTimePickerService, DefaultFocusIndicatorDirective, DragDirective, DragModule, DragService, DropDirective, DurationPipe, DurationPipeModule, EboxComponent, EboxContentDirective, EboxHeaderDirective, EboxModule, Facet, FacetCheckListComponent, FacetCheckListItemComponent, FacetClearButtonDirective, FacetContainerComponent, FacetDeselect, FacetDeselectAll, FacetHeaderComponent, FacetSelect, FacetService, FacetTypeaheadHighlight, FacetTypeaheadListComponent, FacetTypeaheadListItemComponent, FacetsModule, FileSizePipe, FileSizePipeModule, FilterAddEvent, FilterContainerComponent, FilterDropdownComponent, FilterDynamicComponent, FilterModule, FilterRemoveAllEvent, FilterRemoveEvent, FilterService, FilterTypeaheadHighlight, FixedHeaderTableDirective, FixedHeaderTableModule, FlippableCardBackDirective, FlippableCardComponent, FlippableCardFrontDirective, FlippableCardModule, FloatLabelDirective, FloatLabelModule, FloatingActionButtonComponent, FloatingActionButtonsComponent, FloatingActionButtonsModule, FocusIfDirective, FocusIfModule, FocusIndicator, FocusIndicatorDirective, FocusIndicatorOptionsDirective, FocusIndicatorOrigin, FocusIndicatorOriginDirective, FocusIndicatorOriginService, FocusIndicatorService, FocusWithinDirective, FocusableItemToken, FrameExtractionService, HelpCenterItemDirective, HelpCenterModule, HelpCenterService, HierarchyBarCollapsedComponent, HierarchyBarComponent, HierarchyBarModule, HierarchyBarNodeIconDirective, HierarchyBarStandardComponent, HoverActionContainerDirective, HoverActionDirective, HoverActionModule, ICON_OPTIONS_TOKEN, IconComponent, IconModule, IconService, IconType, InfiniteScrollDirective, InfiniteScrollLoadButtonDirective, InfiniteScrollLoadErrorEvent, InfiniteScrollLoadedEvent, InfiniteScrollLoadingDirective, InfiniteScrollLoadingEvent, InfiniteScrollModule, InputDropdownComponent, InputDropdownModule, ItemDisplayPanelComponent, ItemDisplayPanelContentDirective, ItemDisplayPanelFooterDirective, ItemDisplayPanelModule, LayoutSwitcherDirective, LayoutSwitcherItemDirective, LayoutSwitcherModule, LocalFocusIndicatorOptions, LocalStorageAdapter, ManagedFocusContainerDirective, ManagedFocusContainerService, MarqueeWizardComponent, MarqueeWizardModule, MarqueeWizardStepComponent, MarqueeWizardStepIconDirective, MediaPlayerBaseExtensionDirective, MediaPlayerComponent, MediaPlayerControlsExtensionComponent, MediaPlayerCustomControlDirective, MediaPlayerModule, MediaPlayerTimelineExtensionComponent, MenuComponent, MenuDividerComponent, MenuInitialFocusDirective, MenuItemComponent, MenuItemCustomControlDirective, MenuItemType, MenuModule, MenuNavigationDirective, MenuNavigationItemDirective, MenuNavigationModule, MenuNavigationToggleDirective, MenuTabbableItemDirective, MenuTriggerDirective, ModeDirection, NAVIGATION_MODULE_OPTIONS, NUMBER_PICKER_VALUE_ACCESSOR, NavigationComponent, NavigationItemComponent, NavigationLinkDirective, NavigationModule, NavigationService, NestedDonutChartComponent, NestedDonutChartModule, NotificationListComponent, NotificationModule, NotificationService, NumberPickerComponent, NumberPickerModule, ObserversModule, OrganizationChartAxis, OrganizationChartComponent, OrganizationChartModule, OverflowDirective, OverlayPlacementService, PAGINATION_CONTROL_VALUE_ACCESSOR, PageHeaderComponent, PageHeaderCustomMenuDirective, PageHeaderIconMenuComponent, PageHeaderModule, PageHeaderNavigationComponent, PaginationComponent, PaginationModule, PartitionMapComponent, PartitionMapModule, PartitionMapSegmentEventsDirective, PersistentDataModule, PersistentDataService, PersistentDataStorageType, PopoverComponent, PopoverDirective, PopoverModule, ProgressBarComponent, ProgressBarModule, RADIOBUTTON_VALUE_ACCESSOR, RADIO_GROUP_CONTROL_VALUE_ACCESSOR, RadioButtonComponent, RadioButtonGroupDirective, RadioButtonModule, ReorderableDirective, ReorderableHandleDirective, ReorderableModelDirective, ReorderableModule, ResizableExpandingTableDirective, ResizableTableCellComponent, ResizableTableColumnComponent, ResizableTableDirective, ResizeDirective, ResizeModule, ResizeService, Rounding, SELECT_VALUE_ACCESSOR, SPIN_BUTTON_VALUE_ACCESSOR, SankeyChart, SankeyChartComponent, SankeyChartModule, SankeyNodeDirective, ScrollIntoViewDirective, ScrollIntoViewIfDirective, ScrollIntoViewService, ScrollModule, SearchBuilderComponent, SearchBuilderFocusService, SearchBuilderGroupComponent, SearchBuilderGroupService, SearchBuilderModule, SearchBuilderOutletDirective, SearchBuilderService, SearchDateComponent, SearchDateRangeComponent, SearchSelectComponent, SearchTextComponent, SelectComponent, SelectListComponent, SelectListItemComponent, SelectListModule, SelectModule, SelectionDirective, SelectionItemDirective, SelectionModule, SelectionService, SelectionStrategy, SessionStorageAdapter, SidePanelCloseDirective, SidePanelComponent, SidePanelModule, SliderCalloutTrigger, SliderComponent, SliderModule, SliderSize, SliderSnap, SliderStyle, SliderThumb, SliderThumbEvent, SliderTickType, SliderType, SparkComponent, SparkModule, SpinButtonComponent, SpinButtonModule, SplitterAccessibilityDirective, StepChangingEvent, StepDirection, StorageAdapter, StringFilterModule, StringFilterPipe, TIME_PICKER_VALUE_ACCESSOR, TOOLBAR_SEARCH_VALUE_ACCESSOR, TabComponent, TabHeadingDirective, TabbableListDirective, TabbableListItemDirective, TabbableListService, TableModule, TabsetComponent, TabsetModule, TabsetService, TagInputComponent, TagInputEvent, TagInputModule, ThemeColor, TimePickerComponent, TimePickerModule, TimelineChartModule, TimelineChartPlugin, TimelineComponent, TimelineEventComponent, TimelineHandle, TimelineModule, ToggleSwitchComponent, ToggleSwitchModule, ToolbarSearchButtonDirective, ToolbarSearchComponent, ToolbarSearchFieldDirective, ToolbarSearchModule, TooltipComponent, TooltipDirective, TooltipModule, TooltipService, TreeGridDirective, TreeGridIndentDirective, TreeGridModule, TreeGridRowDirective, TreeGridState, TypeaheadComponent, TypeaheadKeyService, TypeaheadModule, TypeaheadOptionEvent, VirtualForContainerComponent, VirtualForDirective, VirtualForService, VirtualScrollCellDirective, VirtualScrollComponent, VirtualScrollLoadButtonDirective, VirtualScrollLoadingDirective, VirtualScrollModule, WizardComponent, WizardModule, WizardService, WizardStepComponent, colorSets, compareDays, dateComparator, dateRange, defaultConduitProps, defaultOptions, differenceBetweenDates, getIconType, getStartOfDay, gridify, isColumnPickerGroupItem, isDateAfter, isDateBefore, isKeyboardTrigger, isMouseTrigger, meridians, months, monthsShort, range, tick, timezoneComparator, timezones, uxIconset, weekdays, weekdaysShort };
//# sourceMappingURL=ux-aspects-ux-aspects.mjs.map