# Autocomplete

- Entry point: `@koobiq/components/autocomplete`
- NgModule: `KbqAutocompleteModule`
- Page: https://koobiq.io/en/components/autocomplete/overview

## Overview

Autocomplete suggests matching options in a dropdown while the user types and lets them select a value without entering it in full.

**Example: Autocomplete** (`autocomplete-overview`)

```ts autocomplete-overview-example.ts
import { AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { KbqAutocompleteModule } from '@koobiq/components/autocomplete';
import { KbqFormsModule, KbqHighlightBackgroundPipe } from '@koobiq/components/core';
import { KbqInputModule } from '@koobiq/components/input';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
    selector: 'autocomplete-overview-example',
    imports: [
        KbqAutocompleteModule,
        KbqInputModule,
        ReactiveFormsModule,
        AsyncPipe,
        KbqFormsModule,
        KbqHighlightBackgroundPipe
    ],
    template: `
        <div class="kbq-form kbq-form-vertical">
            <div class="kbq-form__row">
                <label class="kbq-form__label">Enter countries to see autocomplete</label>
                <kbq-form-field>
                    <input kbqInput type="text" [formControl]="control" [kbqAutocomplete]="auto" />

                    <kbq-autocomplete #auto="kbqAutocomplete">
                        @for (option of filteredOptions | async; track option) {
                            <kbq-option [value]="option">
                                <span [innerHTML]="option | kbqHighlightBackground: control.value"></span>
                            </kbq-option>
                        }
                    </kbq-autocomplete>

                    <kbq-cleaner />
                </kbq-form-field>
            </div>
        </div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AutocompleteOverviewExample implements OnInit {
    options = [
        'Abkhazia',
        'Australia',
        'Austria',
        'Azerbaijan',
        'Aland Islands',
        'Albania',
        'Algeria',
        'Anguilla',
        'Angola',
        'Andorra',
        'Argentina',
        'Armenia',
        'Aruba',
        'Afghanistan',
        'Bahamas',
        'Bangladesh',
        'Barbados',
        'Bahrain',
        'Belarus',
        'Belize',
        'Belgium',
        'Benin',
        'Bulgaria',
        'Bolivia',
        'Bosnia & Herzegovina',
        'Botswana',
        'Brazil',
        'Brunei Darussalam',
        'Burundi',
        'Bhutan',
        'Vatican City',
        'United Kingdom',
        'Hungary',
        'Venezuela',
        'Timor',
        'Viet Nam',
        'Gabon',
        'Haiti',
        'Gambia',
        'Ghana',
        'Guadeloupe',
        'Guatemala',
        'Guinea',
        'Guinea-Bissau',
        'Germany',
        'Gibraltar',
        'Hong Kong',
        'Honduras',
        'Grenada',
        'Greenland',
        'Greece',
        'Georgia',
        'Guam',
        'Denmark',
        'Dominica',
        'Dominican Republic',
        'Egypt',
        'Zambia',
        'Western Sahara',
        'Zimbabwe',
        'Israel',
        'India',
        'Indonesia',
        'Jordan',
        'Iraq',
        'Iran',
        'Ireland',
        'Iceland',
        'Spain',
        'Italy',
        'Yemen',
        'Kazakhstan',
        'Cambodia',
        'Cameroon',
        'Canada',
        'Qatar',
        'Kenya',
        'Cyprus',
        'Kyrgyzstan',
        'Kiribati',
        'China',
        'Colombia',
        'Korea, D.P.R.',
        'Korea',
        'Costa Rica',
        "Cote d'Ivoire",
        'Cuba',
        'Kuwait',
        'Lao P.D.R.',
        'Latvia',
        'Lesotho',
        'Liberia',
        'Lebanon',
        'Libyan Arab Jamahiriya',
        'Lithuania',
        'Liechtenstein',
        'Luxembourg',
        'Mauritius',
        'Mauritania',
        'Madagascar',
        'Macedonia',
        'Malawi',
        'Malaysia',
        'Mali',
        'Maldives',
        'Malta',
        'Morocco',
        'Mexico',
        'Mozambique',
        'Moldova',
        'Monaco',
        'Mongolia',
        'Namibia',
        'Nepal',
        'Niger',
        'Nigeria',
        'Netherlands',
        'Nicaragua',
        'New Zealand',
        'Norway',
        'United Arab Emirates',
        'Oman',
        'Pakistan',
        'Panama',
        'Paraguay',
        'Peru',
        'Poland',
        'Portugal',
        'Russia',
        'Romania',
        'San Marino',
        'Saudi Arabia',
        'Senegal',
        'Serbia',
        'Singapore',
        'Syrian Arab Republic',
        'Slovakia',
        'Slovenia',
        'Somalia',
        'Sudan',
        'USA',
        'Tajikistan',
        'Thailand',
        'Tanzania',
        'Togo',
        'Tunisia',
        'Turkmenistan',
        'Turkey',
        'Uganda',
        'Uzbekistan',
        'Ukraine',
        'Uruguay',
        'Micronesia',
        'Fiji',
        'Philippines',
        'Finland',
        'France',
        'Croatia',
        'Chad',
        'Montenegro',
        'Czech Republic',
        'Chile',
        'Switzerland',
        'Sweden',
        'Sri Lanka',
        'Ecuador',
        'Eritrea',
        'Estonia',
        'Ethiopia',
        'South Africa',
        'Jamaica',
        'Japan'
    ];

    control = new FormControl('');
    filteredOptions: Observable<string[]>;

    ngOnInit(): void {
        this.filteredOptions = this.control.valueChanges.pipe(
            startWith(''),
            map((value) => this.filter(value as string))
        );
    }

    private filter(value: string): string[] {
        const filterValue = value ? value.toLowerCase() : '';

        return this.options.filter((option) => option.toLowerCase().includes(filterValue));
    }
}
```

### Search

To make **Autocomplete** search for each word in a query independently and ignore case and diacritics, add `createSearchPredicate` to the option filtering — this enables [smart search](https://koobiq.io/en/other/search-smart.md).

**Example: Autocomplete smart search** (`autocomplete-search-smart`)

```ts autocomplete-search-smart-example.ts
import { AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { KbqAutocompleteModule } from '@koobiq/components/autocomplete';
import {
    createSearchPredicate,
    KbqFormsModule,
    KbqHighlightBackgroundPipe,
    tokenizeSearchQuery
} from '@koobiq/components/core';
import { KbqInputModule } from '@koobiq/components/input';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
    selector: 'autocomplete-search-smart-example',
    imports: [
        KbqAutocompleteModule,
        KbqInputModule,
        ReactiveFormsModule,
        AsyncPipe,
        KbqFormsModule,
        KbqHighlightBackgroundPipe
    ],
    template: `
        <div class="kbq-form kbq-form-vertical">
            <div class="kbq-form__row">
                <label class="kbq-form__label">
                    Try
                    <strong>republic african</strong>
                    or
                    <strong>korea south</strong>
                </label>
                <kbq-form-field>
                    <input kbqInput type="text" [formControl]="control" [kbqAutocomplete]="auto" />

                    <kbq-autocomplete #auto="kbqAutocomplete">
                        @for (option of filteredOptions | async; track option) {
                            <kbq-option [value]="option">
                                <span [innerHTML]="option | kbqHighlightBackground: searchTokens"></span>
                            </kbq-option>
                        }
                    </kbq-autocomplete>

                    <kbq-cleaner />
                </kbq-form-field>
            </div>
        </div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AutocompleteSearchSmartExample implements OnInit {
    options = [
        'United Kingdom',
        'United Arab Emirates',
        'United States',
        'South Africa',
        'South Korea',
        'North Korea',
        'Central African Republic',
        'Dominican Republic',
        "Cote d'Ivoire",
        'Costa Rica'
    ];

    control = new FormControl('');
    filteredOptions: Observable<string[]>;
    protected searchTokens: string[] = [];

    ngOnInit(): void {
        this.filteredOptions = this.control.valueChanges.pipe(
            startWith(''),
            map((value) => this.filter(value as string))
        );
    }

    private filter(value: string): string[] {
        this.searchTokens = tokenizeSearchQuery(value ?? '');

        return this.options.filter(createSearchPredicate(value ?? ''));
    }
}
```

### Suggestions next to the caret

`kbqAutocompleteRelativeToCaret` opens the dropdown next to the caret. In wide fields, suggestions appear where the user is likely looking.

### Text autocomplete

`kbqAutocompleteTextMode` inserts the selected option after the caret instead of replacing the field's content. It is useful for multiline fields, slash commands and mentions.

**Example: Autocomplete in text** (`autocomplete-textarea`)

```ts autocomplete-textarea-example.ts
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { KbqAutocompleteModule } from '@koobiq/components/autocomplete';
import { KbqHighlightBackgroundPipe, KbqTextQuery } from '@koobiq/components/core';
import { KbqFormFieldModule } from '@koobiq/components/form-field';
import { KbqTextareaModule } from '@koobiq/components/textarea';

@Component({
    selector: 'autocomplete-textarea-example',
    imports: [
        KbqAutocompleteModule,
        KbqFormFieldModule,
        KbqTextareaModule,
        ReactiveFormsModule,
        KbqHighlightBackgroundPipe
    ],
    template: `
        <kbq-form-field>
            <kbq-label>Incident description</kbq-label>
            <textarea
                kbqTextarea
                placeholder="Type fire, thr or vul"
                [formControl]="control"
                [kbqAutocomplete]="auto"
                [kbqAutocompleteRelativeToCaret]="true"
                [kbqAutocompleteTextMode]="true"
                (kbqAutocompleteQueryChange)="query.set($event)"
            ></textarea>

            <kbq-autocomplete #auto="kbqAutocomplete" [autoActiveFirstOption]="true">
                @for (option of filteredOptions(); track option) {
                    <kbq-option [value]="option">
                        <span [innerHTML]="option | kbqHighlightBackground: query()?.text ?? ''"></span>
                    </kbq-option>
                }
            </kbq-autocomplete>
        </kbq-form-field>
    `,
    styles: `
        kbq-form-field {
            width: 480px;
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-row layout-align-center-center'
    }
})
export class AutocompleteTextareaExample {
    protected readonly control = new FormControl('');
    protected readonly query = signal<KbqTextQuery | null>(null);

    // Prefix matches keep the active option continuing the word, which is what the inline hint needs.
    protected readonly filteredOptions = computed(() => {
        const text = this.query()?.text.toLowerCase();

        return text ? this.options.filter((option) => option.toLowerCase().startsWith(text)) : [];
    });

    private readonly options = [
        'false positive',
        'file integrity',
        'filter rule',
        'fingerprint',
        'firewall',
        'firmware',
        'threat',
        'threat hunting',
        'threat intelligence',
        'token',
        'traffic',
        'trojan',
        'virus',
        'VPN',
        'vulnerability',
        'vulnerability scan'
    ];
}
```

#### Trigger characters

`kbqAutocompleteTriggers` sets the characters and strings that start a query. For example, use `/` for commands and `@` for mentions.

**Example: Autocomplete with triggers** (`autocomplete-triggers`)

```ts autocomplete-triggers-example.ts
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { KbqAutocompleteModule } from '@koobiq/components/autocomplete';
import { KbqTextQuery } from '@koobiq/components/core';
import { KbqFormFieldModule } from '@koobiq/components/form-field';
import { KbqTextareaModule } from '@koobiq/components/textarea';

interface Suggestion {
    /** What the option shows. */
    label: string;
    /** What choosing the option puts into the text in place of the query. */
    insert: string;
}

@Component({
    selector: 'autocomplete-triggers-example',
    imports: [
        KbqAutocompleteModule,
        KbqFormFieldModule,
        KbqTextareaModule,
        ReactiveFormsModule
    ],
    template: `
        <kbq-form-field>
            <kbq-label>Comment</kbq-label>
            <textarea
                kbqTextarea
                placeholder="Type / for a snippet or @ to mention a colleague"
                [formControl]="control"
                [kbqAutocomplete]="auto"
                [kbqAutocompleteRelativeToCaret]="true"
                [kbqAutocompleteTextMode]="true"
                [kbqAutocompleteTriggers]="triggers"
                (kbqAutocompleteQueryChange)="query.set($event)"
            ></textarea>

            <kbq-autocomplete #auto="kbqAutocomplete" [autoActiveFirstOption]="true" [displayWith]="insertText">
                @for (suggestion of filteredSuggestions(); track suggestion.label) {
                    <kbq-option [value]="suggestion">{{ suggestion.label }}</kbq-option>
                }
            </kbq-autocomplete>
        </kbq-form-field>
    `,
    styles: `
        kbq-form-field {
            width: 480px;
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-row layout-align-center-center'
    }
})
export class AutocompleteTriggersExample {
    protected readonly control = new FormControl('');
    protected readonly triggers = ['/', '@'];
    protected readonly query = signal<KbqTextQuery | null>(null);

    protected readonly filteredSuggestions = computed(() => {
        const query = this.query();

        if (!query) return [];

        const suggestions = query.trigger === '@' ? this.colleagues : this.snippets;
        const text = query.text.toLowerCase();

        return suggestions.filter(({ insert, label }) =>
            (query.trigger === '@' ? insert.slice(1) : label.slice(1)).toLowerCase().startsWith(text)
        );
    });

    protected readonly insertText = (suggestion: Suggestion): string => suggestion.insert;

    private readonly snippets: Suggestion[] = [
        { label: '/closed — closing note', insert: 'Closed as a false positive.' },
        { label: '/escalate — escalation note', insert: 'Escalated to the second line.' },
        { label: '/sign — signature', insert: 'Best regards, SOC team' }
    ];

    private readonly colleagues: Suggestion[] = [
        { label: 'Alice Johnson', insert: '@alice' },
        { label: 'Bob Smith', insert: '@bob' },
        { label: 'Carol White', insert: '@carol' }
    ];
}
```

#### Inline hint

Faded text after the caret shows how the input will be completed. With `autoActiveFirstOption` disabled, the hint appears after the user starts navigating the list.

### Panel width

By default the panel sizes to its content and is never narrower than the field or than `panelMinWidth`, which is 200px. This can be changed with the `panelWidth` and `panelMinWidth` attributes:

```html
<!-- Match the field, but never go below panelMinWidth -->
<kbq-autocomplete panelWidth="auto" />

<!-- An exact width; panelMinWidth is not applied -->
<kbq-autocomplete [panelWidth]="400" />

<!-- Any CSS value works, e.g. shrink to fit the options -->
<kbq-autocomplete panelWidth="fit-content" />

<!-- Widen the default lower bound -->
<kbq-autocomplete [panelMinWidth]="350" />

<!-- Let the panel grow further with its content; the default cap is 640px -->
<kbq-autocomplete [panelMaxWidth]="800" />
```

Growth by content stops at 640px. The cap is soft — it never makes the panel narrower than the field, and never clamps an explicit `panelWidth`. Change it globally through the `--kbq-panel-size-width-max` token, which is shared with select, tree-select and dropdown.

### Footer

You can place auxiliary elements in the footer: [buttons](https://koobiq.io/en/components/button.md), [links](https://koobiq.io/en/components/link.md), hints.

**Example: Autocomplete with footer** (`autocomplete-with-footer`)

```ts autocomplete-with-footer-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { KbqAutocompleteModule } from '@koobiq/components/autocomplete';
import { KbqHighlightBackgroundPipe } from '@koobiq/components/core';
import { KbqInputModule } from '@koobiq/components/input';
import { KbqLink } from '@koobiq/components/link';
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';

@Component({
    selector: 'autocomplete-with-footer-example',
    imports: [
        KbqAutocompleteModule,
        KbqInputModule,
        ReactiveFormsModule,
        KbqLink,
        KbqHighlightBackgroundPipe
    ],
    template: `
        <kbq-form-field>
            <kbq-label>Label</kbq-label>
            <kbq-hint>Autocomplete with footer</kbq-hint>
            <input
                #trigger="kbqAutocompleteTrigger"
                kbqInput
                [formControl]="control"
                [kbqAutocomplete]="autocomplete"
            />
            <kbq-autocomplete #autocomplete="kbqAutocomplete">
                @for (option of filteredOptions(); track option) {
                    <kbq-option [value]="option">
                        <span [innerHTML]="option | kbqHighlightBackground: control.value"></span>
                    </kbq-option>
                }
                <kbq-autocomplete-footer>
                    <a kbqLink external (click)="trigger.closePanel()">Link</a>
                </kbq-autocomplete-footer>
            </kbq-autocomplete>
            <kbq-cleaner />
        </kbq-form-field>
    `,
    styles: `
        :host {
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .kbq-form-field {
            width: 320px;
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AutocompleteWithFooterExample {
    readonly options = Array.from({ length: 100 }, (_, i) => `Option ${i + 1}`);
    readonly control = new FormControl('');
    readonly filteredOptions = toSignal(
        this.control.valueChanges.pipe(
            debounceTime(300),
            distinctUntilChanged(),
            map((value) => this.options.filter((option) => option.toLowerCase().includes((value ?? '').toLowerCase())))
        ),
        { initialValue: this.options }
    );
}
```

### Keyboard navigation

| Key           | Action                                                                                                                                                            |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ↓             | Opens the panel. If the panel is open, moves focus to the next enabled option.                                                                                    |
| ↑             | Moves focus to the previous enabled option                                                                                                                        |
| →             | Accepts the suggestion in inline hint mode, closes the panel, and leaves focus in the field                                                                       |
| Enter         | If an option is active, selects it, closes the panel, and leaves focus in the field. In text mode, inserts the selected option at the caret without a line break. |
| Esc / Alt + ↑ | Closes the panel and leaves focus in the field                                                                                                                    |
| Tab           | Closes the panel and moves focus to the next element. In inline hint mode, inserts the suggestion, closes the panel, and leaves focus in the field.               |

## API

### Components

#### KbqAutocomplete (component)

```ts
@Component({ selector: 'kbq-autocomplete', exportAs: 'kbqAutocomplete' })
class KbqAutocomplete implements KbqOptionParentComponent {
    readonly displayWith = input<((value: any) => string) | null>(null);
    readonly panelWidth = input<KbqPanelWidth | undefined>();
    readonly panelMinWidth = input<number>(KBQ_PANEL_DEFAULT_MIN_WIDTH);
    readonly panelMaxWidth = input<KbqPanelMaxWidth>(null);
    readonly hostClass = input<string>('', { alias: 'class' });
    readonly autoActiveFirstOption = input<boolean>(
        !!this.defaultOptions.autoActiveFirstOption
    );
    readonly openOnFocus = input<boolean>(true);
    readonly optionSelected = output<KbqAutocompleteSelectedEvent>();
    readonly opened = output<void>();
    readonly closed = output<void>();
    readonly id: string;
    readonly template: Signal<TemplateRef<any>>;
    readonly panel: Signal<ElementRef<any>>;
    get options(): QueryList<KbqOption>;
    readonly optionGroups: Signal<readonly KbqOptgroup[]>;
    readonly isOpen: Signal<boolean>;
}
```

- `[displayWith]`: `((value: any) => string) | null` — Function that maps an option's control value to its display value in the trigger.
- `[panelWidth]`: `KbqPanelWidth | undefined` — Specify the width of the autocomplete panel. If set to `auto`, the panel will match the width of
  its host, but will never be narrower than `panelMinWidth`. Any other CSS sizing value is used as an
  exact width, and `panelMinWidth` is not applied. When not set, the panel sizes to its content and is
  at least as wide as its host.
- `[panelMinWidth]`: `number` — Minimum width of the panel in pixels.
  When panelWidth is not set, the panel will be at least as wide as its host and no less than panelMinWidth.
- `[panelMaxWidth]`: `KbqPanelMaxWidth` — Maximum width of the panel in pixels. Caps how far the panel grows with its content — it never makes
  the panel narrower than its host, and never clamps an explicit `panelWidth`.
  When null, the `--kbq-panel-size-width-max` token applies.
- `[class]`: `string` — Classes set on the host `kbq-autocomplete` element. They are applied to the panel inside the
  overlay container instead, to allow for easy styling.

  Accepts every shape Angular's own `[class]` binding does. The binding value arrives here raw:
  Angular converts it to a string before handing it to a styling-shadowing input only when the element
  carries a static `class` attribute, which `<kbq-autocomplete>` does not.
- `[autoActiveFirstOption]`: `boolean` — Whether the first option should be highlighted when the autocomplete panel is opened.
  Can be configured globally through the `KBQ_AUTOCOMPLETE_DEFAULT_OPTIONS` token.
- `[openOnFocus]`: `boolean` — Whether the panel opens as soon as the trigger receives focus.
- `(optionSelected)`: `KbqAutocompleteSelectedEvent` — Event that is emitted whenever an option from the list is selected.
- `(opened)`: `void` — Event that is emitted when the autocomplete panel is opened.
- `(closed)`: `void` — Event that is emitted when the autocomplete panel is closed.
- `id`: `string` — Unique id, rendered as the panel element's `id`.
- `isOpen`: `Signal<boolean>` — Whether the panel is visible: attached by the trigger and holding at least one option.

### Directives

#### KbqAutocompleteFooter (directive)

Footer that is rendered below the autocomplete options panel.

```ts
@Directive({ selector: '[kbqAutocompleteFooter], kbq-autocomplete-footer' })
class KbqAutocompleteFooter {}
```

#### KbqAutocompleteOrigin (directive)

Directive applied to an element to make it usable
as a connection point for an autocomplete panel.

```ts
@Directive({
    selector: '[kbqAutocompleteOrigin]',
    exportAs: 'kbqAutocompleteOrigin'
})
class KbqAutocompleteOrigin {
    elementRef: ElementRef<HTMLElement>;
}
```

#### KbqAutocompleteTrigger (directive)

```ts
@Directive({
    selector: 'input[kbqAutocomplete], textarea[kbqAutocomplete]',
    exportAs: 'kbqAutocompleteTrigger'
})
class KbqAutocompleteTrigger implements ControlValueAccessor, KeyboardNavigationHandler, KbqSiblingPopup {
    readonly autocomplete = input<KbqAutocomplete>(undefined, {
        alias: 'kbqAutocomplete'
    });
    readonly connectedTo = input<KbqAutocompleteOrigin>(undefined, {
        alias: 'kbqAutocompleteConnectedTo'
    });
    readonly autocompleteDisabled = input<boolean>(false, {
        alias: 'kbqAutocompleteDisabled'
    });
    readonly onInputBlur = input<(event: FocusEvent) => boolean>(
        (event: FocusEvent): boolean => {
            const target: HTMLElement = event.relatedTarget as HTMLElement;

            return !target || target.tagName !== 'KBQ-OPTION';
        },
        { alias: 'kbqAutocompleteOnBlur' }
    );
    readonly relativeToCaret = input<boolean>(false, {
        alias: 'kbqAutocompleteRelativeToCaret'
    });
    readonly textMode = input<boolean>(false, {
        alias: 'kbqAutocompleteTextMode'
    });
    readonly queryTriggers = input<readonly string[]>([], {
        alias: 'kbqAutocompleteTriggers'
    });
    readonly queryMinLength = input<number | undefined>(undefined, {
        alias: 'kbqAutocompleteMinLength'
    });
    readonly inlineHint = input<boolean>(true, {
        alias: 'kbqAutocompleteInlineHint'
    });
    readonly queryChange = output<KbqTextQuery | null>({
        alias: 'kbqAutocompleteQueryChange'
    });
    get activeOption(): KbqOption | null;
    get panelOpen(): boolean;
    get isAttached(): boolean;
    readonly openedChange: Observable<boolean>;
    readonly optionSelections: Observable<KbqOptionSelectionChange>;
    get panelClosingActions(): Observable<KbqOptionSelectionChange | null>;
    open(): void;
    closePanel(): void;
    updatePosition(): void;
    handleKeydown(event: KeyboardEvent): void;
    handleInput(event: Event): void;
    handleFocus(): void;
    handleClick($event: Event): void;
    scrollActiveOptionIntoView(): void;
}
```

- `[kbqAutocomplete]`: `KbqAutocomplete` — The autocomplete panel to be attached to this trigger.
- `[kbqAutocompleteConnectedTo]`: `KbqAutocompleteOrigin` — Reference relative to which to position the autocomplete panel.
  Defaults to the autocomplete trigger element.
- `[kbqAutocompleteDisabled]`: `boolean` — Whether the autocomplete is disabled. When disabled, the element will
  act as a regular input and the user won't be able to open the panel.
- `[kbqAutocompleteOnBlur]`: `(event: FocusEvent) => boolean` — Event handler for input blur events.
  Determines whether the blur event is triggered outside the specific target
- `[kbqAutocompleteRelativeToCaret]`: `boolean` — Whether the panel opens from the text caret instead of the field: its left edge at the caret, right below the
  caret's line, following the caret while it moves. The panel is only as wide as its options — `panelMinWidth`
  does not apply there, an explicit `panelWidth` still does.
- `[kbqAutocompleteTextMode]`: `boolean` — Whether the autocomplete completes the text at the caret instead of the whole value. The query is the word
  before the caret, or the text after one of `kbqAutocompleteTriggers`; choosing an option replaces the query,
  trigger included, with the option. The form value stays the whole text.
- `[kbqAutocompleteTriggers]`: `readonly string[]` — Strings that start a query in text mode, such as `/` or `@`. When empty, the query is the word before the caret.
- `[kbqAutocompleteMinLength]`: `number | undefined` — Shortest query that opens the panel in text mode. Defaults to `1` for a word and to `0` after a trigger.
- `[kbqAutocompleteInlineHint]`: `boolean` — Whether text mode draws the rest of the active option after the caret, to be accepted with `Tab` or `→`. The
  field has to be inside `kbq-form-field`.
- `(kbqAutocompleteQueryChange)`: `KbqTextQuery | null` — Emits the query at the caret in text mode whenever it changes, or `null` once there is none. The query is read
  again as the user types or clicks in the field, and as the caret moves while the panel is open. It carries the
  trigger it starts with, which tells apart the options to show for each trigger.
- `activeOption`: `KbqOption | null` — The currently active option, coerced to MatOption type.
- `isAttached`: `boolean` — Whether the autocomplete panel is currently on screen. Part of the `KbqSiblingPopup` contract.
- `openedChange`: `Observable<boolean>` — Emits `true` when the panel opens and `false` when it closes. Part of the `KbqSiblingPopup`
  contract.

  Built on top of the `autocomplete` signal rather than read once: the panel is bound after the consumers
  of this stream are created (a tooltip on the same element subscribes in its constructor).
- `panelClosingActions`: `Observable<KbqOptionSelectionChange | null>` — A stream of actions that should close the autocomplete panel, including
  when an option is selected, on blur, and when TAB is pressed.
- `open()`: `void` — Opens the autocomplete suggestion panel.
- `updatePosition()`: `void` — Updates the position of the autocomplete suggestion panel to ensure that it fits all options
  within the viewport.

### Classes

#### KbqAutocompleteSelectedEvent (class)

```ts
class KbqAutocompleteSelectedEvent {
    source: KbqAutocomplete;
    option: KbqOption;
    constructor(source: KbqAutocomplete, option: KbqOption);
}
```

### Functions

#### KBQ_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY (function)

```ts
function KBQ_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY(): KbqAutocompleteDefaultOptions;
```

### Types

#### KbqAutocompleteDefaultOptions (interface)

Default `kbq-autocomplete` options that can be overridden.

```ts
interface KbqAutocompleteDefaultOptions {
    autoActiveFirstOption?: boolean;
}
```

- `autoActiveFirstOption?`: `boolean` — Whether the first option should be highlighted when an autocomplete panel is opened.

### Constants

#### KBQ_AUTOCOMPLETE_DEFAULT_OPTIONS (const)

Injection token to be used to override the default options for `kbq-autocomplete`.

```ts
const KBQ_AUTOCOMPLETE_DEFAULT_OPTIONS: InjectionToken<KbqAutocompleteDefaultOptions>;
```

#### KBQ_AUTOCOMPLETE_SCROLL_STRATEGY (const)

Injection token that determines the scroll handling while the autocomplete panel is open. The root default
keeps the trigger usable outside `KbqAutocompleteModule`'s injector; providing the token anywhere still wins
over it.

```ts
const KBQ_AUTOCOMPLETE_SCROLL_STRATEGY: InjectionToken<() => ScrollStrategy>;
```
