@neuravision/ng-construct 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7790,6 +7790,681 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
7790
7790
  `, styles: [":host{display:block}\n"] }]
7791
7791
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], comboboxId: [{ type: i0.Input, args: [{ isSignal: true, alias: "comboboxId", required: false }] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], listboxRef: [{ type: i0.ViewChild, args: ['listboxEl', { isSignal: true }] }] } });
7792
7792
 
7793
+ /**
7794
+ * Marks an `<ng-template>` as the custom row renderer for
7795
+ * {@link AfAutocompleteComponent}. Without it the component falls back to
7796
+ * rendering `label` + `description`.
7797
+ *
7798
+ * @example
7799
+ * <af-autocomplete [options]="opts">
7800
+ * <ng-template afAutocompleteOption let-option="option">
7801
+ * <af-avatar [name]="option.label" />
7802
+ * {{ option.label }}
7803
+ * </ng-template>
7804
+ * </af-autocomplete>
7805
+ */
7806
+ class AfAutocompleteOptionDirective {
7807
+ template = inject(TemplateRef);
7808
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfAutocompleteOptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
7809
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: AfAutocompleteOptionDirective, isStandalone: true, selector: "ng-template[afAutocompleteOption]", ngImport: i0 });
7810
+ }
7811
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfAutocompleteOptionDirective, decorators: [{
7812
+ type: Directive,
7813
+ args: [{ selector: 'ng-template[afAutocompleteOption]' }]
7814
+ }] });
7815
+ /**
7816
+ * Accessible async / remote autocomplete (typeahead) following the WAI-ARIA 1.2
7817
+ * combobox-with-listbox pattern.
7818
+ *
7819
+ * Unlike {@link AfComboboxComponent} — which owns a static option list and
7820
+ * filters it by label substring — this component is **external-filter only**:
7821
+ * the consumer owns fetching and filtering, then feeds the resolved results in
7822
+ * via `options` (and toggles `loading`). That makes it suitable for remote
7823
+ * search across multiple sources, where a hit may have matched on a field that
7824
+ * is not part of its visible label (e.g. a user matched by e-mail).
7825
+ *
7826
+ * It emits a selection **event** rather than binding a value, so the same box
7827
+ * can drive an action (apply a filter, navigate) while keeping the free text.
7828
+ *
7829
+ * Features: option groups with headings, a loading row, an empty row, rich
7830
+ * rows via {@link AfAutocompleteOptionDirective}, full keyboard support
7831
+ * (Arrow/Home/End/Enter/Escape/Tab) and screen-reader announcements.
7832
+ *
7833
+ * @example
7834
+ * <af-autocomplete
7835
+ * label="Search"
7836
+ * [(query)]="query"
7837
+ * [options]="results()"
7838
+ * [loading]="loading()"
7839
+ * [minChars]="2"
7840
+ * clearQueryOnSelect
7841
+ * (optionSelected)="apply($event)"
7842
+ * >
7843
+ * <ng-template afAutocompleteOption let-option="option">…</ng-template>
7844
+ * </af-autocomplete>
7845
+ */
7846
+ class AfAutocompleteComponent {
7847
+ static nextId = 0;
7848
+ /** Field label rendered above the input. */
7849
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
7850
+ /** Placeholder text for the input. */
7851
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
7852
+ /** Hint text shown below the input (hidden while {@link error} is set). */
7853
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
7854
+ /** Error message — shows the error state and message. */
7855
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
7856
+ /** Whether the field is required (renders the `*` indicator). */
7857
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : {}), transform: booleanAttribute });
7858
+ /** Whether the input is disabled. */
7859
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
7860
+ /** Size variant. */
7861
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
7862
+ /**
7863
+ * The suggestions to display, already filtered and ordered by the consumer.
7864
+ * The component does not filter them.
7865
+ */
7866
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
7867
+ /** Shows the loading row / spinner while a fetch is in flight. */
7868
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : {}), transform: booleanAttribute });
7869
+ /** Minimum trimmed query length before the listbox opens. */
7870
+ minChars = input(1, ...(ngDevMode ? [{ debugName: "minChars" }] : []));
7871
+ /** Text shown when a query is present but no options match. */
7872
+ emptyText = input('No results found', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
7873
+ /** Text shown in the loading row. */
7874
+ loadingText = input('Searching…', ...(ngDevMode ? [{ debugName: "loadingText" }] : []));
7875
+ /** Whether re-focusing the input re-opens the listbox. */
7876
+ openOnFocus = input(true, { ...(ngDevMode ? { debugName: "openOnFocus" } : {}), transform: booleanAttribute });
7877
+ /**
7878
+ * When true the panel stays closed while there are no options and no fetch
7879
+ * is in flight — instead of showing the empty row. Useful when the same box
7880
+ * also drives a side effect (e.g. a live text filter) so an empty suggestion
7881
+ * list should be silent rather than intrusive.
7882
+ */
7883
+ hideOnEmpty = input(false, { ...(ngDevMode ? { debugName: "hideOnEmpty" } : {}), transform: booleanAttribute });
7884
+ /** When true the query text is cleared after a selection (action-style use). */
7885
+ clearQueryOnSelect = input(false, { ...(ngDevMode ? { debugName: "clearQueryOnSelect" } : {}), transform: booleanAttribute });
7886
+ /** Render the leading search icon. */
7887
+ showSearchIcon = input(true, { ...(ngDevMode ? { debugName: "showSearchIcon" } : {}), transform: booleanAttribute });
7888
+ /** Render the trailing clear (×) button when the query is non-empty. */
7889
+ showClear = input(true, { ...(ngDevMode ? { debugName: "showClear" } : {}), transform: booleanAttribute });
7890
+ /** Accessible label for the clear button. */
7891
+ clearAriaLabel = input('Clear search', ...(ngDevMode ? [{ debugName: "clearAriaLabel" }] : []));
7892
+ /** Maps a group key to its display heading; falls back to the key itself. */
7893
+ groupLabels = input({}, ...(ngDevMode ? [{ debugName: "groupLabels" }] : []));
7894
+ /** Explicit group ordering by key; unlisted keys keep first-seen order. */
7895
+ groupOrder = input([], ...(ngDevMode ? [{ debugName: "groupOrder" }] : []));
7896
+ /** Unique base id for the field's `id`/`aria` wiring. */
7897
+ autocompleteId = input(`af-autocomplete-${AfAutocompleteComponent.nextId++}`, ...(ngDevMode ? [{ debugName: "autocompleteId" }] : []));
7898
+ /** Two-way bound query text. The consumer debounces this and feeds `options`. */
7899
+ query = model('', ...(ngDevMode ? [{ debugName: "query" }] : []));
7900
+ /** Emitted when the user selects an enabled option. */
7901
+ optionSelected = output();
7902
+ optionTemplate = contentChild(AfAutocompleteOptionDirective, ...(ngDevMode ? [{ debugName: "optionTemplate" }] : []));
7903
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : []));
7904
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
7905
+ highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : []));
7906
+ get inputId() {
7907
+ return `${this.autocompleteId()}-input`;
7908
+ }
7909
+ get listboxId() {
7910
+ return `${this.autocompleteId()}-listbox`;
7911
+ }
7912
+ get hintId() {
7913
+ return `${this.autocompleteId()}-hint`;
7914
+ }
7915
+ get errorId() {
7916
+ return `${this.autocompleteId()}-error`;
7917
+ }
7918
+ rootClasses = computed(() => {
7919
+ const classes = ['ct-autocomplete'];
7920
+ const s = this.size();
7921
+ if (s === 'sm')
7922
+ classes.push('ct-autocomplete--sm');
7923
+ if (s === 'lg')
7924
+ classes.push('ct-autocomplete--lg');
7925
+ return classes.join(' ');
7926
+ }, ...(ngDevMode ? [{ debugName: "rootClasses" }] : []));
7927
+ /** True once the trimmed query reaches {@link minChars}. */
7928
+ hasMinChars = computed(() => this.query().trim().length >= this.minChars(), ...(ngDevMode ? [{ debugName: "hasMinChars" }] : []));
7929
+ /** Flat, display-ordered options used for keyboard navigation. */
7930
+ flatOptions = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flatOptions" }] : []));
7931
+ /** Options grouped for rendering, each paired with its flat index. */
7932
+ displayGroups = computed(() => {
7933
+ const order = this.groupOrder();
7934
+ const labels = this.groupLabels();
7935
+ const groups = new Map();
7936
+ this.options().forEach((option, flatIndex) => {
7937
+ const key = option.group ?? '';
7938
+ let group = groups.get(key);
7939
+ if (!group) {
7940
+ group = { key, label: key ? (labels[key] ?? key) : undefined, items: [] };
7941
+ groups.set(key, group);
7942
+ }
7943
+ group.items.push({ option, flatIndex });
7944
+ });
7945
+ const result = [...groups.values()];
7946
+ if (order.length > 0) {
7947
+ result.sort((a, b) => {
7948
+ const ia = order.indexOf(a.key);
7949
+ const ib = order.indexOf(b.key);
7950
+ return (ia === -1 ? order.length : ia) - (ib === -1 ? order.length : ib);
7951
+ });
7952
+ }
7953
+ return result;
7954
+ }, ...(ngDevMode ? [{ debugName: "displayGroups" }] : []));
7955
+ /** Whether the listbox is visually open. */
7956
+ panelOpen = computed(() => {
7957
+ if (!this.isOpen() || !this.hasMinChars())
7958
+ return false;
7959
+ if (this.hideOnEmpty() && !this.loading() && this.flatOptions().length === 0)
7960
+ return false;
7961
+ return true;
7962
+ }, ...(ngDevMode ? [{ debugName: "panelOpen" }] : []));
7963
+ /** Whether to render the "no results" row. */
7964
+ showEmpty = computed(() => !this.loading() && this.flatOptions().length === 0 && this.hasMinChars(), ...(ngDevMode ? [{ debugName: "showEmpty" }] : []));
7965
+ activeDescendantId = computed(() => {
7966
+ const idx = this.highlightedIndex();
7967
+ if (!this.panelOpen() || idx < 0 || idx >= this.flatOptions().length)
7968
+ return null;
7969
+ return this.optionDomId(idx);
7970
+ }, ...(ngDevMode ? [{ debugName: "activeDescendantId" }] : []));
7971
+ statusMessage = computed(() => {
7972
+ if (!this.panelOpen())
7973
+ return '';
7974
+ if (this.loading())
7975
+ return this.loadingText();
7976
+ const count = this.flatOptions().length;
7977
+ if (count === 0)
7978
+ return this.emptyText();
7979
+ return `${count} result${count === 1 ? '' : 's'} available`;
7980
+ }, ...(ngDevMode ? [{ debugName: "statusMessage" }] : []));
7981
+ ariaDescribedBy() {
7982
+ if (this.error())
7983
+ return this.errorId;
7984
+ if (this.hint())
7985
+ return this.hintId;
7986
+ return null;
7987
+ }
7988
+ optionDomId(flatIndex) {
7989
+ return `${this.autocompleteId()}-option-${flatIndex}`;
7990
+ }
7991
+ groupLabelId(groupIndex) {
7992
+ return `${this.autocompleteId()}-group-${groupIndex}`;
7993
+ }
7994
+ onInput(event) {
7995
+ const value = event.target.value;
7996
+ this.query.set(value);
7997
+ // Manual-selection pattern (WAI-ARIA APG): keep nothing highlighted until
7998
+ // the user navigates, so a free-text Enter never hijacks into a selection.
7999
+ this.highlightedIndex.set(-1);
8000
+ this.isOpen.set(value.trim().length >= this.minChars());
8001
+ }
8002
+ onFocus() {
8003
+ if (this.openOnFocus() && this.hasMinChars()) {
8004
+ this.isOpen.set(true);
8005
+ }
8006
+ }
8007
+ /** Selects an enabled option: emits it, then clears or restores the text. */
8008
+ selectOption(option) {
8009
+ if (option.disabled)
8010
+ return;
8011
+ this.optionSelected.emit(option);
8012
+ this.query.set(this.clearQueryOnSelect() ? '' : option.label);
8013
+ this.close();
8014
+ this.inputRef()?.nativeElement.focus();
8015
+ }
8016
+ /** Clears the query text and closes the listbox. */
8017
+ clear() {
8018
+ this.query.set('');
8019
+ this.close();
8020
+ this.inputRef()?.nativeElement.focus();
8021
+ }
8022
+ /** Closes the listbox without changing the query. */
8023
+ close() {
8024
+ this.isOpen.set(false);
8025
+ this.highlightedIndex.set(-1);
8026
+ }
8027
+ onKeydown(event) {
8028
+ const options = this.flatOptions();
8029
+ switch (event.key) {
8030
+ case 'ArrowDown':
8031
+ event.preventDefault();
8032
+ if (!this.panelOpen()) {
8033
+ this.isOpen.set(true);
8034
+ this.highlightedIndex.set(this.firstEnabledIndex());
8035
+ }
8036
+ else {
8037
+ this.moveHighlight(1);
8038
+ }
8039
+ break;
8040
+ case 'ArrowUp':
8041
+ event.preventDefault();
8042
+ if (!this.panelOpen()) {
8043
+ this.isOpen.set(true);
8044
+ this.highlightedIndex.set(this.lastEnabledIndex());
8045
+ }
8046
+ else {
8047
+ this.moveHighlight(-1);
8048
+ }
8049
+ break;
8050
+ case 'Enter': {
8051
+ const idx = this.highlightedIndex();
8052
+ if (this.panelOpen() && idx >= 0 && idx < options.length && !options[idx].disabled) {
8053
+ event.preventDefault();
8054
+ this.selectOption(options[idx]);
8055
+ }
8056
+ break;
8057
+ }
8058
+ case 'Escape':
8059
+ if (this.panelOpen()) {
8060
+ event.preventDefault();
8061
+ this.close();
8062
+ }
8063
+ break;
8064
+ case 'Home':
8065
+ if (this.panelOpen()) {
8066
+ event.preventDefault();
8067
+ this.highlightedIndex.set(this.firstEnabledIndex());
8068
+ this.scrollHighlightedIntoView();
8069
+ }
8070
+ break;
8071
+ case 'End':
8072
+ if (this.panelOpen()) {
8073
+ event.preventDefault();
8074
+ this.highlightedIndex.set(this.lastEnabledIndex());
8075
+ this.scrollHighlightedIntoView();
8076
+ }
8077
+ break;
8078
+ case 'Tab':
8079
+ if (this.panelOpen()) {
8080
+ const idx = this.highlightedIndex();
8081
+ if (idx >= 0 && idx < options.length && !options[idx].disabled) {
8082
+ this.selectOption(options[idx]);
8083
+ }
8084
+ this.close();
8085
+ }
8086
+ break;
8087
+ }
8088
+ }
8089
+ onDocumentClick(event) {
8090
+ const host = this.inputRef()?.nativeElement.closest('.ct-autocomplete');
8091
+ if (host && !host.contains(event.target)) {
8092
+ this.close();
8093
+ }
8094
+ }
8095
+ moveHighlight(direction) {
8096
+ const options = this.flatOptions();
8097
+ if (options.length === 0)
8098
+ return;
8099
+ let idx = this.highlightedIndex();
8100
+ let attempts = 0;
8101
+ do {
8102
+ idx += direction;
8103
+ if (idx < 0)
8104
+ idx = options.length - 1;
8105
+ if (idx >= options.length)
8106
+ idx = 0;
8107
+ attempts++;
8108
+ } while (options[idx]?.disabled && attempts <= options.length);
8109
+ if (!options[idx]?.disabled) {
8110
+ this.highlightedIndex.set(idx);
8111
+ this.scrollHighlightedIntoView();
8112
+ }
8113
+ }
8114
+ firstEnabledIndex() {
8115
+ return this.flatOptions().findIndex((o) => !o.disabled);
8116
+ }
8117
+ lastEnabledIndex() {
8118
+ const options = this.flatOptions();
8119
+ for (let i = options.length - 1; i >= 0; i--) {
8120
+ if (!options[i].disabled)
8121
+ return i;
8122
+ }
8123
+ return -1;
8124
+ }
8125
+ scrollHighlightedIntoView() {
8126
+ queueMicrotask(() => {
8127
+ const host = this.inputRef()?.nativeElement.closest('.ct-autocomplete');
8128
+ const optionEl = host?.querySelector('[data-highlighted]');
8129
+ optionEl?.scrollIntoView?.({ block: 'nearest' });
8130
+ });
8131
+ }
8132
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfAutocompleteComponent, isStandalone: true, selector: "af-autocomplete", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, openOnFocus: { classPropertyName: "openOnFocus", publicName: "openOnFocus", isSignal: true, isRequired: false, transformFunction: null }, hideOnEmpty: { classPropertyName: "hideOnEmpty", publicName: "hideOnEmpty", isSignal: true, isRequired: false, transformFunction: null }, clearQueryOnSelect: { classPropertyName: "clearQueryOnSelect", publicName: "clearQueryOnSelect", isSignal: true, isRequired: false, transformFunction: null }, showSearchIcon: { classPropertyName: "showSearchIcon", publicName: "showSearchIcon", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, groupLabels: { classPropertyName: "groupLabels", publicName: "groupLabels", isSignal: true, isRequired: false, transformFunction: null }, groupOrder: { classPropertyName: "groupOrder", publicName: "groupOrder", isSignal: true, isRequired: false, transformFunction: null }, autocompleteId: { classPropertyName: "autocompleteId", publicName: "autocompleteId", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", query: "queryChange", optionSelected: "optionSelected" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, queries: [{ propertyName: "optionTemplate", first: true, predicate: AfAutocompleteOptionDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
8134
+ <div class="ct-field" [class.ct-field--error]="error()">
8135
+ @if (label()) {
8136
+ <label class="ct-field__label" [attr.for]="inputId">
8137
+ {{ label() }}
8138
+ @if (required()) {
8139
+ <span aria-label="required"> *</span>
8140
+ }
8141
+ </label>
8142
+ }
8143
+
8144
+ <div [class]="rootClasses()" [attr.data-state]="panelOpen() ? 'open' : 'closed'">
8145
+ <div class="ct-autocomplete__input-wrap">
8146
+ @if (showSearchIcon()) {
8147
+ <span class="ct-autocomplete__leading" aria-hidden="true">
8148
+ <svg viewBox="0 0 16 16" fill="none">
8149
+ <circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.5" />
8150
+ <path
8151
+ d="M10.5 10.5L14 14"
8152
+ stroke="currentColor"
8153
+ stroke-width="1.5"
8154
+ stroke-linecap="round"
8155
+ />
8156
+ </svg>
8157
+ </span>
8158
+ }
8159
+ <input
8160
+ #inputEl
8161
+ class="ct-autocomplete__input"
8162
+ [class.ct-autocomplete__input--with-leading]="showSearchIcon()"
8163
+ [id]="inputId"
8164
+ type="text"
8165
+ role="combobox"
8166
+ autocomplete="off"
8167
+ autocapitalize="off"
8168
+ spellcheck="false"
8169
+ [attr.aria-expanded]="panelOpen()"
8170
+ [attr.aria-controls]="listboxId"
8171
+ aria-autocomplete="list"
8172
+ [attr.aria-activedescendant]="activeDescendantId()"
8173
+ [attr.aria-invalid]="error() ? true : null"
8174
+ [attr.aria-describedby]="ariaDescribedBy()"
8175
+ [attr.aria-busy]="loading() || null"
8176
+ [attr.aria-required]="required() || null"
8177
+ [placeholder]="placeholder()"
8178
+ [disabled]="disabled()"
8179
+ [value]="query()"
8180
+ (input)="onInput($event)"
8181
+ (focus)="onFocus()"
8182
+ (keydown)="onKeydown($event)"
8183
+ />
8184
+
8185
+ @if (loading()) {
8186
+ <span class="ct-autocomplete__spinner" aria-hidden="true"></span>
8187
+ } @else if (showClear() && query()) {
8188
+ <button
8189
+ type="button"
8190
+ class="ct-autocomplete__clear"
8191
+ [attr.aria-label]="clearAriaLabel()"
8192
+ (click)="clear()"
8193
+ (mousedown)="$event.preventDefault()"
8194
+ >
8195
+ <svg viewBox="0 0 16 16" fill="none" aria-hidden="true">
8196
+ <path
8197
+ d="M4 4l8 8M12 4l-8 8"
8198
+ stroke="currentColor"
8199
+ stroke-width="1.5"
8200
+ stroke-linecap="round"
8201
+ />
8202
+ </svg>
8203
+ </button>
8204
+ }
8205
+ </div>
8206
+
8207
+ <div
8208
+ #listboxEl
8209
+ [id]="listboxId"
8210
+ class="ct-autocomplete__listbox"
8211
+ role="listbox"
8212
+ [attr.aria-label]="label() || placeholder() || 'Suggestions'"
8213
+ [attr.aria-busy]="loading() || null"
8214
+ >
8215
+ @if (panelOpen()) {
8216
+ @for (group of displayGroups(); track group.key; let gi = $index) {
8217
+ @if (group.label) {
8218
+ <div
8219
+ class="ct-autocomplete__group"
8220
+ role="group"
8221
+ [attr.aria-labelledby]="groupLabelId(gi)"
8222
+ >
8223
+ <div class="ct-autocomplete__group-label" [id]="groupLabelId(gi)">
8224
+ {{ group.label }}
8225
+ </div>
8226
+ @for (item of group.items; track item.option.id) {
8227
+ <ng-container
8228
+ [ngTemplateOutlet]="optionRow"
8229
+ [ngTemplateOutletContext]="{ $implicit: item }"
8230
+ />
8231
+ }
8232
+ </div>
8233
+ } @else {
8234
+ @for (item of group.items; track item.option.id) {
8235
+ <ng-container
8236
+ [ngTemplateOutlet]="optionRow"
8237
+ [ngTemplateOutletContext]="{ $implicit: item }"
8238
+ />
8239
+ }
8240
+ }
8241
+ }
8242
+
8243
+ @if (loading()) {
8244
+ <div class="ct-autocomplete__loading" role="presentation">
8245
+ <span class="ct-autocomplete__spinner" aria-hidden="true"></span>
8246
+ {{ loadingText() }}
8247
+ </div>
8248
+ } @else if (showEmpty()) {
8249
+ <div class="ct-autocomplete__empty" role="presentation">{{ emptyText() }}</div>
8250
+ }
8251
+ }
8252
+ </div>
8253
+
8254
+ <div class="ct-autocomplete__status" role="status" aria-live="polite" aria-atomic="true">
8255
+ {{ statusMessage() }}
8256
+ </div>
8257
+ </div>
8258
+
8259
+ @if (hint() && !error()) {
8260
+ <div class="ct-field__hint" [id]="hintId">{{ hint() }}</div>
8261
+ }
8262
+ @if (error()) {
8263
+ <div class="ct-field__error" role="alert" [id]="errorId">{{ error() }}</div>
8264
+ }
8265
+ </div>
8266
+
8267
+ <ng-template #optionRow let-item>
8268
+ <div
8269
+ class="ct-autocomplete__option"
8270
+ role="option"
8271
+ [id]="optionDomId(item.flatIndex)"
8272
+ [attr.aria-selected]="highlightedIndex() === item.flatIndex"
8273
+ [attr.aria-disabled]="item.option.disabled || null"
8274
+ [attr.data-highlighted]="highlightedIndex() === item.flatIndex ? '' : null"
8275
+ (click)="selectOption(item.option)"
8276
+ (mouseenter)="highlightedIndex.set(item.flatIndex)"
8277
+ (mousedown)="$event.preventDefault()"
8278
+ >
8279
+ @if (optionTemplate(); as tpl) {
8280
+ <ng-container
8281
+ [ngTemplateOutlet]="tpl.template"
8282
+ [ngTemplateOutletContext]="{
8283
+ $implicit: item.option,
8284
+ option: item.option,
8285
+ query: query(),
8286
+ }"
8287
+ />
8288
+ } @else {
8289
+ <span class="ct-autocomplete__option-label">{{ item.option.label }}</span>
8290
+ @if (item.option.description) {
8291
+ <span class="ct-autocomplete__option-description">{{ item.option.description }}</span>
8292
+ }
8293
+ }
8294
+ </div>
8295
+ </ng-template>
8296
+ `, isInline: true, styles: [":host{display:block}.ct-autocomplete{--ct-ac-height: var(--control-height-md);--ct-ac-font-size: var(--font-size-md);--ct-ac-line-height: var(--line-height-md);--ct-ac-padding-x: var(--space-5);--ct-ac-option-padding-x: var(--space-4);--ct-ac-option-padding-y: var(--space-3);--ct-ac-listbox-max-height: 320px;position:relative;display:flex;flex-direction:column;width:100%}.ct-autocomplete--sm{--ct-ac-height: var(--control-height-sm);--ct-ac-font-size: var(--font-size-sm);--ct-ac-line-height: var(--line-height-sm);--ct-ac-padding-x: var(--space-4);--ct-ac-option-padding-y: var(--space-2)}.ct-autocomplete--lg{--ct-ac-height: var(--control-height-lg);--ct-ac-font-size: var(--font-size-lg);--ct-ac-line-height: var(--line-height-lg);--ct-ac-padding-x: var(--space-6);--ct-ac-option-padding-y: var(--space-4)}.ct-autocomplete__input-wrap{position:relative;display:flex;align-items:center}.ct-autocomplete__leading{position:absolute;inset-inline-start:var(--ct-ac-padding-x);display:inline-flex;align-items:center;justify-content:center;color:var(--color-text-muted);pointer-events:none}.ct-autocomplete__leading svg{width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem)}.ct-autocomplete__input{width:100%;height:var(--ct-ac-height);padding:0 var(--ct-ac-padding-x);padding-inline-end:calc(var(--ct-ac-padding-x) + var(--icon-md, 1.25rem) + var(--space-2));border:var(--border-thin) solid var(--ct-control-border);border-radius:var(--ct-control-radius);background:var(--ct-control-bg);color:var(--ct-control-text);font-size:var(--ct-ac-font-size);line-height:var(--ct-ac-line-height);transition:var(--ct-control-transition)}.ct-autocomplete__input--with-leading{padding-inline-start:calc(var(--ct-ac-padding-x) + var(--icon-sm, 1rem) + var(--space-2))}.ct-autocomplete__input::placeholder{color:var(--ct-control-placeholder)}@media(hover:hover){.ct-autocomplete__input:hover{border-color:var(--ct-control-border-hover)}}.ct-autocomplete__input:focus-visible{border-color:var(--ct-control-border-focus);outline:2px solid var(--color-focus-ring);outline-offset:-1px}.ct-autocomplete__input:disabled{background:var(--ct-control-bg-disabled);border-color:var(--color-border-subtle);color:var(--color-text-muted);pointer-events:none}.ct-autocomplete__input[aria-invalid=true]{border-color:var(--color-state-danger)}.ct-autocomplete__input[aria-invalid=true]:focus-visible{outline-color:var(--color-state-danger)}.ct-autocomplete__clear{appearance:none;position:absolute;inset-inline-end:var(--space-2);display:inline-flex;align-items:center;justify-content:center;width:var(--icon-lg, 1.5rem);height:var(--icon-lg, 1.5rem);padding:0;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);cursor:pointer;transition:color var(--duration-fast) var(--easing-standard),background var(--duration-fast) var(--easing-standard)}.ct-autocomplete__clear svg{width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem)}@media(hover:hover){.ct-autocomplete__clear:hover{color:var(--color-text-secondary);background:var(--color-bg-muted)}}.ct-autocomplete__clear:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:1px}.ct-autocomplete__spinner{position:absolute;inset-inline-end:var(--space-4);width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem);border:2px solid var(--color-border-subtle);border-top-color:var(--color-brand-primary);border-radius:var(--radius-full, 999px);animation:ct-ac-spin .6s linear infinite}.ct-autocomplete__loading .ct-autocomplete__spinner,.ct-autocomplete__empty .ct-autocomplete__spinner{position:static;inset-inline-end:auto}@keyframes ct-ac-spin{to{transform:rotate(360deg)}}.ct-autocomplete__listbox{position:absolute;inset-inline-start:0;inset-inline-end:0;inset-block-start:calc(100% + var(--space-2));z-index:var(--z-dropdown, 50);max-height:var(--ct-ac-listbox-max-height);overflow-y:auto;padding:var(--space-2);border-radius:var(--radius-md);border:var(--border-thin) solid var(--color-border-subtle);background:var(--color-bg-elevated);box-shadow:var(--shadow-dropdown);opacity:0;visibility:hidden;transform:translateY(4px);transition:opacity var(--duration-fast) var(--easing-standard),transform var(--duration-fast) var(--easing-standard),visibility 0s linear var(--duration-fast)}.ct-autocomplete[data-state=open] .ct-autocomplete__listbox{opacity:1;visibility:visible;transform:translateY(0);transition:opacity var(--duration-fast) var(--easing-standard),transform var(--duration-fast) var(--easing-standard),visibility 0s linear 0s}.ct-autocomplete__group+.ct-autocomplete__group{margin-top:var(--space-1);border-top:var(--border-thin) solid var(--color-border-subtle);padding-top:var(--space-1)}.ct-autocomplete__group-label{padding:var(--space-2) var(--ct-ac-option-padding-x);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide, .04em)}.ct-autocomplete__option{display:flex;align-items:center;gap:var(--space-3);padding:var(--ct-ac-option-padding-y) var(--ct-ac-option-padding-x);border-radius:var(--radius-sm);color:var(--color-text-primary);font-size:var(--ct-ac-font-size);line-height:var(--ct-ac-line-height);cursor:pointer;transition:background var(--duration-fast) var(--easing-standard)}@media(hover:hover){.ct-autocomplete__option:hover{background:var(--color-bg-muted)}}.ct-autocomplete__option[data-highlighted]{background:var(--color-bg-muted);outline:var(--border-medium) solid var(--color-brand-primary);outline-offset:-2px}.ct-autocomplete__option[aria-disabled=true]{opacity:var(--opacity-disabled, .5);pointer-events:none;cursor:not-allowed}.ct-autocomplete__option-label{flex:1;min-width:0}.ct-autocomplete__option-description{font-size:var(--font-size-xs);color:var(--color-text-muted)}.ct-autocomplete__empty,.ct-autocomplete__loading{padding:var(--space-6) var(--ct-ac-option-padding-x);text-align:center;color:var(--color-text-muted);font-size:var(--font-size-sm)}.ct-autocomplete__loading{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ct-autocomplete__status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}@media(prefers-reduced-motion:reduce){.ct-autocomplete__listbox{transition:none}.ct-autocomplete__spinner{animation-duration:1.5s}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8297
+ }
8298
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfAutocompleteComponent, decorators: [{
8299
+ type: Component,
8300
+ args: [{ selector: 'af-autocomplete', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: {
8301
+ '(document:click)': 'onDocumentClick($event)',
8302
+ }, template: `
8303
+ <div class="ct-field" [class.ct-field--error]="error()">
8304
+ @if (label()) {
8305
+ <label class="ct-field__label" [attr.for]="inputId">
8306
+ {{ label() }}
8307
+ @if (required()) {
8308
+ <span aria-label="required"> *</span>
8309
+ }
8310
+ </label>
8311
+ }
8312
+
8313
+ <div [class]="rootClasses()" [attr.data-state]="panelOpen() ? 'open' : 'closed'">
8314
+ <div class="ct-autocomplete__input-wrap">
8315
+ @if (showSearchIcon()) {
8316
+ <span class="ct-autocomplete__leading" aria-hidden="true">
8317
+ <svg viewBox="0 0 16 16" fill="none">
8318
+ <circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.5" />
8319
+ <path
8320
+ d="M10.5 10.5L14 14"
8321
+ stroke="currentColor"
8322
+ stroke-width="1.5"
8323
+ stroke-linecap="round"
8324
+ />
8325
+ </svg>
8326
+ </span>
8327
+ }
8328
+ <input
8329
+ #inputEl
8330
+ class="ct-autocomplete__input"
8331
+ [class.ct-autocomplete__input--with-leading]="showSearchIcon()"
8332
+ [id]="inputId"
8333
+ type="text"
8334
+ role="combobox"
8335
+ autocomplete="off"
8336
+ autocapitalize="off"
8337
+ spellcheck="false"
8338
+ [attr.aria-expanded]="panelOpen()"
8339
+ [attr.aria-controls]="listboxId"
8340
+ aria-autocomplete="list"
8341
+ [attr.aria-activedescendant]="activeDescendantId()"
8342
+ [attr.aria-invalid]="error() ? true : null"
8343
+ [attr.aria-describedby]="ariaDescribedBy()"
8344
+ [attr.aria-busy]="loading() || null"
8345
+ [attr.aria-required]="required() || null"
8346
+ [placeholder]="placeholder()"
8347
+ [disabled]="disabled()"
8348
+ [value]="query()"
8349
+ (input)="onInput($event)"
8350
+ (focus)="onFocus()"
8351
+ (keydown)="onKeydown($event)"
8352
+ />
8353
+
8354
+ @if (loading()) {
8355
+ <span class="ct-autocomplete__spinner" aria-hidden="true"></span>
8356
+ } @else if (showClear() && query()) {
8357
+ <button
8358
+ type="button"
8359
+ class="ct-autocomplete__clear"
8360
+ [attr.aria-label]="clearAriaLabel()"
8361
+ (click)="clear()"
8362
+ (mousedown)="$event.preventDefault()"
8363
+ >
8364
+ <svg viewBox="0 0 16 16" fill="none" aria-hidden="true">
8365
+ <path
8366
+ d="M4 4l8 8M12 4l-8 8"
8367
+ stroke="currentColor"
8368
+ stroke-width="1.5"
8369
+ stroke-linecap="round"
8370
+ />
8371
+ </svg>
8372
+ </button>
8373
+ }
8374
+ </div>
8375
+
8376
+ <div
8377
+ #listboxEl
8378
+ [id]="listboxId"
8379
+ class="ct-autocomplete__listbox"
8380
+ role="listbox"
8381
+ [attr.aria-label]="label() || placeholder() || 'Suggestions'"
8382
+ [attr.aria-busy]="loading() || null"
8383
+ >
8384
+ @if (panelOpen()) {
8385
+ @for (group of displayGroups(); track group.key; let gi = $index) {
8386
+ @if (group.label) {
8387
+ <div
8388
+ class="ct-autocomplete__group"
8389
+ role="group"
8390
+ [attr.aria-labelledby]="groupLabelId(gi)"
8391
+ >
8392
+ <div class="ct-autocomplete__group-label" [id]="groupLabelId(gi)">
8393
+ {{ group.label }}
8394
+ </div>
8395
+ @for (item of group.items; track item.option.id) {
8396
+ <ng-container
8397
+ [ngTemplateOutlet]="optionRow"
8398
+ [ngTemplateOutletContext]="{ $implicit: item }"
8399
+ />
8400
+ }
8401
+ </div>
8402
+ } @else {
8403
+ @for (item of group.items; track item.option.id) {
8404
+ <ng-container
8405
+ [ngTemplateOutlet]="optionRow"
8406
+ [ngTemplateOutletContext]="{ $implicit: item }"
8407
+ />
8408
+ }
8409
+ }
8410
+ }
8411
+
8412
+ @if (loading()) {
8413
+ <div class="ct-autocomplete__loading" role="presentation">
8414
+ <span class="ct-autocomplete__spinner" aria-hidden="true"></span>
8415
+ {{ loadingText() }}
8416
+ </div>
8417
+ } @else if (showEmpty()) {
8418
+ <div class="ct-autocomplete__empty" role="presentation">{{ emptyText() }}</div>
8419
+ }
8420
+ }
8421
+ </div>
8422
+
8423
+ <div class="ct-autocomplete__status" role="status" aria-live="polite" aria-atomic="true">
8424
+ {{ statusMessage() }}
8425
+ </div>
8426
+ </div>
8427
+
8428
+ @if (hint() && !error()) {
8429
+ <div class="ct-field__hint" [id]="hintId">{{ hint() }}</div>
8430
+ }
8431
+ @if (error()) {
8432
+ <div class="ct-field__error" role="alert" [id]="errorId">{{ error() }}</div>
8433
+ }
8434
+ </div>
8435
+
8436
+ <ng-template #optionRow let-item>
8437
+ <div
8438
+ class="ct-autocomplete__option"
8439
+ role="option"
8440
+ [id]="optionDomId(item.flatIndex)"
8441
+ [attr.aria-selected]="highlightedIndex() === item.flatIndex"
8442
+ [attr.aria-disabled]="item.option.disabled || null"
8443
+ [attr.data-highlighted]="highlightedIndex() === item.flatIndex ? '' : null"
8444
+ (click)="selectOption(item.option)"
8445
+ (mouseenter)="highlightedIndex.set(item.flatIndex)"
8446
+ (mousedown)="$event.preventDefault()"
8447
+ >
8448
+ @if (optionTemplate(); as tpl) {
8449
+ <ng-container
8450
+ [ngTemplateOutlet]="tpl.template"
8451
+ [ngTemplateOutletContext]="{
8452
+ $implicit: item.option,
8453
+ option: item.option,
8454
+ query: query(),
8455
+ }"
8456
+ />
8457
+ } @else {
8458
+ <span class="ct-autocomplete__option-label">{{ item.option.label }}</span>
8459
+ @if (item.option.description) {
8460
+ <span class="ct-autocomplete__option-description">{{ item.option.description }}</span>
8461
+ }
8462
+ }
8463
+ </div>
8464
+ </ng-template>
8465
+ `, styles: [":host{display:block}.ct-autocomplete{--ct-ac-height: var(--control-height-md);--ct-ac-font-size: var(--font-size-md);--ct-ac-line-height: var(--line-height-md);--ct-ac-padding-x: var(--space-5);--ct-ac-option-padding-x: var(--space-4);--ct-ac-option-padding-y: var(--space-3);--ct-ac-listbox-max-height: 320px;position:relative;display:flex;flex-direction:column;width:100%}.ct-autocomplete--sm{--ct-ac-height: var(--control-height-sm);--ct-ac-font-size: var(--font-size-sm);--ct-ac-line-height: var(--line-height-sm);--ct-ac-padding-x: var(--space-4);--ct-ac-option-padding-y: var(--space-2)}.ct-autocomplete--lg{--ct-ac-height: var(--control-height-lg);--ct-ac-font-size: var(--font-size-lg);--ct-ac-line-height: var(--line-height-lg);--ct-ac-padding-x: var(--space-6);--ct-ac-option-padding-y: var(--space-4)}.ct-autocomplete__input-wrap{position:relative;display:flex;align-items:center}.ct-autocomplete__leading{position:absolute;inset-inline-start:var(--ct-ac-padding-x);display:inline-flex;align-items:center;justify-content:center;color:var(--color-text-muted);pointer-events:none}.ct-autocomplete__leading svg{width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem)}.ct-autocomplete__input{width:100%;height:var(--ct-ac-height);padding:0 var(--ct-ac-padding-x);padding-inline-end:calc(var(--ct-ac-padding-x) + var(--icon-md, 1.25rem) + var(--space-2));border:var(--border-thin) solid var(--ct-control-border);border-radius:var(--ct-control-radius);background:var(--ct-control-bg);color:var(--ct-control-text);font-size:var(--ct-ac-font-size);line-height:var(--ct-ac-line-height);transition:var(--ct-control-transition)}.ct-autocomplete__input--with-leading{padding-inline-start:calc(var(--ct-ac-padding-x) + var(--icon-sm, 1rem) + var(--space-2))}.ct-autocomplete__input::placeholder{color:var(--ct-control-placeholder)}@media(hover:hover){.ct-autocomplete__input:hover{border-color:var(--ct-control-border-hover)}}.ct-autocomplete__input:focus-visible{border-color:var(--ct-control-border-focus);outline:2px solid var(--color-focus-ring);outline-offset:-1px}.ct-autocomplete__input:disabled{background:var(--ct-control-bg-disabled);border-color:var(--color-border-subtle);color:var(--color-text-muted);pointer-events:none}.ct-autocomplete__input[aria-invalid=true]{border-color:var(--color-state-danger)}.ct-autocomplete__input[aria-invalid=true]:focus-visible{outline-color:var(--color-state-danger)}.ct-autocomplete__clear{appearance:none;position:absolute;inset-inline-end:var(--space-2);display:inline-flex;align-items:center;justify-content:center;width:var(--icon-lg, 1.5rem);height:var(--icon-lg, 1.5rem);padding:0;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);cursor:pointer;transition:color var(--duration-fast) var(--easing-standard),background var(--duration-fast) var(--easing-standard)}.ct-autocomplete__clear svg{width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem)}@media(hover:hover){.ct-autocomplete__clear:hover{color:var(--color-text-secondary);background:var(--color-bg-muted)}}.ct-autocomplete__clear:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:1px}.ct-autocomplete__spinner{position:absolute;inset-inline-end:var(--space-4);width:var(--icon-sm, 1rem);height:var(--icon-sm, 1rem);border:2px solid var(--color-border-subtle);border-top-color:var(--color-brand-primary);border-radius:var(--radius-full, 999px);animation:ct-ac-spin .6s linear infinite}.ct-autocomplete__loading .ct-autocomplete__spinner,.ct-autocomplete__empty .ct-autocomplete__spinner{position:static;inset-inline-end:auto}@keyframes ct-ac-spin{to{transform:rotate(360deg)}}.ct-autocomplete__listbox{position:absolute;inset-inline-start:0;inset-inline-end:0;inset-block-start:calc(100% + var(--space-2));z-index:var(--z-dropdown, 50);max-height:var(--ct-ac-listbox-max-height);overflow-y:auto;padding:var(--space-2);border-radius:var(--radius-md);border:var(--border-thin) solid var(--color-border-subtle);background:var(--color-bg-elevated);box-shadow:var(--shadow-dropdown);opacity:0;visibility:hidden;transform:translateY(4px);transition:opacity var(--duration-fast) var(--easing-standard),transform var(--duration-fast) var(--easing-standard),visibility 0s linear var(--duration-fast)}.ct-autocomplete[data-state=open] .ct-autocomplete__listbox{opacity:1;visibility:visible;transform:translateY(0);transition:opacity var(--duration-fast) var(--easing-standard),transform var(--duration-fast) var(--easing-standard),visibility 0s linear 0s}.ct-autocomplete__group+.ct-autocomplete__group{margin-top:var(--space-1);border-top:var(--border-thin) solid var(--color-border-subtle);padding-top:var(--space-1)}.ct-autocomplete__group-label{padding:var(--space-2) var(--ct-ac-option-padding-x);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide, .04em)}.ct-autocomplete__option{display:flex;align-items:center;gap:var(--space-3);padding:var(--ct-ac-option-padding-y) var(--ct-ac-option-padding-x);border-radius:var(--radius-sm);color:var(--color-text-primary);font-size:var(--ct-ac-font-size);line-height:var(--ct-ac-line-height);cursor:pointer;transition:background var(--duration-fast) var(--easing-standard)}@media(hover:hover){.ct-autocomplete__option:hover{background:var(--color-bg-muted)}}.ct-autocomplete__option[data-highlighted]{background:var(--color-bg-muted);outline:var(--border-medium) solid var(--color-brand-primary);outline-offset:-2px}.ct-autocomplete__option[aria-disabled=true]{opacity:var(--opacity-disabled, .5);pointer-events:none;cursor:not-allowed}.ct-autocomplete__option-label{flex:1;min-width:0}.ct-autocomplete__option-description{font-size:var(--font-size-xs);color:var(--color-text-muted)}.ct-autocomplete__empty,.ct-autocomplete__loading{padding:var(--space-6) var(--ct-ac-option-padding-x);text-align:center;color:var(--color-text-muted);font-size:var(--font-size-sm)}.ct-autocomplete__loading{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ct-autocomplete__status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}@media(prefers-reduced-motion:reduce){.ct-autocomplete__listbox{transition:none}.ct-autocomplete__spinner{animation-duration:1.5s}}\n"] }]
8466
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], openOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnFocus", required: false }] }], hideOnEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideOnEmpty", required: false }] }], clearQueryOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearQueryOnSelect", required: false }] }], showSearchIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearchIcon", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], clearAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaLabel", required: false }] }], groupLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupLabels", required: false }] }], groupOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupOrder", required: false }] }], autocompleteId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocompleteId", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }, { type: i0.Output, args: ["queryChange"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], optionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AfAutocompleteOptionDirective), { isSignal: true }] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }] } });
8467
+
7793
8468
  /**
7794
8469
  * File upload component with drag-and-drop, validation, and form control support.
7795
8470
  *
@@ -13626,5 +14301,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
13626
14301
  * Generated bundle index. Do not edit.
13627
14302
  */
13628
14303
 
13629
- export { AF_ACCORDION_I18N, AF_ALERT_I18N, AF_CHART_I18N, AF_CHART_PALETTE_SIZE, AF_INPUT_I18N, AF_SELECT_I18N, AF_SELECT_MENU_I18N, AF_TREE_I18N, AVATAR_SEED_PALETTE_SIZE, AfAccordionComponent, AfAccordionHarness, AfAccordionItemComponent, AfAccordionItemHarness, AfAlertComponent, AfAlertHarness, AfAppShellComponent, AfAppShellPageHeaderComponent, AfAppShellV2Component, AfAppShellV2ToolbarComponent, AfAvatarComponent, AfBadgeComponent, AfBadgeHarness, AfBannerComponent, AfBarChartComponent, AfBarChartHarness, AfBreadcrumbsComponent, AfButtonComponent, AfButtonHarness, AfCardComponent, AfCellDefDirective, AfChartDataTableComponent, AfCheckboxComponent, AfChipComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDonutChartComponent, AfDonutChartHarness, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfGaugeComponent, AfGaugeHarness, AfIconComponent, AfInputComponent, AfInputHarness, AfLineChartComponent, AfLineChartHarness, AfModalComponent, AfNavItemComponent, AfNavTabsComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfRadioGroupComponent, AfSelectComponent, AfSelectHarness, AfSelectMenuComponent, AfSelectMenuHarness, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSparklineComponent, AfSparklineHarness, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective, AfTreeComponent, AfTreeHarness, AfTreeNodeHarness };
14304
+ export { AF_ACCORDION_I18N, AF_ALERT_I18N, AF_CHART_I18N, AF_CHART_PALETTE_SIZE, AF_INPUT_I18N, AF_SELECT_I18N, AF_SELECT_MENU_I18N, AF_TREE_I18N, AVATAR_SEED_PALETTE_SIZE, AfAccordionComponent, AfAccordionHarness, AfAccordionItemComponent, AfAccordionItemHarness, AfAlertComponent, AfAlertHarness, AfAppShellComponent, AfAppShellPageHeaderComponent, AfAppShellV2Component, AfAppShellV2ToolbarComponent, AfAutocompleteComponent, AfAutocompleteOptionDirective, AfAvatarComponent, AfBadgeComponent, AfBadgeHarness, AfBannerComponent, AfBarChartComponent, AfBarChartHarness, AfBreadcrumbsComponent, AfButtonComponent, AfButtonHarness, AfCardComponent, AfCellDefDirective, AfChartDataTableComponent, AfCheckboxComponent, AfChipComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDonutChartComponent, AfDonutChartHarness, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfGaugeComponent, AfGaugeHarness, AfIconComponent, AfInputComponent, AfInputHarness, AfLineChartComponent, AfLineChartHarness, AfModalComponent, AfNavItemComponent, AfNavTabsComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfRadioGroupComponent, AfSelectComponent, AfSelectHarness, AfSelectMenuComponent, AfSelectMenuHarness, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSparklineComponent, AfSparklineHarness, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective, AfTreeComponent, AfTreeHarness, AfTreeNodeHarness };
13630
14305
  //# sourceMappingURL=neuravision-ng-construct.mjs.map