@3kles/kles-material-dynamicforms 21.7.4 → 21.7.6

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.
@@ -4,7 +4,7 @@ import * as i1 from '@angular/common';
4
4
  import { CommonModule, AsyncPipe } from '@angular/common';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { Validators, FormControl, ReactiveFormsModule, FormsModule, FormControlDirective, FormControlName, FormArray, FormGroup, UntypedFormBuilder, NG_VALUE_ACCESSOR, UntypedFormGroup } from '@angular/forms';
7
- import { concat, of, Subject, combineLatest, startWith, map as map$1, takeUntil, Observable, defer, distinctUntilChanged as distinctUntilChanged$1, filter as filter$1, switchMap as switchMap$1, timer, shareReplay as shareReplay$1, isObservable } from 'rxjs';
7
+ import { concat, of, Subject, combineLatest, startWith, map as map$1, takeUntil, Observable, defer, ReplaySubject, switchMap as switchMap$1, isObservable, timer, distinctUntilChanged as distinctUntilChanged$1, shareReplay as shareReplay$1 } from 'rxjs';
8
8
  import { tap, take, catchError, map, takeUntil as takeUntil$1, distinctUntilChanged, filter, switchMap, startWith as startWith$1, shareReplay, debounceTime, debounce } from 'rxjs/operators';
9
9
  import * as i3 from '@angular/material/form-field';
10
10
  import { MatError, MatFormFieldModule, MatFormField, MatHint, MatLabel, MatSuffix } from '@angular/material/form-field';
@@ -29,7 +29,7 @@ import { MatInputModule, MatInput, MatLabel as MatLabel$1, MatFormField as MatFo
29
29
  import * as i6 from '@angular/material/select';
30
30
  import { MatSelectModule, MatSelect, MatSelectTrigger } from '@angular/material/select';
31
31
  import { MatRadioModule, MatRadioButton, MatRadioGroup } from '@angular/material/radio';
32
- import * as i2$2 from '@angular/material/chips';
32
+ import * as i3$6 from '@angular/material/chips';
33
33
  import { MatChipsModule, MatChipOption, MatChipListbox } from '@angular/material/chips';
34
34
  import * as i4$1 from '@angular/material/tooltip';
35
35
  import { MatTooltipModule, MatTooltip } from '@angular/material/tooltip';
@@ -344,16 +344,16 @@ function isDestroyable(value) {
344
344
  }
345
345
 
346
346
  class KlesDynamicFieldDirective {
347
- constructor(container, injector) {
347
+ constructor(container, injector, applicationRef) {
348
348
  this.container = container;
349
349
  this.injector = injector;
350
+ this.applicationRef = applicationRef;
350
351
  this.context = null;
351
352
  this.subComponents = [];
353
+ this.subComponentViews = new Map();
352
354
  }
353
355
  ngOnDestroy() {
354
- if (this.componentRef) {
355
- this.componentRef.destroy();
356
- }
356
+ this.destroyComponents();
357
357
  }
358
358
  ngOnInit() {
359
359
  this.buildComponent();
@@ -384,11 +384,7 @@ class KlesDynamicFieldDirective {
384
384
  }
385
385
  }
386
386
  buildComponent() {
387
- if (this.componentRef) {
388
- this.subComponents.forEach((c) => c.destroy());
389
- this.subComponents = [];
390
- this.componentRef.destroy();
391
- }
387
+ this.destroyComponents();
392
388
  const options = {
393
389
  providers: [
394
390
  ...(this.field.providers || []),
@@ -481,6 +477,14 @@ class KlesDynamicFieldDirective {
481
477
  createSubComponent(componentType, options) {
482
478
  const injector = Injector.create(options);
483
479
  const component = this.container.createComponent(componentType, { injector });
480
+ // The host element is projected into the field component. Keeping its view
481
+ // in this container would make Angular's logical DOM differ from the real DOM.
482
+ const viewIndex = this.container.indexOf(component.hostView);
483
+ const detachedView = viewIndex !== -1 ? this.container.detach(viewIndex) : null;
484
+ if (detachedView) {
485
+ this.applicationRef.attachView(detachedView);
486
+ this.subComponentViews.set(component, detachedView);
487
+ }
484
488
  component.onDestroy(() => {
485
489
  if (isDestroyable(injector)) {
486
490
  injector.destroy();
@@ -488,7 +492,20 @@ class KlesDynamicFieldDirective {
488
492
  });
489
493
  return component;
490
494
  }
491
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: KlesDynamicFieldDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Directive }); }
495
+ destroyComponents() {
496
+ this.componentRef?.destroy();
497
+ this.componentRef = undefined;
498
+ this.subComponents.forEach((component) => {
499
+ const view = this.subComponentViews.get(component);
500
+ if (view) {
501
+ this.applicationRef.detachView(view);
502
+ }
503
+ component.destroy();
504
+ });
505
+ this.subComponents = [];
506
+ this.subComponentViews.clear();
507
+ }
508
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: KlesDynamicFieldDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.Injector }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Directive }); }
492
509
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.20", type: KlesDynamicFieldDirective, isStandalone: true, selector: "[klesDynamicField]", inputs: { field: "field", group: "group", ui: "ui", siblingFields: "siblingFields", context: "context" }, usesOnChanges: true, ngImport: i0 }); }
493
510
  }
494
511
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: KlesDynamicFieldDirective, decorators: [{
@@ -497,7 +514,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
497
514
  selector: '[klesDynamicField]',
498
515
  standalone: true,
499
516
  }]
500
- }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Injector }], propDecorators: { field: [{
517
+ }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i0.ApplicationRef }], propDecorators: { field: [{
501
518
  type: Input
502
519
  }], group: [{
503
520
  type: Input
@@ -5338,208 +5355,311 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
5338
5355
  class KlesFormChipGridComponent extends KlesFieldAbstract {
5339
5356
  constructor() {
5340
5357
  super(...arguments);
5341
- this.options = this.field.options;
5342
5358
  this.control = this.group.controls[this.field.name];
5343
5359
  this.intl = inject(KlesDynamicFormIntl);
5344
5360
  this.separatorKeysCodes = [ENTER, COMMA];
5345
- this.isFocused = new Subject();
5346
- this.searchControl = new FormControl('aaa');
5361
+ /*
5362
+ * any est volontaire :
5363
+ * MatAutocomplete peut momentanément pousser l'objet
5364
+ * sélectionné dans le FormControl de recherche.
5365
+ */
5366
+ this.searchControl = new FormControl('', {
5367
+ nonNullable: true,
5368
+ });
5369
+ /*
5370
+ * ReplaySubject permet de ne pas perdre le premier focus
5371
+ * si l'Observable lazy n'est pas encore abonné.
5372
+ */
5373
+ this.loadOptions$ = new ReplaySubject(1);
5347
5374
  }
5348
5375
  ngOnInit() {
5349
- if (this.field.lazy) {
5350
- this.options$ = this.isFocused.pipe(distinctUntilChanged$1(), filter$1((isFocused) => isFocused), switchMap$1((isFocused) => {
5351
- if (isFocused) {
5352
- let obs$;
5353
- if (this.field.options instanceof Observable) {
5354
- obs$ = this.field.options;
5355
- }
5356
- else if (this.field.options instanceof Function) {
5357
- obs$ = this.field.options();
5358
- }
5359
- else {
5360
- obs$ = of(this.field.options ?? []);
5361
- }
5362
- return concat(of({ loading: true, options: [] }), obs$.pipe(map$1((options) => {
5363
- return { loading: false, options };
5364
- })));
5365
- }
5366
- else {
5367
- return of({ loading: false, options: [] });
5368
- }
5369
- }));
5370
- }
5371
- else {
5372
- if (this.field.options instanceof Observable) {
5373
- this.options$ = concat(of({ loading: true, options: [] }), this.field.options.pipe(map$1((options) => {
5374
- return { loading: false, options };
5375
- })));
5376
- }
5377
- else if (this.field.options instanceof Function) {
5378
- this.options$ = concat(of({ loading: true, options: [] }), this.field.options().pipe(map$1((options) => {
5379
- return { loading: false, options };
5380
- })));
5381
- }
5382
- else {
5383
- this.options$ = of({ loading: false, options: this.field.options });
5384
- }
5385
- }
5386
- this.filteredOption$ = concat(combineLatest([this.searchControl?.valueChanges.pipe(startWith('')) ?? of(''), this.options$]).pipe(map$1(([data, response]) => {
5376
+ this.options$ = this.createOptionsStream();
5377
+ this.filteredOption$ = combineLatest([this.searchControl.valueChanges.pipe(startWith(this.searchControl.value)), this.options$, this.control.valueChanges.pipe(startWith(this.control.value ?? []))]).pipe(map$1(([search, response, selectedValues]) => {
5387
5378
  if (response.loading) {
5388
5379
  return response;
5389
5380
  }
5390
- else {
5391
- return { loading: false, options: data && response.options ? this.filterData(data, response.options) : response.options };
5381
+ const selected = Array.isArray(selectedValues) ? selectedValues : [];
5382
+ /*
5383
+ * On retire de la liste les options
5384
+ * déjà présentes dans les chips.
5385
+ */
5386
+ let options = response.options.filter((option) => !selected.some((selectedValue) => this.isSameOption(option, selectedValue)));
5387
+ /*
5388
+ * Le contrôle peut momentanément contenir
5389
+ * un objet pendant une sélection autocomplete.
5390
+ */
5391
+ if (typeof search === 'string' && search.trim()) {
5392
+ options = this.filterData(search, options);
5392
5393
  }
5393
- })));
5394
+ return {
5395
+ loading: false,
5396
+ options,
5397
+ };
5398
+ }));
5394
5399
  super.ngOnInit();
5395
5400
  }
5396
- ngOnDestroy() {
5397
- super.ngOnDestroy();
5398
- }
5399
5401
  onFocus() {
5400
5402
  if (this.field.lazy) {
5401
- this.isFocused.next(true);
5403
+ this.loadOptions$.next();
5402
5404
  }
5403
5405
  super.onFocus();
5404
5406
  }
5405
5407
  onTokenEnd(event) {
5406
- this.addChip(event.value);
5408
+ /*
5409
+ * Quand property est renseignée, les valeurs sont
5410
+ * considérées comme des objets provenant de l'autocomplete.
5411
+ *
5412
+ * On évite donc d'ajouter une string libre au tableau.
5413
+ */
5414
+ if (this.field.property) {
5415
+ return;
5416
+ }
5417
+ const value = event.value.trim();
5418
+ if (!value) {
5419
+ return;
5420
+ }
5421
+ this.addChip(value);
5407
5422
  event.chipInput.clear();
5423
+ this.searchControl.setValue('');
5408
5424
  }
5409
- addChip(rawValue) {
5410
- const value = (rawValue || '').trim();
5411
- if (value) {
5412
- this.control.setValue([...(this.control.value ?? []), value]);
5425
+ addChip(value) {
5426
+ if (value === null || value === undefined || value === '') {
5427
+ return;
5413
5428
  }
5429
+ const values = Array.isArray(this.control.value) ? this.control.value : [];
5430
+ /*
5431
+ * Empêche d'ajouter deux fois la même valeur.
5432
+ */
5433
+ const exists = values.some((currentValue) => this.isSameOption(currentValue, value));
5434
+ if (exists) {
5435
+ return;
5436
+ }
5437
+ this.control.setValue([...values, value]);
5438
+ this.control.markAsDirty();
5414
5439
  }
5415
5440
  removeChip(value) {
5416
- const values = this.control.value;
5417
- const index = values.lastIndexOf(value);
5418
- if (index > -1) {
5419
- values.splice(index, 1);
5420
- this.control.setValue(values);
5441
+ const values = Array.isArray(this.control.value) ? [...this.control.value] : [];
5442
+ const index = values.findIndex((currentValue) => this.isSameOption(currentValue, value));
5443
+ if (index === -1) {
5444
+ return;
5421
5445
  }
5446
+ values.splice(index, 1);
5447
+ this.control.setValue(values);
5448
+ this.control.markAsDirty();
5422
5449
  }
5423
- selected(event) {
5424
- this.addChip(event.option.viewValue);
5450
+ selected(event, chipInput) {
5451
+ const value = event.option.value;
5452
+ /*
5453
+ * On ajoute d'abord l'objet sélectionné aux chips.
5454
+ */
5455
+ this.addChip(value);
5456
+ /*
5457
+ * L'autocomplete n'est ici qu'une source de sélection.
5458
+ * La sélection réelle est représentée par les chips.
5459
+ */
5425
5460
  event.option.deselect();
5426
- this.searchControl.reset();
5461
+ /*
5462
+ * Important :
5463
+ * MatChipInput.clear() nettoie réellement l'input HTML.
5464
+ */
5465
+ chipInput.clear();
5466
+ /*
5467
+ * Et on synchronise le FormControl afin de :
5468
+ * - vider l'état Angular ;
5469
+ * - supprimer le filtre ;
5470
+ * - recalculer filteredOption$.
5471
+ */
5472
+ this.searchControl.setValue('');
5427
5473
  }
5428
- filterData(value, options) {
5429
- let filterValue = undefined;
5430
- const property = this.field.property;
5431
- if (typeof value === 'string' && Object.prototype.toString.call(value) === '[object String]') {
5432
- filterValue = value.toLowerCase();
5474
+ displayValue(value) {
5475
+ if (value === null || value === undefined) {
5476
+ return '';
5433
5477
  }
5434
- else {
5435
- if (property) {
5436
- filterValue = value[property]?.toLowerCase();
5437
- }
5478
+ if (this.field.property) {
5479
+ return value?.[this.field.property] ?? '';
5438
5480
  }
5439
- if (filterValue != undefined) {
5440
- if (property) {
5441
- return options.filter((option) => option[property].toLowerCase().indexOf(filterValue) === 0);
5442
- }
5443
- return options.filter((option) => option.toLowerCase().indexOf(filterValue) === 0);
5481
+ return value;
5482
+ }
5483
+ createOptionsStream() {
5484
+ /*
5485
+ * Lazy :
5486
+ * recharge les options à chaque focus.
5487
+ */
5488
+ if (this.field.lazy) {
5489
+ return this.loadOptions$.pipe(switchMap$1(() => concat(of({
5490
+ loading: true,
5491
+ options: [],
5492
+ }), this.resolveOptions().pipe(map$1((options) => ({
5493
+ loading: false,
5494
+ options,
5495
+ }))))));
5444
5496
  }
5445
- else {
5497
+ /*
5498
+ * Liste statique :
5499
+ * aucun chargement à afficher.
5500
+ */
5501
+ if (Array.isArray(this.field.options)) {
5502
+ return of({
5503
+ loading: false,
5504
+ options: this.field.options,
5505
+ });
5506
+ }
5507
+ /*
5508
+ * Observable ou fonction :
5509
+ * spinner jusqu'à réception des options.
5510
+ */
5511
+ return concat(of({
5512
+ loading: true,
5513
+ options: [],
5514
+ }), this.resolveOptions().pipe(map$1((options) => ({
5515
+ loading: false,
5516
+ options,
5517
+ }))));
5518
+ }
5519
+ resolveOptions() {
5520
+ const source = typeof this.field.options === 'function' ? this.field.options() : this.field.options;
5521
+ if (isObservable(source)) {
5522
+ return source;
5523
+ }
5524
+ return of(source ?? []);
5525
+ }
5526
+ filterData(search, options) {
5527
+ if (typeof search !== 'string') {
5528
+ return options;
5529
+ }
5530
+ const filterValue = search.trim().toLowerCase();
5531
+ if (!filterValue) {
5446
5532
  return options;
5447
5533
  }
5534
+ return options.filter((option) => {
5535
+ const value = this.displayValue(option);
5536
+ return String(value ?? '')
5537
+ .toLowerCase()
5538
+ .startsWith(filterValue);
5539
+ });
5540
+ }
5541
+ isSameOption(a, b) {
5542
+ /*
5543
+ * Primitive identique ou même référence objet.
5544
+ */
5545
+ if (a === b) {
5546
+ return true;
5547
+ }
5548
+ /*
5549
+ * En mode objet, property sert actuellement
5550
+ * également à identifier les doublons.
5551
+ */
5552
+ if (this.field.property && a !== null && a !== undefined && b !== null && b !== undefined) {
5553
+ return a[this.field.property] === b[this.field.property];
5554
+ }
5555
+ return false;
5448
5556
  }
5449
5557
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: KlesFormChipGridComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
5450
5558
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: KlesFormChipGridComponent, isStandalone: true, selector: "kles-form-chip-grid", usesInheritance: true, ngImport: i0, template: `
5451
5559
  <mat-form-field [formGroup]="group" [matTooltip]="tooltip()" [appearance]="appearance()">
5452
5560
  <mat-label>{{ label() }}</mat-label>
5453
- <mat-chip-grid #reactiveChipGrid [formControl]="group.controls[field.name]">
5454
- @for (value of group.controls[field.name].value ?? []; track value) {
5455
- <mat-chip-row (removed)="removeChip(value)">
5456
- {{ (field.property ? value[field.property] : value) | klesTransform: field.pipeTransform }}
5561
+
5562
+ <mat-chip-grid #reactiveChipGrid [formControl]="control">
5563
+ @for (value of control.value ?? []; track $index) {
5564
+ <mat-chip-row [value]="value" (removed)="removeChip(value)">
5565
+ {{ displayValue(value) | klesTransform: field.pipeTransform }}
5566
+
5457
5567
  <button matChipRemove>
5458
5568
  <mat-icon>cancel</mat-icon>
5459
5569
  </button>
5460
5570
  </mat-chip-row>
5461
5571
  }
5462
5572
  </mat-chip-grid>
5573
+
5463
5574
  <input
5464
- #search
5575
+ #chipInput="matChipInput"
5465
5576
  [formControl]="searchControl"
5466
5577
  [placeholder]="placeholder()"
5467
5578
  [matChipInputFor]="reactiveChipGrid"
5468
- (matChipInputTokenEnd)="onTokenEnd($event)"
5469
5579
  [matAutocomplete]="auto"
5470
5580
  [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
5581
+ (matChipInputTokenEnd)="onTokenEnd($event)"
5471
5582
  (focus)="onFocus()"
5472
5583
  (blur)="onBlur()"
5473
5584
  />
5474
5585
 
5475
- <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event); search.value = ''">
5586
+ <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event, chipInput)">
5476
5587
  @if (filteredOption$ | async; as filteredOption) {
5477
5588
  @if (filteredOption.loading) {
5478
5589
  <mat-option disabled>
5479
5590
  <div class="loadingSelect">
5480
5591
  {{ intl.loading }}...
5481
- <mat-spinner class="spinner" diameter="20"></mat-spinner>
5592
+
5593
+ <mat-spinner class="spinner" diameter="20" />
5482
5594
  </div>
5483
5595
  </mat-option>
5484
5596
  } @else {
5485
5597
  @for (item of filteredOption.options; track item) {
5486
- <mat-option [value]="item" [disabled]="item?.disabled">{{ (field.property ? item[field.property] : item) | klesTransform: field.pipeTransform }}</mat-option>
5598
+ <mat-option [value]="item" [disabled]="item?.disabled">
5599
+ {{ displayValue(item) | klesTransform: field.pipeTransform }}
5600
+ </mat-option>
5487
5601
  }
5488
5602
  }
5489
5603
  }
5490
5604
  </mat-autocomplete>
5491
5605
 
5492
- <mat-error matErrorMessage [validations]="field.validations" [asyncValidations]="field.asyncValidations"></mat-error>
5606
+ <mat-error matErrorMessage [validations]="field.validations" [asyncValidations]="field.asyncValidations" />
5493
5607
  </mat-form-field>
5494
- `, isInline: true, styles: [".suffix{display:flex;align-items:center}\n", ".field-bottom .mat-mdc-form-field-bottom-align:before{content:none!important}\n", ".loadingSelect{display:flex;flex-direction:row;justify-content:space-between;align-items:center}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i2$2.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i2$2.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i2$2.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i2$2.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { 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.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: MatErrorMessageDirective, selector: "[matErrorMessage]", inputs: ["validations", "asyncValidations"] }, { kind: "pipe", type: KlesTransformPipe, name: "klesTransform" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
5608
+ `, isInline: true, styles: [".suffix{display:flex;align-items:center}\n", ".field-bottom .mat-mdc-form-field-bottom-align:before{content:none!important}\n", ".loadingSelect{display:flex;flex-direction:row;justify-content:space-between;align-items:center}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i3$6.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i3$6.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i3$6.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i3$6.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: MatErrorMessageDirective, selector: "[matErrorMessage]", inputs: ["validations", "asyncValidations"] }, { kind: "ngmodule", type: MatFormFieldModule }, { 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: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: KlesTransformPipe, name: "klesTransform" }] }); }
5495
5609
  }
5496
5610
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: KlesFormChipGridComponent, decorators: [{
5497
5611
  type: Component,
5498
5612
  args: [{ selector: 'kles-form-chip-grid', template: `
5499
5613
  <mat-form-field [formGroup]="group" [matTooltip]="tooltip()" [appearance]="appearance()">
5500
5614
  <mat-label>{{ label() }}</mat-label>
5501
- <mat-chip-grid #reactiveChipGrid [formControl]="group.controls[field.name]">
5502
- @for (value of group.controls[field.name].value ?? []; track value) {
5503
- <mat-chip-row (removed)="removeChip(value)">
5504
- {{ (field.property ? value[field.property] : value) | klesTransform: field.pipeTransform }}
5615
+
5616
+ <mat-chip-grid #reactiveChipGrid [formControl]="control">
5617
+ @for (value of control.value ?? []; track $index) {
5618
+ <mat-chip-row [value]="value" (removed)="removeChip(value)">
5619
+ {{ displayValue(value) | klesTransform: field.pipeTransform }}
5620
+
5505
5621
  <button matChipRemove>
5506
5622
  <mat-icon>cancel</mat-icon>
5507
5623
  </button>
5508
5624
  </mat-chip-row>
5509
5625
  }
5510
5626
  </mat-chip-grid>
5627
+
5511
5628
  <input
5512
- #search
5629
+ #chipInput="matChipInput"
5513
5630
  [formControl]="searchControl"
5514
5631
  [placeholder]="placeholder()"
5515
5632
  [matChipInputFor]="reactiveChipGrid"
5516
- (matChipInputTokenEnd)="onTokenEnd($event)"
5517
5633
  [matAutocomplete]="auto"
5518
5634
  [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
5635
+ (matChipInputTokenEnd)="onTokenEnd($event)"
5519
5636
  (focus)="onFocus()"
5520
5637
  (blur)="onBlur()"
5521
5638
  />
5522
5639
 
5523
- <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event); search.value = ''">
5640
+ <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event, chipInput)">
5524
5641
  @if (filteredOption$ | async; as filteredOption) {
5525
5642
  @if (filteredOption.loading) {
5526
5643
  <mat-option disabled>
5527
5644
  <div class="loadingSelect">
5528
5645
  {{ intl.loading }}...
5529
- <mat-spinner class="spinner" diameter="20"></mat-spinner>
5646
+
5647
+ <mat-spinner class="spinner" diameter="20" />
5530
5648
  </div>
5531
5649
  </mat-option>
5532
5650
  } @else {
5533
5651
  @for (item of filteredOption.options; track item) {
5534
- <mat-option [value]="item" [disabled]="item?.disabled">{{ (field.property ? item[field.property] : item) | klesTransform: field.pipeTransform }}</mat-option>
5652
+ <mat-option [value]="item" [disabled]="item?.disabled">
5653
+ {{ displayValue(item) | klesTransform: field.pipeTransform }}
5654
+ </mat-option>
5535
5655
  }
5536
5656
  }
5537
5657
  }
5538
5658
  </mat-autocomplete>
5539
5659
 
5540
- <mat-error matErrorMessage [validations]="field.validations" [asyncValidations]="field.asyncValidations"></mat-error>
5660
+ <mat-error matErrorMessage [validations]="field.validations" [asyncValidations]="field.asyncValidations" />
5541
5661
  </mat-form-field>
5542
- `, standalone: true, imports: [MatIconModule, KlesTransformPipe, MatChipsModule, MatTooltip, ReactiveFormsModule, MatFormFieldModule, MatAutocompleteModule, AsyncPipe, MatProgressSpinnerModule, MatError, MatErrorMessageDirective], styles: [".suffix{display:flex;align-items:center}\n", ".field-bottom .mat-mdc-form-field-bottom-align:before{content:none!important}\n", ".loadingSelect{display:flex;flex-direction:row;justify-content:space-between;align-items:center}\n", "mat-form-field{width:100%}\n"] }]
5662
+ `, standalone: true, imports: [AsyncPipe, KlesTransformPipe, MatAutocompleteModule, MatChipsModule, MatError, MatErrorMessageDirective, MatFormFieldModule, MatIconModule, MatProgressSpinnerModule, MatTooltip, ReactiveFormsModule], styles: [".suffix{display:flex;align-items:center}\n", ".field-bottom .mat-mdc-form-field-bottom-align:before{content:none!important}\n", ".loadingSelect{display:flex;flex-direction:row;justify-content:space-between;align-items:center}\n", "mat-form-field{width:100%}\n"] }]
5543
5663
  }] });
5544
5664
 
5545
5665
  const components = [