import * as i0 from '@angular/core';
import { contentChildren, signal, viewChild, TemplateRef, input, booleanAttribute, output, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, ElementRef, ViewContainerRef, Injector, effect, Directive } from '@angular/core';
import { Option, OPTION_PARENT } from '@ngstarter-ui/components/option';
export { Option } from '@ngstarter-ui/components/option';
import { NgClass } from '@angular/common';
import { toObservable } from '@angular/core/rxjs-interop';
import { Overlay } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { Subscription, merge, fromEvent } from 'rxjs';
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
import { FormField } from '@ngstarter-ui/components/form-field';
import { ChipInput } from '@ngstarter-ui/components/chips';

let nextId = 0;
class Autocomplete {
    options = contentChildren(Option, { ...(ngDevMode ? { debugName: "options" } : /* istanbul ignore next */ {}), descendants: true });
    multiple = signal(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
    template = viewChild.required(TemplateRef);
    panel = viewChild('panel', ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
    id = `ngs-autocomplete-${nextId++}`;
    ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
    ariaLabelledby = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabelledby" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
    autoActiveFirstOption = input(false, { ...(ngDevMode ? { debugName: "autoActiveFirstOption" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    autoSelectActiveOption = input(false, { ...(ngDevMode ? { debugName: "autoSelectActiveOption" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    classList = input(undefined, { ...(ngDevMode ? { debugName: "classList" } : /* istanbul ignore next */ {}), alias: 'class' });
    disableRipple = input(false, { ...(ngDevMode ? { debugName: "disableRipple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    displayWith = input(() => null, ...(ngDevMode ? [{ debugName: "displayWith" }] : /* istanbul ignore next */ []));
    hideSingleSelectionIndicator = input(false, { ...(ngDevMode ? { debugName: "hideSingleSelectionIndicator" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    panelWidth = input(undefined, ...(ngDevMode ? [{ debugName: "panelWidth" }] : /* istanbul ignore next */ []));
    requireSelection = input(false, { ...(ngDevMode ? { debugName: "requireSelection" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    closed = output();
    opened = output();
    optionActivated = output();
    optionSelected = output();
    _emitSelectEvent(option) {
        this.optionSelected.emit({ source: this, option });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Autocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", type: Autocomplete, isStandalone: true, selector: "ngs-autocomplete", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, autoActiveFirstOption: { classPropertyName: "autoActiveFirstOption", publicName: "autoActiveFirstOption", isSignal: true, isRequired: false, transformFunction: null }, autoSelectActiveOption: { classPropertyName: "autoSelectActiveOption", publicName: "autoSelectActiveOption", isSignal: true, isRequired: false, transformFunction: null }, classList: { classPropertyName: "classList", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, disableRipple: { classPropertyName: "disableRipple", publicName: "disableRipple", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, hideSingleSelectionIndicator: { classPropertyName: "hideSingleSelectionIndicator", publicName: "hideSingleSelectionIndicator", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null }, requireSelection: { classPropertyName: "requireSelection", publicName: "requireSelection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", opened: "opened", optionActivated: "optionActivated", optionSelected: "optionSelected" }, providers: [
            {
                provide: OPTION_PARENT,
                useExisting: forwardRef(() => Autocomplete)
            }
        ], queries: [{ propertyName: "options", predicate: Option, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true, isSignal: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], exportAs: ["ngsAutocomplete"], ngImport: i0, template: "<ng-template>\n  <div class=\"ngs-autocomplete-panel\"\n       role=\"listbox\"\n       [id]=\"id\"\n       [attr.aria-label]=\"ariaLabel()\"\n       [attr.aria-labelledby]=\"ariaLabelledby()\"\n       [ngClass]=\"classList()\"\n       #panel>\n    <ng-content />\n  </div>\n</ng-template>\n", styles: [".ngs-autocomplete-panel{background:var(--ngs-autocomplete-panel-bg, var(--ngs-dropdown-bg));box-shadow:var(--ngs-autocomplete-panel-shadow, var(--ngs-dropdown-shadow));border-radius:var(--ngs-dropdown-radius);max-height:var(--ngs-autocomplete-panel-max-height, var(--ngs-dropdown-max-height, 256px));overflow:auto;min-width:100%;transform-origin:top;animation:ngs-dropdown-panel-showing .15s cubic-bezier(0,0,.2,1);padding:var(--ngs-autocomplete-panel-padding, var(--ngs-dropdown-padding, calc(var(--spacing, .25rem) * 2)));outline:var(--ngs-autocomplete-panel-border, var(--ngs-dropdown-border))}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Autocomplete, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-autocomplete', exportAs: 'ngsAutocomplete', imports: [
                        NgClass
                    ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
                        {
                            provide: OPTION_PARENT,
                            useExisting: forwardRef(() => Autocomplete)
                        }
                    ], template: "<ng-template>\n  <div class=\"ngs-autocomplete-panel\"\n       role=\"listbox\"\n       [id]=\"id\"\n       [attr.aria-label]=\"ariaLabel()\"\n       [attr.aria-labelledby]=\"ariaLabelledby()\"\n       [ngClass]=\"classList()\"\n       #panel>\n    <ng-content />\n  </div>\n</ng-template>\n", styles: [".ngs-autocomplete-panel{background:var(--ngs-autocomplete-panel-bg, var(--ngs-dropdown-bg));box-shadow:var(--ngs-autocomplete-panel-shadow, var(--ngs-dropdown-shadow));border-radius:var(--ngs-dropdown-radius);max-height:var(--ngs-autocomplete-panel-max-height, var(--ngs-dropdown-max-height, 256px));overflow:auto;min-width:100%;transform-origin:top;animation:ngs-dropdown-panel-showing .15s cubic-bezier(0,0,.2,1);padding:var(--ngs-autocomplete-panel-padding, var(--ngs-dropdown-padding, calc(var(--spacing, .25rem) * 2)));outline:var(--ngs-autocomplete-panel-border, var(--ngs-dropdown-border))}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
        }], propDecorators: { options: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => Option), { ...{ descendants: true }, isSignal: true }] }], template: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], autoActiveFirstOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoActiveFirstOption", required: false }] }], autoSelectActiveOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSelectActiveOption", required: false }] }], classList: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], disableRipple: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableRipple", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], hideSingleSelectionIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideSingleSelectionIndicator", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], requireSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireSelection", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], opened: [{ type: i0.Output, args: ["opened"] }], optionActivated: [{ type: i0.Output, args: ["optionActivated"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }] } });

class AutocompleteTrigger {
    _elementRef = inject((ElementRef));
    _overlay = inject(Overlay);
    _viewContainerRef = inject(ViewContainerRef);
    _formField = inject(FormField, { optional: true });
    _chipInput = inject(ChipInput, { optional: true });
    _injector = inject(Injector);
    autocomplete = input.required({ ...(ngDevMode ? { debugName: "autocomplete" } : /* istanbul ignore next */ {}), alias: 'ngsAutocomplete' });
    _overlayRef = null;
    _portal = null;
    _closingSubscription = Subscription.EMPTY;
    _keyManager;
    _canOpenOnInput = true;
    _resizeObserver = null;
    _optionsSubscription = Subscription.EMPTY;
    constructor() {
        effect(() => {
            const chipsLength = this._chipInput?.chipGrid?.chipsLength;
            if (chipsLength === 0 && this.panelOpen) {
                this._elementRef.nativeElement.focus();
            }
        });
    }
    ngOnDestroy() {
        this._destroyOverlay();
        this._stopResizeObserver();
        this._optionsSubscription.unsubscribe();
    }
    _startResizeObserver() {
        this._resizeObserver = new ResizeObserver(() => {
            this.updatePosition();
        });
        this._resizeObserver.observe(this._getConnectedElement().nativeElement);
    }
    _stopResizeObserver() {
        this._resizeObserver?.disconnect();
        this._resizeObserver = null;
    }
    _handleInput() {
        if (!this.panelOpen && this._canOpenOnInput) {
            this.openPanel();
        }
        this._syncSelectedOption();
    }
    _syncSelectedOption() {
        const value = this._elementRef.nativeElement.value;
        this.autocomplete().options().forEach(option => {
            if (value && option.viewValue.toLowerCase() === value.toLowerCase()) {
                option.select();
            }
            else {
                option.deselect();
            }
        });
    }
    _handleFocus() {
        if (!this.panelOpen && this._canOpenOnInput) {
            this.openPanel();
        }
        this._syncSelectedOption();
    }
    _handleClick() {
        if (!this.panelOpen && this._canOpenOnInput) {
            this.openPanel();
        }
        this._syncSelectedOption();
    }
    _handleKeydown(event) {
        if (event.defaultPrevented) {
            return;
        }
        if (event.key === 'Escape' && this.panelOpen) {
            this.closePanel();
            event.stopPropagation();
        }
        else if (this._keyManager) {
            const activeItemBefore = this._keyManager.activeItem;
            this._keyManager.onKeydown(event);
            if (event.key === 'Enter' && this._keyManager.activeItem && this._keyManager.activeItem === activeItemBefore) {
                this._selectOption(this._keyManager.activeItem);
                event.preventDefault();
            }
        }
    }
    get panelOpen() {
        return !!this._overlayRef && this._overlayRef.hasAttached();
    }
    openPanel() {
        if (this.panelOpen || !this.autocomplete().options().length) {
            return;
        }
        if (!this._overlayRef) {
            this._createOverlay();
        }
        this._updatePanelWidth();
        if (!this._overlayRef.hasAttached()) {
            this._portal = new TemplatePortal(this.autocomplete().template(), this._viewContainerRef);
            this._overlayRef.attach(this._portal);
            this._closingSubscription = this._subscribeToClosingIndices();
            this._setupSelectionListeners();
            this._startResizeObserver();
            this.autocomplete().opened.emit();
        }
        this._initKeyManager();
        this._setActiveItem();
    }
    _setActiveItem() {
        const value = this._elementRef.nativeElement.value;
        if (this.autocomplete().options().length > 0) {
            const activeOption = this.autocomplete().options().find(option => {
                return option.viewValue.toLowerCase() === value.toLowerCase();
            });
            if (activeOption) {
                this._keyManager.setActiveItem(activeOption);
            }
            else {
                if (value && this.autocomplete().autoActiveFirstOption()) {
                    this._keyManager.setFirstItemActive();
                }
                else {
                    this._keyManager.setActiveItem(-1);
                }
            }
            this._scrollToOption();
        }
    }
    _scrollToOption() {
        const index = this._keyManager.activeItemIndex || 0;
        const option = this.autocomplete().options()[index];
        if (option && this.autocomplete().panel()) {
            const panel = this.autocomplete().panel().nativeElement;
            const optionElement = option.elementRef.nativeElement;
            const panelTop = panel.scrollTop;
            const panelBottom = panelTop + panel.clientHeight;
            const optionTop = optionElement.offsetTop;
            const optionBottom = optionTop + optionElement.clientHeight;
            if (optionTop < panelTop) {
                panel.scrollTop = optionTop;
            }
            else if (optionBottom > panelBottom) {
                panel.scrollTop = optionBottom - panel.clientHeight;
            }
        }
    }
    closePanel() {
        if (this.panelOpen) {
            this._overlayRef.detach();
            this._closingSubscription.unsubscribe();
            this._stopResizeObserver();
            if (this.autocomplete().requireSelection() && !this._elementRef.nativeElement.value) {
                this._elementRef.nativeElement.value = '';
                this._elementRef.nativeElement.dispatchEvent(new Event('input', { bubbles: true }));
            }
            this.autocomplete().closed.emit();
        }
    }
    updatePosition() {
        if (this._overlayRef) {
            this._updatePanelWidth();
            this._overlayRef.updatePosition();
        }
    }
    _getConnectedElement() {
        if (this._formField) {
            return this._formField.wrapper();
        }
        return this._elementRef;
    }
    _getPanelWidth() {
        return this.autocomplete().panelWidth() || this._getConnectedElement().nativeElement.offsetWidth;
    }
    _createOverlay() {
        const strategy = this._overlay
            .position()
            .flexibleConnectedTo(this._getConnectedElement())
            .withPositions([
            { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
            { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }
        ]);
        this._overlayRef = this._overlay.create({
            positionStrategy: strategy,
            scrollStrategy: this._overlay.scrollStrategies.reposition(),
            width: this._getPanelWidth(),
        });
    }
    _destroyOverlay() {
        if (this._overlayRef) {
            this.closePanel();
            this._overlayRef.dispose();
            this._overlayRef = null;
        }
    }
    _subscribeToClosingIndices() {
        const backdropClick = this._overlayRef.backdropClick();
        const detachments = this._overlayRef.detachments();
        const outsidePointerEvents = this._overlayRef.outsidePointerEvents();
        return merge(backdropClick, detachments, outsidePointerEvents).subscribe((event) => {
            if (event instanceof MouseEvent) {
                if (this._elementRef.nativeElement.contains(event.target)) {
                    return;
                }
                const connectedElement = this._getConnectedElement().nativeElement;
                if (connectedElement.contains(event.target)) {
                    return;
                }
            }
            this.closePanel();
        });
    }
    _initKeyManager() {
        if (this._keyManager) {
            return;
        }
        this._keyManager = new ActiveDescendantKeyManager(this.autocomplete().options())
            .withWrap()
            .withTypeAhead();
        this._optionsSubscription = toObservable(this.autocomplete().options, { injector: this._injector }).subscribe(() => {
            this._setupSelectionListeners();
            this._setActiveItem();
            this._syncSelectedOption();
            if (this.autocomplete().options().length === 0 && this.panelOpen) {
                this.closePanel();
            }
            else if (this.autocomplete().options().length > 0 && !this.panelOpen && this._elementRef.nativeElement === document.activeElement && this._canOpenOnInput) {
                this.openPanel();
            }
        });
        this._keyManager.change.subscribe(() => {
            this._scrollToOption();
            if (this.autocomplete().autoSelectActiveOption() && this._keyManager.activeItem) {
                this._selectOption(this._keyManager.activeItem);
            }
            this.autocomplete().optionActivated.emit({
                source: this.autocomplete(),
                option: this._keyManager.activeItem
            });
        });
    }
    _setupSelectionListeners() {
        this.autocomplete().options().forEach(option => {
            // Prevent input from losing focus on mousedown, but do not select yet
            const mousedownSub = fromEvent(option.elementRef.nativeElement, 'mousedown').subscribe((event) => {
                event.preventDefault();
            });
            // Select option on click (after mouseup) to match native/select behavior
            const clickSub = fromEvent(option.elementRef.nativeElement, 'click').subscribe((event) => {
                if (option.disabled) {
                    event.preventDefault();
                    return;
                }
                this._selectOption(option);
            });
            if (this._closingSubscription && !this._closingSubscription.closed) {
                this._closingSubscription.add(mousedownSub);
                this._closingSubscription.add(clickSub);
            }
        });
    }
    _updatePanelWidth() {
        if (this._overlayRef) {
            this._overlayRef.updateSize({
                width: this._getPanelWidth()
            });
        }
    }
    _selectOption(option) {
        const value = option.viewValue;
        this.autocomplete().options().forEach(o => o.deselect());
        option.select();
        this._canOpenOnInput = false;
        this._elementRef.nativeElement.value = value;
        this._elementRef.nativeElement.dispatchEvent(new Event('input', { bubbles: true }));
        this._elementRef.nativeElement.dispatchEvent(new Event('change', { bubbles: true }));
        this.autocomplete()._emitSelectEvent(option);
        this.closePanel();
        setTimeout(() => this._canOpenOnInput = true);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AutocompleteTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: AutocompleteTrigger, isStandalone: true, selector: "[ngsAutocomplete]", inputs: { autocomplete: { classPropertyName: "autocomplete", publicName: "ngsAutocomplete", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "combobox", "aria-autocomplete": "list", "aria-haspopup": "listbox" }, listeners: { "input": "_handleInput()", "focusin": "_handleFocus()", "click": "_handleClick()", "keydown": "_handleKeydown($event)" } }, exportAs: ["ngsAutocompleteTrigger"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AutocompleteTrigger, decorators: [{
            type: Directive,
            args: [{
                    selector: '[ngsAutocomplete]',
                    exportAs: 'ngsAutocompleteTrigger',
                    host: {
                        'role': 'combobox',
                        'aria-autocomplete': 'list',
                        'aria-haspopup': 'listbox',
                        '(input)': '_handleInput()',
                        '(focusin)': '_handleFocus()',
                        '(click)': '_handleClick()',
                        '(keydown)': '_handleKeydown($event)',
                    }
                }]
        }], ctorParameters: () => [], propDecorators: { autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsAutocomplete", required: true }] }] } });

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

export { Autocomplete, AutocompleteTrigger };
//# sourceMappingURL=ngstarter-ui-components-autocomplete.mjs.map
