@js-smart/ng-kit 21.7.1 → 21.9.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ChangeDetectorRef, input, signal, output, effect, ChangeDetectionStrategy, Component, Pipe, Injectable, HostListener, ViewChild, viewChild, computed, forwardRef, DOCUMENT, Input, Directive, ElementRef, ViewContainerRef, Inject } from '@angular/core';
2
+ import { inject, ChangeDetectorRef, input, signal, output, effect, ChangeDetectionStrategy, Component, Pipe, Injectable, HostListener, ViewChild, viewChild, computed, forwardRef, ElementRef, Renderer2, DestroyRef, Directive, DOCUMENT, Input, ViewContainerRef, Inject } from '@angular/core';
3
3
  import { trigger, state, transition, style, animate } from '@angular/animations';
4
4
  import { BehaviorSubject, Subject, throttleTime } from 'rxjs';
5
5
  import { filter, takeUntil } from 'rxjs/operators';
@@ -8,9 +8,9 @@ import * as i1$1 from '@angular/material/progress-spinner';
8
8
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
9
9
  import { NgClass } from '@angular/common';
10
10
  import * as i1$2 from '@angular/forms';
11
- import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
11
+ import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
12
12
  import * as i2 from '@angular/material/autocomplete';
13
- import { MatAutocompleteTrigger, MatAutocompleteModule } from '@angular/material/autocomplete';
13
+ import { MatAutocompleteTrigger, MatAutocomplete, MatAutocompleteModule } from '@angular/material/autocomplete';
14
14
  import * as i1$4 from '@angular/material/button';
15
15
  import { MatButtonModule, MatAnchor, MatButton } from '@angular/material/button';
16
16
  import { MatFormFieldModule } from '@angular/material/form-field';
@@ -421,6 +421,8 @@ class AutocompleteComponent {
421
421
  constructor() {
422
422
  /** Gets reference to the MatAutocompleteTrigger to programmatically open/close the panel */
423
423
  this.autocompleteTrigger = viewChild.required(MatAutocompleteTrigger);
424
+ /** MatAutocomplete instance (used to scroll the selected option into view when the panel opens) */
425
+ this.matAutocomplete = viewChild.required(MatAutocomplete);
424
426
  /** Gets reference to the input element for re-focusing after clear */
425
427
  this.inputElement = viewChild.required('inputEl');
426
428
  /** Label of the autocomplete form field */
@@ -447,8 +449,10 @@ class AutocompleteComponent {
447
449
  this.noOptionsText = input('No values found', ...(ngDevMode ? [{ debugName: "noOptionsText" }] : []));
448
450
  /** Emits the selected value when an option is picked from the dropdown */
449
451
  this.selectionChange = output();
452
+ /** Emits the raw text in the input on each keystroke (filter text while typing) */
453
+ this.onInputChange = output();
450
454
  /** Internal form control for the autocomplete input */
451
- this.control = new FormControl('');
455
+ this.control = new FormControl(null);
452
456
  /** Signal that tracks whether the autocomplete panel is currently open */
453
457
  this.isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
454
458
  /** Signal that tracks the current filter text typed by the user */
@@ -468,7 +472,7 @@ class AutocompleteComponent {
468
472
  * selected value in the input field. Returns empty string for null/undefined values.
469
473
  */
470
474
  this.displayFn = (value) => {
471
- if (value == null) {
475
+ if (value == null || value === '') {
472
476
  return '';
473
477
  }
474
478
  return this.displayWith()(value);
@@ -487,7 +491,9 @@ class AutocompleteComponent {
487
491
  * when the form control value is set programmatically.
488
492
  */
489
493
  writeValue(value) {
490
- this.control.setValue(value ?? '', { emitEvent: false });
494
+ // Use null (not '') so MatAutocompleteTrigger clears mat-option selection and stays in sync with the parent CVA.
495
+ const internal = value == null ? null : value;
496
+ this.control.setValue(internal, { emitEvent: false });
491
497
  }
492
498
  /**
493
499
  * Registers a callback function that is called when the control's value
@@ -519,18 +525,38 @@ class AutocompleteComponent {
519
525
  onInput(event) {
520
526
  const value = event.target.value;
521
527
  this.filterText.set(value);
528
+ this.onInputChange.emit(value);
522
529
  }
523
530
  /**
524
531
  * Clears the input value, resets the filter, notifies the parent form,
525
532
  * and re-focuses the input element.
526
533
  */
527
534
  clearInput(event) {
528
- this.control.setValue('');
535
+ event.preventDefault();
536
+ event.stopPropagation();
537
+ this.autocompleteTrigger().closePanel();
538
+ this.control.setValue(null, { emitEvent: false });
529
539
  this.filterText.set('');
530
540
  this.onChange(null);
531
541
  this.onTouched();
532
- event.stopPropagation();
533
- this.inputElement().nativeElement.focus();
542
+ queueMicrotask(() => this.inputElement().nativeElement.focus());
543
+ }
544
+ /** Runs when the autocomplete overlay opens: keep expanded state and scroll the selected option into view. */
545
+ onPanelOpened() {
546
+ this.isExpanded.set(true);
547
+ this.scrollSelectedOptionIntoView();
548
+ }
549
+ scrollSelectedOptionIntoView() {
550
+ requestAnimationFrame(() => {
551
+ requestAnimationFrame(() => {
552
+ const panelEl = this.matAutocomplete().panel?.nativeElement;
553
+ if (!panelEl) {
554
+ return;
555
+ }
556
+ const selected = panelEl.querySelector('.mat-mdc-option.mdc-list-item--selected');
557
+ selected?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
558
+ });
559
+ });
534
560
  }
535
561
  /** Opens the autocomplete panel programmatically */
536
562
  openPanel() {
@@ -547,13 +573,13 @@ class AutocompleteComponent {
547
573
  this.selectionChange.emit(value);
548
574
  }
549
575
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
550
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: AutocompleteComponent, isStandalone: true, selector: "autocomplete, lib-autocomplete", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, noOptionsText: { classPropertyName: "noOptionsText", publicName: "noOptionsText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, providers: [
576
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: AutocompleteComponent, isStandalone: true, selector: "autocomplete, lib-autocomplete", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, noOptionsText: { classPropertyName: "noOptionsText", publicName: "noOptionsText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", onInputChange: "onInputChange" }, providers: [
551
577
  {
552
578
  provide: NG_VALUE_ACCESSOR,
553
579
  useExisting: forwardRef(() => AutocompleteComponent),
554
580
  multi: true,
555
581
  },
556
- ], viewQueries: [{ propertyName: "autocompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }, { propertyName: "inputElement", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field\n\t#origin=\"matAutocompleteOrigin\"\n\t[appearance]=\"appearance()\"\n\t[ngClass]=\"classes()\"\n\tmatAutocompleteOrigin\n\tsubscriptSizing=\"dynamic\">\n\t<mat-label>{{ label() }}</mat-label>\n\n\t<input\n\t\t#inputEl\n\t\t#trigger=\"matAutocompleteTrigger\"\n\t\t(click)=\"trigger.openPanel()\"\n\t\t(input)=\"onInput($event)\"\n\t\t[formControl]=\"control\"\n\t\t[matAutocompleteConnectedTo]=\"origin\"\n\t\t[matAutocomplete]=\"auto\"\n\t\t[placeholder]=\"placeholder()\"\n\t\tmatInput\n\t\ttype=\"text\" />\n\n\t<div class=\"autocomplete-suffix\" matSuffix>\n\t\t@if (control.value) {\n\t\t\t<button type=\"button\" (click)=\"clearInput($event)\" class=\"suffix-action-btn\" mat-icon-button>\n\t\t\t\t<mat-icon>close</mat-icon>\n\t\t\t</button>\n\t\t} @else {\n\t\t\t<span aria-hidden=\"true\" class=\"suffix-placeholder\"></span>\n\t\t}\n\t\t<button\n\t\t\t(click)=\"trigger.panelOpen ? trigger.closePanel() : trigger.openPanel(); $event.stopPropagation()\"\n\t\t\t[attr.aria-label]=\"trigger.panelOpen ? 'Close options' : 'Open options'\"\n\t\t\tclass=\"suffix-action-btn\"\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\">\n\t\t\t@if (isExpanded()) {\n\t\t\t\t<mat-icon>arrow_drop_up</mat-icon>\n\t\t\t} @else {\n\t\t\t\t<mat-icon>arrow_drop_down</mat-icon>\n\t\t\t}\n\t\t</button>\n\t</div>\n\n\t<mat-autocomplete\n\t\t#auto=\"matAutocomplete\"\n\t\t(closed)=\"isExpanded.set(false)\"\n\t\t(opened)=\"isExpanded.set(true)\"\n\t\t(optionSelected)=\"onOptionSelected($event.option.value)\"\n\t\t[displayWith]=\"displayFn\">\n\t\t@if (loading()) {\n\t\t\t<mat-option disabled>\n\t\t\t\t<div style=\"display: flex; align-items: center; gap: 8px\">\n\t\t\t\t\t<mat-spinner diameter=\"20\"></mat-spinner>\n\t\t\t\t\t<span>{{ loadingText() }}</span>\n\t\t\t\t</div>\n\t\t\t</mat-option>\n\t\t} @else {\n\t\t\t@for (option of filteredOptions(); track option) {\n\t\t\t\t<mat-option [value]=\"option\">{{ displayWith()(option) }}</mat-option>\n\t\t\t} @empty {\n\t\t\t\t<mat-option disabled>{{ noOptionsText() }}</mat-option>\n\t\t\t}\n\t\t}\n\t</mat-autocomplete>\n</mat-form-field>\n", styles: [".autocomplete-suffix{display:flex;align-items:center}.suffix-action-btn{width:40px;height:40px;padding:0}.suffix-placeholder{display:inline-block;width:40px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "directive", type: i2.MatAutocompleteOrigin, selector: "[matAutocompleteOrigin]", exportAs: ["matAutocompleteOrigin"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i1$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
582
+ ], viewQueries: [{ propertyName: "autocompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }, { propertyName: "matAutocomplete", first: true, predicate: MatAutocomplete, descendants: true, isSignal: true }, { propertyName: "inputElement", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field\n\t#origin=\"matAutocompleteOrigin\"\n\t[appearance]=\"appearance()\"\n\t[ngClass]=\"classes()\"\n\tmatAutocompleteOrigin\n\tsubscriptSizing=\"dynamic\">\n\t<mat-label>{{ label() }}</mat-label>\n\n\t<input\n\t\t#inputEl\n\t\t#trigger=\"matAutocompleteTrigger\"\n\t\t(click)=\"trigger.openPanel()\"\n\t\t(input)=\"onInput($event)\"\n\t\t[formControl]=\"control\"\n\t\t[matAutocompleteConnectedTo]=\"origin\"\n\t\t[matAutocomplete]=\"auto\"\n\t\t[placeholder]=\"placeholder()\"\n\t\tmatInput\n\t\ttype=\"text\" />\n\n\t<div class=\"autocomplete-suffix\" matSuffix>\n\t\t@if (control.value) {\n\t\t\t<button type=\"button\" (click)=\"clearInput($event)\" class=\"suffix-action-btn\" mat-icon-button>\n\t\t\t\t<mat-icon>close</mat-icon>\n\t\t\t</button>\n\t\t} @else {\n\t\t\t<span aria-hidden=\"true\" class=\"suffix-placeholder\"></span>\n\t\t}\n\t\t<button\n\t\t\t(click)=\"trigger.panelOpen ? trigger.closePanel() : trigger.openPanel(); $event.stopPropagation()\"\n\t\t\t[attr.aria-label]=\"trigger.panelOpen ? 'Close options' : 'Open options'\"\n\t\t\tclass=\"suffix-action-btn\"\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\">\n\t\t\t@if (isExpanded()) {\n\t\t\t\t<mat-icon>arrow_drop_up</mat-icon>\n\t\t\t} @else {\n\t\t\t\t<mat-icon>arrow_drop_down</mat-icon>\n\t\t\t}\n\t\t</button>\n\t</div>\n\n\t<mat-autocomplete\n\t\t#auto=\"matAutocomplete\"\n\t\t(closed)=\"isExpanded.set(false)\"\n\t\t(opened)=\"onPanelOpened()\"\n\t\t(optionSelected)=\"onOptionSelected($event.option.value)\"\n\t\t[displayWith]=\"displayFn\">\n\t\t@if (loading()) {\n\t\t\t<mat-option disabled>\n\t\t\t\t<div style=\"display: flex; align-items: center; gap: 8px\">\n\t\t\t\t\t<mat-spinner diameter=\"20\"></mat-spinner>\n\t\t\t\t\t<span>{{ loadingText() }}</span>\n\t\t\t\t</div>\n\t\t\t</mat-option>\n\t\t} @else {\n\t\t\t@for (option of filteredOptions(); track option) {\n\t\t\t\t<mat-option [value]=\"option\">{{ displayWith()(option) }}</mat-option>\n\t\t\t} @empty {\n\t\t\t\t<mat-option disabled>{{ noOptionsText() }}</mat-option>\n\t\t\t}\n\t\t}\n\t</mat-autocomplete>\n</mat-form-field>\n", styles: [".autocomplete-suffix{display:flex;align-items:center}.suffix-action-btn{width:40px;height:40px;padding:0}.suffix-placeholder{display:inline-block;width:40px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "directive", type: i2.MatAutocompleteOrigin, selector: "[matAutocompleteOrigin]", exportAs: ["matAutocompleteOrigin"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i1$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
557
583
  }
558
584
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AutocompleteComponent, decorators: [{
559
585
  type: Component,
@@ -572,8 +598,194 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
572
598
  useExisting: forwardRef(() => AutocompleteComponent),
573
599
  multi: true,
574
600
  },
575
- ], template: "<mat-form-field\n\t#origin=\"matAutocompleteOrigin\"\n\t[appearance]=\"appearance()\"\n\t[ngClass]=\"classes()\"\n\tmatAutocompleteOrigin\n\tsubscriptSizing=\"dynamic\">\n\t<mat-label>{{ label() }}</mat-label>\n\n\t<input\n\t\t#inputEl\n\t\t#trigger=\"matAutocompleteTrigger\"\n\t\t(click)=\"trigger.openPanel()\"\n\t\t(input)=\"onInput($event)\"\n\t\t[formControl]=\"control\"\n\t\t[matAutocompleteConnectedTo]=\"origin\"\n\t\t[matAutocomplete]=\"auto\"\n\t\t[placeholder]=\"placeholder()\"\n\t\tmatInput\n\t\ttype=\"text\" />\n\n\t<div class=\"autocomplete-suffix\" matSuffix>\n\t\t@if (control.value) {\n\t\t\t<button type=\"button\" (click)=\"clearInput($event)\" class=\"suffix-action-btn\" mat-icon-button>\n\t\t\t\t<mat-icon>close</mat-icon>\n\t\t\t</button>\n\t\t} @else {\n\t\t\t<span aria-hidden=\"true\" class=\"suffix-placeholder\"></span>\n\t\t}\n\t\t<button\n\t\t\t(click)=\"trigger.panelOpen ? trigger.closePanel() : trigger.openPanel(); $event.stopPropagation()\"\n\t\t\t[attr.aria-label]=\"trigger.panelOpen ? 'Close options' : 'Open options'\"\n\t\t\tclass=\"suffix-action-btn\"\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\">\n\t\t\t@if (isExpanded()) {\n\t\t\t\t<mat-icon>arrow_drop_up</mat-icon>\n\t\t\t} @else {\n\t\t\t\t<mat-icon>arrow_drop_down</mat-icon>\n\t\t\t}\n\t\t</button>\n\t</div>\n\n\t<mat-autocomplete\n\t\t#auto=\"matAutocomplete\"\n\t\t(closed)=\"isExpanded.set(false)\"\n\t\t(opened)=\"isExpanded.set(true)\"\n\t\t(optionSelected)=\"onOptionSelected($event.option.value)\"\n\t\t[displayWith]=\"displayFn\">\n\t\t@if (loading()) {\n\t\t\t<mat-option disabled>\n\t\t\t\t<div style=\"display: flex; align-items: center; gap: 8px\">\n\t\t\t\t\t<mat-spinner diameter=\"20\"></mat-spinner>\n\t\t\t\t\t<span>{{ loadingText() }}</span>\n\t\t\t\t</div>\n\t\t\t</mat-option>\n\t\t} @else {\n\t\t\t@for (option of filteredOptions(); track option) {\n\t\t\t\t<mat-option [value]=\"option\">{{ displayWith()(option) }}</mat-option>\n\t\t\t} @empty {\n\t\t\t\t<mat-option disabled>{{ noOptionsText() }}</mat-option>\n\t\t\t}\n\t\t}\n\t</mat-autocomplete>\n</mat-form-field>\n", styles: [".autocomplete-suffix{display:flex;align-items:center}.suffix-action-btn{width:40px;height:40px;padding:0}.suffix-placeholder{display:inline-block;width:40px}\n"] }]
576
- }], propDecorators: { autocompleteTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocompleteTrigger), { isSignal: true }] }], inputElement: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], noOptionsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noOptionsText", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
601
+ ], template: "<mat-form-field\n\t#origin=\"matAutocompleteOrigin\"\n\t[appearance]=\"appearance()\"\n\t[ngClass]=\"classes()\"\n\tmatAutocompleteOrigin\n\tsubscriptSizing=\"dynamic\">\n\t<mat-label>{{ label() }}</mat-label>\n\n\t<input\n\t\t#inputEl\n\t\t#trigger=\"matAutocompleteTrigger\"\n\t\t(click)=\"trigger.openPanel()\"\n\t\t(input)=\"onInput($event)\"\n\t\t[formControl]=\"control\"\n\t\t[matAutocompleteConnectedTo]=\"origin\"\n\t\t[matAutocomplete]=\"auto\"\n\t\t[placeholder]=\"placeholder()\"\n\t\tmatInput\n\t\ttype=\"text\" />\n\n\t<div class=\"autocomplete-suffix\" matSuffix>\n\t\t@if (control.value) {\n\t\t\t<button type=\"button\" (click)=\"clearInput($event)\" class=\"suffix-action-btn\" mat-icon-button>\n\t\t\t\t<mat-icon>close</mat-icon>\n\t\t\t</button>\n\t\t} @else {\n\t\t\t<span aria-hidden=\"true\" class=\"suffix-placeholder\"></span>\n\t\t}\n\t\t<button\n\t\t\t(click)=\"trigger.panelOpen ? trigger.closePanel() : trigger.openPanel(); $event.stopPropagation()\"\n\t\t\t[attr.aria-label]=\"trigger.panelOpen ? 'Close options' : 'Open options'\"\n\t\t\tclass=\"suffix-action-btn\"\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\">\n\t\t\t@if (isExpanded()) {\n\t\t\t\t<mat-icon>arrow_drop_up</mat-icon>\n\t\t\t} @else {\n\t\t\t\t<mat-icon>arrow_drop_down</mat-icon>\n\t\t\t}\n\t\t</button>\n\t</div>\n\n\t<mat-autocomplete\n\t\t#auto=\"matAutocomplete\"\n\t\t(closed)=\"isExpanded.set(false)\"\n\t\t(opened)=\"onPanelOpened()\"\n\t\t(optionSelected)=\"onOptionSelected($event.option.value)\"\n\t\t[displayWith]=\"displayFn\">\n\t\t@if (loading()) {\n\t\t\t<mat-option disabled>\n\t\t\t\t<div style=\"display: flex; align-items: center; gap: 8px\">\n\t\t\t\t\t<mat-spinner diameter=\"20\"></mat-spinner>\n\t\t\t\t\t<span>{{ loadingText() }}</span>\n\t\t\t\t</div>\n\t\t\t</mat-option>\n\t\t} @else {\n\t\t\t@for (option of filteredOptions(); track option) {\n\t\t\t\t<mat-option [value]=\"option\">{{ displayWith()(option) }}</mat-option>\n\t\t\t} @empty {\n\t\t\t\t<mat-option disabled>{{ noOptionsText() }}</mat-option>\n\t\t\t}\n\t\t}\n\t</mat-autocomplete>\n</mat-form-field>\n", styles: [".autocomplete-suffix{display:flex;align-items:center}.suffix-action-btn{width:40px;height:40px;padding:0}.suffix-placeholder{display:inline-block;width:40px}\n"] }]
602
+ }], propDecorators: { autocompleteTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocompleteTrigger), { isSignal: true }] }], matAutocomplete: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocomplete), { isSignal: true }] }], inputElement: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], noOptionsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noOptionsText", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], onInputChange: [{ type: i0.Output, args: ["onInputChange"] }] } });
603
+
604
+ /**
605
+ * Single directive that adds clear and dropdown-toggle icon buttons to a
606
+ * Material autocomplete input. Unlike {@link LibAutocompleteActionsDirective},
607
+ * this directive is applied directly to the `input` element and does not require
608
+ * an anchor directive or a separate suffix component.
609
+ *
610
+ * Usage:
611
+ * ```html
612
+ * <mat-form-field appearance="outline">
613
+ * <mat-label>City</mat-label>
614
+ * <input matInput [matAutocomplete]="auto" formControlName="city" autocompleteSuffix />
615
+ * <mat-autocomplete #auto="matAutocomplete"> … </mat-autocomplete>
616
+ * </mat-form-field>
617
+ * ```
618
+ *
619
+ * @author Pavan Kumar Jadda
620
+ * @since 21.8.0
621
+ */
622
+ class AutocompleteSuffixDirective {
623
+ constructor() {
624
+ this.trigger = inject(MatAutocompleteTrigger);
625
+ this.el = inject((ElementRef));
626
+ this.renderer = inject(Renderer2);
627
+ this.destroyRef = inject(DestroyRef);
628
+ this.ngControl = inject(NgControl, { optional: true, self: true });
629
+ this.clearBtn = null;
630
+ this.clearPlaceholder = null;
631
+ this.dropdownIconSpan = null;
632
+ this.isExpanded = false;
633
+ this.unlisten = [];
634
+ }
635
+ ngAfterViewInit() {
636
+ this.createSuffixElements();
637
+ this.setupListeners();
638
+ this.updateClearButtonVisibility();
639
+ }
640
+ createSuffixElements() {
641
+ const formField = this.el.nativeElement.closest('mat-form-field');
642
+ if (!formField) {
643
+ return;
644
+ }
645
+ const flexContainer = formField.querySelector('.mat-mdc-form-field-flex');
646
+ if (!flexContainer) {
647
+ return;
648
+ }
649
+ // Find or create the suffix wrapper
650
+ let suffixWrapper = flexContainer.querySelector('.mat-mdc-form-field-icon-suffix');
651
+ if (!suffixWrapper) {
652
+ suffixWrapper = this.renderer.createElement('div');
653
+ this.renderer.addClass(suffixWrapper, 'mat-mdc-form-field-icon-suffix');
654
+ this.renderer.setAttribute(suffixWrapper, 'data-mat-icon-type', 'font');
655
+ this.renderer.appendChild(flexContainer, suffixWrapper);
656
+ }
657
+ const container = this.renderer.createElement('div');
658
+ this.renderer.setStyle(container, 'display', 'flex');
659
+ this.renderer.setStyle(container, 'align-items', 'center');
660
+ // Clear button (hidden when input is empty)
661
+ this.clearBtn = this.createIconButton('close', 'Clear value', (e) => this.clearInput(e));
662
+ this.renderer.setStyle(this.clearBtn, 'display', 'none');
663
+ // Placeholder keeps layout stable when clear button is hidden
664
+ this.clearPlaceholder = this.renderer.createElement('span');
665
+ this.renderer.setStyle(this.clearPlaceholder, 'display', 'inline-block');
666
+ this.renderer.setStyle(this.clearPlaceholder, 'width', '35px');
667
+ // Dropdown toggle button
668
+ const dropdownBtn = this.createIconButton('arrow_drop_down', 'Open options', (e) => this.togglePanel(e));
669
+ this.dropdownIconSpan = dropdownBtn.querySelector('.material-icons');
670
+ this.renderer.appendChild(container, this.clearBtn);
671
+ this.renderer.appendChild(container, this.clearPlaceholder);
672
+ this.renderer.appendChild(container, dropdownBtn);
673
+ this.renderer.appendChild(suffixWrapper, container);
674
+ }
675
+ createIconButton(iconName, ariaLabel, handler) {
676
+ const button = this.renderer.createElement('button');
677
+ this.renderer.setAttribute(button, 'type', 'button');
678
+ this.renderer.setAttribute(button, 'aria-label', ariaLabel);
679
+ this.renderer.setStyle(button, 'width', '35px');
680
+ this.renderer.setStyle(button, 'height', '35px');
681
+ this.renderer.setStyle(button, 'padding', '0');
682
+ this.renderer.setStyle(button, 'border', 'none');
683
+ this.renderer.setStyle(button, 'background', 'transparent');
684
+ this.renderer.setStyle(button, 'cursor', 'pointer');
685
+ this.renderer.setStyle(button, 'display', 'inline-flex');
686
+ this.renderer.setStyle(button, 'align-items', 'center');
687
+ this.renderer.setStyle(button, 'justify-content', 'center');
688
+ this.renderer.setStyle(button, 'border-radius', '50%');
689
+ this.renderer.setStyle(button, 'color', 'inherit');
690
+ const icon = this.renderer.createElement('span');
691
+ this.renderer.addClass(icon, 'material-icons');
692
+ this.renderer.setStyle(icon, 'font-size', '24px');
693
+ this.renderer.appendChild(icon, this.renderer.createText(iconName));
694
+ this.renderer.appendChild(button, icon);
695
+ this.unlisten.push(this.renderer.listen(button, 'click', handler));
696
+ return button;
697
+ }
698
+ setupListeners() {
699
+ // Track user typing
700
+ this.unlisten.push(this.renderer.listen(this.el.nativeElement, 'input', () => this.updateClearButtonVisibility()));
701
+ const autocomplete = this.trigger.autocomplete;
702
+ // Track autocomplete panel open/close
703
+ const openedSub = autocomplete.opened.subscribe(() => {
704
+ this.isExpanded = true;
705
+ this.updateDropdownIcon();
706
+ this.scrollSelectedOptionIntoView();
707
+ });
708
+ const closedSub = autocomplete.closed.subscribe(() => {
709
+ this.isExpanded = false;
710
+ this.updateDropdownIcon();
711
+ this.updateClearButtonVisibility();
712
+ });
713
+ // Track option selection
714
+ const selectedSub = autocomplete.optionSelected.subscribe(() => this.updateClearButtonVisibility());
715
+ // Track programmatic value changes via NgControl
716
+ let valueChangesSub = null;
717
+ if (this.ngControl?.control) {
718
+ valueChangesSub = this.ngControl.control.valueChanges.subscribe(() => this.updateClearButtonVisibility());
719
+ }
720
+ this.destroyRef.onDestroy(() => {
721
+ this.unlisten.forEach((fn) => fn());
722
+ openedSub.unsubscribe();
723
+ closedSub.unsubscribe();
724
+ selectedSub.unsubscribe();
725
+ valueChangesSub?.unsubscribe();
726
+ });
727
+ }
728
+ clearInput(event) {
729
+ event.preventDefault();
730
+ event.stopPropagation();
731
+ this.trigger.closePanel();
732
+ this.el.nativeElement.value = '';
733
+ if (this.ngControl?.control) {
734
+ this.ngControl.control.setValue(null, { emitEvent: true });
735
+ this.ngControl.control.markAsTouched();
736
+ }
737
+ this.updateClearButtonVisibility();
738
+ queueMicrotask(() => this.el.nativeElement.focus());
739
+ }
740
+ togglePanel(event) {
741
+ event.stopPropagation();
742
+ if (this.trigger.panelOpen) {
743
+ this.trigger.closePanel();
744
+ }
745
+ else {
746
+ this.trigger.openPanel();
747
+ }
748
+ }
749
+ updateClearButtonVisibility() {
750
+ const hasValue = !!(this.el.nativeElement.value || this.ngControl?.control?.value);
751
+ if (this.clearBtn) {
752
+ this.renderer.setStyle(this.clearBtn, 'display', hasValue ? 'inline-flex' : 'none');
753
+ }
754
+ if (this.clearPlaceholder) {
755
+ this.renderer.setStyle(this.clearPlaceholder, 'display', hasValue ? 'none' : 'inline-block');
756
+ }
757
+ }
758
+ scrollSelectedOptionIntoView() {
759
+ requestAnimationFrame(() => {
760
+ requestAnimationFrame(() => {
761
+ const panelEl = this.trigger.autocomplete.panel?.nativeElement;
762
+ if (!panelEl) {
763
+ return;
764
+ }
765
+ const selected = panelEl.querySelector('.mat-mdc-option.mdc-list-item--selected');
766
+ selected?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
767
+ });
768
+ });
769
+ }
770
+ updateDropdownIcon() {
771
+ if (this.dropdownIconSpan) {
772
+ this.dropdownIconSpan.textContent = this.isExpanded ? 'arrow_drop_up' : 'arrow_drop_down';
773
+ }
774
+ const button = this.dropdownIconSpan?.parentElement;
775
+ if (button) {
776
+ this.renderer.setAttribute(button, 'aria-label', this.isExpanded ? 'Close options' : 'Open options');
777
+ }
778
+ }
779
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AutocompleteSuffixDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
780
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: AutocompleteSuffixDirective, isStandalone: true, selector: "input[matAutocomplete][autocompleteSuffix]", ngImport: i0 }); }
781
+ }
782
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AutocompleteSuffixDirective, decorators: [{
783
+ type: Directive,
784
+ args: [{
785
+ selector: 'input[matAutocomplete][autocompleteSuffix]',
786
+ standalone: true,
787
+ }]
788
+ }] });
577
789
 
578
790
  class PrintOptions {
579
791
  constructor(options) {
@@ -2427,5 +2639,5 @@ const markError = (progressState, message) => {
2427
2639
  * Generated bundle index. Do not edit.
2428
2640
  */
2429
2641
 
2430
- export { AlertComponent, AutocompleteComponent, BaseButtonDirective, BsLinkButtonComponent, BsLinkButtonDirective, CloseButtonDirective, ConfirmDialogComponent, DeleteButtonComponent, DeleteButtonDirective, EditBsButtonComponent, EditBsButtonDirective, EditButtonComponent, EditButtonDirective, EditSolidSvgComponent, EditSvgIconButtonComponent, EditSvgIconButtonDirective, EntityStore, ExcelExportButtonComponent, ExcelExportButtonDirective, ManageButtonComponent, ManageButtonDirective, MatSnackBarService, NgxPrintDirective, NgxSpinnerComponent, NgxSpinnerService, PdfExportButtonComponent, PdfExportButtonDirective, PreventMultipleClicksDirective, PrimaryButtonComponent, PrimaryButtonDirective, SavePrimaryButtonComponent, SavePrimaryButtonDirective, SearchButtonComponent, SpinnerComponent, Store, SuccessButtonComponent, SuccessButtonDirective, ViewButtonComponent, ViewButtonDirective, ViewPrimaryButtonComponent, ViewPrimaryButtonDirective, initializeState, markError, markLoading, markSuccess };
2642
+ export { AlertComponent, AutocompleteComponent, AutocompleteSuffixDirective, BaseButtonDirective, BsLinkButtonComponent, BsLinkButtonDirective, CloseButtonDirective, ConfirmDialogComponent, DeleteButtonComponent, DeleteButtonDirective, EditBsButtonComponent, EditBsButtonDirective, EditButtonComponent, EditButtonDirective, EditSolidSvgComponent, EditSvgIconButtonComponent, EditSvgIconButtonDirective, EntityStore, ExcelExportButtonComponent, ExcelExportButtonDirective, ManageButtonComponent, ManageButtonDirective, MatSnackBarService, NgxPrintDirective, NgxSpinnerComponent, NgxSpinnerService, PdfExportButtonComponent, PdfExportButtonDirective, PreventMultipleClicksDirective, PrimaryButtonComponent, PrimaryButtonDirective, SavePrimaryButtonComponent, SavePrimaryButtonDirective, SearchButtonComponent, SpinnerComponent, Store, SuccessButtonComponent, SuccessButtonDirective, ViewButtonComponent, ViewButtonDirective, ViewPrimaryButtonComponent, ViewPrimaryButtonDirective, initializeState, markError, markLoading, markSuccess };
2431
2643
  //# sourceMappingURL=js-smart-ng-kit.mjs.map