@sumaris-net/ngx-components 1.6.2 → 1.6.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.
Files changed (25) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +665 -294
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +13 -0
  6. package/esm2015/src/app/core/auth/modal/modal-auth.js +21 -9
  7. package/esm2015/src/app/shared/form/field.model.js +1 -1
  8. package/esm2015/src/app/shared/material/autocomplete/material.autocomplete.js +143 -122
  9. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +6 -6
  10. package/esm2015/src/app/shared/material/chips/material.chips.js +372 -94
  11. package/esm2015/src/app/shared/material/chips/testing/chips.test.js +54 -18
  12. package/esm2015/src/app/shared/material/swipe/material.swipe.js +5 -5
  13. package/esm2015/src/app/shared/material/swipe/testing/swipe.test.js +2 -2
  14. package/fesm2015/sumaris-net.ngx-components.js +616 -272
  15. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  16. package/package.json +1 -1
  17. package/src/app/core/auth/modal/modal-auth.d.ts +12 -5
  18. package/src/app/shared/form/field.model.d.ts +2 -1
  19. package/src/app/shared/material/autocomplete/material.autocomplete.d.ts +39 -38
  20. package/src/app/shared/material/autocomplete/testing/autocomplete.test.d.ts +4 -5
  21. package/src/app/shared/material/chips/material.chips.d.ts +42 -13
  22. package/src/app/shared/material/chips/testing/chips.test.d.ts +1 -1
  23. package/src/app/shared/material/swipe/material.swipe.d.ts +2 -2
  24. package/src/theme/_ngx-components.scss +3 -1
  25. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, EventEmitter, forwardRef, Component, ChangeDetectionStrategy, ChangeDetectorRef, Optional, Input, HostBinding, Output, ViewChild, Pipe, Injectable, Directive, Inject, NgModule, ElementRef, ViewChildren, HostListener, ComponentFactoryResolver, ApplicationRef, Injector, Renderer2 } from '@angular/core';
2
+ import { InjectionToken, EventEmitter, Component, forwardRef, ChangeDetectionStrategy, ChangeDetectorRef, Optional, Input, Output, ViewChild, Pipe, Injectable, Directive, Inject, NgModule, ElementRef, ViewChildren, HostListener, ComponentFactoryResolver, ApplicationRef, Injector, Renderer2, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1$3 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, Location } from '@angular/common';
5
5
  import { CdkTableModule } from '@angular/cdk/table';
@@ -36,7 +36,7 @@ import { MatAutocomplete, MatAutocompleteTrigger, MatAutocompleteModule, MAT_AUT
36
36
  import { __awaiter, __decorate } from 'tslib';
37
37
  import * as i1$6 from '@angular/forms';
38
38
  import { NG_VALUE_ACCESSOR, FormGroupDirective, AbstractControl, FormGroup, FormArray, FormControl, ReactiveFormsModule, Validators, FormBuilder } from '@angular/forms';
39
- import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, noop as noop$8, Observable, defer, of, from, combineLatest, EMPTY } from 'rxjs';
39
+ import { timer, merge, fromEvent, BehaviorSubject, Subscription, Subject, isObservable, noop as noop$9, Observable, defer, of, from, combineLatest, EMPTY } from 'rxjs';
40
40
  import { filter, first, map, takeUntil, switchMap, startWith, debounceTime, takeWhile, tap, distinctUntilChanged, mergeMap, catchError, throttleTime, skip } from 'rxjs/operators';
41
41
  import * as i1$1 from '@ngx-translate/core';
42
42
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
@@ -976,11 +976,11 @@ class MatAutocompleteConfigHolder {
976
976
  const attributesOrFn = attributes.map((a, index) => typeof a === 'function' && options.attributes[index] || a);
977
977
  const filter = Object.assign({ searchAttribute: attributes.length === 1 ? attributes[0] : undefined, searchAttributes: attributes.length > 1 ? attributes : undefined }, options.filter);
978
978
  const displayWith = options.displayWith || ((obj) => obj && joinPropertiesPath(obj, attributes));
979
- const compareWith = options.compareWith || ((o1, o2) => o1 && o2 && o1.id === o2.id);
979
+ const equals = options.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
980
980
  const config = Object.assign(Object.assign({}, options), { attributes: attributesOrFn, suggestFn,
981
981
  filter,
982
982
  displayWith,
983
- compareWith });
983
+ equals });
984
984
  this.fields[fieldName] = config;
985
985
  return config;
986
986
  }
@@ -991,27 +991,14 @@ class MatAutocompleteConfigHolder {
991
991
  return this.fields[fieldName] || this.add(fieldName);
992
992
  }
993
993
  }
994
- const DEFAULT_VALUE_ACCESSOR$6 = {
995
- provide: NG_VALUE_ACCESSOR,
996
- useExisting: forwardRef(() => MatAutocompleteField),
997
- multi: true
998
- };
999
- const noop$7 = () => { };
1000
- const ɵ0$a = noop$7;
994
+ const noop$8 = (_) => { };
995
+ const ɵ0$b = noop$8;
1001
996
  class MatAutocompleteField {
1002
997
  constructor(cd, formGroupDir) {
1003
998
  this.cd = cd;
1004
999
  this.formGroupDir = formGroupDir;
1005
- this._onChangeCallback = noop$7;
1006
- this._onTouchedCallback = noop$7;
1007
- this._subscription = new Subscription();
1008
- this._filter$ = new BehaviorSubject(undefined);
1009
- this._reload$ = new Subject();
1010
- this._readonly = false;
1011
- this.$inputItems = new BehaviorSubject(undefined);
1012
- this.$filteredItems = new BehaviorSubject(undefined);
1013
- this.displayValue = '';
1014
- this._fetchMore$ = new EventEmitter();
1000
+ this._onChangeCallback = noop$8;
1001
+ this._onTouchedCallback = noop$8;
1015
1002
  this.logPrefix = '[mat-autocomplete] ';
1016
1003
  this.formControlName = null;
1017
1004
  this.required = false;
@@ -1024,13 +1011,21 @@ class MatAutocompleteField {
1024
1011
  this.fetchMoreThreshold = '15%';
1025
1012
  this.suggestLengthThreshold = null;
1026
1013
  this.showLoadingSpinner = true;
1027
- this.animationsDisabled = true;
1028
- this.onClick = new EventEmitter();
1029
- this.onBlur = new EventEmitter();
1030
- this.onFocus = new EventEmitter();
1031
- this.onDropButtonClick = new EventEmitter(true);
1032
- this.onKeydownEscape = new EventEmitter();
1033
- this.onKeyupEnter = new EventEmitter();
1014
+ this.debug = false;
1015
+ this.clicked = new EventEmitter();
1016
+ this.blurred = new EventEmitter();
1017
+ this.focused = new EventEmitter();
1018
+ this.dropButtonClick = new EventEmitter(true);
1019
+ this.keydownEscape = new EventEmitter();
1020
+ this.keyupEnter = new EventEmitter();
1021
+ this._readonly = false;
1022
+ this.$inputItems = new BehaviorSubject(undefined);
1023
+ this.$filteredItems = new BehaviorSubject(undefined);
1024
+ this.displayValue = '';
1025
+ this._fetchMore$ = new EventEmitter();
1026
+ this._subscription = new Subscription();
1027
+ this._$filter = new BehaviorSubject(undefined);
1028
+ this._reload$ = new Subject();
1034
1029
  }
1035
1030
  get itemCount() {
1036
1031
  return this._itemCount;
@@ -1039,14 +1034,14 @@ class MatAutocompleteField {
1039
1034
  return this._onFetchMoreCallback && this._moreItemsCount > 0;
1040
1035
  }
1041
1036
  set filter(value) {
1042
- if (value !== this._filter$.value) {
1037
+ if (value !== this._$filter.value) {
1043
1038
  // DEBUG
1044
1039
  //console.debug(this.logPrefix + " Setting filter:", value);
1045
- this._filter$.next(value);
1040
+ this._$filter.next(value);
1046
1041
  }
1047
1042
  }
1048
1043
  get filter() {
1049
- return this._filter$.value;
1044
+ return this._$filter.value;
1050
1045
  }
1051
1046
  set readonly(value) {
1052
1047
  this._readonly = value;
@@ -1093,6 +1088,9 @@ class MatAutocompleteField {
1093
1088
  get enabled() {
1094
1089
  return !this._readonly && this.formControl.enabled;
1095
1090
  }
1091
+ get loading() {
1092
+ return isNil(this.$filteredItems.value);
1093
+ }
1096
1094
  ngOnInit() {
1097
1095
  var _a, _b;
1098
1096
  this.formControl = this.formControl || this.formControlName && this.formGroupDir && this.formGroupDir.form.get(this.formControlName);
@@ -1109,10 +1107,11 @@ class MatAutocompleteField {
1109
1107
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
1110
1108
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
1111
1109
  this.displayWith = this.displayWith || this.config.displayWith;
1110
+ this.equals = this.equals || this.config.equals;
1112
1111
  this.mobile = toBoolean(this.mobile, this.config.mobile);
1113
1112
  this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
1114
1113
  this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
1115
- this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus));
1114
+ this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus, true));
1116
1115
  this.classList = this.classList || this.config.class;
1117
1116
  this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
1118
1117
  this.highlightAccent = toBoolean(this.highlightAccent, toBoolean(this.config.highlightAccent));
@@ -1123,20 +1122,18 @@ class MatAutocompleteField {
1123
1122
  this.displayColumnSizes = this.displayColumnSizes
1124
1123
  // if only column: auto size
1125
1124
  || (this.displayAttributes.length === 1 && [undefined])
1126
- || this.displayAttributes.map(attr =>
1127
- // If label, set col size = 2
1128
- (attr && attr.endsWith('label')) ? 2 :
1129
- // If rankOrder => col size = 1
1130
- ((attr && attr.endsWith('rankOrder')) ? 1
1131
- // Else => auto size
1125
+ // Computed column size:
1126
+ // - if label then col size = 2
1127
+ // - if rankOrder then col size = 1
1128
+ // - ese => auto size
1129
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
1130
+ : ((attr && attr.endsWith('rankOrder')) ? 1
1132
1131
  : undefined));
1133
1132
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
1134
1133
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
1135
1134
  this.mobile = toBoolean(this.mobile, false);
1136
1135
  this.searchable = !this.mobile && !this.multiple;
1137
- // Default comparator (only need when using mat-select)
1138
- if (!this.searchable && !this.compareWith)
1139
- this.compareWith = (o1, o2) => o1 && o2 && o1.id === o2.id;
1136
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
1140
1137
  const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
1141
1138
  this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
1142
1139
  || (classList.includes('min-width-large') && '400px')
@@ -1151,7 +1148,7 @@ class MatAutocompleteField {
1151
1148
  if (!this.suggestFn) {
1152
1149
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
1153
1150
  // DEBUG
1154
- //console.debug(this.logPrefix + " Calling suggestFromArray with value=", value);
1151
+ //console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
1155
1152
  return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
1156
1153
  });
1157
1154
  // Wait (once) that items are loaded, then call suggest from array fn
@@ -1169,10 +1166,18 @@ class MatAutocompleteField {
1169
1166
  }
1170
1167
  else if (!this.searchable) {
1171
1168
  // Manually fill input items, from the given suggest function
1172
- this._subscription.add(this._filter$.pipe(startWith(this.filter), debounceTime(250), takeWhile((_) => !this.searchable), // Close subscription, when enabling search (no more mat-select)
1169
+ this._subscription.add(this._$filter.pipe(startWith(this.filter), debounceTime(250), takeWhile((_) => !this.searchable), // Close subscription, when enabling search (no more mat-select)
1173
1170
  switchMap((filter) => this.suggest('*', filter)))
1174
1171
  .subscribe(items => this.$inputItems.next(items)));
1175
1172
  }
1173
+ // Send value to suggest when :
1174
+ // - is nil and '*', and no min length threshold
1175
+ // - is not nil and not a string type (e.g. object)
1176
+ // - is not nil and is a string, and has enough character
1177
+ const thresholdFilterFn = (value) => (isNilOrBlank(value) || (value === '*')
1178
+ ? (this.suggestLengthThreshold <= 0)
1179
+ : (!(typeof value === 'string')
1180
+ || value.length >= this.suggestLengthThreshold));
1176
1181
  const filteredItemsChanges = merge(
1177
1182
  // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
1178
1183
  this._reload$,
@@ -1180,23 +1185,19 @@ class MatAutocompleteField {
1180
1185
  this.formControl.valueChanges
1181
1186
  .pipe(filter(value => this.enabled && this.searchable && isNotNilString(value)),
1182
1187
  // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
1183
- tap(value => {
1184
- if (this.showLoadingSpinner) {
1185
- this.markAsLoading();
1186
- }
1187
- }),
1188
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
1188
1189
  // Filter if not enough character
1189
1190
  filter(value => value.length >= this.suggestLengthThreshold),
1190
1191
  // Wait a debounce time
1191
- debounceTime(this.debounceTime)
1192
+ debounceTime(this.debounceTime),
1192
1193
  // DEBUG
1193
- //tap((value) => console.debug(this.logPrefix + ' Search text changes:', value)),
1194
- ),
1194
+ tap((value) => console.debug(this.logPrefix + ' Search text changes:', value))),
1195
1195
  // Object set: use it (no distinctUntilChanged() pipe need)
1196
1196
  this.formControl.valueChanges
1197
1197
  .pipe(startWith(this.formControl.value), filter(value => (this.searchable && isNotNil(value) && typeof value === 'object')), tap(value => {
1198
1198
  // DEBUG
1199
- //console.debug(this.logPrefix + ' object changes:', value)
1199
+ if (this.debug)
1200
+ console.debug(this.logPrefix + ' object changes:', value);
1200
1201
  // Update the display value (if need)
1201
1202
  const displayValue = this.displayWith(value) || '';
1202
1203
  if (this.displayValue !== displayValue) {
@@ -1206,37 +1207,35 @@ class MatAutocompleteField {
1206
1207
  this.matInputText.nativeElement.value = this.displayValue;
1207
1208
  }
1208
1209
  }
1209
- })
1210
+ }),
1210
1211
  // DEBUG
1211
- //tap(value => console.debug(this.logPrefix + ' Received an object value: ', value))
1212
- ),
1212
+ tap(value => this.debug && console.debug(this.logPrefix + ' Received an object value: ', value))),
1213
+ // On dropdown button click (no distinctUntilChanged(), nor debounceTime)
1214
+ this.dropButtonClick
1215
+ .pipe(filter(event => this.searchable && this.enabled && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.formControl.value),
1216
+ // Make enough characters
1217
+ filter(thresholdFilterFn)),
1213
1218
  // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
1214
1219
  merge(
1215
1220
  // Focus or click => Load all
1216
- merge(this.onFocus, this.onClick)
1217
- .pipe(filter(_ => this.enabled && this.searchable), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)),
1218
- // On dropdown button click
1219
- this.onDropButtonClick
1220
- .pipe(filter(event => this.enabled && this.searchable && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)), merge(this.$inputItems, this._filter$)
1221
- .pipe(filter(event => this.enabled && this.searchable), map(_ => this.formControl.value)))
1221
+ merge(this.focused, this.clicked)
1222
+ .pipe(filter(_ => this.enabled && this.searchable), map(_ => this.showAllOnFocus ? '*' : this.formControl.value),
1223
+ // DEBUG
1224
+ tap(value => this.debug && console.debug(this.logPrefix + ' Received focused or clicked event: ', value))), merge(this.$inputItems, this._$filter)
1225
+ .pipe(filter(_ => this.searchable && this.enabled), map(_ => this.formControl.value),
1226
+ // DEBUG
1227
+ tap(value => this.debug && console.debug(this.logPrefix + ' Received $inputItems or filter event: ', value))))
1222
1228
  .pipe(
1223
- // Start to suggest when :
1224
- // - '*'
1225
- // - is nil, and there is no min length threshold
1226
- // - is not nil and not a string type (e.g. object)
1227
- // - is not nil and is a string, and has enough character
1228
- filter(value => value === '*'
1229
- || (isNil(value)
1230
- ? (this.suggestLengthThreshold <= 0)
1231
- : (!(typeof value === 'string')
1232
- || value.length >= this.suggestLengthThreshold))),
1229
+ // Make sure there is enough characters
1230
+ filter(thresholdFilterFn),
1233
1231
  // Distinguish changes
1234
- distinctUntilChanged()))
1232
+ // - do NOT filter if loading, to force update after a blur event
1233
+ distinctUntilChanged((v1, v2) => !this.loading && (v1 === v2 || this.equals(v1, v2)))))
1235
1234
  .pipe(
1236
1235
  // Suggest values
1237
1236
  mergeMap(value => this.suggest(value, this.filter)),
1238
1237
  // DEBUG
1239
- //tap(items => console.debug(this.logPrefix + ' Received from suggest: ', items)),
1238
+ tap(items => this.debug && console.debug(this.logPrefix + ' Received from suggest: ', items)),
1240
1239
  // Store implicit value (will use it onBlur if not other value selected)
1241
1240
  tap(items => this.updateImplicitValue(items)));
1242
1241
  // Fetch more events
@@ -1257,7 +1256,7 @@ class MatAutocompleteField {
1257
1256
  // Make sure control value is inside (mat-select)
1258
1257
  if (items && !this.searchable) {
1259
1258
  const value = this.formControl.value;
1260
- if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
1259
+ if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.equals(item, value)) === -1) {
1261
1260
  // If form value is missing: add it to the list
1262
1261
  items = items.concat(value);
1263
1262
  }
@@ -1266,10 +1265,12 @@ class MatAutocompleteField {
1266
1265
  this.$filteredItems.next(items);
1267
1266
  }));
1268
1267
  // Applying implicit value, on blur
1269
- this._subscription.add(this.onBlur
1268
+ this._subscription.add(this.blurred
1270
1269
  .pipe(
1271
1270
  // Skip if not searchable
1272
- filter(_ => this.searchable), map(_ => this.formControl.value))
1271
+ filter(_ => this.searchable), map(_ => this.formControl.value),
1272
+ // DEBUG
1273
+ tap(value => console.debug(this.logPrefix + ' Received blurred event: ', value)))
1273
1274
  .subscribe(value => {
1274
1275
  // When leave component without object, use implicit value if :
1275
1276
  // - an explicit value
@@ -1280,12 +1281,14 @@ class MatAutocompleteField {
1280
1281
  this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
1281
1282
  this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
1282
1283
  this.checkIfTouched();
1284
+ this.$filteredItems.next([this._implicitValue]);
1283
1285
  }
1284
1286
  else if (isNilOrBlank(value)) {
1285
1287
  this.writeValue(null);
1286
1288
  this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
1287
1289
  this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
1288
1290
  this.checkIfTouched();
1291
+ this.$filteredItems.next(undefined);
1289
1292
  }
1290
1293
  this._implicitValue = null; // reset the implicit value
1291
1294
  }));
@@ -1296,7 +1299,7 @@ class MatAutocompleteField {
1296
1299
  distinctUntilChanged())
1297
1300
  // Update component state
1298
1301
  .subscribe(() => this.checkIfTouched()));
1299
- this._subscription.add(this.onKeydownEscape.subscribe(event => {
1302
+ this._subscription.add(this.keydownEscape.subscribe(event => {
1300
1303
  if (this.isOpen) {
1301
1304
  event === null || event === void 0 ? void 0 : event.stopPropagation();
1302
1305
  event === null || event === void 0 ? void 0 : event.preventDefault();
@@ -1315,20 +1318,22 @@ class MatAutocompleteField {
1315
1318
  this._openedSubscription.unsubscribe();
1316
1319
  }
1317
1320
  this.$inputItems.complete();
1321
+ this.$inputItems.unsubscribe();
1318
1322
  this.$filteredItems.complete();
1323
+ this.$filteredItems.unsubscribe();
1319
1324
  this._reload$.complete();
1320
- this._filter$.complete();
1321
- this.onDropButtonClick.complete();
1322
- this.onClick.complete();
1323
- this.onBlur.complete();
1324
- this.onFocus.complete();
1325
- this.onKeydownEscape.complete();
1326
- this.onKeyupEnter.complete();
1325
+ this._$filter.complete();
1326
+ this.dropButtonClick.complete();
1327
+ this.clicked.complete();
1328
+ this.blurred.complete();
1329
+ this.focused.complete();
1330
+ this.keydownEscape.complete();
1331
+ this.keyupEnter.complete();
1327
1332
  this._fetchMore$.complete();
1328
1333
  this._implicitValue = undefined;
1329
1334
  this.formControl = null;
1330
1335
  this.displayWith = null;
1331
- this.compareWith = null;
1336
+ this.equals = null;
1332
1337
  this.suggestFn = null;
1333
1338
  this.$filteredItems = null;
1334
1339
  this.matSelectItems$ = null;
@@ -1338,8 +1343,8 @@ class MatAutocompleteField {
1338
1343
  this._onTouchedCallback = null;
1339
1344
  this._onFetchMoreCallback = null;
1340
1345
  // Destroy the 'autocomplete' instance, to avoid memory leak
1341
- if (this.matAutocomplete) {
1342
- this.matAutocomplete.ngOnDestroy();
1346
+ if (this.autocomplete) {
1347
+ this.autocomplete.ngOnDestroy();
1343
1348
  }
1344
1349
  }
1345
1350
  /**
@@ -1351,7 +1356,7 @@ class MatAutocompleteField {
1351
1356
  }
1352
1357
  writeValue(value) {
1353
1358
  // DEBUG
1354
- // console.debug(this.logPrefix + " Writing value: ", value);
1359
+ // console.debug(this.logPrefix + ' Writing value: ', value);
1355
1360
  if (value !== this.formControl.value) {
1356
1361
  this.formControl.patchValue(value, { emitEvent: false });
1357
1362
  this._onChangeCallback(value);
@@ -1365,7 +1370,7 @@ class MatAutocompleteField {
1365
1370
  }
1366
1371
  setDisabledState(isDisabled) {
1367
1372
  var _a;
1368
- if (isDisabled && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1373
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1369
1374
  this.closePanel();
1370
1375
  }
1371
1376
  this.cd.markForCheck();
@@ -1379,7 +1384,7 @@ class MatAutocompleteField {
1379
1384
  (_a = this.matSelect) === null || _a === void 0 ? void 0 : _a.focus();
1380
1385
  }
1381
1386
  }
1382
- filterInputTextFocusInEvent(event) {
1387
+ filterInputTextFocusEvent(event) {
1383
1388
  if (!event || event.defaultPrevented)
1384
1389
  return;
1385
1390
  // Ignore event from mat-option
@@ -1388,15 +1393,20 @@ class MatAutocompleteField {
1388
1393
  if (event.stopPropagation)
1389
1394
  event.stopPropagation();
1390
1395
  event.returnValue = false;
1391
- //DEBUG console.debug(this.logPrefix + " Cancelling focus event");
1396
+ // DEBUG
1397
+ if (this.debug)
1398
+ console.debug(this.logPrefix + ' Cancelling focus event');
1392
1399
  return false;
1393
1400
  }
1394
- const hasContent = selectInputContent(event);
1401
+ if (this.debug)
1402
+ console.debug(this.logPrefix + ' Select input content');
1403
+ const hasContent = true; // selectInputContent(event);
1395
1404
  // If combo is empty, or if has content but should force to show panel on focus
1396
- if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
1405
+ if (!hasContent || this.showPanelOnFocus || this.showAllOnFocus) {
1397
1406
  // DEBUG
1398
- //console.debug(this.logPrefix + ' Emit focus event');
1399
- this.onFocus.emit(event);
1407
+ if (this.debug)
1408
+ console.debug(this.logPrefix + ' Emit focus event');
1409
+ this.focused.emit();
1400
1410
  return true;
1401
1411
  }
1402
1412
  return false;
@@ -1410,14 +1420,17 @@ class MatAutocompleteField {
1410
1420
  if (event.stopPropagation)
1411
1421
  event.stopPropagation();
1412
1422
  event.returnValue = false;
1413
- //DEBUG console.debug(this.logPrefix + " Cancelling blur event");
1423
+ // DEBUG
1424
+ if (this.debug)
1425
+ console.debug(this.logPrefix + " Cancelling blur event");
1414
1426
  return false;
1415
1427
  }
1416
1428
  //DEBUG
1417
- //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
1429
+ if (this.debug)
1430
+ console.debug(this.logPrefix + ' Blur: move caret to the beginning');
1418
1431
  // Move caret to the beginning (fix issue IMAGINE-469)
1419
1432
  selectInputRange(event.target, 0, 0);
1420
- this.onBlur.emit(event);
1433
+ this.blurred.emit(event);
1421
1434
  return true;
1422
1435
  }
1423
1436
  filterMatSelectFocusEvent(event) {
@@ -1425,7 +1438,7 @@ class MatAutocompleteField {
1425
1438
  return;
1426
1439
  // DEBUG
1427
1440
  // console.debug(this.logPrefix + " Received <mat-select> focus event", event);
1428
- this.onFocus.emit(event);
1441
+ this.focused.emit(event);
1429
1442
  }
1430
1443
  filterMatSelectBlurEvent(event) {
1431
1444
  if (!event || event.defaultPrevented)
@@ -1442,7 +1455,7 @@ class MatAutocompleteField {
1442
1455
  // console.debug(this.logPrefix + " Cancelling <mat-select> blur event");
1443
1456
  return false;
1444
1457
  }
1445
- this.onBlur.emit(event);
1458
+ this.blurred.emit(event);
1446
1459
  return true;
1447
1460
  }
1448
1461
  toggleSearch(event) {
@@ -1464,7 +1477,7 @@ class MatAutocompleteField {
1464
1477
  event.stopPropagation();
1465
1478
  }
1466
1479
  _initAutocompleteInfiniteScroll(threshold) {
1467
- if (!this.matAutocomplete)
1480
+ if (!this.autocomplete)
1468
1481
  return; // Skip
1469
1482
  // DEBUG
1470
1483
  //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
@@ -1472,12 +1485,12 @@ class MatAutocompleteField {
1472
1485
  this._openedSubscription.unsubscribe();
1473
1486
  this._subscription.remove(this._openedSubscription);
1474
1487
  }
1475
- const opened$ = this.matAutocomplete._isOpen
1488
+ const opened$ = this.autocomplete._isOpen
1476
1489
  ? timer(0)
1477
- : this.matAutocomplete.opened.pipe(debounceTime(200));
1490
+ : this.autocomplete.opened.pipe(debounceTime(200));
1478
1491
  const scrollEnd$ = opened$
1479
1492
  .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
1480
- .pipe(takeUntil(this.matAutocomplete.closed))));
1493
+ .pipe(takeUntil(this.autocomplete.closed))));
1481
1494
  // Trigger fetch more, end end scroll reached
1482
1495
  this._openedSubscription = scrollEnd$
1483
1496
  .subscribe(() => this._fetchMore$.emit());
@@ -1508,14 +1521,14 @@ class MatAutocompleteField {
1508
1521
  }
1509
1522
  get isOpen() {
1510
1523
  var _a, _b;
1511
- return (this.searchable && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1524
+ return (this.searchable && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1512
1525
  || ((_b = this.matSelect) === null || _b === void 0 ? void 0 : _b.panelOpen)
1513
1526
  || false;
1514
1527
  }
1515
1528
  closePanel() {
1516
1529
  var _a, _b, _c, _d;
1517
- if ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1518
- (_b = this.matAutocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1530
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1531
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1519
1532
  return;
1520
1533
  }
1521
1534
  if ((_c = this.matSelect) === null || _c === void 0 ? void 0 : _c.panelOpen) {
@@ -1525,6 +1538,7 @@ class MatAutocompleteField {
1525
1538
  /* -- private method -- */
1526
1539
  suggest(value, filter) {
1527
1540
  return __awaiter(this, void 0, void 0, function* () {
1541
+ // Call suggestion function
1528
1542
  let res = yield this.suggestFn(value, filter);
1529
1543
  // DEBUG
1530
1544
  //console.debug(this.logPrefix + ' Filtered items by suggestFn:', value, res);
@@ -1545,6 +1559,9 @@ class MatAutocompleteField {
1545
1559
  this._moreItemsCount = fetchMore && (this._itemCount - data.length) || 0;
1546
1560
  res = data;
1547
1561
  }
1562
+ // DEBUG
1563
+ if (this.debug)
1564
+ console.debug(this.logPrefix + ' Filtered items by suggestFn:', value, res);
1548
1565
  return res;
1549
1566
  });
1550
1567
  }
@@ -1582,7 +1599,7 @@ class MatAutocompleteField {
1582
1599
  }
1583
1600
  getAutocompletePanel() {
1584
1601
  var _a, _b;
1585
- const ele = (_b = (_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1602
+ const ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1586
1603
  if (!ele) {
1587
1604
  // DEBUG
1588
1605
  console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
@@ -1616,10 +1633,14 @@ MatAutocompleteField.decorators = [
1616
1633
  { type: Component, args: [{
1617
1634
  // eslint-disable-next-line @angular-eslint/component-selector
1618
1635
  selector: 'mat-autocomplete-field',
1619
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- readonly -->\n <ng-container>\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n </ng-container>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"onKeydownEscape.emit($event)\"\n (keyup.enter)=\"onKeyupEnter.emit($event)\"\n [compareWith]=\"compareWith\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"onClick.emit($event)\"\n (focus)=\"filterInputTextFocusInEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"onKeydownEscape.emit($event)\"\n (keyup.enter)=\"onKeyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.opened && onDropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [class.cdk-visually-hidden]=\"!searchable\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count \" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"onDropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1620
- providers: [DEFAULT_VALUE_ACCESSOR$6],
1636
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <!-- readonly -->\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1637
+ providers: [{
1638
+ provide: NG_VALUE_ACCESSOR,
1639
+ multi: true,
1640
+ useExisting: forwardRef(() => MatAutocompleteField)
1641
+ }],
1621
1642
  changeDetection: ChangeDetectionStrategy.OnPush,
1622
- styles: [":host{display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}.fact-scroll-viewport{height:100%;width:100%}.fact-item{height:54px;display:flex}.fact-item .fact-date{min-width:100px;text-align:center;font-size:24pt}.fact-item .fact-date,.fact-item .fact-text{height:100%;align-items:center;justify-content:center;display:flex}.fact-item .fact-text{border-radius:10px}cdk-virtual-scroll-viewport{border:1px solid #000}cdk-virtual-scroll-viewport li{list-style:none}"]
1643
+ styles: [":host{display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}"]
1623
1644
  },] }
1624
1645
  ];
1625
1646
  MatAutocompleteField.ctorParameters = () => [
@@ -1627,7 +1648,7 @@ MatAutocompleteField.ctorParameters = () => [
1627
1648
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
1628
1649
  ];
1629
1650
  MatAutocompleteField.propDecorators = {
1630
- compareWith: [{ type: Input }],
1651
+ equals: [{ type: Input }],
1631
1652
  logPrefix: [{ type: Input }],
1632
1653
  formControl: [{ type: Input }],
1633
1654
  formControlName: [{ type: Input }],
@@ -1657,21 +1678,21 @@ MatAutocompleteField.propDecorators = {
1657
1678
  fetchMoreThreshold: [{ type: Input }],
1658
1679
  suggestLengthThreshold: [{ type: Input }],
1659
1680
  showLoadingSpinner: [{ type: Input }],
1660
- animationsDisabled: [{ type: HostBinding, args: ['@.disabled',] }],
1681
+ debug: [{ type: Input }],
1682
+ clicked: [{ type: Output, args: ['click',] }],
1683
+ blurred: [{ type: Output, args: ['blur',] }],
1684
+ focused: [{ type: Output, args: ['focus',] }],
1685
+ dropButtonClick: [{ type: Output }],
1686
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
1687
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
1688
+ matSelect: [{ type: ViewChild, args: ['matSelect',] }],
1689
+ matInputText: [{ type: ViewChild, args: ['matInputText',] }],
1690
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
1691
+ autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
1661
1692
  filter: [{ type: Input }],
1662
1693
  readonly: [{ type: Input }],
1663
1694
  tabindex: [{ type: Input }],
1664
- items: [{ type: Input }],
1665
- onClick: [{ type: Output, args: ['click',] }],
1666
- onBlur: [{ type: Output, args: ['blur',] }],
1667
- onFocus: [{ type: Output, args: ['focus',] }],
1668
- onDropButtonClick: [{ type: Output, args: ['dropButtonClick',] }],
1669
- onKeydownEscape: [{ type: Output, args: ['keydown.escape',] }],
1670
- onKeyupEnter: [{ type: Output, args: ['keyup.enter',] }],
1671
- matSelect: [{ type: ViewChild, args: ['matSelect',] }],
1672
- matInputText: [{ type: ViewChild, args: ['matInputText',] }],
1673
- matAutocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
1674
- matAutocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }]
1695
+ items: [{ type: Input }]
1675
1696
  };
1676
1697
 
1677
1698
  class DateFormatPipe {
@@ -4085,9 +4106,9 @@ const MASKS = {
4085
4106
  DD: [/[+-]/, /\d/, /\d/, /\d/, '.', /\d/, /\d/, /\d/, /\d/, /\d/, /\d/, /\d/, '°']
4086
4107
  }
4087
4108
  };
4088
- const noop$6 = () => {
4109
+ const noop$7 = () => {
4089
4110
  };
4090
- const ɵ0$9 = noop$6;
4111
+ const ɵ0$a = noop$7;
4091
4112
  class MatLatLongField {
4092
4113
  constructor(platform, translate, formBuilder, cd, formGroupDir) {
4093
4114
  this.platform = platform;
@@ -4095,8 +4116,8 @@ class MatLatLongField {
4095
4116
  this.formBuilder = formBuilder;
4096
4117
  this.cd = cd;
4097
4118
  this.formGroupDir = formGroupDir;
4098
- this._onChangeCallback = noop$6;
4099
- this._onTouchedCallback = noop$6;
4119
+ this._onChangeCallback = noop$7;
4120
+ this._onTouchedCallback = noop$7;
4100
4121
  this._subscription = new Subscription();
4101
4122
  this.disabling = false;
4102
4123
  this.writing = false;
@@ -4438,8 +4459,8 @@ const DEFAULT_VALUE_ACCESSOR$5 = {
4438
4459
  multi: true
4439
4460
  };
4440
4461
  const DAY_MASK$2 = [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/];
4441
- const noop$5 = () => { };
4442
- const ɵ0$8 = noop$5;
4462
+ const noop$6 = () => { };
4463
+ const ɵ0$9 = noop$6;
4443
4464
  class MatDate {
4444
4465
  constructor(platform, dateAdapter, translate, formBuilder, cd, keyboard, formGroupDir) {
4445
4466
  this.dateAdapter = dateAdapter;
@@ -4448,8 +4469,8 @@ class MatDate {
4448
4469
  this.cd = cd;
4449
4470
  this.keyboard = keyboard;
4450
4471
  this.formGroupDir = formGroupDir;
4451
- this._onChangeCallback = noop$5;
4452
- this._onTouchedCallback = noop$5;
4472
+ this._onChangeCallback = noop$6;
4473
+ this._onTouchedCallback = noop$6;
4453
4474
  this._subscription = new Subscription();
4454
4475
  this.writing = true;
4455
4476
  this.disabling = false;
@@ -4731,9 +4752,9 @@ const DEFAULT_VALUE_ACCESSOR$4 = {
4731
4752
  const DAY_MASK$1 = [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/];
4732
4753
  const HOUR_REGEXP = /^[012][0-9][:][012345][0-9]$/;
4733
4754
  const HOUR_MASK$1 = [/[012]/, /\d/, ':', /[012345]/, /\d/];
4734
- const noop$4 = () => {
4755
+ const noop$5 = () => {
4735
4756
  };
4736
- const ɵ0$7 = noop$4;
4757
+ const ɵ0$8 = noop$5;
4737
4758
  class MatDateTime {
4738
4759
  constructor(platform, dateAdapter, translate, formBuilder, keyboard, cd, formGroupDir) {
4739
4760
  this.platform = platform;
@@ -4743,8 +4764,8 @@ class MatDateTime {
4743
4764
  this.keyboard = keyboard;
4744
4765
  this.cd = cd;
4745
4766
  this.formGroupDir = formGroupDir;
4746
- this._onChangeCallback = noop$4;
4747
- this._onTouchedCallback = noop$4;
4767
+ this._onChangeCallback = noop$5;
4768
+ this._onTouchedCallback = noop$5;
4748
4769
  this._subscription = new Subscription();
4749
4770
  this.writing = true;
4750
4771
  this.disabling = false;
@@ -5102,9 +5123,9 @@ const DEFAULT_VALUE_ACCESSOR$3 = {
5102
5123
  multi: true
5103
5124
  };
5104
5125
  const DAY_MASK = [/\d/, /\d/, /\d/, /\d/];
5105
- const noop$3 = () => {
5126
+ const noop$4 = () => {
5106
5127
  };
5107
- const ɵ0$6 = noop$3;
5128
+ const ɵ0$7 = noop$4;
5108
5129
  class MatDateShort {
5109
5130
  constructor(platform, dateAdapter, translate, formBuilder, cd, keyboard, formGroupDir) {
5110
5131
  this.dateAdapter = dateAdapter;
@@ -5113,8 +5134,8 @@ class MatDateShort {
5113
5134
  this.cd = cd;
5114
5135
  this.keyboard = keyboard;
5115
5136
  this.formGroupDir = formGroupDir;
5116
- this._onChangeCallback = noop$3;
5117
- this._onTouchedCallback = noop$3;
5137
+ this._onChangeCallback = noop$4;
5138
+ this._onTouchedCallback = noop$4;
5118
5139
  this.writing = true;
5119
5140
  this.disabling = false;
5120
5141
  this.dayMask = DAY_MASK;
@@ -5437,9 +5458,9 @@ const DEFAULT_VALUE_ACCESSOR$2 = {
5437
5458
  };
5438
5459
  const HOUR_TIME_PATTERN = /[0-9]\d\d:[0-5]\d/;
5439
5460
  const HOUR_MASK = [/\d/, /\d/, /\d/, ':', /[0-5]/, /\d/];
5440
- const noop$2 = () => {
5461
+ const noop$3 = () => {
5441
5462
  };
5442
- const ɵ0$5 = noop$2;
5463
+ const ɵ0$6 = noop$3;
5443
5464
  class MatDuration {
5444
5465
  constructor(platform, dateAdapter, translate, formBuilder, cd, formGroupDir) {
5445
5466
  this.dateAdapter = dateAdapter;
@@ -5448,8 +5469,8 @@ class MatDuration {
5448
5469
  this.cd = cd;
5449
5470
  this.formGroupDir = formGroupDir;
5450
5471
  this._subscription = new Subscription();
5451
- this._onChangeCallback = noop$2;
5452
- this._onTouchedCallback = noop$2;
5472
+ this._onChangeCallback = noop$3;
5473
+ this._onTouchedCallback = noop$3;
5453
5474
  this.writing = true;
5454
5475
  this.disabling = false;
5455
5476
  this.hourMask = HOUR_MASK;
@@ -5694,17 +5715,17 @@ const DEFAULT_VALUE_ACCESSOR$1 = {
5694
5715
  useExisting: forwardRef(() => MatBooleanField),
5695
5716
  multi: true
5696
5717
  };
5697
- const noop$1 = () => {
5718
+ const noop$2 = () => {
5698
5719
  };
5699
- const ɵ0$4 = noop$1;
5720
+ const ɵ0$5 = noop$2;
5700
5721
  class MatBooleanField {
5701
5722
  constructor(translate, formBuilder, cd, formGroupDir) {
5702
5723
  this.translate = translate;
5703
5724
  this.formBuilder = formBuilder;
5704
5725
  this.cd = cd;
5705
5726
  this.formGroupDir = formGroupDir;
5706
- this._onChangeCallback = noop$1;
5707
- this._onTouchedCallback = noop$1;
5727
+ this._onChangeCallback = noop$2;
5728
+ this._onTouchedCallback = noop$2;
5708
5729
  this._writing = false;
5709
5730
  this.showRadio = false;
5710
5731
  this.disabled = false;
@@ -6487,8 +6508,8 @@ class MatSwipeField {
6487
6508
  constructor(cd, formGroupDir) {
6488
6509
  this.cd = cd;
6489
6510
  this.formGroupDir = formGroupDir;
6490
- this._onChangeCallback = noop$8;
6491
- this._onTouchedCallback = noop$8;
6511
+ this._onChangeCallback = noop$9;
6512
+ this._onTouchedCallback = noop$9;
6492
6513
  this._subscription = new Subscription();
6493
6514
  this._writing = false;
6494
6515
  this.showSlides = false;
@@ -6552,7 +6573,7 @@ class MatSwipeField {
6552
6573
  // Default values
6553
6574
  this.displayAttributes = this.displayAttributes || ['label', 'name'];
6554
6575
  this.displayWith = this.displayWith || ((obj) => obj && joinPropertiesPath(obj, this.displayAttributes));
6555
- this.compareWithFn = this.compareWithFn || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6576
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6556
6577
  this.mobile = toBoolean(this.mobile, false);
6557
6578
  }
6558
6579
  ngOnDestroy() {
@@ -6612,7 +6633,7 @@ class MatSwipeField {
6612
6633
  if (this._writing)
6613
6634
  return;
6614
6635
  this._writing = true;
6615
- if (!this.compareWithFn(value, this._value)) {
6636
+ if (!this.equals(value, this._value)) {
6616
6637
  this._value = value;
6617
6638
  this.showSlides = isNotNil(this._value);
6618
6639
  this.updateSlides();
@@ -6696,7 +6717,7 @@ class MatSwipeField {
6696
6717
  }
6697
6718
  updateSlides() {
6698
6719
  if (this.showSlides) {
6699
- const itemIndex = (this.$items.getValue() || []).findIndex(value => this.compareWithFn(value, this._value));
6720
+ const itemIndex = (this.$items.getValue() || []).findIndex(value => this.equals(value, this._value));
6700
6721
  if (itemIndex >= 0) {
6701
6722
  this.slideTo(itemIndex);
6702
6723
  }
@@ -6760,7 +6781,7 @@ MatSwipeField.propDecorators = {
6760
6781
  required: [{ type: Input }],
6761
6782
  mobile: [{ type: Input }],
6762
6783
  clearable: [{ type: Input }],
6763
- compareWithFn: [{ type: Input }],
6784
+ equals: [{ type: Input }],
6764
6785
  displayWith: [{ type: Input }],
6765
6786
  displayAttributes: [{ type: Input }],
6766
6787
  appAutofocus: [{ type: Input }],
@@ -6799,43 +6820,57 @@ SharedMatSwipeModule.decorators = [
6799
6820
  },] }
6800
6821
  ];
6801
6822
 
6823
+ const noop$1 = () => { };
6824
+ const ɵ0$4 = noop$1;
6802
6825
  class MatChipsField {
6803
6826
  constructor(cd, formGroupDir) {
6804
6827
  this.cd = cd;
6805
6828
  this.formGroupDir = formGroupDir;
6806
- this._onChangeCallback = (_) => { };
6807
- this._onTouchedCallback = () => { };
6829
+ this._onChangeCallback = noop$1;
6830
+ this._onTouchedCallback = noop$1;
6808
6831
  this.logPrefix = '[mat-chips] ';
6832
+ this.formControlName = null;
6809
6833
  this.required = false;
6810
6834
  this.readonly = false;
6811
6835
  this.clearable = false;
6812
- this.debounceTime = 250;
6813
- this.highlightAccent = false;
6836
+ this.debounceTime = null;
6814
6837
  this.i18nPrefix = 'REFERENTIAL.';
6815
6838
  this.noResultMessage = 'COMMON.NO_RESULT';
6839
+ this.itemSize = '48px';
6840
+ this.fetchMoreThreshold = '15%';
6841
+ this.suggestLengthThreshold = null;
6842
+ this.showLoadingSpinner = true;
6843
+ this.chipColor = null;
6816
6844
  this.debug = false;
6817
6845
  this.clicked = new EventEmitter();
6818
6846
  this.blurred = new EventEmitter();
6819
6847
  this.focused = new EventEmitter();
6848
+ this.dropButtonClick = new EventEmitter(true);
6849
+ this.keydownEscape = new EventEmitter();
6850
+ this.keyupEnter = new EventEmitter();
6820
6851
  this.$inputItems = new BehaviorSubject(undefined);
6821
- this.onDropButtonClick = new EventEmitter(true);
6852
+ this.$filteredItems = new BehaviorSubject(undefined);
6822
6853
  this.inputControl = new FormControl();
6823
- this.getPropertyByPath = getPropertyByPath;
6854
+ this._fetchMore$ = new EventEmitter();
6824
6855
  this._subscription = new Subscription();
6825
6856
  this._$filter = new BehaviorSubject(undefined);
6857
+ this._reload$ = new Subject();
6826
6858
  }
6827
6859
  get itemCount() {
6828
6860
  return this._itemCount;
6829
6861
  }
6862
+ get canFetchMore() {
6863
+ return this._onFetchMoreCallback && this._moreItemsCount > 0;
6864
+ }
6830
6865
  set filter(value) {
6831
- if (this.debug)
6832
- console.debug(this.logPrefix + ' Setting filter:', value);
6833
- if (value !== this._$filter.getValue()) {
6866
+ if (value !== this._$filter.value) {
6867
+ // DEBUG
6868
+ //console.debug(this.logPrefix + " Setting filter:", value);
6834
6869
  this._$filter.next(value);
6835
6870
  }
6836
6871
  }
6837
6872
  get filter() {
6838
- return this._$filter.getValue();
6873
+ return this._$filter.value;
6839
6874
  }
6840
6875
  set tabindex(value) {
6841
6876
  this._tabindex = value;
@@ -6847,19 +6882,25 @@ class MatChipsField {
6847
6882
  set items(value) {
6848
6883
  // Remove previous subscription on items, (if exits)
6849
6884
  if (this._itemsSubscription) {
6850
- console.warn('Items received twice !');
6885
+ console.warn(this.logPrefix + ' Items received twice !');
6851
6886
  this._subscription.remove(this._itemsSubscription);
6852
6887
  this._itemsSubscription.unsubscribe();
6853
6888
  }
6854
6889
  if (isObservable(value)) {
6855
- this._itemsSubscription = this._subscription.add(value.subscribe(v => this.$inputItems.next(v)));
6890
+ this._itemsSubscription = this._subscription.add(value.subscribe(v => {
6891
+ //console.warn(this.logPrefix + " Received items: ", v);
6892
+ this.$inputItems.next(v);
6893
+ }));
6856
6894
  }
6857
6895
  else {
6858
- if (value !== this.$inputItems.getValue()) {
6896
+ if (value !== this.$inputItems.value) {
6859
6897
  this.$inputItems.next(value);
6860
6898
  }
6861
6899
  }
6862
6900
  }
6901
+ get items() {
6902
+ return this.$inputItems;
6903
+ }
6863
6904
  get value() {
6864
6905
  return this.formControl.value || [];
6865
6906
  }
@@ -6869,7 +6910,11 @@ class MatChipsField {
6869
6910
  get enabled() {
6870
6911
  return !this.readonly && this.formControl.enabled;
6871
6912
  }
6913
+ get loading() {
6914
+ return isNil(this.$filteredItems.value);
6915
+ }
6872
6916
  ngOnInit() {
6917
+ var _a, _b;
6873
6918
  this.formControl = this.formControl || this.formControlName && this.formGroupDir && this.formGroupDir.form.get(this.formControlName);
6874
6919
  if (!this.formControl)
6875
6920
  throw new Error('Missing mandatory attribute \'formControl\' or \'formControlName\' in <mat-chips-field>.');
@@ -6884,85 +6929,204 @@ class MatChipsField {
6884
6929
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
6885
6930
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
6886
6931
  this.displayWith = this.displayWith || this.config.displayWith;
6887
- this.compareWith = this.compareWith || this.config.compareWith;
6932
+ this.equals = this.equals || this.config.equals;
6888
6933
  this.mobile = toBoolean(this.mobile, this.config.mobile);
6889
- this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, true));
6934
+ this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
6935
+ this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
6890
6936
  this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus, true));
6891
- this.class = this.class || this.config.class;
6937
+ this.classList = this.classList || this.config.class;
6938
+ this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
6939
+ this.highlightAccent = toBoolean(this.highlightAccent, toBoolean(this.config.highlightAccent));
6892
6940
  }
6893
6941
  // Default values
6894
- this.displayAttributes = this.displayAttributes || (this.filter && this.filter.attributes) || ['label', 'name'];
6942
+ this.displayAttributes = this.displayAttributes || ((_a = this.filter) === null || _a === void 0 ? void 0 : _a.attributes) || ['label', 'name'];
6895
6943
  this.displayWith = this.displayWith || ((obj) => obj && joinPropertiesPath(obj, this.displayAttributes));
6896
- this.compareWith = this.compareWith || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6897
- this.displayColumnSizes = this.displayColumnSizes || this.displayAttributes.map(attr => (
6898
- // If label then col size = 2
6899
- attr && attr.endsWith('label')) ? 2 :
6900
- // If rankOrder then col size = 1
6901
- (attr && attr.endsWith('rankOrder') ? 1 :
6902
- // Else, auto size
6903
- undefined));
6944
+ this.displayColumnSizes = this.displayColumnSizes
6945
+ // if only column: auto size
6946
+ || (this.displayAttributes.length === 1 && [undefined])
6947
+ // Computed column size:
6948
+ // - if label then col size = 2
6949
+ // - if rankOrder then col size = 1
6950
+ // - ese => auto size
6951
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
6952
+ : ((attr && attr.endsWith('rankOrder')) ? 1
6953
+ : undefined));
6904
6954
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
6905
6955
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
6906
6956
  this.mobile = toBoolean(this.mobile, false);
6957
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6958
+ const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
6959
+ this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
6960
+ || (classList.includes('min-width-large') && '400px')
6961
+ || (classList.includes('min-width-xlarge') && '450px')
6962
+ || (classList.includes('mat-autocomplete-panel-full-size') && '100vw')));
6963
+ this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, true);
6964
+ this.debounceTime = toNumber(this.debounceTime, 250);
6965
+ this.highlightAccent = toBoolean(this.highlightAccent, false);
6966
+ this.suggestLengthThreshold = this.suggestLengthThreshold || 0;
6967
+ this.matAutocompletePosition = this.matAutocompletePosition || (this.mobile ? 'above' : 'auto'); // On mobile, set position to above (because of bottom keyboard)
6907
6968
  // No suggestFn: filter on the given items
6908
6969
  if (!this.suggestFn) {
6909
6970
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6910
6971
  if (this.debug)
6911
6972
  console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
6912
- const { data, total } = yield suggestFromArray(this.$inputItems.getValue(), value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
6913
- this._itemCount = toNumber(total, data && data.length || 0);
6914
- return { data, total };
6973
+ return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
6915
6974
  });
6916
6975
  // Wait (once) that items are loaded, then call suggest from array fn
6917
6976
  this.suggestFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6918
- if (isNil(this.$inputItems.getValue())) {
6977
+ if (isNil(this.$inputItems.value)) {
6919
6978
  if (this.debug)
6920
6979
  console.debug('[mat-chips] Waiting items to be set...');
6921
6980
  yield firstNotNilPromise(this.$inputItems);
6922
6981
  if (this.debug)
6923
- console.debug('[mat-chips] Received items:', this.$inputItems.getValue());
6982
+ console.debug('[mat-chips] Received items:', this.$inputItems.value);
6924
6983
  }
6925
6984
  this.suggestFn = suggestFromArrayFn;
6926
- return this.suggest(value, filter); // Loop
6985
+ return this.suggestFn(value, filter); // Loop
6927
6986
  });
6928
6987
  }
6929
- // this.matSelectItems$ = this.$inputItems
6930
- // .pipe(
6931
- // filter(isNotNil),
6932
- // map((items) => {
6933
- // // Make sure control value is inside
6934
- // const value = this.inputControl.value;
6935
- // // If form value is missing: add it to the list
6936
- // if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
6937
- // // console.debug(this.logPrefix + " Add form value to items, because missed in items", value);
6938
- // return items.concat(value);
6939
- // }
6940
- // return items;
6941
- // })
6942
- // );
6943
- this._subscription.add(this.onDropButtonClick
6944
- .pipe(filter(event => (!event || !event.defaultPrevented) && this.formControl.enabled))
6945
- .subscribe(() => {
6946
- this.matInputText.nativeElement.focus();
6947
- }));
6948
- const updateFilteredItemsEvents$ = merge(
6988
+ // Send value to suggest when :
6989
+ // - is nil and '*', and no min length threshold
6990
+ // - is not nil and not a string type (e.g. object)
6991
+ // - is not nil and is a string, and has enough character
6992
+ const thresholdFilterFn = (value) => (isNilOrBlank(value) || (value === '*')
6993
+ ? (this.suggestLengthThreshold <= 0)
6994
+ : (!(typeof value === 'string')
6995
+ || value.length >= this.suggestLengthThreshold));
6996
+ const filteredItemsChanges = merge(
6997
+ // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
6998
+ this._reload$, this.inputControl.valueChanges
6999
+ .pipe(filter(value => this.enabled && isNotNilString(value)),
7000
+ // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
7001
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
7002
+ // Filter if not enough character
7003
+ filter(value => value.length >= this.suggestLengthThreshold),
7004
+ // Wait a debounce time
7005
+ debounceTime(this.debounceTime)
7006
+ // DEBUG
7007
+ //tap((value) => console.debug(this.logPrefix + ' Search text changes:', value))
7008
+ ),
7009
+ // Object set: use it (no distinctUntilChanged() pipe need)
7010
+ this.inputControl.valueChanges
7011
+ .pipe(startWith(this.inputControl.value), filter(value => isNotNil(value) && typeof value === 'object'),
7012
+ // DEBUG
7013
+ tap(value => this.debug && console.debug(this.logPrefix + ' Received an object value: ', value))),
7014
+ // On dropdown button click (no distinctUntilChanged(), nor debounceTime)
7015
+ this.dropButtonClick
7016
+ .pipe(filter(event => this.enabled && (!event || !event.defaultPrevented)), tap(_ => this.matInputText.nativeElement.focus()), map(_ => this.showAllOnFocus ? '*' : this.formControl.value),
7017
+ // Make sure there is enough characters
7018
+ filter(thresholdFilterFn)),
7019
+ // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
7020
+ merge(
6949
7021
  // Focus or click => Load all
6950
7022
  merge(this.focused, this.clicked)
6951
- .pipe(filter(_ => this.enabled), map((_) => this.showAllOnFocus ? '*' : this.inputControl.value)), this.inputControl.valueChanges
6952
- .pipe(startWith(this.inputControl.value), filter(value => (isNotNil(value) && typeof value === 'object') || (isNotNilString(value) && !value.includes('**'))), tap((value) => {
6953
- if (this.debug)
6954
- console.debug(this.logPrefix + ' valueChanges:', value);
6955
- }), debounceTime(this.debounceTime)), this.$inputItems
6956
- .pipe(map(() => this.formControl.value)), this._$filter
6957
- .pipe(map(() => this.formControl.value)));
6958
- this.filteredItems$ = updateFilteredItemsEvents$
6959
- .pipe(distinctUntilChanged(),
6960
- //tap(value => this.debug && console.debug(this.logPrefix + " Received update event: ", value)),
6961
- switchMap((value) => this.suggest(value, this.filter)));
7023
+ .pipe(filter(_ => this.enabled), map((_) => this.showAllOnFocus ? '*' : this.inputControl.value)), merge(this.$inputItems, this._$filter)
7024
+ .pipe(filter(_ => this.enabled), map(_ => this.inputControl.value)))
7025
+ .pipe(
7026
+ // Make sure there is enough characters
7027
+ filter(thresholdFilterFn),
7028
+ // Distinguish changes
7029
+ distinctUntilChanged()))
7030
+ .pipe(
7031
+ // Suggest values
7032
+ mergeMap(value => this.suggest(value, this.filter)),
7033
+ // DEBUG
7034
+ //tap(items => console.debug(this.logPrefix + ' Received from suggest: ', items)),
7035
+ // Store implicit value (will use it onBlur if not other value selected)
7036
+ tap(items => this.updateImplicitValue(items)));
7037
+ // Fetch more events
7038
+ const fetchMoreItemsChanges = this._fetchMore$
7039
+ .pipe(tap(event => event === null || event === void 0 ? void 0 : event.preventDefault()), // Avoid to close the mat-select
7040
+ filter(() => this.canFetchMore), mergeMap(() => this.fetchMore()),
7041
+ // DEBUG
7042
+ //tap(moreItems => console.debug(this.logPrefix + ' Received from fetch More: ', moreItems)),
7043
+ // Concat to existing items
7044
+ map(moreItems => (this.$filteredItems.value || []).concat(moreItems)));
7045
+ // Update filtered items
7046
+ this._subscription.add(merge(this.$inputItems,
7047
+ // Update items events
7048
+ filteredItemsChanges,
7049
+ // Fetch more events
7050
+ fetchMoreItemsChanges)
7051
+ // Emit items
7052
+ .subscribe(items => this.$filteredItems.next(items)));
7053
+ // Applying implicit value, on blur
7054
+ this._subscription.add(this.blurred
7055
+ .pipe(map(_ => this.formControl.value))
7056
+ .subscribe(value => {
7057
+ // When leave component without object, use implicit value if :
7058
+ // - an explicit value
7059
+ // - field is not empty (user fill something)
7060
+ // - OR field empty but is required
7061
+ if (this._implicitValue && ((this.required && isNilOrBlank(value)) || (isNotNilOrBlank(value) && typeof value !== 'object'))) {
7062
+ this.writeValue(this._implicitValue);
7063
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7064
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7065
+ this.checkIfTouched();
7066
+ this.$filteredItems.next([this._implicitValue]);
7067
+ }
7068
+ else if (isNilOrBlank(value)) {
7069
+ this.writeValue(null);
7070
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7071
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7072
+ this.checkIfTouched();
7073
+ this.$filteredItems.next(undefined);
7074
+ }
7075
+ this._implicitValue = null; // reset the implicit value
7076
+ }));
7077
+ // Listen form control status change
7078
+ this._subscription.add(this.formControl.statusChanges
7079
+ .pipe(
7080
+ //filter(() => this.enabled),
7081
+ distinctUntilChanged())
7082
+ // Update component state
7083
+ .subscribe(() => this.checkIfTouched()));
7084
+ this._subscription.add(this.keydownEscape.subscribe(event => {
7085
+ if (this.isOpen) {
7086
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
7087
+ event === null || event === void 0 ? void 0 : event.preventDefault();
7088
+ // DEBUG
7089
+ //console.debug(this.logPrefix + " Closing the panel (keydown.escape)");
7090
+ this.closePanel();
7091
+ }
7092
+ }));
6962
7093
  }
6963
7094
  ngOnDestroy() {
6964
7095
  this._subscription.unsubscribe();
7096
+ if (this._itemsSubscription) {
7097
+ this._itemsSubscription.unsubscribe();
7098
+ }
7099
+ if (this._openedSubscription) {
7100
+ this._openedSubscription.unsubscribe();
7101
+ }
6965
7102
  this.$inputItems.complete();
7103
+ this.$inputItems.unsubscribe();
7104
+ this.$filteredItems.complete();
7105
+ this.$filteredItems.unsubscribe();
7106
+ this._reload$.complete();
7107
+ this._$filter.complete();
7108
+ this.dropButtonClick.complete();
7109
+ this.clicked.complete();
7110
+ this.blurred.complete();
7111
+ this.focused.complete();
7112
+ this.keydownEscape.complete();
7113
+ this.keyupEnter.complete();
7114
+ this._fetchMore$.complete();
7115
+ this._implicitValue = undefined;
7116
+ this.formControl = null;
7117
+ this.displayWith = null;
7118
+ this.equals = null;
7119
+ this.suggestFn = null;
7120
+ this.$filteredItems = null;
7121
+ this.config = null;
7122
+ this.classList = null;
7123
+ this._onChangeCallback = null;
7124
+ this._onTouchedCallback = null;
7125
+ this._onFetchMoreCallback = null;
7126
+ // Destroy the 'autocomplete' instance, to avoid memory leak
7127
+ if (this.autocomplete) {
7128
+ this.autocomplete.ngOnDestroy();
7129
+ }
6966
7130
  }
6967
7131
  /**
6968
7132
  * Add item to chips
@@ -6983,27 +7147,35 @@ class MatChipsField {
6983
7147
  * @param item
6984
7148
  */
6985
7149
  remove(item) {
6986
- const index = this.value && this.value.indexOf(item);
6987
- if (index >= 0) {
6988
- const value = this.value.slice();
6989
- value.splice(index, 1);
6990
- this.writeValue(value);
6991
- this.inputControl.setValue('');
7150
+ const value = this.value;
7151
+ const index = value ? value.indexOf(item) : -1;
7152
+ if (index !== -1) {
7153
+ const newValue = value.slice();
7154
+ newValue.splice(index, 1);
7155
+ this.writeValue(newValue);
7156
+ this.closePanel();
6992
7157
  }
6993
7158
  }
6994
7159
  /**
6995
7160
  * Allow to reload content. Useful when filter has been changed but not detected
6996
7161
  */
6997
- reloadItems() {
6998
- this.onDropButtonClick.emit();
7162
+ reloadItems(value) {
7163
+ // Force a refresh
7164
+ this._reload$.next(value || '*');
6999
7165
  }
7000
7166
  writeValue(value) {
7001
- if (this.debug)
7002
- console.debug(this.logPrefix + ' Writing value: ', value);
7167
+ if (value && !Array.isArray(value)) {
7168
+ value = [value];
7169
+ }
7003
7170
  if (value !== this.formControl.value) {
7171
+ if (this.debug)
7172
+ console.debug(this.logPrefix + ' Writing value: ', value);
7004
7173
  this.formControl.patchValue(value, { emitEvent: false });
7005
7174
  this._onChangeCallback(value);
7006
7175
  }
7176
+ else {
7177
+ this.markForCheck();
7178
+ }
7007
7179
  }
7008
7180
  registerOnChange(fn) {
7009
7181
  this._onChangeCallback = fn;
@@ -7012,6 +7184,10 @@ class MatChipsField {
7012
7184
  this._onTouchedCallback = fn;
7013
7185
  }
7014
7186
  setDisabledState(isDisabled) {
7187
+ var _a;
7188
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
7189
+ this.closePanel();
7190
+ }
7015
7191
  this.cd.markForCheck();
7016
7192
  }
7017
7193
  focus() {
@@ -7031,6 +7207,7 @@ class MatChipsField {
7031
7207
  return false;
7032
7208
  }
7033
7209
  const hasContent = selectInputContent(event);
7210
+ // If combo is empty, or if has content but should force to show panel on focus
7034
7211
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
7035
7212
  if (this.debug)
7036
7213
  console.debug(this.logPrefix + ' Emit focus event');
@@ -7039,17 +7216,75 @@ class MatChipsField {
7039
7216
  }
7040
7217
  return false;
7041
7218
  }
7219
+ filterInputTextBlurEvent(event) {
7220
+ if (!event || event.defaultPrevented)
7221
+ return;
7222
+ // Ignore event from mat-option
7223
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.tagName === 'MAT-OPTION') {
7224
+ event.preventDefault();
7225
+ if (event.stopPropagation)
7226
+ event.stopPropagation();
7227
+ event.returnValue = false;
7228
+ //DEBUG console.debug(this.logPrefix + " Cancelling blur event");
7229
+ return false;
7230
+ }
7231
+ //DEBUG
7232
+ //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
7233
+ // Move caret to the beginning (fix issue IMAGINE-469)
7234
+ selectInputRange(event.target, 0, 0);
7235
+ this.blurred.emit(event);
7236
+ return true;
7237
+ }
7238
+ filterChipsFocusEvent(event) {
7239
+ // DEBUG
7240
+ if (this.debug)
7241
+ console.debug(this.logPrefix + ' Focus in chips list', event);
7242
+ // Ignore event in button
7243
+ this.matInputText.nativeElement.focus();
7244
+ }
7042
7245
  clearValue(event) {
7043
7246
  this.writeValue(null);
7044
7247
  event.stopPropagation();
7045
7248
  }
7249
+ _initAutocompleteInfiniteScroll(threshold) {
7250
+ if (!this.autocomplete)
7251
+ return; // Skip
7252
+ // DEBUG
7253
+ //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
7254
+ if (this._openedSubscription) {
7255
+ this._openedSubscription.unsubscribe();
7256
+ this._subscription.remove(this._openedSubscription);
7257
+ }
7258
+ const opened$ = this.autocomplete._isOpen
7259
+ ? timer(0)
7260
+ : this.autocomplete.opened.pipe(debounceTime(200));
7261
+ const scrollEnd$ = opened$
7262
+ .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
7263
+ .pipe(takeUntil(this.autocomplete.closed))));
7264
+ // Trigger fetch more, end end scroll reached
7265
+ this._openedSubscription = scrollEnd$
7266
+ .subscribe(() => this._fetchMore$.emit());
7267
+ // Register subscription
7268
+ this._subscription.add(this._openedSubscription);
7269
+ }
7270
+ get isOpen() {
7271
+ var _a;
7272
+ return ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
7273
+ }
7274
+ closePanel() {
7275
+ var _a, _b;
7276
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
7277
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
7278
+ return;
7279
+ }
7280
+ }
7046
7281
  /* -- private method -- */
7047
7282
  suggest(value, filter) {
7048
7283
  return __awaiter(this, void 0, void 0, function* () {
7049
7284
  // Call suggestion function
7050
7285
  let res = yield this.suggestFn(value, filter);
7051
7286
  // DEBUG
7052
- // console.debug(this.logPrefix + " Filtered items by suggestFn:", value, res);
7287
+ //console.debug(this.logPrefix + ' Filtered items by suggestFn:', value, res);
7053
7288
  if (!res) {
7054
7289
  this._itemCount = 0;
7055
7290
  res = [];
@@ -7059,37 +7294,88 @@ class MatChipsField {
7059
7294
  this._itemCount = res.length || 0;
7060
7295
  }
7061
7296
  else {
7062
- const { data, total } = res;
7063
- res = data;
7297
+ const { data, total, fetchMore } = res;
7064
7298
  this._itemCount = toNumber(total, data && data.length || 0);
7299
+ this._onFetchMoreCallback = fetchMore;
7300
+ this._moreItemsCount = fetchMore && (this._itemCount - data.length) || 0;
7301
+ res = data;
7065
7302
  }
7066
7303
  // Filter out existing items
7067
- const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.compareWith(item1, item2)) === -1);
7068
- // Update items count
7069
- this._itemCount = this._itemCount - (((res === null || res === void 0 ? void 0 : res.length) || 0) - ((filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) || 0));
7304
+ const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.equals(item1, item2)) === -1);
7305
+ const removedItemCount = (((res === null || res === void 0 ? void 0 : res.length) || 0) - ((filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) || 0));
7306
+ // Decrease the item count, with delta
7307
+ this._itemCount -= removedItemCount;
7070
7308
  if (this.debug)
7071
7309
  console.debug(this.logPrefix + ' Filtered items by suggestFn:', value, res);
7072
7310
  return res;
7073
7311
  });
7074
7312
  }
7313
+ fetchMore() {
7314
+ return __awaiter(this, void 0, void 0, function* () {
7315
+ if (!this._onFetchMoreCallback)
7316
+ return [];
7317
+ // Save then remove fetch function (to avoid multi-call)
7318
+ const fetchMoreFn = this._onFetchMoreCallback;
7319
+ this._onFetchMoreCallback = undefined;
7320
+ const { data, total, fetchMore } = yield fetchMoreFn();
7321
+ this._itemCount = total || this._itemCount;
7322
+ this._onFetchMoreCallback = fetchMore;
7323
+ this._moreItemsCount = Math.max(0, this._moreItemsCount - data.length); // Borne min = 0
7324
+ return data;
7325
+ });
7326
+ }
7327
+ updateImplicitValue(items) {
7328
+ // Store implicit value (will use it onBlur if not other value selected)
7329
+ if (items && items.length === 1) {
7330
+ this._implicitValue = items[0];
7331
+ //this.formControl.setErrors(null);
7332
+ }
7333
+ else {
7334
+ this._implicitValue = undefined;
7335
+ }
7336
+ }
7337
+ checkIfTouched() {
7338
+ if (this.formControl.touched) {
7339
+ this.cd.markForCheck();
7340
+ this._onTouchedCallback();
7341
+ }
7342
+ }
7343
+ getAutocompletePanel() {
7344
+ var _a, _b;
7345
+ const ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
7346
+ if (!ele) {
7347
+ // DEBUG
7348
+ console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
7349
+ }
7350
+ return ele;
7351
+ }
7075
7352
  markForCheck() {
7076
7353
  this.cd.markForCheck();
7077
7354
  }
7355
+ markAsLoading() {
7356
+ if (this.$filteredItems.value) {
7357
+ this.$filteredItems.next(undefined);
7358
+ this._itemCount = undefined;
7359
+ this._implicitValue = undefined;
7360
+ this._onFetchMoreCallback = undefined;
7361
+ this._moreItemsCount = 0;
7362
+ this.markForCheck();
7363
+ }
7364
+ }
7078
7365
  }
7079
7366
  MatChipsField.decorators = [
7080
7367
  { type: Component, args: [{
7081
7368
  // eslint-disable-next-line @angular-eslint/component-selector
7082
7369
  selector: 'mat-chips-field',
7083
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\" class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-content select=\"[matPrefix]\"></ng-content>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList>\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\">\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autoCombo\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"appAutofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (blur)=\"blurred.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (keyup.arrowdown)=\"!autoCombo.showPanel && onDropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autoCombo=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"class\">\n <ng-container *ngIf=\"(filteredItems$ | async) as items\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"isLast\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: itemCount} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <!-- No item option -->\n <mat-option *ngIf=\"!items.length\" class=\"ion-no-padding\" [disabled]=\"true\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row>\n <ion-col *ngFor=\"let attr of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"getPropertyByPath(item, attr) | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n </ng-container>\n </mat-autocomplete>\n\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"onDropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n <ng-content matSuffix select=\"[matSuffix]\"></ng-content>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n",
7084
- providers: [
7085
- {
7370
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\" class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList\n (focus)=\"filterChipsFocusEvent($event)\" >\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\"\n [color]=\"chipColor\"\n >\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
7371
+ providers: [{
7086
7372
  provide: NG_VALUE_ACCESSOR,
7087
7373
  multi: true,
7088
7374
  useExisting: forwardRef(() => MatChipsField)
7089
- }
7090
- ],
7375
+ }],
7376
+ encapsulation: ViewEncapsulation.None,
7091
7377
  changeDetection: ChangeDetectionStrategy.OnPush,
7092
- styles: [":host{position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}"]
7378
+ styles: [":host{width:100%;display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}.mat-form-field-suffix{position:absolute!important;right:0!important;top:calc(100% - 50px)!important}.mat-chip.mat-standard-chip.mat-primary{background-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast)}.mat-chip.mat-standard-chip.mat-secondary{background-color:var(--ion-color-secondary);color:var(--ion-color-secondary-contrast)}.mat-chip.mat-standard-chip.mat-tertiary{background-color:var(--ion-color-tertiary);color:var(--ion-color-tertiary-contrast)}.mat-chip.mat-standard-chip.mat-accent{background-color:var(--ion-color-accent);color:var(--ion-color-accent-contrast)}.mat-chip.mat-standard-chip.mat-warning{background-color:var(--ion-color-warning);color:var(--ion-color-warning-contrast)}"]
7093
7379
  },] }
7094
7380
  ];
7095
7381
  MatChipsField.ctorParameters = () => [
@@ -7097,7 +7383,7 @@ MatChipsField.ctorParameters = () => [
7097
7383
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
7098
7384
  ];
7099
7385
  MatChipsField.propDecorators = {
7100
- compareWith: [{ type: Input }],
7386
+ equals: [{ type: Input }],
7101
7387
  logPrefix: [{ type: Input }],
7102
7388
  formControl: [{ type: Input }],
7103
7389
  formControlName: [{ type: Input }],
@@ -7116,16 +7402,27 @@ MatChipsField.propDecorators = {
7116
7402
  highlightAccent: [{ type: Input }],
7117
7403
  showAllOnFocus: [{ type: Input }],
7118
7404
  showPanelOnFocus: [{ type: Input }],
7119
- appAutofocus: [{ type: Input }],
7405
+ autofocus: [{ type: Input }],
7120
7406
  config: [{ type: Input }],
7121
7407
  i18nPrefix: [{ type: Input }],
7122
7408
  noResultMessage: [{ type: Input }],
7123
- class: [{ type: Input }],
7409
+ classList: [{ type: Input, args: ['class',] }],
7410
+ panelWidth: [{ type: Input }],
7411
+ matAutocompletePosition: [{ type: Input }],
7412
+ itemSize: [{ type: Input }],
7413
+ fetchMoreThreshold: [{ type: Input }],
7414
+ suggestLengthThreshold: [{ type: Input }],
7415
+ showLoadingSpinner: [{ type: Input }],
7416
+ chipColor: [{ type: Input }],
7124
7417
  debug: [{ type: Input }],
7125
7418
  clicked: [{ type: Output }],
7126
7419
  blurred: [{ type: Output }],
7127
7420
  focused: [{ type: Output }],
7421
+ dropButtonClick: [{ type: Output }],
7422
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
7423
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
7128
7424
  matInputText: [{ type: ViewChild, args: ['matInputText',] }],
7425
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
7129
7426
  autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
7130
7427
  filter: [{ type: Input }],
7131
7428
  tabindex: [{ type: Input }],
@@ -16239,7 +16536,7 @@ class AutocompleteTestPage {
16239
16536
  constructor(formBuilder) {
16240
16537
  this.formBuilder = formBuilder;
16241
16538
  this._items = deepCopy$1(FAKE_ENTITIES$1, 100);
16242
- this.$items = new BehaviorSubject(undefined);
16539
+ this._$items = new BehaviorSubject(undefined);
16243
16540
  this.autocompleteFields = new MatAutocompleteConfigHolder();
16244
16541
  this.memoryHide = false;
16245
16542
  this.memoryMobile = true;
@@ -16268,7 +16565,7 @@ class AutocompleteTestPage {
16268
16565
  });
16269
16566
  // From $items observable
16270
16567
  this.autocompleteFields.add('entity-$items', {
16271
- items: this.$items,
16568
+ items: this._$items,
16272
16569
  attributes: ['label', 'name'],
16273
16570
  displayWith: this.entityToString
16274
16571
  });
@@ -16306,7 +16603,7 @@ class AutocompleteTestPage {
16306
16603
  }
16307
16604
  loadItems() {
16308
16605
  return __awaiter(this, void 0, void 0, function* () {
16309
- this.$items.next(this._items.slice());
16606
+ this._$items.next(this._items.slice());
16310
16607
  });
16311
16608
  }
16312
16609
  entityToString(item) {
@@ -16336,7 +16633,7 @@ class AutocompleteTestPage {
16336
16633
  doSubmit(event) {
16337
16634
  console.debug('Validate form: ', this.form.value);
16338
16635
  }
16339
- compareWithFn(o1, o2) {
16636
+ equals(o1, o2) {
16340
16637
  return o1 && o2 && o1.id === o2.id;
16341
16638
  }
16342
16639
  toggleMode(value) {
@@ -16367,7 +16664,7 @@ class AutocompleteTestPage {
16367
16664
  AutocompleteTestPage.decorators = [
16368
16665
  { type: Component, args: [{
16369
16666
  selector: 'app-autocomplete-test',
16370
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [compareWith]=\"compareWithFn\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16667
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-suggest]'\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16371
16668
  },] }
16372
16669
  ];
16373
16670
  AutocompleteTestPage.ctorParameters = () => [
@@ -16522,7 +16819,7 @@ class SwipeTestPage {
16522
16819
  SwipeTestPage.decorators = [
16523
16820
  { type: Component, args: [{
16524
16821
  selector: 'app-swipe-test',
16525
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Swipe test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <h1>Swipe page:</h1>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n <!-- empty -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"empty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [tabindex]=\"1\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- date -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"date\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"2\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <!-- disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledEmpty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"3\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n<!-- read-only-->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled with date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledDate\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"4\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16822
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Swipe test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <h1>Swipe page:</h1>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n <!-- empty -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"empty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [tabindex]=\"1\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- date -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"date\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"2\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <!-- disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledEmpty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"3\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n<!-- read-only-->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled with date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledDate\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"4\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16526
16823
  },] }
16527
16824
  ];
16528
16825
  SwipeTestPage.ctorParameters = () => [
@@ -16628,15 +16925,30 @@ class EntityTestClass {
16628
16925
  const FAKE_ENTITIES = [
16629
16926
  { id: 1, label: 'AAA', name: 'Item A' },
16630
16927
  { id: 2, label: 'BBB', name: 'Item B' },
16631
- { id: 3, label: 'CCC', name: 'Item C' }
16928
+ { id: 3, label: 'CCC', name: 'Item C' },
16929
+ { id: 4, label: 'DDD', name: 'Item D' },
16930
+ { id: 5, label: 'EEE', name: 'Item E' },
16931
+ { id: 6, label: 'FFF', name: 'Item F' },
16932
+ { id: 7, label: 'GGG', name: 'Item G' }
16632
16933
  ];
16633
- function deepCopy(values) {
16634
- return (values || FAKE_ENTITIES).map(entity => Object.assign({}, entity));
16934
+ function deepCopy(values, size) {
16935
+ if (isNil(size)) {
16936
+ return (values || FAKE_ENTITIES).map(entity => Object.assign({}, entity));
16937
+ }
16938
+ let result = [];
16939
+ let i = 1;
16940
+ while (result.length < size) {
16941
+ result = result.concat((values || FAKE_ENTITIES).map(entity => {
16942
+ const name = entity.name + ' #' + i;
16943
+ return Object.assign(Object.assign({}, entity), { id: i++, name });
16944
+ }));
16945
+ }
16946
+ return result.slice(0, size);
16635
16947
  }
16636
16948
  class ChipsTestPage {
16637
16949
  constructor(formBuilder) {
16638
16950
  this.formBuilder = formBuilder;
16639
- this._items = deepCopy(FAKE_ENTITIES);
16951
+ this._items = deepCopy(FAKE_ENTITIES, 100);
16640
16952
  this._$items = new BehaviorSubject(undefined);
16641
16953
  this.autocompleteFields = new MatAutocompleteConfigHolder();
16642
16954
  this.form = formBuilder.group({
@@ -16651,15 +16963,17 @@ class ChipsTestPage {
16651
16963
  this.autocompleteFields.add('entity-suggestFn', {
16652
16964
  suggestFn: (value, filter) => this.suggest(value, filter),
16653
16965
  attributes: ['label', 'name'],
16654
- displayWith: this.entityToString
16966
+ displayWith: this.entityToString,
16967
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16655
16968
  });
16656
16969
  // From items
16657
16970
  this.autocompleteFields.add('entity-items', {
16658
- items: FAKE_ENTITIES.slice(),
16971
+ items: this._items.slice(),
16659
16972
  attributes: ['label', 'name'],
16660
- displayWith: this.entityToString
16973
+ displayWith: this.entityToString,
16974
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16661
16975
  });
16662
- // From items
16976
+ // From $items observable
16663
16977
  this.autocompleteFields.add('entity-$items', {
16664
16978
  items: this._$items,
16665
16979
  attributes: ['label', 'name'],
@@ -16673,18 +16987,20 @@ class ChipsTestPage {
16673
16987
  // Load the form with data
16674
16988
  loadData() {
16675
16989
  return __awaiter(this, void 0, void 0, function* () {
16990
+ const items = deepCopy(FAKE_ENTITIES);
16676
16991
  const data = {
16677
- entity: [],
16678
- // THis item is NOT in the items list => i should be displayed anyway
16679
- entityInitial: FAKE_ENTITIES.slice(1, 2),
16680
- disableEntity: FAKE_ENTITIES.slice(1, 2)
16992
+ entity: items.slice(0, 1),
16993
+ entityInitial: items.slice(1, 2),
16994
+ disableEntity: items.slice(1, 2)
16681
16995
  };
16682
16996
  this.form.setValue(data);
16997
+ // Load observables
16998
+ setTimeout(() => this.loadItems(), 1000);
16683
16999
  });
16684
17000
  }
16685
17001
  loadItems() {
16686
17002
  return __awaiter(this, void 0, void 0, function* () {
16687
- this._$items.next(deepCopy(FAKE_ENTITIES));
17003
+ this._$items.next(this._items.slice());
16688
17004
  });
16689
17005
  }
16690
17006
  entityToString(item) {
@@ -16692,13 +17008,29 @@ class ChipsTestPage {
16692
17008
  }
16693
17009
  suggest(value, filter) {
16694
17010
  return __awaiter(this, void 0, void 0, function* () {
16695
- return suggestFromArray(this._items, value, Object.assign(Object.assign({}, filter), { searchAttributes: ['label', 'name'] }));
17011
+ filter = Object.assign({ offset: 0, size: 30 }, filter);
17012
+ const res = suggestFromArray(this._items, value, Object.assign(Object.assign({}, filter), { searchAttributes: ['label', 'name'] }));
17013
+ // Simulate a size = 30
17014
+ const total = res.total || res.data.length;
17015
+ const nextOffset = filter.offset + res.data.length;
17016
+ if (nextOffset < total) {
17017
+ filter = Object.assign(Object.assign({}, filter), { offset: nextOffset });
17018
+ return {
17019
+ total,
17020
+ data: res.data,
17021
+ fetchMore: (variables) => __awaiter(this, void 0, void 0, function* () {
17022
+ yield sleep(1000);
17023
+ return this.suggest(value, Object.assign(Object.assign({}, filter), variables));
17024
+ })
17025
+ };
17026
+ }
17027
+ return res;
16696
17028
  });
16697
17029
  }
16698
17030
  doSubmit(event) {
16699
17031
  console.debug('Validate form: ', this.form.value);
16700
17032
  }
16701
- compareWithFn(o1, o2) {
17033
+ equals(o1, o2) {
16702
17034
  return o1 && o2 && o1.id === o2.id;
16703
17035
  }
16704
17036
  /* -- protected methods -- */
@@ -16709,7 +17041,7 @@ class ChipsTestPage {
16709
17041
  ChipsTestPage.decorators = [
16710
17042
  { type: Component, args: [{
16711
17043
  selector: 'app-chips-test',
16712
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Chips field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Suggest() function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-empty] '\"\n [compareWith]=\"compareWithFn\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function with initial value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entityInitial\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-initialized] '\"\n ></mat-chips-field>\n {{stringify(form.controls.entityInitial.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-disabled] '\"\n ></mat-chips-field>\n {{stringify(form.controls.disableEntity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-readonly] '\"\n [readonly]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
17044
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Chips field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Suggest() function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-empty] '\"\n chipColor=\"accent\"\n [debug]=\"false\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function with initial value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entityInitial\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-initialized] '\"\n chipColor=\"primary\"\n [debug]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entityInitial.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[mat-chips-disabled] '\"\n chipColor=\"warning\"\n [debug]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.disableEntity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[mat-chips-readonly] '\"\n [readonly]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16713
17045
  },] }
16714
17046
  ];
16715
17047
  ChipsTestPage.ctorParameters = () => [
@@ -17253,11 +17585,18 @@ AuthForm.propDecorators = {
17253
17585
  };
17254
17586
 
17255
17587
  class AuthModal {
17256
- constructor(accountService, viewCtrl, changeDetectorRef) {
17588
+ constructor(accountService, viewCtrl, cd) {
17257
17589
  this.accountService = accountService;
17258
17590
  this.viewCtrl = viewCtrl;
17259
- this.changeDetectorRef = changeDetectorRef;
17260
- this.loading = false;
17591
+ this.cd = cd;
17592
+ }
17593
+ get loading() {
17594
+ var _a;
17595
+ return (_a = this.form) === null || _a === void 0 ? void 0 : _a.loading;
17596
+ }
17597
+ ngOnInit() {
17598
+ this.form.markAsReady({ emitEvent: false });
17599
+ this.form.markAsLoaded();
17261
17600
  }
17262
17601
  cancel() {
17263
17602
  this.viewCtrl.dismiss();
@@ -17267,10 +17606,10 @@ class AuthModal {
17267
17606
  if (this.form.disabled)
17268
17607
  return;
17269
17608
  if (!this.form.valid) {
17270
- this.form.markAsTouched();
17609
+ this.form.markAllAsTouched();
17271
17610
  return;
17272
17611
  }
17273
- this.loading = true;
17612
+ this.markAsLoading();
17274
17613
  try {
17275
17614
  const data = this.form.value;
17276
17615
  // Disable the form
@@ -17279,9 +17618,8 @@ class AuthModal {
17279
17618
  return this.viewCtrl.dismiss(account);
17280
17619
  }
17281
17620
  catch (err) {
17282
- this.loading = false;
17283
17621
  this.form.error = err && err.message || err;
17284
- this.markForCheck();
17622
+ this.markAsLoaded();
17285
17623
  // Enable the form
17286
17624
  this.form.enable();
17287
17625
  // Reset form error on next changes
@@ -17294,7 +17632,13 @@ class AuthModal {
17294
17632
  });
17295
17633
  }
17296
17634
  markForCheck() {
17297
- this.changeDetectorRef.markForCheck();
17635
+ this.cd.markForCheck();
17636
+ }
17637
+ markAsLoading(opts) {
17638
+ this.form.markAsLoading(opts);
17639
+ }
17640
+ markAsLoaded(opts) {
17641
+ this.form.markAsLoaded(opts);
17298
17642
  }
17299
17643
  }
17300
17644
  AuthModal.decorators = [
@@ -24633,5 +24977,5 @@ const ErrorCodes = {
24633
24977
  * Generated bundle index. Do not edit.
24634
24978
  */
24635
24979
 
24636
- export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_TESTING_PAGES, AboutModal, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, Alerts, AnimationState, AppEditor, AppEditorOptions, AppEntityEditor, AppForm, AppFormField, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppInMemoryTable, AppInstallUpgradeCard, AppListForm, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppTabEditor, AppTabEditorOptions, AppTable, AppTableDataSourceOptions, AppTableUtils, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AuthForm, AuthGuardService, AuthModal, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CryptoService, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileService, FileSizePipe, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, Fragments, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialChipsModule, MaterialTestingModule, MaterialTestingPage, MathAbsPipe, MenuComponent, MenuItems, MenuService, ModalToolbarComponent, NetworkService, NgInitDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialRef, ReferentialUtils, RegisterConfirmPage, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SettingsPage, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedValidators, SocialModule, Software, StartableService, StatusById, StatusIds, StatusList, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UserEvent, UserEventFilter, UserEventService, UserEventTypes, UserEventsTable, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, isControlHasInput, isEmptyArray, isInputElement, isInstanceOf, isInt, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForTrue, waitIdle, waitWhilePending, ɵ0$5 as ɵ0, CustomReuseStrategy as ɵa, MatNumpadDomService as ɵb, SharedMatBadgeIconModule as ɵc, MatBadgeIconDirective as ɵd, isFocusableElement as ɵe, MatBadgeIconTestPage as ɵf, RegisterForm as ɵg, AccountValidatorService as ɵh, RegisterModal as ɵi, UserSettingsValidatorService as ɵj, LocalSettingsValidatorService as ɵk, AppIconComponent as ɵl };
24980
+ export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_TESTING_PAGES, AboutModal, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, Alerts, AnimationState, AppEditor, AppEditorOptions, AppEntityEditor, AppForm, AppFormField, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppInMemoryTable, AppInstallUpgradeCard, AppListForm, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppTabEditor, AppTabEditorOptions, AppTable, AppTableDataSourceOptions, AppTableUtils, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AuthForm, AuthGuardService, AuthModal, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CryptoService, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileService, FileSizePipe, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, Fragments, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialChipsModule, MaterialTestingModule, MaterialTestingPage, MathAbsPipe, MenuComponent, MenuItems, MenuService, ModalToolbarComponent, NetworkService, NgInitDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialRef, ReferentialUtils, RegisterConfirmPage, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SettingsPage, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedValidators, SocialModule, Software, StartableService, StatusById, StatusIds, StatusList, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UserEvent, UserEventFilter, UserEventService, UserEventTypes, UserEventsTable, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, isControlHasInput, isEmptyArray, isInputElement, isInstanceOf, isInt, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForTrue, waitIdle, waitWhilePending, CustomReuseStrategy as ɵa, MatNumpadDomService as ɵb, SharedMatBadgeIconModule as ɵc, MatBadgeIconDirective as ɵd, isFocusableElement as ɵe, MatBadgeIconTestPage as ɵf, RegisterForm as ɵg, AccountValidatorService as ɵh, RegisterModal as ɵi, UserSettingsValidatorService as ɵj, LocalSettingsValidatorService as ɵk, AppIconComponent as ɵl };
24637
24981
  //# sourceMappingURL=sumaris-net.ngx-components.js.map