import { Overlay } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import * as i0 from '@angular/core';
import { Directive, inject, computed, viewChild, ElementRef, HostListener, ChangeDetectionStrategy, Component, untracked, input, booleanAttribute, numberAttribute, output, signal, Injector, effect, NgModule } from '@angular/core';
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import * as i1 from '@siemens/element-ng/autocomplete';
import { SiAutocompleteDirective, SiAutocompleteListboxDirective, SiAutocompleteOptionDirective } from '@siemens/element-ng/autocomplete';
import { SiTranslatePipe, t } from '@siemens/element-translate-ng/translate';
import { ReplaySubject, isObservable, Subscription, catchError, timer } from 'rxjs';
import { map, switchMap, share, takeUntil } from 'rxjs/operators';
import { NgTemplateOutlet } from '@angular/common';
import { SiIconComponent } from '@siemens/element-ng/icon';
import { SiLoadingSpinnerDirective } from '@siemens/element-ng/loading-spinner';

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiTypeaheadItemTemplateDirective {
    static ngTemplateContextGuard(dir, ctx) {
        return true;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadItemTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.8", type: SiTypeaheadItemTemplateDirective, isStandalone: true, selector: "[siTypeaheadItemTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadItemTemplateDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[siTypeaheadItemTemplate]'
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiTypeaheadComponent {
    parent = inject(SiTypeaheadDirective);
    matches = computed(() => this.parent.typeaheadOptionsLimit()
        ? this.parent.foundMatches().slice(0, this.parent.typeaheadOptionsLimit())
        : this.parent.foundMatches(), ...(ngDevMode ? [{ debugName: "matches" }] : []));
    multiselect = computed(() => this.parent.typeaheadMultiSelect(), ...(ngDevMode ? [{ debugName: "multiselect" }] : []));
    typeaheadElement = viewChild.required('typeahead', {
        read: ElementRef
    });
    autocompleteDirective = inject(SiAutocompleteDirective);
    ngAfterViewInit() {
        this.setHeight(this.typeaheadElement());
    }
    onMouseDown(event) {
        event.preventDefault();
    }
    /*
     * Set the height of the element passed to it (typeahead) if there are items displayed,
     * the number of displayed items changed and it is scrollable.
     */
    setHeight(element) {
        if (this.matches().length) {
            if (this.parent.typeaheadScrollable() &&
                this.parent.typeaheadOptionsInScrollableView() < this.matches().length) {
                const computedStyle = getComputedStyle(element.nativeElement);
                const matchComputedStyle = getComputedStyle(element.nativeElement.firstElementChild);
                const matchHeight = parseFloat(matchComputedStyle.height || '0');
                const paddingTop = parseFloat(computedStyle.paddingTop || '0');
                const paddingBottom = parseFloat(computedStyle.paddingBottom || '');
                const height = this.parent.typeaheadOptionsInScrollableView() * matchHeight;
                element.nativeElement.style.maxBlockSize = `${height + paddingTop + paddingBottom + this.parent.typeaheadScrollableAdditionalHeight()}px`;
            }
            else {
                element.nativeElement.style.maxBlockSize = 'auto';
            }
        }
    }
    // Gets called when a match is selected by clicking on it.
    selectMatch(match) {
        if (match) {
            this.parent.selectMatch(match);
        }
        else {
            this.parent.createOption();
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiTypeaheadComponent, isStandalone: true, selector: "si-typeahead", host: { listeners: { "mousedown": "onMouseDown($event)" }, classAttribute: "w-100" }, viewQueries: [{ propertyName: "typeaheadElement", first: true, predicate: ["typeahead"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<!-- Template to be used for every match, can be replaced using an input. -->\n<ng-template #defaultItemTemplate let-match=\"match\" siTypeaheadItemTemplate>\n  @if (multiselect()) {\n    <div class=\"d-flex pe-4\" aria-hidden=\"true\">\n      <span class=\"form-check-input si-form-checkbox\" [class.checked]=\"match.itemSelected\"></span>\n    </div>\n  }\n  @if (match.iconClass) {\n    <si-icon class=\"icon me-2\" [icon]=\"match.iconClass\" />\n  }\n  @for (segment of match.result; track $index) {\n    <span [class.typeahead-match-segment-matching]=\"segment.isMatching\">{{ segment.text }}</span>\n  }\n</ng-template>\n\n@let createOption = parent.typeaheadCreateOption();\n\n<!-- Only display the component if there are any matches and set the CSS transform to properly position the typeahead -->\n<ul\n  #typeahead\n  class=\"typeahead dropdown-menu\"\n  [class.empty-loading]=\"parent.typeaheadLoading() && matches().length === 0\"\n  [siLoading]=\"parent.typeaheadLoading() && matches().length === 0\"\n  [initialDelay]=\"false\"\n  [siAutocompleteListboxFor]=\"autocompleteDirective\"\n  [siAutocompleteDefaultIndex]=\"parent.typeaheadAutoSelectIndex()\"\n  [attr.aria-label]=\"parent.typeaheadAutocompleteListLabel() | translate\"\n  (siAutocompleteOptionSubmitted)=\"selectMatch($event)\"\n>\n  @if (parent.typeaheadLoading() && matches().length) {\n    <li class=\"dropdown-item\" [siLoading]=\"true\" [initialDelay]=\"false\"></li>\n  }\n  <!-- Loop through every match and bind events, the mousedown prevent default is to prevent the host from losing focus on click -->\n  @for (match of matches(); track $index) {\n    <li\n      #typeaheadMatch\n      class=\"dropdown-item\"\n      [siAutocompleteOption]=\"match\"\n      [attr.aria-label]=\"match.text\"\n      [attr.aria-selected]=\"multiselect() ? match.itemSelected : null\"\n      (click)=\"$event.stopPropagation()\"\n      (mousedown)=\"$event.preventDefault()\"\n    >\n      <!-- Display either a template set as the input or the template above -->\n      <ng-template\n        [ngTemplateOutlet]=\"parent.typeaheadItemTemplate() || defaultItemTemplate\"\n        [ngTemplateOutletContext]=\"{\n          item: match.option,\n          index: $index,\n          match: match,\n          query: parent.query()\n        }\"\n      />\n    </li>\n  }\n  @if (createOption && parent.query().length) {\n    <li class=\"dropdown-item\" [siAutocompleteOption]=\"undefined\">\n      {{ createOption | translate: { query: parent.query() } }}\n    </li>\n  }\n</ul>\n", styles: [":host{--loading-spinner-size: 24px}.dropdown-menu{display:block;position:relative;inset-block-start:0;inset-inline-start:0;margin-block-start:1px;overflow-y:auto;overflow-x:hidden;max-block-size:100%}.dropdown-menu.empty-loading{min-block-size:176px}.typeahead-match-segment-matching{font-weight:600}.dropdown-item *{flex-shrink:0}.dropdown-item span{white-space:pre-wrap}\n"], dependencies: [{ kind: "directive", type: SiAutocompleteListboxDirective, selector: "[siAutocompleteListboxFor]", inputs: ["id", "siAutocompleteListboxFor", "siAutocompleteDefaultIndex"], outputs: ["siAutocompleteOptionSubmitted"], exportAs: ["siAutocompleteListbox"] }, { kind: "directive", type: SiAutocompleteOptionDirective, selector: "[siAutocompleteOption]", inputs: ["id", "disabled", "siAutocompleteOption"], exportAs: ["siAutocompleteOption"] }, { kind: "component", type: SiIconComponent, selector: "si-icon", inputs: ["icon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: SiTypeaheadItemTemplateDirective, selector: "[siTypeaheadItemTemplate]" }, { kind: "directive", type: SiLoadingSpinnerDirective, selector: "[siLoading]", inputs: ["siLoading", "blocking", "initialDelay"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadComponent, decorators: [{
            type: Component,
            args: [{ selector: 'si-typeahead', imports: [
                        SiAutocompleteListboxDirective,
                        SiAutocompleteOptionDirective,
                        SiIconComponent,
                        NgTemplateOutlet,
                        SiTranslatePipe,
                        SiTypeaheadItemTemplateDirective,
                        SiLoadingSpinnerDirective
                    ], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'w-100' }, template: "<!-- Template to be used for every match, can be replaced using an input. -->\n<ng-template #defaultItemTemplate let-match=\"match\" siTypeaheadItemTemplate>\n  @if (multiselect()) {\n    <div class=\"d-flex pe-4\" aria-hidden=\"true\">\n      <span class=\"form-check-input si-form-checkbox\" [class.checked]=\"match.itemSelected\"></span>\n    </div>\n  }\n  @if (match.iconClass) {\n    <si-icon class=\"icon me-2\" [icon]=\"match.iconClass\" />\n  }\n  @for (segment of match.result; track $index) {\n    <span [class.typeahead-match-segment-matching]=\"segment.isMatching\">{{ segment.text }}</span>\n  }\n</ng-template>\n\n@let createOption = parent.typeaheadCreateOption();\n\n<!-- Only display the component if there are any matches and set the CSS transform to properly position the typeahead -->\n<ul\n  #typeahead\n  class=\"typeahead dropdown-menu\"\n  [class.empty-loading]=\"parent.typeaheadLoading() && matches().length === 0\"\n  [siLoading]=\"parent.typeaheadLoading() && matches().length === 0\"\n  [initialDelay]=\"false\"\n  [siAutocompleteListboxFor]=\"autocompleteDirective\"\n  [siAutocompleteDefaultIndex]=\"parent.typeaheadAutoSelectIndex()\"\n  [attr.aria-label]=\"parent.typeaheadAutocompleteListLabel() | translate\"\n  (siAutocompleteOptionSubmitted)=\"selectMatch($event)\"\n>\n  @if (parent.typeaheadLoading() && matches().length) {\n    <li class=\"dropdown-item\" [siLoading]=\"true\" [initialDelay]=\"false\"></li>\n  }\n  <!-- Loop through every match and bind events, the mousedown prevent default is to prevent the host from losing focus on click -->\n  @for (match of matches(); track $index) {\n    <li\n      #typeaheadMatch\n      class=\"dropdown-item\"\n      [siAutocompleteOption]=\"match\"\n      [attr.aria-label]=\"match.text\"\n      [attr.aria-selected]=\"multiselect() ? match.itemSelected : null\"\n      (click)=\"$event.stopPropagation()\"\n      (mousedown)=\"$event.preventDefault()\"\n    >\n      <!-- Display either a template set as the input or the template above -->\n      <ng-template\n        [ngTemplateOutlet]=\"parent.typeaheadItemTemplate() || defaultItemTemplate\"\n        [ngTemplateOutletContext]=\"{\n          item: match.option,\n          index: $index,\n          match: match,\n          query: parent.query()\n        }\"\n      />\n    </li>\n  }\n  @if (createOption && parent.query().length) {\n    <li class=\"dropdown-item\" [siAutocompleteOption]=\"undefined\">\n      {{ createOption | translate: { query: parent.query() } }}\n    </li>\n  }\n</ul>\n", styles: [":host{--loading-spinner-size: 24px}.dropdown-menu{display:block;position:relative;inset-block-start:0;inset-inline-start:0;margin-block-start:1px;overflow-y:auto;overflow-x:hidden;max-block-size:100%}.dropdown-menu.empty-loading{min-block-size:176px}.typeahead-match-segment-matching{font-weight:600}.dropdown-item *{flex-shrink:0}.dropdown-item span{white-space:pre-wrap}\n"] }]
        }], propDecorators: { typeaheadElement: [{ type: i0.ViewChild, args: ['typeahead', { ...{
                            read: ElementRef
                        }, isSignal: true }] }], onMouseDown: [{
                type: HostListener,
                args: ['mousedown', ['$event']]
            }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Constructs a typeahead search and provides the matches as a signal.
 *
 * @param options - Factory function that should return the array of options to search in.
 * Is run in a reactive context.
 * @param query - Factory function that should return the current search query. Is run in a reactive context.
 * @param config - Configuration for the search. Is run in a reactive context.
 *
 * @example
 * In a real world, myOptions and mayQuery would be signals.
 * ```ts
 * const search = typeaheadSearch(
 *   () => myOptions().map(...),
 *   () => myQuery().toLowerCase(),
 *   () => ({ matchAllTokens: 'separately' })
 * )
 * ```
 */
const typeaheadSearch = (options, query, config) => computed(() => new TypeaheadSearch(options, query, config()).matches());
class TypeaheadSearch {
    datasource;
    query;
    options;
    matches = computed(() => this.search(this.datasource(), this.readQuery()), ...(ngDevMode ? [{ debugName: "matches" }] : []));
    constructor(datasource, query, options) {
        this.datasource = datasource;
        this.query = query;
        this.options = options;
    }
    readQuery() {
        if (this.options.lazy) {
            return untracked(() => this.query());
        }
        else {
            return this.query();
        }
    }
    escapeRegex(query) {
        return query.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
    }
    search(options, query) {
        try {
            const entireQueryRegex = new RegExp(this.escapeRegex(query), 'gi');
            const queryParts = !this.options.disableTokenizing
                ? query.split(/\s+/g).filter(queryPart => queryPart)
                : query
                    ? [query]
                    : [];
            const queryRegexes = queryParts.map(queryPart => new RegExp(this.escapeRegex(queryPart), 'gi'));
            // Process the options.
            const matches = [];
            options.forEach(option => {
                const optionValue = option.text;
                const stringMatch = optionValue.toLocaleLowerCase().trim() === query.toLocaleLowerCase().trim();
                const candidate = {
                    option: option.option,
                    text: optionValue,
                    result: [],
                    stringMatch,
                    atBeginning: false,
                    matches: 0,
                    uniqueMatches: 0,
                    uniqueSeparateMatches: 0,
                    matchesEntireQuery: false,
                    matchesAllParts: false,
                    matchesAllPartsSeparately: false
                };
                // Only search the options if a part of the query is at least one character long to prevent an endless loop.
                if (queryParts.length === 0) {
                    if (optionValue) {
                        candidate.result.push({
                            text: optionValue,
                            isMatching: false,
                            matches: 0,
                            uniqueMatches: 0
                        });
                    }
                    matches.push(candidate);
                }
                else {
                    const allResults = [];
                    const allIndexes = [];
                    candidate.matchesEntireQuery = !!optionValue.match(entireQueryRegex);
                    // Loop through the option value to find multiple matches, then store every segment (matching or non-matching) in the results.
                    queryRegexes.forEach((queryRegex, index) => {
                        let regexMatch = queryRegex.exec(optionValue);
                        while (regexMatch) {
                            allResults.push({
                                index,
                                start: regexMatch.index,
                                end: regexMatch.index + regexMatch[0].length,
                                result: regexMatch[0]
                            });
                            if (!regexMatch.index) {
                                candidate.atBeginning = true;
                            }
                            if (!allIndexes.includes(index)) {
                                allIndexes.push(index);
                            }
                            regexMatch = queryRegex.exec(optionValue);
                        }
                    });
                    candidate.matchesAllParts = allIndexes.length === queryParts.length;
                    // Check if all parts of the query match at least once (if required).
                    if (this.options.matchAllTokens === 'no' || candidate.matchesAllParts) {
                        const combinedResults = [];
                        // First combine intersecting (or if set to independently adjacent) results to combined results.
                        // We achieve this by first sorting them by the starting index, then by the ending index and then looking for overlaps.
                        allResults
                            .sort((a, b) => a.start - b.start || a.end - b.end)
                            .forEach(result => {
                            if (combinedResults.length) {
                                const foundPreviousResult = combinedResults.find(previousResult => this.options.matchAllTokens === 'independently'
                                    ? result.start <= previousResult.end
                                    : result.start < previousResult.end);
                                if (foundPreviousResult) {
                                    foundPreviousResult.result += result.result.slice(foundPreviousResult.end - result.start, result.result.length);
                                    if (result.end > foundPreviousResult.end) {
                                        foundPreviousResult.end = result.end;
                                    }
                                    foundPreviousResult.indexes.push(result.index);
                                    if (!foundPreviousResult.uniqueIndexes.includes(result.index)) {
                                        foundPreviousResult.uniqueIndexes.push(result.index);
                                    }
                                    return;
                                }
                            }
                            combinedResults.push({
                                ...result,
                                indexes: [result.index],
                                uniqueIndexes: [result.index]
                            });
                        });
                        // Recursively go through all unique combinations of the unique indexes to get the option which has the most indexes.
                        const countUniqueSubindexes = (indexIndex = 0, previousIndexes = []) => indexIndex === combinedResults.length
                            ? previousIndexes.length
                            : Math.max(previousIndexes.length, ...combinedResults[indexIndex].uniqueIndexes
                                .filter(index => !previousIndexes.includes(index))
                                .map(index => countUniqueSubindexes(indexIndex + 1, [index, ...previousIndexes])));
                        candidate.uniqueSeparateMatches = countUniqueSubindexes();
                        candidate.matchesAllPartsSeparately =
                            candidate.uniqueSeparateMatches === queryParts.length;
                        let currentPreviousEnd = 0;
                        // Add the combined results to the candidate including the non-matching parts in between.
                        combinedResults.forEach(result => {
                            const textBefore = optionValue.slice(currentPreviousEnd, result.start);
                            if (textBefore) {
                                candidate.result.push({
                                    text: textBefore,
                                    isMatching: false,
                                    matches: 0,
                                    uniqueMatches: 0
                                });
                            }
                            candidate.result.push({
                                text: result.result,
                                isMatching: true,
                                matches: result.indexes.length,
                                uniqueMatches: result.uniqueIndexes.length
                            });
                            currentPreviousEnd = result.end;
                            candidate.matches += result.indexes.length;
                            candidate.uniqueMatches += result.uniqueIndexes.length;
                        });
                        // Check if there are result segments and all parts are matched independently (if required).
                        if (candidate.result.length !== 0 &&
                            ((this.options.matchAllTokens !== 'separately' &&
                                this.options.matchAllTokens !== 'independently') ||
                                candidate.matchesAllPartsSeparately)) {
                            const textAtEnd = optionValue.slice(currentPreviousEnd);
                            if (textAtEnd) {
                                candidate.result.push({
                                    text: textAtEnd,
                                    isMatching: false,
                                    matches: 0,
                                    uniqueMatches: 0
                                });
                            }
                            matches.push(candidate);
                        }
                    }
                }
            });
            return matches;
        }
        catch {
            // Could not create regex (only in extremely rare cases, maybe even impossible), so return an empty array.
            return [];
        }
    }
}

class SiTypeaheadSorting {
    sortMatches(matches) {
        // Sort the matches,
        // first is the option and query an exact match.
        // then according to whether it is matching in the beginning,
        // then whether it matches the entire untokenized query.
        // then according to how many unique separate matches it contains.
        // then according to how many unique matches it contains.
        // then according to how many matches it contains.
        return matches.sort((matchA, matchB) => {
            if (matchA.stringMatch || matchB.stringMatch) {
                return matchA.stringMatch ? -1 : 1;
            }
            if (matchA.atBeginning) {
                return !matchB.atBeginning ? -1 : this.compareMatches(matchA, matchB);
            }
            else {
                return matchB.atBeginning ? 1 : this.compareMatches(matchA, matchB);
            }
        });
    }
    compareMatchesNumbers(matchA, matchB) {
        return (matchB.uniqueSeparateMatches - matchA.uniqueSeparateMatches ||
            matchB.uniqueMatches - matchA.uniqueMatches ||
            matchB.matches - matchA.matches);
    }
    compareMatches(matchA, matchB) {
        if (matchA.matchesEntireQuery) {
            return !matchB.matchesEntireQuery ? -1 : this.compareMatchesNumbers(matchA, matchB);
        }
        else {
            return matchB.matchesEntireQuery ? 1 : this.compareMatchesNumbers(matchA, matchB);
        }
    }
}

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiTypeaheadDirective {
    static overlayPositions = [
        {
            overlayX: 'start',
            overlayY: 'top',
            originX: 'start',
            originY: 'bottom',
            offsetY: 2
        },
        {
            overlayX: 'start',
            overlayY: 'bottom',
            originX: 'start',
            originY: 'top',
            offsetY: -4
        },
        {
            overlayX: 'end',
            overlayY: 'top',
            originX: 'end',
            originY: 'bottom',
            offsetY: 2
        },
        {
            overlayX: 'end',
            overlayY: 'bottom',
            originX: 'end',
            originY: 'top',
            offsetY: -4
        }
    ];
    /**
     * Set the options of the typeahead.
     * Has to be either an Array or an Observable of an Array
     * of options (string or object)
     */
    siTypeahead = input.required(...(ngDevMode ? [{ debugName: "siTypeahead" }] : []));
    /**
     * Turns on/off the processing (searching and sorting) of the typeahead options.
     * Is used when searching and sorting is done externally.
     *
     * @defaultValue true
     */
    typeaheadProcess = input(true, { ...(ngDevMode ? { debugName: "typeaheadProcess" } : {}), transform: booleanAttribute });
    /**
     * Makes the typeahead scrollable and sets its height.
     * Uses {@link typeaheadOptionsInScrollableView} and {@link typeaheadScrollableAdditionalHeight}.
     *
     * @defaultValue false
     */
    typeaheadScrollable = input(false, { ...(ngDevMode ? { debugName: "typeaheadScrollable" } : {}), transform: booleanAttribute });
    /**
     * If {@link typeaheadScrollable} is `true`, defines the number of items visible at once.
     *
     * @defaultValue 10
     */
    typeaheadOptionsInScrollableView = input(10, ...(ngDevMode ? [{ debugName: "typeaheadOptionsInScrollableView" }] : []));
    /**
     * Defines the maximum number of items added into the DOM. Default is 20 and 0 means unlimited.
     *
     * @defaultValue 20
     */
    typeaheadOptionsLimit = input(20, ...(ngDevMode ? [{ debugName: "typeaheadOptionsLimit" }] : []));
    /**
     * If {@link typeaheadScrollable} is `true`, defines the number of additional pixels
     * to be added the the bottom of the typeahead to show users that it is scrollable.
     *
     * @defaultValue 13
     */
    typeaheadScrollableAdditionalHeight = input(13, ...(ngDevMode ? [{ debugName: "typeaheadScrollableAdditionalHeight" }] : []));
    /**
     * Defines the index of the item which should automatically be selected.
     *
     * @defaultValue 0
     */
    typeaheadAutoSelectIndex = input(0, { ...(ngDevMode ? { debugName: "typeaheadAutoSelectIndex" } : {}), transform: numberAttribute });
    /**
     * Defines whether the typeahead can be closed using escape.
     *
     * @defaultValue true
     */
    typeaheadCloseOnEsc = input(true, { ...(ngDevMode ? { debugName: "typeaheadCloseOnEsc" } : {}), transform: booleanAttribute });
    /**
     * Defines whether the host value should be cleared when a value is selected.
     *
     * @defaultValue false
     */
    typeaheadClearValueOnSelect = input(false, { ...(ngDevMode ? { debugName: "typeaheadClearValueOnSelect" } : {}), transform: booleanAttribute });
    /**
     * Defines the number of milliseconds to wait before displaying a typeahead after the host was
     * focused or a value inputted.
     *
     * @defaultValue 0
     */
    typeaheadWaitMs = input(0, ...(ngDevMode ? [{ debugName: "typeaheadWaitMs" }] : []));
    /**
     * Defines the number of characters the value of the host needs to be before a typeahead is displayed.
     * Use `0` to have it display when focussing the host (clicking or tabbing into it).
     *
     * @defaultValue 1
     */
    typeaheadMinLength = input(1, ...(ngDevMode ? [{ debugName: "typeaheadMinLength" }] : []));
    /**
     * Defines the name of the field/property the option string is in when the typeahead options are objects.
     *
     * @defaultValue 'name'
     */
    typeaheadOptionField = input('name', ...(ngDevMode ? [{ debugName: "typeaheadOptionField" }] : []));
    /**
     * Defines whether multiselection of typeahead is possible with checkboxes.
     *
     * @defaultValue false
     */
    typeaheadMultiSelect = input(false, { ...(ngDevMode ? { debugName: "typeaheadMultiSelect" } : {}), transform: booleanAttribute });
    /**
     * Defines whether to tokenize the search or match the whole search.
     *
     * @defaultValue true
     */
    typeaheadTokenize = input(true, { ...(ngDevMode ? { debugName: "typeaheadTokenize" } : {}), transform: booleanAttribute });
    /**
     * Defines whether and how to require to match with all the tokens if {@link typeaheadTokenize} is enabled.
     * - `no` does not require all of the tokens to match.
     * - `once` requires all of the parts to be found in the search.
     * - `separately` requires all of the parts to be found in the search where there is not an overlapping different result.
     * - `independently` requires all of the parts to be found in the search where there is not an overlapping or adjacent different result.
     *  ('independently' also slightly changes sorting behavior in the same way.)
     *
     * @defaultValue 'separately'
     */
    typeaheadMatchAllTokens = input('separately', ...(ngDevMode ? [{ debugName: "typeaheadMatchAllTokens" }] : []));
    /**
     * Defines an optional template to use as the typeahead match item instead of the one built in.
     * Gets the {@link TypeaheadOptionItemContext} passed to it.
     */
    typeaheadItemTemplate = input(...(ngDevMode ? [undefined, { debugName: "typeaheadItemTemplate" }] : []));
    /**
     * Skip the sorting of matches.
     * If the value is `true`, the matches are sorted according to {@link SiTypeaheadSorting}.
     *
     * @defaultValue false
     */
    typeaheadSkipSortingMatches = input(false, { ...(ngDevMode ? { debugName: "typeaheadSkipSortingMatches" } : {}), transform: booleanAttribute });
    /**
     * Screen reader only label for the autocomplete list.
     *
     * @defaultValue
     * ```
     * t(() => $localize`:@@SI_TYPEAHEAD.AUTOCOMPLETE_LIST_LABEL:Suggestions`)
     * ```
     */
    typeaheadAutocompleteListLabel = input(t(() => $localize `:@@SI_TYPEAHEAD.AUTOCOMPLETE_LIST_LABEL:Suggestions`), ...(ngDevMode ? [{ debugName: "typeaheadAutocompleteListLabel" }] : []));
    /**
     * If set, the typeahead will at minium have the width of the connected input field.
     *
     * @defaultValue false
     */
    typeaheadFullWidth = input(false, { ...(ngDevMode ? { debugName: "typeaheadFullWidth" } : {}), transform: booleanAttribute });
    /**
     * Optional CDK scroll strategy used for the typeahead overlay.
     * If not provided, no strategy is set explicitly and CDK will apply its default behavior.
     */
    typeaheadScrollStrategy = input(...(ngDevMode ? [undefined, { debugName: "typeaheadScrollStrategy" }] : []));
    /**
     * This option will be shown at the end of the typeahead.
     * Use it to allow users to create new options on the fly.
     *
     * Use the `{{ query }}` parameter in translation values to include the current text from the input.
     *
     * @experimental
     */
    typeaheadCreateOption = input(...(ngDevMode ? [undefined, { debugName: "typeaheadCreateOption" }] : []));
    /**
     * Emits an Event when the input field is changed.
     */
    typeaheadOnInput = output();
    /**
     * Emits an Event when a typeahead match is selected.
     * The event is a {@link TypeaheadMatch}
     */
    typeaheadOnSelect = output();
    /**
     * Emits an Event when a typeahead full match exists. A full match occurs when the entered text
     * is equal to one of the typeahead options.
     * The event is a {@link TypeaheadMatch}
     */
    typeaheadOnFullMatch = output();
    /** Emits whenever the typeahead overlay is opened or closed. */
    typeaheadOpenChange = output();
    /**
     * Emits when the create option is selected.
     * It will emit the current search query.
     *
     * @experimental
     */
    typeaheadOnCreateOption = output();
    /** @internal */
    foundMatches = computed(() => this.typeaheadProcess() ? this.processedSearch() : this.unprocessedSearch(), ...(ngDevMode ? [{ debugName: "foundMatches" }] : []));
    /** @internal */
    query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : []));
    /**
     * Indicates whether the typeahead is shown.
     */
    get typeaheadOpen() {
        return !!this.componentRef;
    }
    overlay = inject(Overlay);
    elementRef = inject(ElementRef);
    injector = inject(Injector);
    $typeahead = new ReplaySubject(1);
    componentRef;
    inputTimer;
    sourceSubscription;
    matchSorter = new SiTypeaheadSorting();
    overlayRef;
    loadingSubscription;
    /**
     * Indicates whether the typeahead is currently loading.
     * When using {@link TypeaheadOptionSource}, this is controlled by the `isLoading()` method
     * or automatically set to `true` while fetching options.
     *
     * @internal
     * @defaultValue false
     */
    typeaheadLoading = signal(false, ...(ngDevMode ? [{ debugName: "typeaheadLoading" }] : []));
    /**
     * Indicates that the typeahead can be potentially open.
     * This signal is typically `true` when the input is focussed.
     * It may be overridden and set to `false` when escape is pressed
     * or when an option was selected.
     */
    canBeOpen = signal(false, ...(ngDevMode ? [{ debugName: "canBeOpen" }] : []));
    selectionCounter = signal(0, ...(ngDevMode ? [{ debugName: "selectionCounter" }] : []));
    typeaheadOptions = toSignal(this.$typeahead.pipe(map(options => options.map(option => ({
        text: this.getOptionValue(option),
        option
    })))), { initialValue: [] });
    typeaheadSearch = typeaheadSearch(this.typeaheadOptions, this.query, computed(() => ({
        matchAllTokens: this.typeaheadMatchAllTokens(),
        disableTokenizing: !this.typeaheadTokenize(),
        skipProcessing: !this.typeaheadProcess(),
        lazy: !(Array.isArray(this.siTypeahead()) || isObservable(this.siTypeahead()))
    })));
    processedSearch = computed(() => {
        this.selectionCounter(); // This is a workaround for the multi-select which needs to trigger a change detection in the typeahead component.
        const matches = this.typeaheadSearch().map(match => ({
            ...match,
            itemSelected: this.typeaheadMultiSelect()
                ? match.option.selected
                : false,
            iconClass: this.getOptionField(match.option, 'iconClass')
        }));
        if (this.typeaheadSkipSortingMatches()) {
            return matches;
        }
        else {
            return this.matchSorter.sortMatches(matches);
        }
    }, ...(ngDevMode ? [{ debugName: "processedSearch" }] : []));
    unprocessedSearch = computed(() => {
        this.selectionCounter(); // This is a workaround for the multi-select which needs to trigger a change detection in the typeahead component.
        return this.typeaheadOptions().map(option => {
            const itemSelected = this.typeaheadMultiSelect()
                ? option.selected
                : false;
            return {
                option,
                text: option.text,
                result: option.text
                    ? [{ text: option.text, isMatching: false, matches: 0, uniqueMatches: 0 }]
                    : [],
                itemSelected,
                iconClass: this.getOptionField(option.option, 'iconClass'),
                stringMatch: false,
                atBeginning: false,
                matches: 0,
                uniqueMatches: 0,
                uniqueSeparateMatches: 0,
                matchesEntireQuery: false,
                matchesAllParts: false,
                matchesAllPartsSeparately: false,
                active: false
            };
        });
    }, ...(ngDevMode ? [{ debugName: "unprocessedSearch" }] : []));
    constructor() {
        effect(() => {
            // The value needs to fulfil the minimum length requirement set.
            const matches = this.foundMatches();
            if (this.canBeOpen() &&
                this.query().length >= this.typeaheadMinLength() &&
                // Ensure that we have content to show: matches, create option or loading indicator
                (matches.length ||
                    (this.typeaheadCreateOption() && this.query().length) ||
                    this.typeaheadLoading())) {
                const escapedQuery = this.escapeRegex(this.query());
                const equalsExp = new RegExp(`^${escapedQuery}$`, 'i');
                const fullMatches = matches.filter(match => match.result.length === 1 && equalsExp.test(match.text));
                if (fullMatches.length > 0) {
                    this.typeaheadOnFullMatch.emit(fullMatches[0]);
                }
                this.loadComponent();
            }
            else {
                this.removeComponent();
            }
        });
    }
    // Every time the main input changes, detect whether it is async and if it is not make an observable out of the array.
    ngOnChanges(changes) {
        if (changes.siTypeahead) {
            this.sourceSubscription?.unsubscribe();
            this.loadingSubscription?.unsubscribe();
            const typeahead = this.siTypeahead();
            if (Array.isArray(typeahead)) {
                // Handle TypeaheadArray
                this.$typeahead.next(typeahead);
                this.typeaheadLoading.set(false);
            }
            else if (isObservable(typeahead)) {
                // Handle TypeaheadObservable
                this.sourceSubscription = typeahead.subscribe(this.$typeahead);
                this.typeaheadLoading.set(false);
            }
            else if (typeof typeahead === 'function') {
                // Handle TypeaheadOptionSource
                this.handleTypeaheadOptionSource(typeahead);
            }
        }
    }
    // Clear the current input timeout (if set) and remove the component when the focus of the host is lost.
    onBlur() {
        this.clearTimer();
        this.canBeOpen.set(false);
    }
    // Start the input timeout to display the typeahead when the host is focussed or a value is inputted into it.
    onInput(event) {
        const target = event.target;
        if (!target) {
            return;
        }
        // Get the value or otherwise textContent of the host element now, because later it could be reset.
        const firstValue = target.value || target.textContent;
        this.inputTimer ??= setTimeout(() => {
            this.inputTimer = undefined;
            const value = (target.value || target.textContent) ?? firstValue ?? '';
            this.query.set(value);
            this.typeaheadOnInput.emit(value ?? '');
            this.canBeOpen.set(true);
        }, this.typeaheadWaitMs());
    }
    onKeydownEscape() {
        if (this.typeaheadCloseOnEsc()) {
            this.clearTimer();
            this.canBeOpen.set(false);
        }
    }
    ngOnDestroy() {
        this.clearTimer();
        this.sourceSubscription?.unsubscribe();
        this.loadingSubscription?.unsubscribe();
        this.overlayRef?.dispose();
    }
    /**
     * Handles TypeaheadOptionSource by subscribing to query changes and fetching options.
     * Manages loading state automatically or uses custom isLoading() observable if provided.
     */
    handleTypeaheadOptionSource(source) {
        this.sourceSubscription = new Subscription();
        const queryObservable = toObservable(this.query, { injector: this.injector });
        const resultObservable = queryObservable.pipe(switchMap(query => source(query)), 
        // Catch errors by the source observable to prevent endless spinner. Showing the error is not our responsibility.
        catchError(e => {
            console.error(e);
            return [];
        }), share());
        this.sourceSubscription.add(queryObservable
            .pipe(switchMap(() => timer(500).pipe(takeUntil(resultObservable))))
            .subscribe(() => this.typeaheadLoading.set(true)));
        this.sourceSubscription.add(resultObservable.subscribe(result => {
            this.$typeahead.next(result);
            this.typeaheadLoading.set(false);
        }));
    }
    // Dynamically create the typeahead component and then set the matches and the query.
    loadComponent() {
        if (!this.overlayRef?.hasAttached()) {
            this.overlayRef?.dispose();
            const overlayConfig = {
                positionStrategy: this.overlay
                    .position()
                    .flexibleConnectedTo(this.elementRef.nativeElement)
                    .withPositions(SiTypeaheadDirective.overlayPositions),
                minWidth: this.typeaheadFullWidth()
                    ? this.elementRef.nativeElement.getBoundingClientRect().width + 2 // 2px border
                    : 0
            };
            const scrollStrategy = this.typeaheadScrollStrategy();
            if (scrollStrategy) {
                overlayConfig.scrollStrategy = scrollStrategy;
            }
            this.overlayRef = this.overlay.create(overlayConfig);
        }
        if (this.overlayRef.hasAttached()) {
            return;
        }
        const typeaheadPortal = new ComponentPortal(SiTypeaheadComponent, null, this.injector);
        this.componentRef = this.overlayRef.attach(typeaheadPortal);
        this.typeaheadOpenChange.emit(true);
    }
    /**
     * Extracts the display value from a typeahead option.
     *
     * For string options, returns the string value directly.
     * For object options, returns the value of the field specified by {@link typeaheadOptionField}
     * (defaults to 'name'), or an empty string if the field doesn't exist.
     *
     * @param option - The typeahead option to extract the value from
     * @returns The string representation of the option for display purposes
     */
    getOptionValue(option) {
        return typeof option !== 'object'
            ? option.toString()
            : (option[this.typeaheadOptionField()] ?? '');
    }
    /**
     * Extracts a specific field value from a typeahead option.
     *
     * This method is used to access additional properties of object-type options,
     * such as 'selected' for multi-select functionality or 'iconClass' for displaying icons.
     *
     * @param option - The typeahead option to extract the field from
     * @param field - The name of the field to extract
     * @returns The field value as a string if the option is an object and the field exists,
     *          otherwise undefined
     */
    getOptionField(option, field) {
        return typeof option !== 'object' ? undefined : option[field];
    }
    /** @internal */
    createOption() {
        this.typeaheadOnCreateOption.emit(this.query());
        this.clearTimer();
        if (!this.typeaheadMultiSelect() && this.typeaheadClearValueOnSelect()) {
            const inputElement = this.elementRef.nativeElement.querySelector('input') ?? this.elementRef.nativeElement;
            inputElement.value = '';
            inputElement.dispatchEvent(new Event('input'));
        }
        this.canBeOpen.set(false);
    }
    // Select a match, either gets called due to a enter keypress or from the component due to a click.
    /** @internal */
    selectMatch(match) {
        match.itemSelected = !match.itemSelected;
        if (!this.typeaheadMultiSelect()) {
            const inputElement = this.elementRef.nativeElement.querySelector('input') ?? this.elementRef.nativeElement;
            inputElement.value = this.typeaheadClearValueOnSelect() ? '' : match.text;
            inputElement.dispatchEvent(new Event('input'));
        }
        // Clear the current input timeout (if set) and remove the typeahead.
        this.clearTimer();
        this.typeaheadOnSelect.emit(match);
        if (!this.typeaheadMultiSelect()) {
            this.canBeOpen.set(false);
        }
    }
    // Remove the component by clearing the viewContainerRef
    removeComponent() {
        if (this.overlayRef?.hasAttached()) {
            this.overlayRef?.detach();
            this.typeaheadOpenChange.emit(false);
        }
        this.componentRef?.destroy();
        this.componentRef = undefined;
    }
    clearTimer() {
        if (this.inputTimer) {
            clearTimeout(this.inputTimer);
            this.inputTimer = undefined;
        }
    }
    escapeRegex(query) {
        return query.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: SiTypeaheadDirective, isStandalone: true, selector: "[siTypeahead]", inputs: { siTypeahead: { classPropertyName: "siTypeahead", publicName: "siTypeahead", isSignal: true, isRequired: true, transformFunction: null }, typeaheadProcess: { classPropertyName: "typeaheadProcess", publicName: "typeaheadProcess", isSignal: true, isRequired: false, transformFunction: null }, typeaheadScrollable: { classPropertyName: "typeaheadScrollable", publicName: "typeaheadScrollable", isSignal: true, isRequired: false, transformFunction: null }, typeaheadOptionsInScrollableView: { classPropertyName: "typeaheadOptionsInScrollableView", publicName: "typeaheadOptionsInScrollableView", isSignal: true, isRequired: false, transformFunction: null }, typeaheadOptionsLimit: { classPropertyName: "typeaheadOptionsLimit", publicName: "typeaheadOptionsLimit", isSignal: true, isRequired: false, transformFunction: null }, typeaheadScrollableAdditionalHeight: { classPropertyName: "typeaheadScrollableAdditionalHeight", publicName: "typeaheadScrollableAdditionalHeight", isSignal: true, isRequired: false, transformFunction: null }, typeaheadAutoSelectIndex: { classPropertyName: "typeaheadAutoSelectIndex", publicName: "typeaheadAutoSelectIndex", isSignal: true, isRequired: false, transformFunction: null }, typeaheadCloseOnEsc: { classPropertyName: "typeaheadCloseOnEsc", publicName: "typeaheadCloseOnEsc", isSignal: true, isRequired: false, transformFunction: null }, typeaheadClearValueOnSelect: { classPropertyName: "typeaheadClearValueOnSelect", publicName: "typeaheadClearValueOnSelect", isSignal: true, isRequired: false, transformFunction: null }, typeaheadWaitMs: { classPropertyName: "typeaheadWaitMs", publicName: "typeaheadWaitMs", isSignal: true, isRequired: false, transformFunction: null }, typeaheadMinLength: { classPropertyName: "typeaheadMinLength", publicName: "typeaheadMinLength", isSignal: true, isRequired: false, transformFunction: null }, typeaheadOptionField: { classPropertyName: "typeaheadOptionField", publicName: "typeaheadOptionField", isSignal: true, isRequired: false, transformFunction: null }, typeaheadMultiSelect: { classPropertyName: "typeaheadMultiSelect", publicName: "typeaheadMultiSelect", isSignal: true, isRequired: false, transformFunction: null }, typeaheadTokenize: { classPropertyName: "typeaheadTokenize", publicName: "typeaheadTokenize", isSignal: true, isRequired: false, transformFunction: null }, typeaheadMatchAllTokens: { classPropertyName: "typeaheadMatchAllTokens", publicName: "typeaheadMatchAllTokens", isSignal: true, isRequired: false, transformFunction: null }, typeaheadItemTemplate: { classPropertyName: "typeaheadItemTemplate", publicName: "typeaheadItemTemplate", isSignal: true, isRequired: false, transformFunction: null }, typeaheadSkipSortingMatches: { classPropertyName: "typeaheadSkipSortingMatches", publicName: "typeaheadSkipSortingMatches", isSignal: true, isRequired: false, transformFunction: null }, typeaheadAutocompleteListLabel: { classPropertyName: "typeaheadAutocompleteListLabel", publicName: "typeaheadAutocompleteListLabel", isSignal: true, isRequired: false, transformFunction: null }, typeaheadFullWidth: { classPropertyName: "typeaheadFullWidth", publicName: "typeaheadFullWidth", isSignal: true, isRequired: false, transformFunction: null }, typeaheadScrollStrategy: { classPropertyName: "typeaheadScrollStrategy", publicName: "typeaheadScrollStrategy", isSignal: true, isRequired: false, transformFunction: null }, typeaheadCreateOption: { classPropertyName: "typeaheadCreateOption", publicName: "typeaheadCreateOption", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { typeaheadOnInput: "typeaheadOnInput", typeaheadOnSelect: "typeaheadOnSelect", typeaheadOnFullMatch: "typeaheadOnFullMatch", typeaheadOpenChange: "typeaheadOpenChange", typeaheadOnCreateOption: "typeaheadOnCreateOption" }, host: { listeners: { "focusout": "onBlur()", "focusin": "onInput($event)", "input": "onInput($event)", "keydown.escape": "onKeydownEscape()" }, classAttribute: "si-typeahead" }, exportAs: ["si-typeahead"], usesOnChanges: true, hostDirectives: [{ directive: i1.SiAutocompleteDirective }], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[siTypeahead]',
                    host: {
                        class: 'si-typeahead'
                    },
                    hostDirectives: [SiAutocompleteDirective],
                    exportAs: 'si-typeahead'
                }]
        }], ctorParameters: () => [], propDecorators: { siTypeahead: [{ type: i0.Input, args: [{ isSignal: true, alias: "siTypeahead", required: true }] }], typeaheadProcess: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadProcess", required: false }] }], typeaheadScrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadScrollable", required: false }] }], typeaheadOptionsInScrollableView: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadOptionsInScrollableView", required: false }] }], typeaheadOptionsLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadOptionsLimit", required: false }] }], typeaheadScrollableAdditionalHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadScrollableAdditionalHeight", required: false }] }], typeaheadAutoSelectIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadAutoSelectIndex", required: false }] }], typeaheadCloseOnEsc: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadCloseOnEsc", required: false }] }], typeaheadClearValueOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadClearValueOnSelect", required: false }] }], typeaheadWaitMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadWaitMs", required: false }] }], typeaheadMinLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadMinLength", required: false }] }], typeaheadOptionField: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadOptionField", required: false }] }], typeaheadMultiSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadMultiSelect", required: false }] }], typeaheadTokenize: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadTokenize", required: false }] }], typeaheadMatchAllTokens: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadMatchAllTokens", required: false }] }], typeaheadItemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadItemTemplate", required: false }] }], typeaheadSkipSortingMatches: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadSkipSortingMatches", required: false }] }], typeaheadAutocompleteListLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadAutocompleteListLabel", required: false }] }], typeaheadFullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadFullWidth", required: false }] }], typeaheadScrollStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadScrollStrategy", required: false }] }], typeaheadCreateOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadCreateOption", required: false }] }], typeaheadOnInput: [{ type: i0.Output, args: ["typeaheadOnInput"] }], typeaheadOnSelect: [{ type: i0.Output, args: ["typeaheadOnSelect"] }], typeaheadOnFullMatch: [{ type: i0.Output, args: ["typeaheadOnFullMatch"] }], typeaheadOpenChange: [{ type: i0.Output, args: ["typeaheadOpenChange"] }], typeaheadOnCreateOption: [{ type: i0.Output, args: ["typeaheadOnCreateOption"] }], onBlur: [{
                type: HostListener,
                args: ['focusout']
            }], onInput: [{
                type: HostListener,
                args: ['focusin', ['$event']]
            }, {
                type: HostListener,
                args: ['input', ['$event']]
            }], onKeydownEscape: [{
                type: HostListener,
                args: ['keydown.escape']
            }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiTypeaheadModule {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadModule, imports: [SiTypeaheadDirective, SiTypeaheadItemTemplateDirective], exports: [SiTypeaheadDirective, SiTypeaheadItemTemplateDirective] });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiTypeaheadModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [SiTypeaheadDirective, SiTypeaheadItemTemplateDirective],
                    exports: [SiTypeaheadDirective, SiTypeaheadItemTemplateDirective]
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */

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

export { SiTypeaheadDirective, SiTypeaheadItemTemplateDirective, SiTypeaheadModule };
//# sourceMappingURL=siemens-element-ng-typeahead.mjs.map
