@recursyve/ngx-material-components 18.0.0-beta.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.
@@ -0,0 +1,772 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, DestroyRef, signal, computed, Injectable, input, viewChild, ElementRef, ChangeDetectorRef, effect, Directive, ViewChildren, Component, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { Subject, switchMap, defer, EMPTY, map, catchError, finalize, startWith, merge, debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs';
5
+ import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
6
+ import * as i1 from '@angular/forms';
7
+ import { FormBuilder, NgControl, ReactiveFormsModule } from '@angular/forms';
8
+ import { MatIconButton } from '@angular/material/button';
9
+ import { _getOptionScrollPosition, MatOption } from '@angular/material/core';
10
+ import { MAT_FORM_FIELD, MatFormFieldControl, MatFormField } from '@angular/material/form-field';
11
+ import { MatInput } from '@angular/material/input';
12
+ import { matSelectAnimations } from '@angular/material/select';
13
+ import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
14
+ import { SelectionModel } from '@angular/cdk/collections';
15
+ import { DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE, hasModifierKey } from '@angular/cdk/keycodes';
16
+
17
+ class NiceAsyncTypeaheadResourceProvider {
18
+ }
19
+
20
+ const NICE_ASYNC_TYPEAHEAD_RESOURCES_PROVIDER = new InjectionToken("nice_async_typeahead_provider");
21
+
22
+ class NiceTypeaheadService {
23
+ resources = inject(NICE_ASYNC_TYPEAHEAD_RESOURCES_PROVIDER, { optional: true }) ?? [];
24
+ destroyRef = inject(DestroyRef);
25
+ fetchResources$ = new Subject();
26
+ fetchActive$ = new Subject();
27
+ resourceProvider = null;
28
+ _items = signal([]);
29
+ _active = signal(null);
30
+ _searchOptions = signal(null);
31
+ _request = signal(null);
32
+ _nextRequest = signal(null);
33
+ _loading = signal(true);
34
+ items = this._items.asReadonly();
35
+ active = this._active.asReadonly();
36
+ loading = this._loading.asReadonly();
37
+ isLastPage = computed(() => !this._nextRequest());
38
+ init(resource) {
39
+ this.fetchResources$.pipe(takeUntilDestroyed(this.destroyRef)).pipe(switchMap((request) => this.fetchResources(request))).subscribe();
40
+ this.fetchActive$.pipe(takeUntilDestroyed(this.destroyRef)).pipe(switchMap((request) => this.fetchActive(request))).subscribe();
41
+ const provider = this.resources.find((resources) => resources.resource === resource);
42
+ if (provider) {
43
+ this.resourceProvider = provider;
44
+ }
45
+ }
46
+ setSearchOptions(options) {
47
+ this._searchOptions.set(options);
48
+ }
49
+ setActive(active) {
50
+ this._active.set(active);
51
+ }
52
+ setActiveFromId(id) {
53
+ this.fetchActive$.next({ id });
54
+ }
55
+ reloadActive() {
56
+ const active = this._active();
57
+ if (!active || !("id" in active)) {
58
+ return;
59
+ }
60
+ if (!(typeof active.id === "number" || typeof active.id === "string")) {
61
+ return;
62
+ }
63
+ this.setActiveFromId(active.id);
64
+ }
65
+ setItems(items) {
66
+ this._items.set(items);
67
+ }
68
+ search(searchQuery) {
69
+ this.fetchResources$.next({
70
+ searchQuery,
71
+ page: 0
72
+ });
73
+ }
74
+ loadMore() {
75
+ const nextRequest = this._nextRequest();
76
+ if (!nextRequest || this._loading()) {
77
+ return;
78
+ }
79
+ this.fetchResources$.next(nextRequest);
80
+ }
81
+ fetchActive(request) {
82
+ return defer(() => {
83
+ if (!this.resourceProvider) {
84
+ return EMPTY;
85
+ }
86
+ this._loading.set(true);
87
+ const localItem = this._items().find((item) => "id" in item && item.id === request.id);
88
+ if (localItem) {
89
+ this._loading.set(false);
90
+ this._items.set([localItem]);
91
+ this._active.set(localItem);
92
+ return EMPTY;
93
+ }
94
+ return this.resourceProvider.getById(request.id);
95
+ }).pipe(map((item) => {
96
+ this._items.set([item]);
97
+ this._active.set(item);
98
+ }), catchError(() => EMPTY), finalize(() => {
99
+ this._loading.set(false);
100
+ }));
101
+ }
102
+ fetchResources(request) {
103
+ return defer(() => {
104
+ if (!this.resourceProvider) {
105
+ return EMPTY;
106
+ }
107
+ this._loading.set(true);
108
+ this._request.set(request);
109
+ return this.resourceProvider.search(request.searchQuery ?? "", request.page, this._searchOptions() ?? {});
110
+ }).pipe(map((result) => {
111
+ if (request.page === 0) {
112
+ this._items.set(result.items);
113
+ }
114
+ else {
115
+ this._items.set([
116
+ ...this._items(),
117
+ ...result.items
118
+ ]);
119
+ }
120
+ if (result.nextPage) {
121
+ this._nextRequest.set({
122
+ ...request,
123
+ page: result.nextPage
124
+ });
125
+ }
126
+ else {
127
+ this._nextRequest.set(null);
128
+ }
129
+ }), catchError(() => EMPTY), finalize(() => {
130
+ this._loading.set(false);
131
+ }));
132
+ }
133
+ formatLabel(item) {
134
+ if (!this.resourceProvider) {
135
+ return "";
136
+ }
137
+ return this.resourceProvider.format(item);
138
+ }
139
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeaheadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
140
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeaheadService });
141
+ }
142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeaheadService, decorators: [{
143
+ type: Injectable
144
+ }] });
145
+
146
+ function provideAsyncTypeaheadResources(providers) {
147
+ return providers.map((provider) => ({
148
+ provide: NICE_ASYNC_TYPEAHEAD_RESOURCES_PROVIDER,
149
+ useClass: provider,
150
+ multi: true
151
+ }));
152
+ }
153
+
154
+ /**
155
+ * Implementation of the same panel and overlay logic as the official Angular MatSelect.
156
+ * This used some of the logic from MatSelect to make this component look and feel like a MatSelect.
157
+ */
158
+ class NiceTypeaheadBase {
159
+ options;
160
+ labelProperty = input();
161
+ formatLabelFn = input();
162
+ static nextId = 0;
163
+ _input = viewChild("input");
164
+ _panel = viewChild("panel");
165
+ _overlayDir = viewChild(CdkConnectedOverlay);
166
+ _focused = false;
167
+ _required = false;
168
+ _disabled = false;
169
+ _panelOpen = false;
170
+ _compareWith = (o1, o2) => o1 === o2;
171
+ _value = signal(null);
172
+ _empty = computed(() => !this._value());
173
+ _placeholder = signal("");
174
+ _searchValue = signal("");
175
+ _elementRef = inject(ElementRef);
176
+ _destroyRef = inject(DestroyRef);
177
+ _changeDetectorRef = inject(ChangeDetectorRef);
178
+ _fb = inject(FormBuilder);
179
+ _searchControl = this._fb.nonNullable.control("");
180
+ _initialized = new Subject();
181
+ _parentFormField = inject(MAT_FORM_FIELD, { optional: true });
182
+ id = `nice-typeahead-${NiceTypeaheadBase.nextId++}`;
183
+ controlType = "nice-typeahead";
184
+ stateChanges = new Subject();
185
+ optionSelectionChanges = defer(() => {
186
+ const options = this.options;
187
+ if (options) {
188
+ return options.changes.pipe(startWith(options), switchMap(() => merge(...options.map(option => option.onSelectionChange))));
189
+ }
190
+ return this._initialized.pipe(switchMap(() => this.optionSelectionChanges));
191
+ });
192
+ ngControl = inject(NgControl, { optional: true, self: true });
193
+ _panelDoneAnimatingStream = new Subject();
194
+ _keyManager;
195
+ _preferredOverlayOrigin;
196
+ _overlayWidth;
197
+ _selectionModel;
198
+ _onChange;
199
+ _onTouch;
200
+ get placeholder() {
201
+ return this._placeholder();
202
+ }
203
+ set placeholder(placeholder) {
204
+ this._placeholder.set(placeholder);
205
+ this.stateChanges.next();
206
+ }
207
+ get focused() {
208
+ return this._focused;
209
+ }
210
+ set focused(isFocused) {
211
+ this._focused = isFocused;
212
+ this.stateChanges.next();
213
+ }
214
+ get required() {
215
+ return this._required;
216
+ }
217
+ set required(isRequired) {
218
+ this._required = isRequired;
219
+ this.stateChanges.next();
220
+ }
221
+ get disabled() {
222
+ return this._disabled;
223
+ }
224
+ set disabled(value) {
225
+ this._disabled = value;
226
+ this.stateChanges.next();
227
+ }
228
+ get value() {
229
+ return this._value();
230
+ }
231
+ set value(value) {
232
+ const hasAssigned = this._assignValue(value);
233
+ if (hasAssigned) {
234
+ this._onChange?.(value);
235
+ }
236
+ }
237
+ get empty() {
238
+ return !this._value();
239
+ }
240
+ get shouldLabelFloat() {
241
+ return this.focused || !this.empty;
242
+ }
243
+ get errorState() {
244
+ return false; // Implement error state logic as needed
245
+ }
246
+ get panelOpen() {
247
+ return this._panelOpen;
248
+ }
249
+ constructor() {
250
+ effect(() => {
251
+ this._input()?.nativeElement.focus();
252
+ });
253
+ if (this.ngControl) {
254
+ // Note: we provide the value accessor through here, instead of
255
+ // the `providers` to avoid running into a circular import.
256
+ this.ngControl.valueAccessor = this;
257
+ }
258
+ }
259
+ ngOnInit() {
260
+ this._selectionModel = new SelectionModel();
261
+ this._searchControl.valueChanges.pipe(takeUntilDestroyed(this._destroyRef), debounceTime(250), distinctUntilChanged()).subscribe((value) => this._searchValue.set(value));
262
+ }
263
+ ngAfterViewInit() {
264
+ this._initialized.next();
265
+ this._initialized.complete();
266
+ this._initKeyManager();
267
+ this.options.changes.pipe(startWith(null), takeUntilDestroyed(this._destroyRef)).subscribe(() => {
268
+ this._resetOptions();
269
+ this._initializeSelection();
270
+ });
271
+ }
272
+ ngOnDestroy() {
273
+ this.stateChanges.complete();
274
+ }
275
+ onContainerClick(event) {
276
+ if (this.disabled) {
277
+ return;
278
+ }
279
+ this.stateChanges.next();
280
+ if (event.target.tagName.toLowerCase() !== "input") {
281
+ this.onFocusChanged(true);
282
+ }
283
+ }
284
+ setDescribedByIds(ids) {
285
+ const controlElement = this._elementRef.nativeElement.querySelector(".nice-typeahead");
286
+ if (!controlElement) {
287
+ return;
288
+ }
289
+ controlElement.setAttribute("aria-describedby", ids.join(" "));
290
+ }
291
+ onFocusChanged(isFocused) {
292
+ this.focused = isFocused;
293
+ this.stateChanges.next();
294
+ if (isFocused) {
295
+ this.open();
296
+ }
297
+ }
298
+ writeValue(value) {
299
+ this._assignValue(value);
300
+ }
301
+ registerOnChange(fn) {
302
+ this._onChange = fn;
303
+ }
304
+ registerOnTouched(fn) {
305
+ this._onTouch = fn;
306
+ }
307
+ formatLabel(item) {
308
+ if (typeof item === "string") {
309
+ return item;
310
+ }
311
+ const fn = this.formatLabelFn();
312
+ if (fn) {
313
+ return fn(item);
314
+ }
315
+ const property = this.labelProperty();
316
+ if (!property) {
317
+ return item?.toString() ?? "";
318
+ }
319
+ if (!(typeof item === "object") || item === null) {
320
+ return item?.toString() ?? "";
321
+ }
322
+ if (property in item) {
323
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
324
+ // @ts-ignore
325
+ return item[property];
326
+ }
327
+ return item.toString();
328
+ }
329
+ removeActiveValue() {
330
+ this.value = null;
331
+ this._selectionModel.clear();
332
+ this._keyManager.setActiveItem(-1);
333
+ this._changeDetectorRef.markForCheck();
334
+ }
335
+ open() {
336
+ if (!this._canOpen()) {
337
+ return;
338
+ }
339
+ // It's important that we read this as late as possible, because doing so earlier will
340
+ // return a different element since it's based on queries in the form field which may
341
+ // not have run yet. Also this needs to be assigned before we measure the overlay width.
342
+ if (this._parentFormField) {
343
+ this._preferredOverlayOrigin = this._parentFormField.getConnectedOverlayOrigin();
344
+ }
345
+ this._overlayWidth = this._getOverlayWidth(this._preferredOverlayOrigin);
346
+ this._panelOpen = true;
347
+ this._keyManager.withHorizontalOrientation(null);
348
+ this._changeDetectorRef.markForCheck();
349
+ // Required for the MDC form field to pick up when the overlay has been opened.
350
+ this.stateChanges.next();
351
+ }
352
+ close() {
353
+ if (!this._panelOpen) {
354
+ return;
355
+ }
356
+ this._focused = false;
357
+ this._panelOpen = false;
358
+ this._changeDetectorRef.markForCheck();
359
+ this._searchValue.set("");
360
+ // Required for the MDC form field to pick up when the overlay has been closed.
361
+ this.stateChanges.next();
362
+ }
363
+ focus(options) {
364
+ this._elementRef.nativeElement.focus(options);
365
+ }
366
+ _handleKeydown(event) {
367
+ if (this.disabled) {
368
+ return;
369
+ }
370
+ if (this.panelOpen) {
371
+ this._handleOpenKeydown(event);
372
+ }
373
+ else {
374
+ this._handleClosedKeydown(event);
375
+ }
376
+ }
377
+ _handleScrollEnd() {
378
+ console.log("scroll end");
379
+ }
380
+ _canOpen() {
381
+ return !this._panelOpen && !this.disabled;
382
+ }
383
+ _onAttached() {
384
+ this._overlayDir()?.positionChange.pipe(take(1)).subscribe(() => {
385
+ this._changeDetectorRef.detectChanges();
386
+ this._positioningSettled();
387
+ });
388
+ }
389
+ _initKeyManager() {
390
+ this._keyManager = new ActiveDescendantKeyManager(this.options)
391
+ .withVerticalOrientation()
392
+ .withHomeAndEnd()
393
+ .withPageUpDown()
394
+ .withAllowedModifierKeys(["shiftKey"])
395
+ .skipPredicate(this._skipPredicate);
396
+ this._keyManager.tabOut.subscribe(() => {
397
+ if (this.panelOpen) {
398
+ if (this._keyManager.activeItem) {
399
+ this._keyManager.activeItem._selectViaInteraction();
400
+ }
401
+ this.focus();
402
+ this.close();
403
+ }
404
+ });
405
+ this._keyManager.change.subscribe(() => {
406
+ if (this._panelOpen && this._panel()) {
407
+ this._scrollOptionIntoView(this._keyManager.activeItemIndex || 0);
408
+ }
409
+ else if (!this._panelOpen && this._keyManager.activeItem) {
410
+ this._keyManager.activeItem._selectViaInteraction();
411
+ }
412
+ });
413
+ }
414
+ _getOverlayWidth(preferredOrigin) {
415
+ const refToMeasure = preferredOrigin instanceof CdkOverlayOrigin
416
+ ? preferredOrigin.elementRef
417
+ : preferredOrigin || this._elementRef;
418
+ return refToMeasure.nativeElement.getBoundingClientRect().width;
419
+ }
420
+ _skipPredicate = (option) => {
421
+ if (this.panelOpen) {
422
+ return false;
423
+ }
424
+ return option.disabled;
425
+ };
426
+ _positioningSettled() {
427
+ this._scrollOptionIntoView(this._keyManager.activeItemIndex || 0);
428
+ }
429
+ _scrollOptionIntoView(index) {
430
+ const option = this.options.toArray()[index];
431
+ if (!option) {
432
+ return;
433
+ }
434
+ const panel = this._panel()?.nativeElement;
435
+ if (!panel) {
436
+ return;
437
+ }
438
+ const element = option._getHostElement();
439
+ if (index === 0) {
440
+ // If we've got one group label before the option and we're at the top option,
441
+ // scroll the list to the top. This is better UX than scrolling the list to the
442
+ // top of the option, because it allows the user to read the top group's label.
443
+ panel.scrollTop = 0;
444
+ }
445
+ else {
446
+ panel.scrollTop = _getOptionScrollPosition(element.offsetTop, element.offsetHeight, panel.scrollTop, panel.offsetHeight);
447
+ }
448
+ }
449
+ _resetOptions() {
450
+ this.optionSelectionChanges.pipe(takeUntil(this.options.changes), takeUntilDestroyed(this._destroyRef)).subscribe((event) => {
451
+ this._onSelect(event.source);
452
+ if (event.isUserInput && this._panelOpen) {
453
+ this.close();
454
+ this.focus();
455
+ }
456
+ });
457
+ }
458
+ _onSelect(option) {
459
+ this.value = option.value;
460
+ this._setSelectionByValue(option.value);
461
+ this._selectOptionByValue(option.value);
462
+ this.stateChanges.next();
463
+ }
464
+ _initializeSelection() {
465
+ // Defer setting the value in order to avoid the "Expression
466
+ // has changed after it was checked" errors from Angular.
467
+ Promise.resolve().then(() => {
468
+ if (this.ngControl) {
469
+ this._value.set(this.ngControl.value);
470
+ }
471
+ const _value = this.value;
472
+ if (_value) {
473
+ this._selectOptionByValue(_value);
474
+ }
475
+ this._setSelectionByValue(_value);
476
+ this._highlightCorrectOption();
477
+ this.stateChanges.next();
478
+ });
479
+ }
480
+ _setSelectionByValue(value) {
481
+ this.options.forEach(option => option.setInactiveStyles());
482
+ this._selectionModel.clear();
483
+ const correspondingOption = value ? this._selectOptionByValue(value) : null;
484
+ // Shift focus to the active item. Note that we shouldn't do this in multiple
485
+ // mode, because we don't know what option the user interacted with last.
486
+ if (correspondingOption) {
487
+ correspondingOption.select(false);
488
+ this._keyManager.updateActiveItem(correspondingOption);
489
+ }
490
+ else if (!this.panelOpen) {
491
+ // Otherwise reset the highlighted option. Note that we only want to do this while
492
+ // closed, because doing it while open can shift the user's focus unnecessarily.
493
+ this._keyManager.updateActiveItem(-1);
494
+ }
495
+ this._changeDetectorRef.markForCheck();
496
+ }
497
+ _selectOptionByValue(value) {
498
+ const correspondingOption = this.options.find((option) => {
499
+ // Skip options that are already in the model. This allows us to handle cases
500
+ // where the same primitive value is selected multiple times.
501
+ if (this._selectionModel.isSelected(option)) {
502
+ return false;
503
+ }
504
+ try {
505
+ // Treat null as a special reset value.
506
+ return option.value != null && this._compareWith(option.value, value);
507
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
508
+ }
509
+ catch (error) {
510
+ return false;
511
+ }
512
+ });
513
+ if (correspondingOption) {
514
+ this._selectionModel.select(correspondingOption);
515
+ }
516
+ return correspondingOption;
517
+ }
518
+ _highlightCorrectOption() {
519
+ if (!this._keyManager) {
520
+ return;
521
+ }
522
+ if (this.empty) {
523
+ // Find the index of the first *enabled* option. Avoid calling `_keyManager.setActiveItem`
524
+ // because it activates the first option that passes the skip predicate, rather than the
525
+ // first *enabled* option.
526
+ let firstEnabledOptionIndex = -1;
527
+ for (let index = 0; index < this.options.length; index++) {
528
+ const option = this.options.get(index);
529
+ if (!option.disabled) {
530
+ firstEnabledOptionIndex = index;
531
+ break;
532
+ }
533
+ }
534
+ this._keyManager.setActiveItem(firstEnabledOptionIndex);
535
+ }
536
+ else {
537
+ this._keyManager.setActiveItem(this._selectionModel.selected[0]);
538
+ }
539
+ }
540
+ _handleClosedKeydown(event) {
541
+ const keyCode = event.keyCode;
542
+ const isArrowKey = keyCode === DOWN_ARROW ||
543
+ keyCode === UP_ARROW ||
544
+ keyCode === LEFT_ARROW ||
545
+ keyCode === RIGHT_ARROW;
546
+ const isOpenKey = keyCode === ENTER || keyCode === SPACE;
547
+ const manager = this._keyManager;
548
+ // Open the select on ALT + arrow key to match the native <select>
549
+ if ((!manager.isTyping() && isOpenKey && !hasModifierKey(event)) ||
550
+ (event.altKey && isArrowKey)) {
551
+ event.preventDefault(); // prevents the page from scrolling down when pressing space
552
+ this.open();
553
+ }
554
+ }
555
+ _handleOpenKeydown(event) {
556
+ const manager = this._keyManager;
557
+ const keyCode = event.keyCode;
558
+ const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
559
+ const isTyping = manager.isTyping();
560
+ if (isArrowKey && event.altKey) {
561
+ // Close the select on ALT + arrow key to match the native <select>
562
+ event.preventDefault();
563
+ this.close();
564
+ // Don't do anything in this case if the user is typing,
565
+ // because the typing sequence can include the space key.
566
+ }
567
+ else if (!isTyping &&
568
+ (keyCode === ENTER || keyCode === SPACE) &&
569
+ manager.activeItem &&
570
+ !hasModifierKey(event)) {
571
+ event.preventDefault();
572
+ manager.activeItem._selectViaInteraction();
573
+ }
574
+ else {
575
+ manager.onKeydown(event);
576
+ }
577
+ }
578
+ _assignValue(newValue) {
579
+ // Always re-assign an array, because it might have been mutated.
580
+ if (newValue !== this.value) {
581
+ if (this.options) {
582
+ this._setSelectionByValue(newValue);
583
+ }
584
+ this._value.set(newValue);
585
+ return true;
586
+ }
587
+ return false;
588
+ }
589
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeaheadBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
590
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "18.2.13", type: NiceTypeaheadBase, inputs: { labelProperty: { classPropertyName: "labelProperty", publicName: "labelProperty", isSignal: true, isRequired: false, transformFunction: null }, formatLabelFn: { classPropertyName: "formatLabelFn", publicName: "formatLabelFn", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "_input", first: true, predicate: ["input"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }, { propertyName: "_overlayDir", first: true, predicate: CdkConnectedOverlay, descendants: true, isSignal: true }, { propertyName: "options", predicate: MatOption, descendants: true }], ngImport: i0 });
591
+ }
592
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeaheadBase, decorators: [{
593
+ type: Directive
594
+ }], ctorParameters: () => [], propDecorators: { options: [{
595
+ type: ViewChildren,
596
+ args: [MatOption]
597
+ }] } });
598
+
599
+ class NiceAsyncTypeahead extends NiceTypeaheadBase {
600
+ resource = input.required();
601
+ filteredValues = computed(() => this.service.items());
602
+ optionsContainer = viewChild("optionsContainer");
603
+ service = inject(NiceTypeaheadService);
604
+ _compareWith = (o1, o2) => {
605
+ if (!(typeof o1 === "object" && o1 && typeof o2 === "object" && o2)) {
606
+ return o1 === o2;
607
+ }
608
+ if (!("id" in o1) || !("id" in o2)) {
609
+ return o1 === o2;
610
+ }
611
+ return o1.id === o2.id;
612
+ };
613
+ /**
614
+ * Infinite scroll internal state
615
+ */
616
+ scrollThresholdPercent = 0.99;
617
+ lastScrollHeight = 0;
618
+ constructor() {
619
+ super();
620
+ effect(() => this.service.search(this._searchValue()), {
621
+ allowSignalWrites: true
622
+ });
623
+ effect(() => {
624
+ const container = this.optionsContainer();
625
+ if (!container) {
626
+ return;
627
+ }
628
+ container.nativeElement.addEventListener("scroll", this.onScroll.bind(this));
629
+ });
630
+ }
631
+ ngOnInit() {
632
+ super.ngOnInit();
633
+ this.service.init(this.resource());
634
+ }
635
+ onFocusChanged(isFocused) {
636
+ super.onFocusChanged(isFocused);
637
+ if (isFocused) {
638
+ this._searchControl.patchValue("");
639
+ }
640
+ }
641
+ formatLabel(item) {
642
+ return this.service.formatLabel(item);
643
+ }
644
+ removeActiveValue() {
645
+ super.removeActiveValue();
646
+ this.service.setActive(null);
647
+ }
648
+ onScroll(event) {
649
+ const target = event.target;
650
+ const threshold = (this.scrollThresholdPercent * 100 * target.scrollHeight) / 100;
651
+ const current = target.scrollTop + target.clientHeight;
652
+ if (this.lastScrollHeight > target.scrollHeight) {
653
+ this.lastScrollHeight = 0;
654
+ }
655
+ if (current > threshold && this.lastScrollHeight < target.scrollHeight) {
656
+ this.service.loadMore();
657
+ this.lastScrollHeight = target.scrollHeight;
658
+ }
659
+ }
660
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceAsyncTypeahead, deps: [], target: i0.ɵɵFactoryTarget.Component });
661
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: NiceAsyncTypeahead, isStandalone: true, selector: "nice-async-typeahead", inputs: { resource: { classPropertyName: "resource", publicName: "resource", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "combobox", "aria-haspopup": "listbox" }, listeners: { "keydown": "_handleKeydown($event)", "focus": "onFocusChanged(true)", "blur": "onFocusChanged(false)" }, properties: { "attr.id": "id", "attr.aria-controls": "panelOpen ? id + \"-panel\" : null", "attr.aria-expanded": "panelOpen", "attr.aria-required": "required.toString()", "attr.aria-disabled": "disabled.toString()", "attr.aria-invalid": "errorState", "class.nice-typeahead-disabled": "disabled", "class.nice-typeahead-invalid": "errorState", "class.nice-typeahead-required": "required", "class.nice-typeahead-empty": "empty" }, classAttribute: "nice-typeahead" }, providers: [
662
+ { provide: MatFormFieldControl, useExisting: NiceAsyncTypeahead },
663
+ NiceTypeaheadService
664
+ ], viewQueries: [{ propertyName: "optionsContainer", first: true, predicate: ["optionsContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n class=\"nice-typeahead\"\n cdk-overlay-origin\n>\n <div class=\"nice-typeahead-value\">\n @if (_empty()) {\n <span class=\"nice-typeahead-placeholder mat-mdc-select-min-line\">{{ _placeholder() }}</span>\n } @else {\n <span class=\"nice-typeahead-value-text\">\n <span class=\"mat-mdc-select-min-line\">\n @if (_value(); as activeValue) {\n {{ formatLabel(activeValue) }}\n }\n </span>\n </span>\n }\n </div>\n\n <div class=\"nice-typeahead-suffix\">\n @if (_empty()) {\n <div class=\"mat-mdc-select-arrow\">\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"></path>\n </svg>\n </div>\n } @else {\n <button class=\"nice-typeahead-remove\" mat-icon-button (click)=\"$event.stopPropagation(); removeActiveValue()\">\n <svg viewBox=\"0 -960 960 960\">\n <path d=\"m291-240-51-51 189-189-189-189 51-51 189 189 189-189 51 51-189 189 189 189-51 51-189-189-189 189Z\"/>\n </svg>\n </button>\n }\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n (attach)=\"_onAttached()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"nice-typeahead-panel nice-typeahead--open\"\n [attr.id]=\"id + '-panel'\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\"\n (scrollend)=\"_handleScrollEnd()\"\n >\n <div class=\"nice-typeahead-search-input\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n #input\n class=\"nice-typeahead__input\"\n matInput\n [formControl]=\"_searchControl\"\n >\n </mat-form-field>\n </div>\n\n <div #optionsContainer class=\"nice-typeahead-options\" role=\"presentation\">\n @for (item of filteredValues(); track item) {\n <mat-option [value]=\"item\">\n {{ formatLabel(item) }}\n </mat-option>\n } @empty {\n <mat-option disabled>\n <span class=\"nice-typeahead__no-items\">No items found</span>\n </mat-option>\n }\n </div>\n </div>\n</ng-template>\n", styles: ["nice-typeahead,nice-async-typeahead{display:inline-block;width:100%;outline:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:var(--mat-select-enabled-trigger-text-color, var(--mat-app-on-surface));font-family:var(--mat-select-trigger-text-font, var(--mat-app-body-large-font));line-height:var(--mat-select-trigger-text-line-height, var(--mat-app-body-large-line-height));font-size:var(--mat-select-trigger-text-size, var(--mat-app-body-large-size));font-weight:var(--mat-select-trigger-text-weight, var(--mat-app-body-large-weight));letter-spacing:var(--mat-select-trigger-text-tracking, var(--mat-app-body-large-tracking))}nice-typeahead .nice-typeahead,nice-async-typeahead .nice-typeahead{width:100%;display:inline-flex;align-items:center;justify-content:space-between;cursor:pointer;position:relative;box-sizing:border-box}nice-typeahead .nice-typeahead-suffix,nice-async-typeahead .nice-typeahead-suffix{height:24px;flex-shrink:0;display:inline-flex;align-items:center;--mdc-icon-button-state-layer-size: 24px}nice-typeahead .nice-typeahead-suffix .nice-typeahead-remove,nice-async-typeahead .nice-typeahead-suffix .nice-typeahead-remove{margin-right:-6px}nice-typeahead .nice-typeahead-value,nice-async-typeahead .nice-typeahead-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nice-typeahead .nice-typeahead-value-text,nice-async-typeahead .nice-typeahead-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}nice-typeahead .mat-mdc-select-min-line:empty:before,nice-async-typeahead .mat-mdc-select-min-line:empty:before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}nice-typeahead .nice-typeahead__input,nice-async-typeahead .nice-typeahead__input{border:none;outline:none;box-shadow:none;background:none;padding:0;margin:0;color:inherit}.mat-mdc-form-field-type-nice-typeahead:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper{cursor:pointer}.mat-mdc-form-field-type-nice-typeahead.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}div.nice-typeahead-panel{width:100%;outline:0;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-select-panel-background-color, var(--mat-app-surface-container))}div.nice-typeahead-panel .nice-typeahead-search-input{padding-left:8px;padding-right:8px;padding-bottom:8px}div.nice-typeahead-panel .nice-typeahead-search-input .mat-mdc-form-field-infix{width:100%}div.nice-typeahead-panel .nice-typeahead-options{overflow:auto;max-height:384px}div.nice-typeahead-panel{box-shadow:var(--mat-select-container-elevation-shadow)}\n"], dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }], animations: [matSelectAnimations.transformPanel], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
665
+ }
666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceAsyncTypeahead, decorators: [{
667
+ type: Component,
668
+ args: [{ selector: "nice-async-typeahead", standalone: true, imports: [
669
+ CdkOverlayOrigin,
670
+ CdkConnectedOverlay,
671
+ ReactiveFormsModule,
672
+ MatOption,
673
+ MatFormField,
674
+ MatInput,
675
+ MatIconButton
676
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, animations: [matSelectAnimations.transformPanel], providers: [
677
+ { provide: MatFormFieldControl, useExisting: NiceAsyncTypeahead },
678
+ NiceTypeaheadService
679
+ ], host: {
680
+ "role": "combobox",
681
+ "aria-haspopup": "listbox",
682
+ "class": "nice-typeahead",
683
+ "[attr.id]": "id",
684
+ "[attr.aria-controls]": "panelOpen ? id + \"-panel\" : null",
685
+ "[attr.aria-expanded]": "panelOpen",
686
+ "[attr.aria-required]": "required.toString()",
687
+ "[attr.aria-disabled]": "disabled.toString()",
688
+ "[attr.aria-invalid]": "errorState",
689
+ "[class.nice-typeahead-disabled]": "disabled",
690
+ "[class.nice-typeahead-invalid]": "errorState",
691
+ "[class.nice-typeahead-required]": "required",
692
+ "[class.nice-typeahead-empty]": "empty",
693
+ "(keydown)": "_handleKeydown($event)",
694
+ "(focus)": "onFocusChanged(true)",
695
+ "(blur)": "onFocusChanged(false)"
696
+ }, template: "<div\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n class=\"nice-typeahead\"\n cdk-overlay-origin\n>\n <div class=\"nice-typeahead-value\">\n @if (_empty()) {\n <span class=\"nice-typeahead-placeholder mat-mdc-select-min-line\">{{ _placeholder() }}</span>\n } @else {\n <span class=\"nice-typeahead-value-text\">\n <span class=\"mat-mdc-select-min-line\">\n @if (_value(); as activeValue) {\n {{ formatLabel(activeValue) }}\n }\n </span>\n </span>\n }\n </div>\n\n <div class=\"nice-typeahead-suffix\">\n @if (_empty()) {\n <div class=\"mat-mdc-select-arrow\">\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"></path>\n </svg>\n </div>\n } @else {\n <button class=\"nice-typeahead-remove\" mat-icon-button (click)=\"$event.stopPropagation(); removeActiveValue()\">\n <svg viewBox=\"0 -960 960 960\">\n <path d=\"m291-240-51-51 189-189-189-189 51-51 189 189 189-189 51 51-189 189 189 189-51 51-189-189-189 189Z\"/>\n </svg>\n </button>\n }\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n (attach)=\"_onAttached()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"nice-typeahead-panel nice-typeahead--open\"\n [attr.id]=\"id + '-panel'\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\"\n (scrollend)=\"_handleScrollEnd()\"\n >\n <div class=\"nice-typeahead-search-input\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n #input\n class=\"nice-typeahead__input\"\n matInput\n [formControl]=\"_searchControl\"\n >\n </mat-form-field>\n </div>\n\n <div #optionsContainer class=\"nice-typeahead-options\" role=\"presentation\">\n @for (item of filteredValues(); track item) {\n <mat-option [value]=\"item\">\n {{ formatLabel(item) }}\n </mat-option>\n } @empty {\n <mat-option disabled>\n <span class=\"nice-typeahead__no-items\">No items found</span>\n </mat-option>\n }\n </div>\n </div>\n</ng-template>\n", styles: ["nice-typeahead,nice-async-typeahead{display:inline-block;width:100%;outline:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:var(--mat-select-enabled-trigger-text-color, var(--mat-app-on-surface));font-family:var(--mat-select-trigger-text-font, var(--mat-app-body-large-font));line-height:var(--mat-select-trigger-text-line-height, var(--mat-app-body-large-line-height));font-size:var(--mat-select-trigger-text-size, var(--mat-app-body-large-size));font-weight:var(--mat-select-trigger-text-weight, var(--mat-app-body-large-weight));letter-spacing:var(--mat-select-trigger-text-tracking, var(--mat-app-body-large-tracking))}nice-typeahead .nice-typeahead,nice-async-typeahead .nice-typeahead{width:100%;display:inline-flex;align-items:center;justify-content:space-between;cursor:pointer;position:relative;box-sizing:border-box}nice-typeahead .nice-typeahead-suffix,nice-async-typeahead .nice-typeahead-suffix{height:24px;flex-shrink:0;display:inline-flex;align-items:center;--mdc-icon-button-state-layer-size: 24px}nice-typeahead .nice-typeahead-suffix .nice-typeahead-remove,nice-async-typeahead .nice-typeahead-suffix .nice-typeahead-remove{margin-right:-6px}nice-typeahead .nice-typeahead-value,nice-async-typeahead .nice-typeahead-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nice-typeahead .nice-typeahead-value-text,nice-async-typeahead .nice-typeahead-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}nice-typeahead .mat-mdc-select-min-line:empty:before,nice-async-typeahead .mat-mdc-select-min-line:empty:before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}nice-typeahead .nice-typeahead__input,nice-async-typeahead .nice-typeahead__input{border:none;outline:none;box-shadow:none;background:none;padding:0;margin:0;color:inherit}.mat-mdc-form-field-type-nice-typeahead:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper{cursor:pointer}.mat-mdc-form-field-type-nice-typeahead.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}div.nice-typeahead-panel{width:100%;outline:0;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-select-panel-background-color, var(--mat-app-surface-container))}div.nice-typeahead-panel .nice-typeahead-search-input{padding-left:8px;padding-right:8px;padding-bottom:8px}div.nice-typeahead-panel .nice-typeahead-search-input .mat-mdc-form-field-infix{width:100%}div.nice-typeahead-panel .nice-typeahead-options{overflow:auto;max-height:384px}div.nice-typeahead-panel{box-shadow:var(--mat-select-container-elevation-shadow)}\n"] }]
697
+ }], ctorParameters: () => [] });
698
+
699
+ class NiceTypeahead extends NiceTypeaheadBase {
700
+ values = input.required();
701
+ searchFn = input();
702
+ filteredValues = computed(() => this.filterValuesFromSearch(this._searchValue(), this.values()));
703
+ onFocusChanged(isFocused) {
704
+ super.onFocusChanged(isFocused);
705
+ if (isFocused) {
706
+ this._searchControl.patchValue("");
707
+ }
708
+ }
709
+ filterValuesFromSearch(searchValue, values) {
710
+ if (!searchValue) {
711
+ return values;
712
+ }
713
+ const _searchValue = searchValue.toLowerCase();
714
+ const fn = this.searchFn();
715
+ if (fn) {
716
+ return values.filter((v) => fn(_searchValue, v));
717
+ }
718
+ return values.filter((v) => {
719
+ if (typeof v === "string") {
720
+ return v.toLowerCase().includes(_searchValue);
721
+ }
722
+ const property = this.labelProperty();
723
+ if (!property) {
724
+ return false;
725
+ }
726
+ if (typeof v === "object" && v && property in v) {
727
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
728
+ // @ts-ignore
729
+ return v[property].toString().toLowerCase().includes(_searchValue);
730
+ }
731
+ return false;
732
+ });
733
+ }
734
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeahead, deps: null, target: i0.ɵɵFactoryTarget.Component });
735
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: NiceTypeahead, isStandalone: true, selector: "nice-typeahead", inputs: { values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: true, transformFunction: null }, searchFn: { classPropertyName: "searchFn", publicName: "searchFn", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "aria-haspopup": "listbox" }, listeners: { "keydown": "_handleKeydown($event)", "focus": "onFocusChanged(true)", "blur": "onFocusChanged(false)" }, properties: { "attr.id": "id", "attr.aria-controls": "panelOpen ? id + \"-panel\" : null", "attr.aria-expanded": "panelOpen", "attr.aria-required": "required.toString()", "attr.aria-disabled": "disabled.toString()", "attr.aria-invalid": "errorState", "class.nice-typeahead-disabled": "disabled", "class.nice-typeahead-invalid": "errorState", "class.nice-typeahead-required": "required", "class.nice-typeahead-empty": "empty" }, classAttribute: "nice-typeahead" }, providers: [{ provide: MatFormFieldControl, useExisting: NiceTypeahead }], usesInheritance: true, ngImport: i0, template: "<div\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n class=\"nice-typeahead\"\n cdk-overlay-origin\n>\n <div class=\"nice-typeahead-value\">\n @if (_empty()) {\n <span class=\"nice-typeahead-placeholder mat-mdc-select-min-line\">{{ _placeholder() }}</span>\n } @else {\n <span class=\"nice-typeahead-value-text\">\n <span class=\"mat-mdc-select-min-line\">\n @if (_value(); as activeValue) {\n {{ formatLabel(activeValue) }}\n }\n </span>\n </span>\n }\n </div>\n\n <div class=\"nice-typeahead-suffix\">\n @if (_empty()) {\n <div class=\"mat-mdc-select-arrow\">\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"></path>\n </svg>\n </div>\n } @else {\n <button class=\"nice-typeahead-remove\" mat-icon-button (click)=\"$event.stopPropagation(); removeActiveValue()\">\n <svg viewBox=\"0 -960 960 960\">\n <path d=\"m291-240-51-51 189-189-189-189 51-51 189 189 189-189 51 51-189 189 189 189-51 51-189-189-189 189Z\"/>\n </svg>\n </button>\n }\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n (attach)=\"_onAttached()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"nice-typeahead-panel nice-typeahead--open\"\n [attr.id]=\"id + '-panel'\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\"\n (scrollend)=\"_handleScrollEnd()\"\n >\n <div class=\"nice-typeahead-search-input\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n #input\n class=\"nice-typeahead__input\"\n matInput\n [formControl]=\"_searchControl\"\n >\n </mat-form-field>\n </div>\n\n <div #optionsContainer class=\"nice-typeahead-options\" role=\"presentation\">\n @for (item of filteredValues(); track item) {\n <mat-option [value]=\"item\">\n {{ formatLabel(item) }}\n </mat-option>\n } @empty {\n <mat-option disabled>\n <span class=\"nice-typeahead__no-items\">No items found</span>\n </mat-option>\n }\n </div>\n </div>\n</ng-template>\n", styles: ["nice-typeahead,nice-async-typeahead{display:inline-block;width:100%;outline:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:var(--mat-select-enabled-trigger-text-color, var(--mat-app-on-surface));font-family:var(--mat-select-trigger-text-font, var(--mat-app-body-large-font));line-height:var(--mat-select-trigger-text-line-height, var(--mat-app-body-large-line-height));font-size:var(--mat-select-trigger-text-size, var(--mat-app-body-large-size));font-weight:var(--mat-select-trigger-text-weight, var(--mat-app-body-large-weight));letter-spacing:var(--mat-select-trigger-text-tracking, var(--mat-app-body-large-tracking))}nice-typeahead .nice-typeahead,nice-async-typeahead .nice-typeahead{width:100%;display:inline-flex;align-items:center;justify-content:space-between;cursor:pointer;position:relative;box-sizing:border-box}nice-typeahead .nice-typeahead-suffix,nice-async-typeahead .nice-typeahead-suffix{height:24px;flex-shrink:0;display:inline-flex;align-items:center;--mdc-icon-button-state-layer-size: 24px}nice-typeahead .nice-typeahead-suffix .nice-typeahead-remove,nice-async-typeahead .nice-typeahead-suffix .nice-typeahead-remove{margin-right:-6px}nice-typeahead .nice-typeahead-value,nice-async-typeahead .nice-typeahead-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nice-typeahead .nice-typeahead-value-text,nice-async-typeahead .nice-typeahead-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}nice-typeahead .mat-mdc-select-min-line:empty:before,nice-async-typeahead .mat-mdc-select-min-line:empty:before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}nice-typeahead .nice-typeahead__input,nice-async-typeahead .nice-typeahead__input{border:none;outline:none;box-shadow:none;background:none;padding:0;margin:0;color:inherit}.mat-mdc-form-field-type-nice-typeahead:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper{cursor:pointer}.mat-mdc-form-field-type-nice-typeahead.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}div.nice-typeahead-panel{width:100%;outline:0;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-select-panel-background-color, var(--mat-app-surface-container))}div.nice-typeahead-panel .nice-typeahead-search-input{padding-left:8px;padding-right:8px;padding-bottom:8px}div.nice-typeahead-panel .nice-typeahead-search-input .mat-mdc-form-field-infix{width:100%}div.nice-typeahead-panel .nice-typeahead-options{overflow:auto;max-height:384px}div.nice-typeahead-panel{box-shadow:var(--mat-select-container-elevation-shadow)}\n"], dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }], animations: [matSelectAnimations.transformPanel], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
736
+ }
737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NiceTypeahead, decorators: [{
738
+ type: Component,
739
+ args: [{ selector: "nice-typeahead", standalone: true, imports: [
740
+ CdkOverlayOrigin,
741
+ CdkConnectedOverlay,
742
+ ReactiveFormsModule,
743
+ MatOption,
744
+ MatFormField,
745
+ MatInput,
746
+ MatIconButton
747
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, animations: [matSelectAnimations.transformPanel], providers: [{ provide: MatFormFieldControl, useExisting: NiceTypeahead }], host: {
748
+ "role": "combobox",
749
+ "aria-haspopup": "listbox",
750
+ "class": "nice-typeahead",
751
+ "[attr.id]": "id",
752
+ "[attr.aria-controls]": "panelOpen ? id + \"-panel\" : null",
753
+ "[attr.aria-expanded]": "panelOpen",
754
+ "[attr.aria-required]": "required.toString()",
755
+ "[attr.aria-disabled]": "disabled.toString()",
756
+ "[attr.aria-invalid]": "errorState",
757
+ "[class.nice-typeahead-disabled]": "disabled",
758
+ "[class.nice-typeahead-invalid]": "errorState",
759
+ "[class.nice-typeahead-required]": "required",
760
+ "[class.nice-typeahead-empty]": "empty",
761
+ "(keydown)": "_handleKeydown($event)",
762
+ "(focus)": "onFocusChanged(true)",
763
+ "(blur)": "onFocusChanged(false)"
764
+ }, template: "<div\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n class=\"nice-typeahead\"\n cdk-overlay-origin\n>\n <div class=\"nice-typeahead-value\">\n @if (_empty()) {\n <span class=\"nice-typeahead-placeholder mat-mdc-select-min-line\">{{ _placeholder() }}</span>\n } @else {\n <span class=\"nice-typeahead-value-text\">\n <span class=\"mat-mdc-select-min-line\">\n @if (_value(); as activeValue) {\n {{ formatLabel(activeValue) }}\n }\n </span>\n </span>\n }\n </div>\n\n <div class=\"nice-typeahead-suffix\">\n @if (_empty()) {\n <div class=\"mat-mdc-select-arrow\">\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"></path>\n </svg>\n </div>\n } @else {\n <button class=\"nice-typeahead-remove\" mat-icon-button (click)=\"$event.stopPropagation(); removeActiveValue()\">\n <svg viewBox=\"0 -960 960 960\">\n <path d=\"m291-240-51-51 189-189-189-189 51-51 189 189 189-189 51 51-189 189 189 189-51 51-189-189-189 189Z\"/>\n </svg>\n </button>\n }\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n (attach)=\"_onAttached()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"nice-typeahead-panel nice-typeahead--open\"\n [attr.id]=\"id + '-panel'\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\"\n (scrollend)=\"_handleScrollEnd()\"\n >\n <div class=\"nice-typeahead-search-input\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n #input\n class=\"nice-typeahead__input\"\n matInput\n [formControl]=\"_searchControl\"\n >\n </mat-form-field>\n </div>\n\n <div #optionsContainer class=\"nice-typeahead-options\" role=\"presentation\">\n @for (item of filteredValues(); track item) {\n <mat-option [value]=\"item\">\n {{ formatLabel(item) }}\n </mat-option>\n } @empty {\n <mat-option disabled>\n <span class=\"nice-typeahead__no-items\">No items found</span>\n </mat-option>\n }\n </div>\n </div>\n</ng-template>\n", styles: ["nice-typeahead,nice-async-typeahead{display:inline-block;width:100%;outline:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:var(--mat-select-enabled-trigger-text-color, var(--mat-app-on-surface));font-family:var(--mat-select-trigger-text-font, var(--mat-app-body-large-font));line-height:var(--mat-select-trigger-text-line-height, var(--mat-app-body-large-line-height));font-size:var(--mat-select-trigger-text-size, var(--mat-app-body-large-size));font-weight:var(--mat-select-trigger-text-weight, var(--mat-app-body-large-weight));letter-spacing:var(--mat-select-trigger-text-tracking, var(--mat-app-body-large-tracking))}nice-typeahead .nice-typeahead,nice-async-typeahead .nice-typeahead{width:100%;display:inline-flex;align-items:center;justify-content:space-between;cursor:pointer;position:relative;box-sizing:border-box}nice-typeahead .nice-typeahead-suffix,nice-async-typeahead .nice-typeahead-suffix{height:24px;flex-shrink:0;display:inline-flex;align-items:center;--mdc-icon-button-state-layer-size: 24px}nice-typeahead .nice-typeahead-suffix .nice-typeahead-remove,nice-async-typeahead .nice-typeahead-suffix .nice-typeahead-remove{margin-right:-6px}nice-typeahead .nice-typeahead-value,nice-async-typeahead .nice-typeahead-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nice-typeahead .nice-typeahead-value-text,nice-async-typeahead .nice-typeahead-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}nice-typeahead .mat-mdc-select-min-line:empty:before,nice-async-typeahead .mat-mdc-select-min-line:empty:before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}nice-typeahead .nice-typeahead__input,nice-async-typeahead .nice-typeahead__input{border:none;outline:none;box-shadow:none;background:none;padding:0;margin:0;color:inherit}.mat-mdc-form-field-type-nice-typeahead:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper{cursor:pointer}.mat-mdc-form-field-type-nice-typeahead.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}div.nice-typeahead-panel{width:100%;outline:0;padding:8px 0;border-radius:4px;box-sizing:border-box;position:static;background-color:var(--mat-select-panel-background-color, var(--mat-app-surface-container))}div.nice-typeahead-panel .nice-typeahead-search-input{padding-left:8px;padding-right:8px;padding-bottom:8px}div.nice-typeahead-panel .nice-typeahead-search-input .mat-mdc-form-field-infix{width:100%}div.nice-typeahead-panel .nice-typeahead-options{overflow:auto;max-height:384px}div.nice-typeahead-panel{box-shadow:var(--mat-select-container-elevation-shadow)}\n"] }]
765
+ }] });
766
+
767
+ /**
768
+ * Generated bundle index. Do not edit.
769
+ */
770
+
771
+ export { NiceAsyncTypeahead, NiceAsyncTypeaheadResourceProvider, NiceTypeahead, NiceTypeaheadService, provideAsyncTypeaheadResources };
772
+ //# sourceMappingURL=recursyve-ngx-material-components-typeahead.mjs.map