@spartan-ng/brain 0.0.1-alpha.608 → 0.0.1-alpha.609

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.
@@ -1,132 +1,287 @@
1
1
  import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, inject, Injector, input, output, signal, contentChildren, computed, effect, untracked, Directive, TemplateRef, ViewContainerRef, PLATFORM_ID, ElementRef, booleanAttribute } from '@angular/core';
4
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { Directive, InjectionToken, inject, forwardRef, Injector, input, booleanAttribute, linkedSignal, model, contentChild, ElementRef, computed, contentChildren, Renderer2, TemplateRef, ViewContainerRef, effect, PLATFORM_ID, signal } from '@angular/core';
4
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
+ import { stringifyAsLabel } from '@spartan-ng/brain/core';
5
6
  import { BrnPopover } from '@spartan-ng/brain/popover';
7
+ import { BrnDialog } from '@spartan-ng/brain/dialog';
6
8
  import { isPlatformBrowser } from '@angular/common';
7
- import { startWith } from 'rxjs/operators';
9
+
10
+ class BrnAutocompleteInputWrapper {
11
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteInputWrapper, deps: [], target: i0.ɵɵFactoryTarget.Directive });
12
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteInputWrapper, isStandalone: true, selector: "[brnAutocompleteInputWrapper]", ngImport: i0 });
13
+ }
14
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteInputWrapper, decorators: [{
15
+ type: Directive,
16
+ args: [{ selector: '[brnAutocompleteInputWrapper]' }]
17
+ }] });
8
18
 
9
19
  const BrnAutocompleteItemToken = new InjectionToken('BrnAutocompleteItemToken');
10
20
  function provideBrnAutocompleteItem(autocomplete) {
11
21
  return { provide: BrnAutocompleteItemToken, useExisting: autocomplete };
12
22
  }
13
23
 
14
- const BrnAutocompleteToken = new InjectionToken('BrnAutocompleteToken');
15
- function provideBrnAutocomplete(autocomplete) {
16
- return { provide: BrnAutocompleteToken, useExisting: autocomplete };
24
+ const BrnAutocompleteBaseToken = new InjectionToken('BrnAutocompleteBaseToken');
25
+ function provideBrnAutocompleteBase(autocomplete) {
26
+ return { provide: BrnAutocompleteBaseToken, useExisting: autocomplete };
27
+ }
28
+ function injectBrnAutocompleteBase() {
29
+ return inject(BrnAutocompleteBaseToken);
30
+ }
31
+ function getDefaultConfig() {
32
+ return {
33
+ isItemEqualToValue: (itemValue, selectedValue) => Object.is(itemValue, selectedValue),
34
+ itemToString: undefined,
35
+ };
36
+ }
37
+ const BrnAutocompleteConfigToken = new InjectionToken('BrnAutocompleteConfig');
38
+ function provideBrnAutocompleteConfig(config) {
39
+ return { provide: BrnAutocompleteConfigToken, useValue: { ...getDefaultConfig(), ...config } };
17
40
  }
18
- function injectBrnAutocomplete() {
19
- return inject(BrnAutocompleteToken);
41
+ function injectBrnAutocompleteConfig() {
42
+ const injectedConfig = inject(BrnAutocompleteConfigToken, { optional: true });
43
+ return injectedConfig ? injectedConfig : getDefaultConfig();
20
44
  }
21
45
 
46
+ const BRN_AUTOCOMPLETE_VALUE_ACCESSOR = {
47
+ provide: NG_VALUE_ACCESSOR,
48
+ useExisting: forwardRef(() => BrnAutocomplete),
49
+ multi: true,
50
+ };
22
51
  class BrnAutocomplete {
23
- static _id = 0;
24
52
  _injector = inject(Injector);
25
- /** The id of the autocomplete */
26
- id = input(`brn-autocomplete-${++BrnAutocomplete._id}`);
27
- /** when the selection has changed */
28
- valueChange = output();
29
- /** when the selection has been cleared */
30
- selectionCleared = output();
53
+ _config = injectBrnAutocompleteConfig();
31
54
  /** Access the popover if present */
32
55
  _brnPopover = inject(BrnPopover, { optional: true });
33
- /** @internal The focus strategy when opening */
34
- _focus = signal('first');
56
+ /** Whether the autocomplete is disabled */
57
+ disabled = input(false, { transform: booleanAttribute });
58
+ _disabled = linkedSignal(this.disabled);
59
+ /** @internal The disabled state as a readonly signal */
60
+ disabledState = this._disabled.asReadonly();
61
+ /** A function to compare an item with the selected value. */
62
+ isItemEqualToValue = input(this._config.isItemEqualToValue);
63
+ /** A function to convert an item to a string for display. */
64
+ itemToString = input(this._config.itemToString);
65
+ /** The selected value of the autocomplete. */
66
+ value = model(null);
67
+ /** The current search query. */
68
+ search = model('');
69
+ _searchInputWrapper = contentChild(BrnAutocompleteInputWrapper, {
70
+ read: ElementRef,
71
+ });
72
+ /** @internal The width of the search input wrapper */
73
+ searchInputWrapperWidth = computed(() => {
74
+ const inputElement = this._searchInputWrapper()?.nativeElement;
75
+ return inputElement ? inputElement.offsetWidth : null;
76
+ });
35
77
  /** @internal Access all the items within the autocomplete */
36
78
  items = contentChildren(BrnAutocompleteItemToken, {
37
79
  descendants: true,
38
80
  });
81
+ /** Determine if the autocomplete has any visible items */
82
+ visibleItems = computed(() => this.items().length > 0);
39
83
  /** @internal The key manager for managing active descendant */
40
84
  keyManager = new ActiveDescendantKeyManager(this.items, this._injector);
41
85
  /** @internal Whether the autocomplete is expanded */
42
86
  isExpanded = computed(() => this._brnPopover?.stateComputed() === 'open');
87
+ _onChange;
88
+ _onTouched;
43
89
  constructor() {
44
90
  this.keyManager
45
91
  .withVerticalOrientation()
46
92
  .withHomeAndEnd()
47
93
  .withWrap()
48
94
  .skipPredicate((item) => item.disabled);
49
- effect(() => {
50
- const items = this.items();
51
- const focus = this._focus();
52
- untracked(() => {
53
- if (!items.length)
54
- return;
55
- const activeItem = this.keyManager.activeItem;
56
- if (!activeItem || !items.includes(activeItem)) {
57
- focus === 'first' ? this.keyManager.setFirstItemActive() : this.keyManager.setLastItemActive();
58
- }
59
- });
60
- });
61
- this.keyManager.change.pipe(takeUntilDestroyed()).subscribe(() => {
62
- const value = this.keyManager.activeItem?.value();
63
- if (value) {
64
- this.valueChange.emit(value);
65
- }
95
+ this._brnPopover?.closed.subscribe(() => {
96
+ this.keyManager.setActiveItem(-1);
66
97
  });
67
98
  }
68
- selectActiveItem() {
69
- if (this._brnPopover?.stateComputed() === 'open') {
70
- this.keyManager.activeItem?.selected.emit();
99
+ updateSearch(value) {
100
+ this.search.set(value);
101
+ this.open();
102
+ if (value === '') {
103
+ this.resetValue();
71
104
  }
72
105
  }
73
- open(focus = 'first') {
106
+ isSelected(itemValue) {
107
+ return this.isItemEqualToValue()(itemValue, this.value());
108
+ }
109
+ select(itemValue) {
110
+ this.value.set(itemValue);
111
+ this._onChange?.(itemValue);
112
+ this.search.set(stringifyAsLabel(itemValue, this.itemToString()));
113
+ this.close();
114
+ }
115
+ /** Select the active item with Enter key. */
116
+ selectActiveItem() {
117
+ if (!this.isExpanded())
118
+ return;
119
+ const value = this.keyManager.activeItem?.value();
120
+ if (value === undefined)
121
+ return;
122
+ this.select(value);
123
+ }
124
+ resetValue() {
125
+ this.value.set(null);
126
+ this.search.set('');
127
+ this._onChange?.(null);
128
+ }
129
+ open() {
130
+ if (this._disabled() || this.isExpanded())
131
+ return;
74
132
  this._brnPopover?.open();
75
- this._focus.set(focus);
76
133
  }
77
134
  close() {
135
+ if (this._disabled() || !this.isExpanded())
136
+ return;
78
137
  this._brnPopover?.close();
79
138
  }
80
139
  toggle() {
140
+ if (this._disabled())
141
+ return;
81
142
  this.isExpanded() ? this.close() : this.open();
82
143
  }
144
+ /** CONTROL VALUE ACCESSOR */
145
+ writeValue(value) {
146
+ this.value.set(value);
147
+ }
148
+ registerOnChange(fn) {
149
+ this._onChange = fn;
150
+ }
151
+ registerOnTouched(fn) {
152
+ this._onTouched = fn;
153
+ }
154
+ setDisabledState(isDisabled) {
155
+ this._disabled.set(isDisabled);
156
+ }
83
157
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Directive });
84
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnAutocomplete, isStandalone: true, selector: "[brnAutocomplete]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", selectionCleared: "selectionCleared" }, host: { listeners: { "keydown.enter": "selectActiveItem()" }, properties: { "id": "id()" } }, providers: [provideBrnAutocomplete(BrnAutocomplete)], queries: [{ propertyName: "items", predicate: BrnAutocompleteItemToken, descendants: true, isSignal: true }], ngImport: i0 });
158
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnAutocomplete, isStandalone: true, selector: "[brnAutocomplete]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, isItemEqualToValue: { classPropertyName: "isItemEqualToValue", publicName: "isItemEqualToValue", isSignal: true, isRequired: false, transformFunction: null }, itemToString: { classPropertyName: "itemToString", publicName: "itemToString", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "searchChange" }, providers: [provideBrnAutocompleteBase(BrnAutocomplete), BRN_AUTOCOMPLETE_VALUE_ACCESSOR], queries: [{ propertyName: "_searchInputWrapper", first: true, predicate: BrnAutocompleteInputWrapper, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "items", predicate: BrnAutocompleteItemToken, descendants: true, isSignal: true }], ngImport: i0 });
85
159
  }
86
160
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocomplete, decorators: [{
87
161
  type: Directive,
88
162
  args: [{
89
163
  selector: '[brnAutocomplete]',
90
- providers: [provideBrnAutocomplete(BrnAutocomplete)],
91
- host: {
92
- '[id]': 'id()',
93
- '(keydown.enter)': 'selectActiveItem()',
94
- },
164
+ providers: [provideBrnAutocompleteBase(BrnAutocomplete), BRN_AUTOCOMPLETE_VALUE_ACCESSOR],
95
165
  }]
96
166
  }], ctorParameters: () => [] });
97
167
 
98
- class BrnAutocompleteEmpty {
168
+ class BrnAutocompleteAnchor {
169
+ _host = inject(ElementRef, { host: true });
170
+ _brnDialog = inject(BrnDialog, { optional: true });
171
+ constructor() {
172
+ if (!this._brnDialog)
173
+ return;
174
+ this._brnDialog.mutableAttachTo.set(this._host.nativeElement);
175
+ }
176
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
177
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteAnchor, isStandalone: true, selector: "[brnAutocompleteAnchor]", ngImport: i0 });
178
+ }
179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteAnchor, decorators: [{
180
+ type: Directive,
181
+ args: [{ selector: '[brnAutocompleteAnchor]' }]
182
+ }], ctorParameters: () => [] });
183
+
184
+ class BrnAutocompleteClear {
185
+ _autocomplete = injectBrnAutocompleteBase();
186
+ _renderer = inject(Renderer2);
99
187
  _templateRef = inject(TemplateRef);
100
188
  _viewContainerRef = inject(ViewContainerRef);
101
- _autocomplete = injectBrnAutocomplete();
102
- /** Determine if the autocomplete has any items */
103
- _visible = computed(() => this._autocomplete.items().length > 0);
189
+ /** Determine if the combobox has a value */
190
+ _hasValue = computed(() => this._autocomplete.value() !== null);
104
191
  constructor() {
105
192
  effect(() => {
106
- if (this._visible()) {
107
- this._viewContainerRef.clear();
108
- }
109
- else {
110
- this._viewContainerRef.createEmbeddedView(this._templateRef);
193
+ this._viewContainerRef.clear();
194
+ if (this._hasValue()) {
195
+ const view = this._viewContainerRef.createEmbeddedView(this._templateRef);
196
+ view.rootNodes.forEach((node) => {
197
+ this._renderer.listen(node, 'click', (e) => {
198
+ e.preventDefault();
199
+ this.clear();
200
+ });
201
+ });
111
202
  }
112
203
  });
113
204
  }
205
+ clear() {
206
+ this._autocomplete.resetValue();
207
+ }
208
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteClear, deps: [], target: i0.ɵɵFactoryTarget.Directive });
209
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteClear, isStandalone: true, selector: "[brnAutocompleteClear]", ngImport: i0 });
210
+ }
211
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteClear, decorators: [{
212
+ type: Directive,
213
+ args: [{
214
+ selector: '[brnAutocompleteClear]',
215
+ }]
216
+ }], ctorParameters: () => [] });
217
+
218
+ class BrnAutocompleteContent {
219
+ _autocomplete = injectBrnAutocompleteBase();
220
+ /** Determine if the autocomplete has any visible items */
221
+ _visibleItems = this._autocomplete.visibleItems;
222
+ _autocompleteWidth = this._autocomplete.searchInputWrapperWidth;
223
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
224
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteContent, isStandalone: true, selector: "[brnAutocompleteContent]", host: { properties: { "attr.data-empty": "!_visibleItems() ? \"\" : null", "style.--brn-autocomplete-width.px": "_autocompleteWidth()" } }, ngImport: i0 });
225
+ }
226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteContent, decorators: [{
227
+ type: Directive,
228
+ args: [{
229
+ selector: '[brnAutocompleteContent]',
230
+ host: {
231
+ '[attr.data-empty]': '!_visibleItems() ? "" : null',
232
+ '[style.--brn-autocomplete-width.px]': '_autocompleteWidth()',
233
+ },
234
+ }]
235
+ }] });
236
+
237
+ class BrnAutocompleteEmpty {
114
238
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
115
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteEmpty, isStandalone: true, selector: "[brnAutocompleteEmpty]", ngImport: i0 });
239
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteEmpty, isStandalone: true, selector: "[brnAutocompleteEmpty]", host: { attributes: { "role": "status", "aria-live": "polite", "aria-atomic": "true" } }, ngImport: i0 });
116
240
  }
117
241
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteEmpty, decorators: [{
118
242
  type: Directive,
119
243
  args: [{
120
244
  selector: '[brnAutocompleteEmpty]',
245
+ host: {
246
+ role: 'status',
247
+ 'aria-live': 'polite',
248
+ 'aria-atomic': 'true',
249
+ },
121
250
  }]
122
- }], ctorParameters: () => [] });
251
+ }] });
123
252
 
124
- class BrnAutocompleteGroup {
253
+ class BrnAutocompleteLabel {
125
254
  static _id = 0;
126
- /** The id of the autocomplete list */
127
- id = input(`brn-autocomplete-group-${++BrnAutocompleteGroup._id}`);
255
+ /** The id of the autocomplete label */
256
+ id = input(`brn-autocomplete-label-${++BrnAutocompleteLabel._id}`);
257
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
258
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteLabel, isStandalone: true, selector: "[brnAutocompleteLabel]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "id()" } }, ngImport: i0 });
259
+ }
260
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteLabel, decorators: [{
261
+ type: Directive,
262
+ args: [{
263
+ selector: '[brnAutocompleteLabel]',
264
+ host: {
265
+ '[id]': 'id()',
266
+ },
267
+ }]
268
+ }] });
269
+
270
+ class BrnAutocompleteGroup {
271
+ /** Get the items in the group */
272
+ _items = contentChildren(BrnAutocompleteItemToken, {
273
+ descendants: true,
274
+ });
275
+ /** Determine if there are any visible items in the group */
276
+ _visible = computed(() => this._items().length > 0);
277
+ /** Get the label associated with the group */
278
+ _label = contentChild(BrnAutocompleteLabel);
279
+ _labelledBy = computed(() => {
280
+ const label = this._label();
281
+ return label ? label.id() : null;
282
+ });
128
283
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
129
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteGroup, isStandalone: true, selector: "[brnAutocompleteGroup]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "id": "id()" } }, ngImport: i0 });
284
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnAutocompleteGroup, isStandalone: true, selector: "[brnAutocompleteGroup]", host: { attributes: { "role": "group" }, properties: { "attr.data-hidden": "!_visible() ? \"\" : null", "attr.aria-labelledby": "_labelledBy()" } }, queries: [{ propertyName: "_items", predicate: BrnAutocompleteItemToken, descendants: true, isSignal: true }, { propertyName: "_label", first: true, predicate: BrnAutocompleteLabel, descendants: true, isSignal: true }], ngImport: i0 });
130
285
  }
131
286
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteGroup, decorators: [{
132
287
  type: Directive,
@@ -134,22 +289,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImpor
134
289
  selector: '[brnAutocompleteGroup]',
135
290
  host: {
136
291
  role: 'group',
137
- '[id]': 'id()',
292
+ '[attr.data-hidden]': '!_visible() ? "" : null',
293
+ '[attr.aria-labelledby]': '_labelledBy()',
138
294
  },
139
295
  }]
140
296
  }] });
141
297
 
298
+ class BrnAutocompleteInput {
299
+ static _id = 0;
300
+ _el = inject(ElementRef);
301
+ _autocomplete = injectBrnAutocompleteBase();
302
+ /** The id of the autocomplete input */
303
+ id = input(`brn-autocomplete-input-${++BrnAutocompleteInput._id}`);
304
+ disabled = this._autocomplete.disabledState;
305
+ /** Whether the autocomplete panel is expanded */
306
+ _isExpanded = this._autocomplete.isExpanded;
307
+ constructor() {
308
+ effect(() => {
309
+ const value = this._autocomplete.value();
310
+ const search = this._autocomplete.search();
311
+ const valueLabel = stringifyAsLabel(value, this._autocomplete.itemToString());
312
+ if (valueLabel === search) {
313
+ this._el.nativeElement.value = valueLabel;
314
+ }
315
+ else if (search === '') {
316
+ this._el.nativeElement.value = '';
317
+ }
318
+ });
319
+ }
320
+ onInput(event) {
321
+ const value = event.target.value;
322
+ this._autocomplete.updateSearch(value);
323
+ }
324
+ onKeyDown(event) {
325
+ if (event.key === 'Enter') {
326
+ // prevent form submission if inside a form
327
+ event.preventDefault();
328
+ this._autocomplete.selectActiveItem();
329
+ }
330
+ if (!this._isExpanded()) {
331
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
332
+ this._autocomplete.open();
333
+ }
334
+ if (event.key === 'Escape') {
335
+ this._autocomplete.resetValue();
336
+ }
337
+ }
338
+ this._autocomplete.keyManager.onKeydown(event);
339
+ }
340
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
341
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteInput, isStandalone: true, selector: "input[brnAutocompleteInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "text", "role": "combobox", "autocomplete": "off", "autocorrect": "off", "autocapitalize": "none", "spellcheck": "false", "aria-autocomplete": "list", "aria-haspopup": "listbox" }, listeners: { "keydown": "onKeyDown($event)", "input": "onInput($event)" }, properties: { "id": "id()", "attr.aria-expanded": "_isExpanded()", "attr.disabled": "disabled() ? \"\" : null" } }, exportAs: ["brnAutocompleteInput"], ngImport: i0 });
342
+ }
343
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteInput, decorators: [{
344
+ type: Directive,
345
+ args: [{
346
+ selector: 'input[brnAutocompleteInput]',
347
+ exportAs: 'brnAutocompleteInput',
348
+ host: {
349
+ '[id]': 'id()',
350
+ type: 'text',
351
+ role: 'combobox',
352
+ autocomplete: 'off',
353
+ autocorrect: 'off',
354
+ autocapitalize: 'none',
355
+ spellcheck: 'false',
356
+ 'aria-autocomplete': 'list',
357
+ 'aria-haspopup': 'listbox',
358
+ '[attr.aria-expanded]': '_isExpanded()',
359
+ '[attr.disabled]': 'disabled() ? "" : null',
360
+ '(keydown)': 'onKeyDown($event)',
361
+ '(input)': 'onInput($event)',
362
+ },
363
+ }]
364
+ }], ctorParameters: () => [] });
365
+
142
366
  class BrnAutocompleteItem {
143
367
  static _id = 0;
144
368
  _platform = inject(PLATFORM_ID);
145
369
  _elementRef = inject(ElementRef);
146
370
  /** Access the autocomplete component */
147
- _autocomplete = injectBrnAutocomplete();
371
+ _autocomplete = injectBrnAutocompleteBase();
148
372
  /** A unique id for the item */
149
373
  id = input(`brn-autocomplete-item-${++BrnAutocompleteItem._id}`);
150
374
  /** The value this item represents. */
151
375
  value = input.required();
152
- /** Whether the item is disabled. */
153
376
  // eslint-disable-next-line @typescript-eslint/naming-convention
154
377
  _disabled = input(false, {
155
378
  alias: 'disabled',
@@ -160,28 +383,27 @@ class BrnAutocompleteItem {
160
383
  return this._disabled();
161
384
  }
162
385
  /** Whether the item is selected. */
163
- _active = signal(false);
164
- /** Emits when the item is selected. */
165
- selected = output();
166
- /** @internal Get the display value */
167
- getLabel() {
168
- return this._elementRef.nativeElement.textContent?.trim() ?? '';
169
- }
170
- /** @internal */
386
+ active = computed(() => this._autocomplete.isSelected(this.value()));
387
+ _highlighted = signal(false);
171
388
  setActiveStyles() {
172
- this._active.set(true);
389
+ this._highlighted.set(true);
173
390
  // ensure the item is in view
174
391
  if (isPlatformBrowser(this._platform)) {
175
392
  this._elementRef.nativeElement.scrollIntoView({ block: 'nearest' });
176
393
  }
177
394
  }
178
- /** @internal */
179
395
  setInactiveStyles() {
180
- this._active.set(false);
396
+ this._highlighted.set(false);
397
+ }
398
+ getLabel() {
399
+ return stringifyAsLabel(this.value(), this._autocomplete.itemToString());
181
400
  }
182
- onClick() {
401
+ select() {
402
+ if (this._disabled()) {
403
+ return;
404
+ }
183
405
  this._autocomplete.keyManager.setActiveItem(this);
184
- this.selected.emit();
406
+ this._autocomplete.select(this.value());
185
407
  }
186
408
  activate() {
187
409
  if (this._disabled()) {
@@ -190,24 +412,21 @@ class BrnAutocompleteItem {
190
412
  this._autocomplete.keyManager.setActiveItem(this);
191
413
  }
192
414
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
193
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteItem, isStandalone: true, selector: "button[brnAutocompleteItem]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, _disabled: { classPropertyName: "_disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, host: { attributes: { "type": "button", "role": "option", "tabIndex": "-1" }, listeners: { "click": "onClick()", "mouseenter": "activate()" }, properties: { "id": "id()", "attr.disabled": "_disabled() ? true : null", "attr.data-disabled": "_disabled() ? \"\" : null", "attr.data-value": "value()", "attr.aria-selected": "_active()", "attr.data-selected": "_active() ? '' : null" } }, providers: [provideBrnAutocompleteItem(BrnAutocompleteItem)], ngImport: i0 });
415
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteItem, isStandalone: true, selector: "[brnAutocompleteItem]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, _disabled: { classPropertyName: "_disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "select()", "mouseenter": "activate()" }, properties: { "id": "id()", "attr.data-highlighted": "_highlighted() ? \"\" : null", "attr.data-value": "value()", "attr.aria-selected": "active()", "attr.aria-disabled": "_disabled()" } }, providers: [provideBrnAutocompleteItem(BrnAutocompleteItem)], ngImport: i0 });
194
416
  }
195
417
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteItem, decorators: [{
196
418
  type: Directive,
197
419
  args: [{
198
- selector: 'button[brnAutocompleteItem]',
420
+ selector: '[brnAutocompleteItem]',
199
421
  providers: [provideBrnAutocompleteItem(BrnAutocompleteItem)],
200
422
  host: {
201
- type: 'button',
202
423
  role: 'option',
203
- tabIndex: '-1',
204
424
  '[id]': 'id()',
205
- '[attr.disabled]': '_disabled() ? true : null',
206
- '[attr.data-disabled]': '_disabled() ? "" : null',
425
+ '[attr.data-highlighted]': '_highlighted() ? "" : null',
207
426
  '[attr.data-value]': 'value()',
208
- '[attr.aria-selected]': '_active()',
209
- '[attr.data-selected]': "_active() ? '' : null",
210
- '(click)': 'onClick()',
427
+ '[attr.aria-selected]': 'active()',
428
+ '[attr.aria-disabled]': '_disabled()',
429
+ '(click)': 'select()',
211
430
  '(mouseenter)': 'activate()',
212
431
  },
213
432
  }]
@@ -215,10 +434,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImpor
215
434
 
216
435
  class BrnAutocompleteList {
217
436
  static _id = 0;
218
- /** The id of the command list */
437
+ _autocomplete = injectBrnAutocompleteBase();
438
+ /** Determine if the autocomplete has any visible items */
439
+ _visibleItems = this._autocomplete.visibleItems;
440
+ /** The id of the autocomplete list */
219
441
  id = input(`brn-autocomplete-list-${++BrnAutocompleteList._id}`);
220
442
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
221
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteList, isStandalone: true, selector: "[brnAutocompleteList]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox" }, properties: { "id": "id()" } }, ngImport: i0 });
443
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteList, isStandalone: true, selector: "[brnAutocompleteList]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox", "tabIndex": "-1", "aria-orientation": "vertical" }, properties: { "id": "id()", "attr.data-empty": "!_visibleItems() ? \"\" : null" } }, ngImport: i0 });
222
444
  }
223
445
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteList, decorators: [{
224
446
  type: Directive,
@@ -226,72 +448,190 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImpor
226
448
  selector: '[brnAutocompleteList]',
227
449
  host: {
228
450
  role: 'listbox',
451
+ tabIndex: '-1',
452
+ 'aria-orientation': 'vertical',
229
453
  '[id]': 'id()',
454
+ '[attr.data-empty]': '!_visibleItems() ? "" : null',
230
455
  },
231
456
  }]
232
457
  }] });
233
458
 
234
- class BrnAutocompleteSearchInput {
235
- _autocomplete = injectBrnAutocomplete();
236
- /** Whether the autocomplete panel is expanded */
237
- _isExpanded = this._autocomplete.isExpanded;
238
- /** The id of the active option */
239
- _activeDescendant = signal(undefined);
459
+ const BRN_AUTOCOMPLETE_SEARCH_VALUE_ACCESSOR = {
460
+ provide: NG_VALUE_ACCESSOR,
461
+ useExisting: forwardRef(() => BrnAutocompleteSearch),
462
+ multi: true,
463
+ };
464
+ class BrnAutocompleteSearch {
465
+ _injector = inject(Injector);
466
+ _config = injectBrnAutocompleteConfig();
467
+ /** Access the popover if present */
468
+ _brnPopover = inject(BrnPopover, { optional: true });
469
+ /** Whether the autocomplete is disabled */
470
+ disabled = input(false, { transform: booleanAttribute });
471
+ _disabled = linkedSignal(this.disabled);
472
+ /** @internal The disabled state as a readonly signal */
473
+ disabledState = this._disabled.asReadonly();
474
+ /** A function to convert an item to a string for display. */
475
+ itemToString = input(this._config.itemToString);
476
+ /** The selected value of the autocomplete. */
477
+ value = model(null);
478
+ /** The current search query. */
479
+ search = model('');
480
+ _searchInputWrapper = contentChild(BrnAutocompleteInputWrapper, {
481
+ read: ElementRef,
482
+ });
483
+ /** @internal The width of the search input wrapper */
484
+ searchInputWrapperWidth = computed(() => {
485
+ const inputElement = this._searchInputWrapper()?.nativeElement;
486
+ return inputElement ? inputElement.offsetWidth : null;
487
+ });
488
+ /** @internal Access all the items within the autocomplete */
489
+ items = contentChildren(BrnAutocompleteItemToken, {
490
+ descendants: true,
491
+ });
492
+ /** Determine if the autocomplete has any visible items */
493
+ visibleItems = computed(() => this.items().length > 0);
494
+ /** @internal The key manager for managing active descendant */
495
+ keyManager = new ActiveDescendantKeyManager(this.items, this._injector);
496
+ /** @internal Whether the autocomplete is expanded */
497
+ isExpanded = computed(() => this._brnPopover?.stateComputed() === 'open');
498
+ _onChange;
499
+ _onTouched;
240
500
  constructor() {
241
- this._autocomplete.keyManager.change
242
- .pipe(startWith(this._autocomplete.keyManager.activeItemIndex), takeUntilDestroyed())
243
- .subscribe(() => {
244
- this._activeDescendant.set(this._autocomplete.keyManager.activeItem?.id());
501
+ this.keyManager
502
+ .withVerticalOrientation()
503
+ .withHomeAndEnd()
504
+ .withWrap()
505
+ .skipPredicate((item) => item.disabled);
506
+ this._brnPopover?.closed.subscribe(() => {
507
+ this.keyManager.setActiveItem(-1);
245
508
  });
246
509
  }
247
- /** Listen for keydown events */
248
- onKeyDown(event) {
249
- if (event.key === 'Enter') {
250
- // prevent form submission if inside a form
251
- event.preventDefault();
510
+ updateSearch(value) {
511
+ this.value.set(value);
512
+ this.search.set(value);
513
+ this._onChange?.(value);
514
+ this.open();
515
+ if (value === '') {
516
+ this.resetValue();
252
517
  }
253
- if (!this._isExpanded()) {
254
- if (event.key === 'ArrowDown') {
255
- this._autocomplete.open('first');
256
- }
257
- if (event.key === 'ArrowUp') {
258
- this._autocomplete.open('last');
259
- }
260
- if (event.key === 'Escape') {
261
- this._autocomplete.selectionCleared.emit();
262
- }
263
- }
264
- this._autocomplete.keyManager.onKeydown(event);
265
518
  }
266
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSearchInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
267
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteSearchInput, isStandalone: true, selector: "input[brnAutocompleteSearchInput]", host: { attributes: { "role": "combobox", "aria-autocomplete": "list" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.aria-activedescendant": "_activeDescendant()", "attr.aria-expanded": "_isExpanded()" } }, ngImport: i0 });
519
+ isSelected(itemValue) {
520
+ return stringifyAsLabel(itemValue, this.itemToString()) === this.value();
521
+ }
522
+ select(itemValue) {
523
+ const label = stringifyAsLabel(itemValue, this.itemToString());
524
+ this.value.set(label);
525
+ this._onChange?.(label);
526
+ this.search.set(label);
527
+ this.close();
528
+ }
529
+ /** Select the active item with Enter key. */
530
+ selectActiveItem() {
531
+ if (!this.isExpanded())
532
+ return;
533
+ const value = this.keyManager.activeItem?.value();
534
+ if (value === undefined)
535
+ return;
536
+ this.select(value);
537
+ }
538
+ resetValue() {
539
+ this.value.set(null);
540
+ this.search.set('');
541
+ this._onChange?.(null);
542
+ }
543
+ open() {
544
+ if (this._disabled() || this.isExpanded())
545
+ return;
546
+ this._brnPopover?.open();
547
+ }
548
+ close() {
549
+ if (this._disabled() || !this.isExpanded())
550
+ return;
551
+ this._brnPopover?.close();
552
+ }
553
+ toggle() {
554
+ if (this._disabled())
555
+ return;
556
+ this.isExpanded() ? this.close() : this.open();
557
+ }
558
+ /** CONTROL VALUE ACCESSOR */
559
+ writeValue(value) {
560
+ this.value.set(value);
561
+ }
562
+ registerOnChange(fn) {
563
+ this._onChange = fn;
564
+ }
565
+ registerOnTouched(fn) {
566
+ this._onTouched = fn;
567
+ }
568
+ setDisabledState(isDisabled) {
569
+ this._disabled.set(isDisabled);
570
+ }
571
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSearch, deps: [], target: i0.ɵɵFactoryTarget.Directive });
572
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnAutocompleteSearch, isStandalone: true, selector: "[brnAutocomplete]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, itemToString: { classPropertyName: "itemToString", publicName: "itemToString", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "searchChange" }, providers: [provideBrnAutocompleteBase(BrnAutocompleteSearch), BRN_AUTOCOMPLETE_SEARCH_VALUE_ACCESSOR], queries: [{ propertyName: "_searchInputWrapper", first: true, predicate: BrnAutocompleteInputWrapper, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "items", predicate: BrnAutocompleteItemToken, descendants: true, isSignal: true }], ngImport: i0 });
573
+ }
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSearch, decorators: [{
575
+ type: Directive,
576
+ args: [{
577
+ selector: '[brnAutocomplete]',
578
+ providers: [provideBrnAutocompleteBase(BrnAutocompleteSearch), BRN_AUTOCOMPLETE_SEARCH_VALUE_ACCESSOR],
579
+ }]
580
+ }], ctorParameters: () => [] });
581
+
582
+ class BrnAutocompleteSeparator {
583
+ orientation = input('horizontal');
584
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
585
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnAutocompleteSeparator, isStandalone: true, selector: "[brnAutocompleteSeparator]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.aria-orientation": "orientation()", "attr.data-orientation": "orientation()" } }, ngImport: i0 });
268
586
  }
269
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSearchInput, decorators: [{
587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteSeparator, decorators: [{
270
588
  type: Directive,
271
589
  args: [{
272
- selector: 'input[brnAutocompleteSearchInput]',
590
+ selector: '[brnAutocompleteSeparator]',
273
591
  host: {
274
- role: 'combobox',
275
- 'aria-autocomplete': 'list',
276
- '[attr.aria-activedescendant]': '_activeDescendant()',
277
- '[attr.aria-expanded]': '_isExpanded()',
278
- '(keydown)': 'onKeyDown($event)',
592
+ role: 'separator',
593
+ '[attr.aria-orientation]': 'orientation()',
594
+ '[attr.data-orientation]': 'orientation()',
279
595
  },
280
596
  }]
281
- }], ctorParameters: () => [] });
597
+ }] });
598
+
599
+ class BrnAutocompleteStatus {
600
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteStatus, deps: [], target: i0.ɵɵFactoryTarget.Directive });
601
+ /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnAutocompleteStatus, isStandalone: true, selector: "[brnAutocompleteStatus]", host: { attributes: { "role": "status", "aria-live": "polite", "aria-atomic": "true" } }, ngImport: i0 });
602
+ }
603
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnAutocompleteStatus, decorators: [{
604
+ type: Directive,
605
+ args: [{
606
+ selector: '[brnAutocompleteStatus]',
607
+ host: {
608
+ role: 'status',
609
+ 'aria-live': 'polite',
610
+ 'aria-atomic': 'true',
611
+ },
612
+ }]
613
+ }] });
282
614
 
283
615
  const BrnAutocompleteImports = [
284
616
  BrnAutocomplete,
617
+ BrnAutocompleteAnchor,
618
+ BrnAutocompleteClear,
619
+ BrnAutocompleteContent,
285
620
  BrnAutocompleteEmpty,
286
621
  BrnAutocompleteGroup,
622
+ BrnAutocompleteInput,
623
+ BrnAutocompleteInputWrapper,
287
624
  BrnAutocompleteItem,
625
+ BrnAutocompleteLabel,
288
626
  BrnAutocompleteList,
289
- BrnAutocompleteSearchInput,
627
+ BrnAutocompleteSearch,
628
+ BrnAutocompleteSeparator,
629
+ BrnAutocompleteStatus,
290
630
  ];
291
631
 
292
632
  /**
293
633
  * Generated bundle index. Do not edit.
294
634
  */
295
635
 
296
- export { BrnAutocomplete, BrnAutocompleteEmpty, BrnAutocompleteGroup, BrnAutocompleteImports, BrnAutocompleteItem, BrnAutocompleteItemToken, BrnAutocompleteList, BrnAutocompleteSearchInput, BrnAutocompleteToken, injectBrnAutocomplete, provideBrnAutocomplete, provideBrnAutocompleteItem };
636
+ export { BRN_AUTOCOMPLETE_SEARCH_VALUE_ACCESSOR, BRN_AUTOCOMPLETE_VALUE_ACCESSOR, BrnAutocomplete, BrnAutocompleteAnchor, BrnAutocompleteBaseToken, BrnAutocompleteClear, BrnAutocompleteContent, BrnAutocompleteEmpty, BrnAutocompleteGroup, BrnAutocompleteImports, BrnAutocompleteInput, BrnAutocompleteInputWrapper, BrnAutocompleteItem, BrnAutocompleteItemToken, BrnAutocompleteLabel, BrnAutocompleteList, BrnAutocompleteSearch, BrnAutocompleteSeparator, BrnAutocompleteStatus, injectBrnAutocompleteBase, injectBrnAutocompleteConfig, provideBrnAutocompleteBase, provideBrnAutocompleteConfig, provideBrnAutocompleteItem };
297
637
  //# sourceMappingURL=spartan-ng-brain-autocomplete.mjs.map