import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Component, Input, HostListener, Output, Directive, NgModule } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { disableScroll, enableScroll } from '@seniorsistemas/angular-components/utils';
import * as i3 from '@angular/common';
import { CommonModule } from '@angular/common';

class TieredMenuService {
    currentItem = null;
    currentItems = [];
    items = [];
    normalizeData(items, parent) {
        return items.map((i) => {
            const item = {
                visible: true,
                ...i,
            };
            if (item.submenu) {
                item.submenu = this.normalizeData(item.submenu, item);
            }
            item.id = this._generateId();
            item.parent = parent;
            item.isOpen = false;
            return item;
        });
    }
    markAllItemsAsClosed(items) {
        return items.map((i) => {
            const item = { ...i };
            if (item.submenu) {
                item.submenu = this.markAllItemsAsClosed(item.submenu);
            }
            item.isOpen = false;
            return item;
        });
    }
    searchTheHierarchy(itemA, itemB) {
        let item = itemB;
        while (item) {
            if (item === itemA) {
                return true;
            }
            item = item.parent ?? null;
        }
        return false;
    }
    cloneItems(items) {
        return JSON.parse(JSON.stringify(items));
    }
    _generateId() {
        return `id-${Math.random().toString(36).substring(2, 9)}-${Math.random().toString(36).substring(2, 9)}-${Math.random().toString(36).substring(2, 9)}`;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuService, decorators: [{
            type: Injectable
        }] });

class TieredMenuGlobalService {
    lastInstance;
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuGlobalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuGlobalService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuGlobalService, decorators: [{
            type: Injectable
        }] });

class TieredMenuEventService {
    incrementCurrentItemEvent = new EventEmitter();
    decrementCurrentItemEvent = new EventEmitter();
    closeAllMenusEvent = new EventEmitter();
    selectItemEvent = new EventEmitter();
    openItemMenuEvent = new EventEmitter();
    closeItemMenuEvent = new EventEmitter();
    createMenuEvent = new EventEmitter();
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuEventService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuEventService, decorators: [{
            type: Injectable
        }] });

class TieredMenuItemComponent {
    _tieredMenuEventService;
    item;
    focused = false;
    highlight = false;
    triggerEvent = 'click';
    closeOnClick = false;
    _showTimeout;
    constructor(_tieredMenuEventService) {
        this._tieredMenuEventService = _tieredMenuEventService;
    }
    onClick() {
        if (this.item.disabled)
            return;
        if (this.item.submenu) {
            if (!this.item.isOpen) {
                this._tieredMenuEventService.openItemMenuEvent.emit(this.item);
            }
            else if (this.closeOnClick) {
                this._tieredMenuEventService.closeItemMenuEvent.emit(this.item);
            }
        }
        else {
            this._tieredMenuEventService.selectItemEvent.emit(this.item);
        }
    }
    onMouseEnter() {
        if (this.item.disabled)
            return;
        if (this.triggerEvent === 'hover' && !this.item.isOpen) {
            this._showTimeout = window.setTimeout(() => {
                this._tieredMenuEventService.openItemMenuEvent.emit(this.item);
            }, 300);
        }
    }
    onMouseLeave() {
        window.clearTimeout(this._showTimeout);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuItemComponent, deps: [{ token: TieredMenuEventService }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: TieredMenuItemComponent, selector: "s-tiered-menu-item", inputs: { item: "item", focused: "focused", highlight: "highlight", triggerEvent: "triggerEvent", closeOnClick: "closeOnClick" }, host: { listeners: { "click": "onClick()", "touchend": "onClick()", "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, ngImport: i0, template: "<div\n    [id]=\"item.id\"\n    class=\"tiered-menu-item\"\n    [attr.data-key]=\"item.dataKey\"\n    [ngClass]=\"{\n        'tiered-menu-item--open': item.isOpen,\n        'tiered-menu-item--focused': focused,\n        'tiered-menu-item--disabled': item.disabled,\n    }\"\n>\n    <div class=\"tiered-menu-item-content\">\n        <span\n            class=\"icon\"\n            [ngClass]=\"item.iconClass\"\n        ></span>\n        <span class=\"label\">{{ item.label }}</span>\n    </div>\n    @if (item.submenu) {\n        <span\n            class=\"submenu-icon fas\"\n            [class.fa-chevron-left]=\"item.isOpen\"\n            [class.fa-chevron-right]=\"!item.isOpen\"\n        >\n        </span>\n    }\n</div>\n", styles: [".tiered-menu-item{align-items:center;display:flex;justify-content:space-between;padding:8px 16px;-webkit-user-select:none;user-select:none;cursor:pointer}.tiered-menu-item .tiered-menu-item-content .label{margin:0 12px}.tiered-menu-item:hover{background-color:#e9e6e6}.tiered-menu-item--focused{background-color:#ccc!important}.tiered-menu-item--open{background-color:#e4e2e2}.tiered-menu-item--disabled{opacity:50%;cursor:default}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-tiered-menu-item', template: "<div\n    [id]=\"item.id\"\n    class=\"tiered-menu-item\"\n    [attr.data-key]=\"item.dataKey\"\n    [ngClass]=\"{\n        'tiered-menu-item--open': item.isOpen,\n        'tiered-menu-item--focused': focused,\n        'tiered-menu-item--disabled': item.disabled,\n    }\"\n>\n    <div class=\"tiered-menu-item-content\">\n        <span\n            class=\"icon\"\n            [ngClass]=\"item.iconClass\"\n        ></span>\n        <span class=\"label\">{{ item.label }}</span>\n    </div>\n    @if (item.submenu) {\n        <span\n            class=\"submenu-icon fas\"\n            [class.fa-chevron-left]=\"item.isOpen\"\n            [class.fa-chevron-right]=\"!item.isOpen\"\n        >\n        </span>\n    }\n</div>\n", styles: [".tiered-menu-item{align-items:center;display:flex;justify-content:space-between;padding:8px 16px;-webkit-user-select:none;user-select:none;cursor:pointer}.tiered-menu-item .tiered-menu-item-content .label{margin:0 12px}.tiered-menu-item:hover{background-color:#e9e6e6}.tiered-menu-item--focused{background-color:#ccc!important}.tiered-menu-item--open{background-color:#e4e2e2}.tiered-menu-item--disabled{opacity:50%;cursor:default}\n"] }]
        }], ctorParameters: () => [{ type: TieredMenuEventService }], propDecorators: { item: [{
                type: Input,
                args: [{ required: true }]
            }], focused: [{
                type: Input
            }], highlight: [{
                type: Input
            }], triggerEvent: [{
                type: Input
            }], closeOnClick: [{
                type: Input
            }], onClick: [{
                type: HostListener,
                args: ['click']
            }, {
                type: HostListener,
                args: ['touchend']
            }], onMouseEnter: [{
                type: HostListener,
                args: ['mouseenter']
            }], onMouseLeave: [{
                type: HostListener,
                args: ['mouseleave']
            }] } });

class TieredMenuDividerComponent {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuDividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: TieredMenuDividerComponent, selector: "s-tiered-menu-divider", ngImport: i0, template: "<div class=\"divider\"></div>\n", styles: [".divider{margin:2px 0;height:1px;background-color:#ccc}\n"] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuDividerComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-tiered-menu-divider', template: "<div class=\"divider\"></div>\n", styles: [".divider{margin:2px 0;height:1px;background-color:#ccc}\n"] }]
        }] });

class TieredMenuNestedComponent {
    tieredMenuService;
    _tieredMenuEventService;
    items;
    top = 0;
    left = 0;
    // public maxHeight = 0;
    // Saves the last opened item to close it when a new one is opened
    _lastOpenItem;
    _unsubscribe$ = new Subject();
    onResize() {
        this._tieredMenuEventService.closeAllMenusEvent.emit();
    }
    onDocumentClick(event) {
        // Closing menu when clicked outside.
        const target = event.target;
        const clickedInside = target.closest('s-tiered-menu-item') || target.closest('s-tiered-menu-divider');
        if (!clickedInside) {
            this._tieredMenuEventService.closeAllMenusEvent.emit();
        }
    }
    onKeydownHandler(event) {
        switch (event.key) {
            case 'Escape':
                this._tieredMenuEventService.closeAllMenusEvent.emit();
                break;
            case ' ':
            case 'Enter':
                this._tieredMenuEventService.selectItemEvent.emit(this.tieredMenuService.currentItem);
                break;
            case 'ArrowLeft':
                // When nested I need a reference to the current item's parent item, otherwise just the current item.
                this._tieredMenuEventService.closeItemMenuEvent.emit(this.tieredMenuService.currentItem?.parent);
                break;
            case 'ArrowRight':
                this._tieredMenuEventService.openItemMenuEvent.emit(this.tieredMenuService.currentItem);
                break;
            case 'ArrowUp':
                this._tieredMenuEventService.decrementCurrentItemEvent.emit();
                break;
            case 'ArrowDown':
                this._tieredMenuEventService.incrementCurrentItemEvent.emit();
                break;
        }
    }
    constructor(tieredMenuService, _tieredMenuEventService) {
        this.tieredMenuService = tieredMenuService;
        this._tieredMenuEventService = _tieredMenuEventService;
    }
    ngOnInit() {
        this.tieredMenuService.currentItems = this.items;
        this._subscribeEvents();
        disableScroll();
    }
    ngOnDestroy() {
        this._unsubscribe$.next();
        this._unsubscribe$.complete();
        enableScroll();
    }
    _incrementCurItem() {
        if (!this.tieredMenuService.currentItem) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
            return;
        }
        const curIndex = this.tieredMenuService.currentItems.indexOf(this.tieredMenuService.currentItem) + 1;
        if (curIndex < this.tieredMenuService.currentItems.length) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[curIndex];
        }
        else {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
        }
        if (this.tieredMenuService.currentItem.divider) {
            this._incrementCurItem();
        }
    }
    _decrementCurItem() {
        if (!this.tieredMenuService.currentItem) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
            return;
        }
        const curIndex = this.tieredMenuService.currentItems.indexOf(this.tieredMenuService.currentItem) - 1;
        if (curIndex >= 0) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[curIndex];
        }
        else {
            this.tieredMenuService.currentItem =
                this.tieredMenuService.currentItems[this.tieredMenuService.currentItems.length - 1];
        }
        if (this.tieredMenuService.currentItem.divider) {
            this._decrementCurItem();
        }
    }
    _closeItem(item) {
        let itemAux = this._lastOpenItem;
        while (itemAux && itemAux != item) {
            itemAux.isOpen = false;
            itemAux = itemAux.parent;
        }
        item.isOpen = false;
        this.tieredMenuService.currentItem = itemAux ?? this.tieredMenuService.items[0];
        this.tieredMenuService.currentItems = itemAux?.parent?.submenu || this.tieredMenuService.items;
    }
    _openItem(item) {
        if (item?.submenu) {
            item.isOpen = true;
            this.tieredMenuService.currentItems = item.submenu;
            // Only has focus if there has already been interaction.
            if (this.tieredMenuService.currentItem) {
                this.tieredMenuService.currentItem = item.submenu[0];
            }
            this._lastOpenItem = item;
        }
    }
    _subscribeEvents() {
        this._tieredMenuEventService.incrementCurrentItemEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._incrementCurItem();
        });
        this._tieredMenuEventService.decrementCurrentItemEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._decrementCurItem();
        });
        this._tieredMenuEventService.selectItemEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (item && item.command) {
                item.command();
                // Close all menus after the item was selected.
                this._tieredMenuEventService.closeAllMenusEvent.emit();
            }
        });
        this._tieredMenuEventService.openItemMenuEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (item) {
                if (!this.tieredMenuService.currentItems.includes(item)) {
                    let itemAux = this._lastOpenItem;
                    while (itemAux?.parent?.parent) {
                        itemAux = itemAux.parent;
                    }
                    this._tieredMenuEventService.closeItemMenuEvent.emit(itemAux?.parent ?? itemAux);
                }
                this._lastOpenItem = item;
                this._openItem(item);
            }
        });
        this._tieredMenuEventService.closeItemMenuEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (item) {
                this._closeItem(item);
            }
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuNestedComponent, deps: [{ token: TieredMenuService }, { token: TieredMenuEventService }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: TieredMenuNestedComponent, selector: "ng-component", host: { listeners: { "window:resize": "onResize()", "document:click": "onDocumentClick($event)", "document:keydown": "onKeydownHandler($event)" } }, ngImport: i0, template: "<div\n    class=\"menu menu--nested\"\n    [ngStyle]=\"{\n        top: top + 'px',\n        left: left + 'px',\n    }\"\n>\n    <ng-container\n        *ngTemplateOutlet=\"itemsTemplate; context: { $implicit: items }\"\n    ></ng-container>\n</div>\n\n<ng-template\n    #itemsTemplate\n    let-items\n>\n    @for (item of items; track item.id) {\n        <div>\n            @if (item.visible && !item.divider) {\n                <s-tiered-menu-item\n                    [item]=\"item\"\n                    [focused]=\"item === tieredMenuService.currentItem\"\n                    [closeOnClick]=\"true\"\n                >\n                </s-tiered-menu-item>\n            }\n            @if (item.divider) {\n                <s-tiered-menu-divider></s-tiered-menu-divider>\n            }\n            @if (item.submenu && item.isOpen) {\n                <div>\n                    <div class=\"submenu\">\n                        <ng-container\n                            *ngTemplateOutlet=\"\n                                itemsTemplate;\n                                context: { $implicit: item.submenu }\n                            \"\n                        ></ng-container>\n                    </div>\n                </div>\n            }\n        </div>\n    }\n</ng-template>\n", styles: [".menu{background-color:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 2px 12px #0000001a;display:inline-flex;flex-direction:column;min-width:176px;padding:4px 0;position:absolute;z-index:9999;overflow:auto;width:calc(100vw - 8px)}.menu .submenu{margin-left:24px}\n"], dependencies: [{ kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: TieredMenuItemComponent, selector: "s-tiered-menu-item", inputs: ["item", "focused", "highlight", "triggerEvent", "closeOnClick"] }, { kind: "component", type: TieredMenuDividerComponent, selector: "s-tiered-menu-divider" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuNestedComponent, decorators: [{
            type: Component,
            args: [{ template: "<div\n    class=\"menu menu--nested\"\n    [ngStyle]=\"{\n        top: top + 'px',\n        left: left + 'px',\n    }\"\n>\n    <ng-container\n        *ngTemplateOutlet=\"itemsTemplate; context: { $implicit: items }\"\n    ></ng-container>\n</div>\n\n<ng-template\n    #itemsTemplate\n    let-items\n>\n    @for (item of items; track item.id) {\n        <div>\n            @if (item.visible && !item.divider) {\n                <s-tiered-menu-item\n                    [item]=\"item\"\n                    [focused]=\"item === tieredMenuService.currentItem\"\n                    [closeOnClick]=\"true\"\n                >\n                </s-tiered-menu-item>\n            }\n            @if (item.divider) {\n                <s-tiered-menu-divider></s-tiered-menu-divider>\n            }\n            @if (item.submenu && item.isOpen) {\n                <div>\n                    <div class=\"submenu\">\n                        <ng-container\n                            *ngTemplateOutlet=\"\n                                itemsTemplate;\n                                context: { $implicit: item.submenu }\n                            \"\n                        ></ng-container>\n                    </div>\n                </div>\n            }\n        </div>\n    }\n</ng-template>\n", styles: [".menu{background-color:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 2px 12px #0000001a;display:inline-flex;flex-direction:column;min-width:176px;padding:4px 0;position:absolute;z-index:9999;overflow:auto;width:calc(100vw - 8px)}.menu .submenu{margin-left:24px}\n"] }]
        }], ctorParameters: () => [{ type: TieredMenuService }, { type: TieredMenuEventService }], propDecorators: { onResize: [{
                type: HostListener,
                args: ['window:resize']
            }], onDocumentClick: [{
                type: HostListener,
                args: ['document:click', ['$event']]
            }], onKeydownHandler: [{
                type: HostListener,
                args: ['document:keydown', ['$event']]
            }] } });

class TieredMenuComponent {
    _appRef;
    _componentFactoryResolver;
    _injector;
    _changeDetectorRef;
    tieredMenuService;
    _tieredMenuEventService;
    items = [];
    top = 0;
    left = 0;
    menuTriggerEvent = 'hover';
    _componentRef = null;
    _menuDivElement = null;
    _unsubscribe$ = new Subject();
    destroyRequest = new EventEmitter();
    constructor(_appRef, _componentFactoryResolver, _injector, _changeDetectorRef, tieredMenuService, _tieredMenuEventService) {
        this._appRef = _appRef;
        this._componentFactoryResolver = _componentFactoryResolver;
        this._injector = _injector;
        this._changeDetectorRef = _changeDetectorRef;
        this.tieredMenuService = tieredMenuService;
        this._tieredMenuEventService = _tieredMenuEventService;
    }
    onResize() {
        this._tieredMenuEventService.closeAllMenusEvent.emit();
    }
    onDocumentClick(event) {
        // Closing menu when clicked outside.
        const target = event.target;
        const clickedInside = target.closest('s-tiered-menu-item') || target.closest('s-tiered-menu-divider');
        if (!clickedInside) {
            this._tieredMenuEventService.closeAllMenusEvent.emit();
        }
    }
    onKeydownHandler(event) {
        if (this.tieredMenuService.currentItem) {
            switch (event.key) {
                case 'Escape':
                    this._tieredMenuEventService.closeAllMenusEvent.emit();
                    break;
                case ' ':
                case 'Enter':
                    if (!this.tieredMenuService.currentItem.disabled) {
                        this._tieredMenuEventService.selectItemEvent.emit(this.tieredMenuService.currentItem);
                    }
                    break;
                case 'ArrowLeft':
                    if (this.items.includes(this.tieredMenuService.currentItem)) {
                        this._tieredMenuEventService.closeItemMenuEvent.emit(this.tieredMenuService.currentItem);
                        this._changeDetectorRef.detectChanges();
                    }
                    break;
                case 'ArrowRight':
                    if (!this.tieredMenuService.currentItem.disabled &&
                        this.items.includes(this.tieredMenuService.currentItem)) {
                        this._tieredMenuEventService.openItemMenuEvent.emit(this.tieredMenuService.currentItem);
                        event.stopImmediatePropagation();
                    }
                    break;
            }
        }
        else {
            switch (event.key) {
                case 'ArrowUp':
                    if (this.items.includes(this.tieredMenuService.currentItem)) {
                        this._tieredMenuEventService.decrementCurrentItemEvent.emit();
                        event.stopImmediatePropagation();
                    }
                    break;
                case 'ArrowDown':
                    if (this.items.includes(this.tieredMenuService.currentItem)) {
                        this._tieredMenuEventService.incrementCurrentItemEvent.emit();
                        event.stopImmediatePropagation();
                    }
                    break;
            }
        }
    }
    ngOnInit() {
        this.tieredMenuService.currentItems = this.items;
        this._subscribeEvents();
        disableScroll();
    }
    ngOnDestroy() {
        this._unsubscribe$.next();
        this._unsubscribe$.complete();
    }
    _incrementCurItem() {
        if (!this.tieredMenuService.currentItem) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
            return;
        }
        else if (!this.items.includes(this.tieredMenuService.currentItem)) {
            // Checking if it is the current menu.
            return;
        }
        const currentIndex = this.tieredMenuService.currentItems.indexOf(this.tieredMenuService.currentItem) + 1;
        if (currentIndex < this.tieredMenuService.currentItems.length) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[currentIndex];
        }
        else {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
        }
        if (this.tieredMenuService.currentItem.divider) {
            this._incrementCurItem();
        }
    }
    _decrementCurItem() {
        if (!this.tieredMenuService.currentItem) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[0];
            return;
            // Checking if it is the current menu.
        }
        else if (!this.items.includes(this.tieredMenuService.currentItem)) {
            return;
        }
        const curIndex = this.tieredMenuService.currentItems.indexOf(this.tieredMenuService.currentItem) - 1;
        if (curIndex >= 0) {
            this.tieredMenuService.currentItem = this.tieredMenuService.currentItems[curIndex];
        }
        else {
            this.tieredMenuService.currentItem =
                this.tieredMenuService.currentItems[this.tieredMenuService.currentItems.length - 1];
        }
        if (this.tieredMenuService.currentItem.divider) {
            this._decrementCurItem();
        }
    }
    _createMenu(items, position) {
        if (!this._componentRef && items) {
            const componentFactory = this._componentFactoryResolver.resolveComponentFactory(TieredMenuComponent);
            this._componentRef = componentFactory.create(this._injector);
            this._appRef.attachView(this._componentRef.hostView);
            const domElem = this._componentRef.hostView.rootNodes[0];
            document.body.appendChild(domElem);
            // Setting the menu items.
            this._componentRef.instance.items = items;
            // Subscribe menu events.
            this._componentRef.instance.destroyRequest.subscribe((propagate) => {
                this._destroy(propagate);
            });
            this._menuDivElement = domElem.querySelector('.menu');
            this._setMenuPosition(position);
        }
    }
    _destroy(propagate = true) {
        if (this._componentRef !== null) {
            this._appRef.detachView(this._componentRef.hostView);
            this._componentRef.destroy();
            this._componentRef = null;
            this._menuDivElement = null;
        }
        if (propagate) {
            this.destroyRequest.emit();
        }
    }
    _setMenuPosition(position) {
        const ITEM_HEIGHT = 37;
        const DIVIDER_HEIGHT = 5;
        const PADDING = 8;
        if (this._componentRef !== null) {
            const { top, right, bottom, left } = position;
            const itemsCount = this._componentRef.instance.items?.reduce((count, item) => {
                return !item.divider ? count + 1 : count;
            }, 0);
            const dividersCount = this._componentRef.instance.items?.reduce((count, item) => {
                return item.divider ? count + 1 : count;
            }, 0);
            // I need to calculate the height of the component because the internal elements have not been created yet.
            const menuHeight = itemsCount * ITEM_HEIGHT + dividersCount * DIVIDER_HEIGHT + PADDING + 8;
            const menuWidth = this._menuDivElement?.getBoundingClientRect().width ?? 0;
            const rightFreeSpace = window.innerWidth - right;
            const bottomFreeSpace = window.innerHeight - bottom;
            if (rightFreeSpace > menuWidth) {
                this._componentRef.instance.left = right;
            }
            else {
                this._componentRef.instance.left = left - menuWidth;
            }
            if (bottomFreeSpace <= menuHeight) {
                this._componentRef.instance.top = Math.max(window.innerHeight - menuHeight, window.scrollY);
            }
            else {
                this._componentRef.instance.top = window.scrollY + top;
            }
        }
    }
    _subscribeEvents() {
        // Increment current item event.
        this._tieredMenuEventService.incrementCurrentItemEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._incrementCurItem();
        });
        // Decrement current item event.
        this._tieredMenuEventService.decrementCurrentItemEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._decrementCurItem();
        });
        // Select item event.
        this._tieredMenuEventService.selectItemEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (item) {
                if (item.submenu) {
                    this._tieredMenuEventService.openItemMenuEvent.emit(item);
                }
                else if (item.command) {
                    this._tieredMenuEventService.closeAllMenusEvent.emit();
                    item.command();
                }
            }
        });
        // Close all menus event.
        this._tieredMenuEventService.closeAllMenusEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._destroy();
            this.tieredMenuService.currentItem = null;
            this.tieredMenuService.currentItems = this.tieredMenuService.items;
            this.tieredMenuService.markAllItemsAsClosed(this.tieredMenuService.items);
            enableScroll();
        });
        // Open item menu event.
        this._tieredMenuEventService.openItemMenuEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (this.tieredMenuService.currentItem) {
                if (this.tieredMenuService.currentItem.parent === item) {
                    return;
                }
                if (this.tieredMenuService.currentItem?.parent) {
                    if (!item)
                        return;
                    if (!this.tieredMenuService.searchTheHierarchy(this.tieredMenuService.currentItem.parent, item)) {
                        let current = this.tieredMenuService.currentItem;
                        current.isOpen = false;
                        while (current?.parent !== item.parent) {
                            this._tieredMenuEventService.closeItemMenuEvent.emit(current);
                            this._changeDetectorRef.detectChanges();
                            current = current?.parent;
                        }
                        if (current) {
                            current.isOpen = false;
                        }
                    }
                }
            }
            if (item?.submenu && !item?.isOpen && this.items.includes(item)) {
                const itemEl = document.querySelector(`#${item.id}`);
                if (!itemEl)
                    return;
                const { top, right, left, bottom } = itemEl.getBoundingClientRect();
                const position = { top, right, left, bottom };
                this._createMenu(item.submenu, position);
                this.tieredMenuService.currentItems = item.submenu;
                this.tieredMenuService.currentItem = item.submenu[0];
                item.isOpen = true;
            }
        });
        // Close item menu event.
        this._tieredMenuEventService.closeItemMenuEvent
            .pipe(takeUntil(this._unsubscribe$))
            .subscribe((item) => {
            if (item === null)
                return;
            if (this.items.some((i) => i.id === item.id)) {
                if (item.parent) {
                    item.parent.isOpen = false;
                }
                else {
                    enableScroll();
                }
                this.tieredMenuService.currentItems = item?.parent?.parent?.submenu || this.tieredMenuService.items;
                this.tieredMenuService.currentItem = item.parent;
                this.destroyRequest.emit(false);
            }
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuComponent, deps: [{ token: i0.ApplicationRef }, { token: i0.ComponentFactoryResolver }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }, { token: TieredMenuService }, { token: TieredMenuEventService }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: TieredMenuComponent, selector: "s-tiered-menu", outputs: { destroyRequest: "destroyRequest" }, host: { listeners: { "window:resize": "onResize()", "document:click": "onDocumentClick($event)", "document:keydown": "onKeydownHandler($event)" } }, ngImport: i0, template: "<div\n    class=\"menu\"\n    [ngStyle]=\"{\n        left: left + 'px',\n        top: top + 'px',\n    }\"\n>\n    <!-- <div *ngFor=\"let item of items\">\n        <s-tiered-menu-item\n            *ngIf=\"item.visible && !item.divider\"\n            [item]=\"item\"\n            [focused]=\"item === tieredMenuService.currentItem\"\n            [highlight]=\"item.isOpen\"\n            triggerEvent=\"hover\"\n            [closeOnClick]=\"false\"\n        >\n        </s-tiered-menu-item>\n        <s-tiered-menu-divider *ngIf=\"item.divider\"></s-tiered-menu-divider>\n    </div> -->\n    @for(item of items; track $index) {\n        @if(item.visible && !item.divider) {\n            <s-tiered-menu-item\n                [item]=\"item\"\n                [focused]=\"item === tieredMenuService.currentItem\"\n                [highlight]=\"item.isOpen\"\n                triggerEvent=\"hover\"\n                [closeOnClick]=\"false\"/>\n        }\n        @if (item.divider) {\n            <s-tiered-menu-divider/>\n        }\n    }\n</div>\n", styles: [".menu{background-color:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 2px 12px #0000001a;display:inline-flex;flex-direction:column;max-height:100vh;min-width:176px;padding:4px 0;overflow-y:auto;position:absolute;z-index:9999}\n"], dependencies: [{ kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: TieredMenuItemComponent, selector: "s-tiered-menu-item", inputs: ["item", "focused", "highlight", "triggerEvent", "closeOnClick"] }, { kind: "component", type: TieredMenuDividerComponent, selector: "s-tiered-menu-divider" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-tiered-menu', template: "<div\n    class=\"menu\"\n    [ngStyle]=\"{\n        left: left + 'px',\n        top: top + 'px',\n    }\"\n>\n    <!-- <div *ngFor=\"let item of items\">\n        <s-tiered-menu-item\n            *ngIf=\"item.visible && !item.divider\"\n            [item]=\"item\"\n            [focused]=\"item === tieredMenuService.currentItem\"\n            [highlight]=\"item.isOpen\"\n            triggerEvent=\"hover\"\n            [closeOnClick]=\"false\"\n        >\n        </s-tiered-menu-item>\n        <s-tiered-menu-divider *ngIf=\"item.divider\"></s-tiered-menu-divider>\n    </div> -->\n    @for(item of items; track $index) {\n        @if(item.visible && !item.divider) {\n            <s-tiered-menu-item\n                [item]=\"item\"\n                [focused]=\"item === tieredMenuService.currentItem\"\n                [highlight]=\"item.isOpen\"\n                triggerEvent=\"hover\"\n                [closeOnClick]=\"false\"/>\n        }\n        @if (item.divider) {\n            <s-tiered-menu-divider/>\n        }\n    }\n</div>\n", styles: [".menu{background-color:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 2px 12px #0000001a;display:inline-flex;flex-direction:column;max-height:100vh;min-width:176px;padding:4px 0;overflow-y:auto;position:absolute;z-index:9999}\n"] }]
        }], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.ComponentFactoryResolver }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }, { type: TieredMenuService }, { type: TieredMenuEventService }], propDecorators: { destroyRequest: [{
                type: Output
            }], onResize: [{
                type: HostListener,
                args: ['window:resize']
            }], onDocumentClick: [{
                type: HostListener,
                args: ['document:click', ['$event']]
            }], onKeydownHandler: [{
                type: HostListener,
                args: ['document:keydown', ['$event']]
            }] } });

class TieredMenuDirective {
    _elementRef;
    _appRef;
    _componentFactoryResolver;
    _injector;
    _tieredMenuEventService;
    _tieredMenuService;
    _tieredMenuGlobalService;
    _changeDetectorRef;
    items = [];
    focusedItem = undefined;
    triggerEvent = 'click';
    _componentRef = null;
    _showTimeout;
    _isNested = false;
    _isOpen = false;
    _unsubscribe$ = new Subject();
    _lastActiveElement;
    _previousItems = [];
    _itemsAreValid;
    _resizeObserver = new ResizeObserver(() => {
        this._setMenuPosition();
        this._componentRef?.hostView.detectChanges();
    });
    constructor(_elementRef, _appRef, _componentFactoryResolver, _injector, _tieredMenuEventService, _tieredMenuService, _tieredMenuGlobalService, _changeDetectorRef) {
        this._elementRef = _elementRef;
        this._appRef = _appRef;
        this._componentFactoryResolver = _componentFactoryResolver;
        this._injector = _injector;
        this._tieredMenuEventService = _tieredMenuEventService;
        this._tieredMenuService = _tieredMenuService;
        this._tieredMenuGlobalService = _tieredMenuGlobalService;
        this._changeDetectorRef = _changeDetectorRef;
    }
    ngOnInit() {
        this._itemsAreValid = !!this.items?.length;
        if (!this._itemsAreValid)
            return;
        this._subscribeEvents();
    }
    ngOnChanges() {
        if (!this._itemsAreValid)
            return;
        if (!this._previousItems) {
            this._previousItems = this._tieredMenuService.cloneItems(this.items);
        }
        let hasChanges = false;
        if (this.items.length !== this._previousItems.length) {
            hasChanges = true;
        }
        else {
            for (let i = 0; i < this.items.length; i++) {
                if (!this._compareItems(this.items[i], this._previousItems[i])) {
                    hasChanges = true;
                    break;
                }
            }
        }
        if (hasChanges) {
            this._updateServiceItems();
            this._changeDetectorRef.detectChanges();
            this._rebuildMenu();
        }
        this._previousItems = this._tieredMenuService.cloneItems(this.items);
    }
    ngOnDestroy() {
        if (!this._itemsAreValid)
            return;
        this._unsubscribe$.next();
        this._unsubscribe$.complete();
        this._destroy();
    }
    onClick(event) {
        if (!this._itemsAreValid)
            return;
        if (this.triggerEvent === 'click' && !this._isOpen) {
            this._lastActiveElement = document.activeElement;
            this._createMenu();
            event.preventDefault();
            event.stopPropagation();
        }
    }
    _createMenu() {
        this._updateServiceItems();
        if (!this._componentRef && this._tieredMenuService.items?.length > 0) {
            this._tieredMenuGlobalService.lastInstance?._destroy();
            this._tieredMenuGlobalService.lastInstance = this;
            this._lastActiveElement?.blur();
            this._isOpen = true;
            this._isNested = document.body.clientWidth < 600;
            this._isNested ? this._createNestedMenu() : this._createTieredMenu();
        }
    }
    _createTieredMenu() {
        if (!this._componentRef && this._tieredMenuService.items?.length > 0) {
            this._componentRef = this._createComponent(TieredMenuComponent);
            this._appRef.attachView(this._componentRef.hostView);
            const domElem = this._componentRef.location.nativeElement;
            document.body.appendChild(domElem);
            this._setMenuComponentProperties();
            this._componentRef.instance.destroyRequest.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
                this._destroy();
            });
            this._setMenuPosition();
        }
    }
    _createNestedMenu() {
        if (!this._componentRef && this._tieredMenuService.items?.length > 0) {
            const componentFactory = this._componentFactoryResolver.resolveComponentFactory(TieredMenuNestedComponent);
            this._componentRef = componentFactory.create(this._injector);
            this._appRef.attachView(this._componentRef.hostView);
            const domElem = this._componentRef.hostView.rootNodes[0];
            document.body.appendChild(domElem);
            this._setMenuComponentProperties();
            this._setMenuPosition();
        }
    }
    _createComponent(componentClass) {
        const componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentClass);
        const _componentRef = componentFactory.create(this._injector);
        this._resizeObserver.observe(_componentRef.location.nativeElement.querySelector('.menu'));
        _componentRef.onDestroy(() => {
            this._resizeObserver.disconnect();
        });
        return _componentRef;
    }
    _destroy() {
        if (this._componentRef) {
            this._isOpen = false;
            window.clearTimeout(this._showTimeout);
            this._appRef.detachView(this._componentRef.hostView);
            this._componentRef.destroy();
            this._componentRef = null;
            this._tieredMenuService.currentItems = this._tieredMenuService.items;
            this._tieredMenuService.currentItem = this._tieredMenuService.items[0];
            this._tieredMenuEventService.closeAllMenusEvent.emit();
        }
    }
    _setMenuPosition() {
        if (!this._componentRef) {
            return;
        }
        this._changeDetectorRef.detectChanges();
        const ITEM_HEIGHT = 37;
        const ITEM_WIDTH = this._componentRef.location.nativeElement?.querySelector('.menu')?.clientWidth || 176;
        const DIVIDER_HEIGHT = 5;
        const PADDING = 8;
        const MARGIN = 4;
        this._componentRef.instance.top = 8;
        let { bottom, left, right } = this._elementRef.nativeElement.getBoundingClientRect();
        const itemsCount = this._componentRef.instance.items?.reduce((count, item) => {
            return !item.divider ? count + 1 : count;
        }, 0);
        const dividersCount = this._componentRef.instance.items?.reduce((count, item) => {
            return item.divider ? count + 1 : count;
        }, 0);
        const menuHeight = itemsCount * ITEM_HEIGHT + dividersCount * DIVIDER_HEIGHT + PADDING + MARGIN;
        const rightFreeSpace = window.innerWidth - right;
        const bottomFreeSpace = window.innerHeight - bottom;
        this._componentRef.instance.top = bottom;
        this._componentRef.instance.left = left;
        if (bottomFreeSpace <= menuHeight) {
            this._componentRef.instance.top = Math.max(scrollY + bottom - menuHeight, 0);
        }
        else {
            this._componentRef.instance.top = window.scrollY + bottom + MARGIN;
        }
        if (rightFreeSpace > 176) {
            this._componentRef.instance.left = window.scrollX + left;
        }
        else {
            this._componentRef.instance.left = window.scrollX + right - ITEM_WIDTH;
        }
        if (this._isNested) {
            this._componentRef.instance.left = MARGIN;
        }
    }
    _setMenuComponentProperties() {
        if (this._componentRef != null) {
            this._componentRef.instance.items = this._tieredMenuService.items;
        }
    }
    _subscribeEvents() {
        this._tieredMenuEventService.closeAllMenusEvent.pipe(takeUntil(this._unsubscribe$)).subscribe(() => {
            this._tieredMenuService.items = this._tieredMenuService.markAllItemsAsClosed(this._tieredMenuService.items);
            this._destroy();
            enableScroll();
        });
    }
    _compareItems(item1, item2) {
        return JSON.stringify(item1) === JSON.stringify(item2);
    }
    _rebuildMenu() {
        this._destroy();
    }
    _updateServiceItems() {
        this._tieredMenuService.items = this._tieredMenuService.normalizeData(this.items);
        this._tieredMenuService.currentItems = this._tieredMenuService.items;
        this._tieredMenuService.currentItem =
            this._tieredMenuService.items.find((i) => i.label === this.focusedItem?.label) ??
                this._tieredMenuService.items[0];
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuDirective, deps: [{ token: i0.ElementRef }, { token: i0.ApplicationRef }, { token: i0.ComponentFactoryResolver }, { token: i0.Injector }, { token: TieredMenuEventService }, { token: TieredMenuService }, { token: TieredMenuGlobalService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: TieredMenuDirective, selector: "[sTieredMenu]", inputs: { items: "items", focusedItem: "focusedItem", triggerEvent: "triggerEvent" }, host: { listeners: { "click": "onClick($event)" } }, providers: [TieredMenuEventService, TieredMenuService], usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[sTieredMenu]',
                    providers: [TieredMenuEventService, TieredMenuService],
                }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ApplicationRef }, { type: i0.ComponentFactoryResolver }, { type: i0.Injector }, { type: TieredMenuEventService }, { type: TieredMenuService }, { type: TieredMenuGlobalService }, { type: i0.ChangeDetectorRef }], propDecorators: { items: [{
                type: Input
            }], focusedItem: [{
                type: Input
            }], triggerEvent: [{
                type: Input
            }], onClick: [{
                type: HostListener,
                args: ['click', ['$event']]
            }] } });

class TieredMenuModule {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuModule, declarations: [TieredMenuDirective,
            TieredMenuComponent,
            TieredMenuNestedComponent,
            TieredMenuItemComponent,
            TieredMenuDividerComponent], imports: [CommonModule], exports: [TieredMenuDirective] });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuModule, providers: [TieredMenuGlobalService], imports: [CommonModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TieredMenuModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [CommonModule],
                    declarations: [
                        TieredMenuDirective,
                        TieredMenuComponent,
                        TieredMenuNestedComponent,
                        TieredMenuItemComponent,
                        TieredMenuDividerComponent,
                    ],
                    exports: [TieredMenuDirective],
                    providers: [TieredMenuGlobalService],
                }]
        }] });

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

export { TieredMenuDirective, TieredMenuGlobalService, TieredMenuModule, TieredMenuService };
//# sourceMappingURL=seniorsistemas-angular-components-tiered-menu.mjs.map
