@sumaris-net/ngx-components 1.6.2 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { 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, HostBinding, Pipe, Injectable, Directive, Inject, NgModule, ElementRef, ViewChildren, HostListener, ComponentFactoryResolver, ApplicationRef, Injector, Renderer2 } 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$8, 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,10 @@ 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 compareWith = 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
- displayWith,
983
- compareWith });
982
+ displayWith, equals: compareWith });
984
983
  this.fields[fieldName] = config;
985
984
  return config;
986
985
  }
@@ -991,11 +990,6 @@ class MatAutocompleteConfigHolder {
991
990
  return this.fields[fieldName] || this.add(fieldName);
992
991
  }
993
992
  }
994
- const DEFAULT_VALUE_ACCESSOR$6 = {
995
- provide: NG_VALUE_ACCESSOR,
996
- useExisting: forwardRef(() => MatAutocompleteField),
997
- multi: true
998
- };
999
993
  const noop$7 = () => { };
1000
994
  const ɵ0$a = noop$7;
1001
995
  class MatAutocompleteField {
@@ -1004,14 +998,6 @@ class MatAutocompleteField {
1004
998
  this.formGroupDir = formGroupDir;
1005
999
  this._onChangeCallback = noop$7;
1006
1000
  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();
1015
1001
  this.logPrefix = '[mat-autocomplete] ';
1016
1002
  this.formControlName = null;
1017
1003
  this.required = false;
@@ -1024,13 +1010,21 @@ class MatAutocompleteField {
1024
1010
  this.fetchMoreThreshold = '15%';
1025
1011
  this.suggestLengthThreshold = null;
1026
1012
  this.showLoadingSpinner = true;
1013
+ this.clicked = new EventEmitter();
1014
+ this.blurred = new EventEmitter();
1015
+ this.focused = new EventEmitter();
1016
+ this.dropButtonClick = new EventEmitter(true);
1017
+ this.keydownEscape = new EventEmitter();
1018
+ this.keyupEnter = new EventEmitter();
1027
1019
  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();
1020
+ this._readonly = false;
1021
+ this.$inputItems = new BehaviorSubject(undefined);
1022
+ this.$filteredItems = new BehaviorSubject(undefined);
1023
+ this.displayValue = '';
1024
+ this._fetchMore$ = new EventEmitter();
1025
+ this._subscription = new Subscription();
1026
+ this._$filter = new BehaviorSubject(undefined);
1027
+ this._reload$ = new Subject();
1034
1028
  }
1035
1029
  get itemCount() {
1036
1030
  return this._itemCount;
@@ -1039,14 +1033,14 @@ class MatAutocompleteField {
1039
1033
  return this._onFetchMoreCallback && this._moreItemsCount > 0;
1040
1034
  }
1041
1035
  set filter(value) {
1042
- if (value !== this._filter$.value) {
1036
+ if (value !== this._$filter.value) {
1043
1037
  // DEBUG
1044
1038
  //console.debug(this.logPrefix + " Setting filter:", value);
1045
- this._filter$.next(value);
1039
+ this._$filter.next(value);
1046
1040
  }
1047
1041
  }
1048
1042
  get filter() {
1049
- return this._filter$.value;
1043
+ return this._$filter.value;
1050
1044
  }
1051
1045
  set readonly(value) {
1052
1046
  this._readonly = value;
@@ -1109,6 +1103,7 @@ class MatAutocompleteField {
1109
1103
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
1110
1104
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
1111
1105
  this.displayWith = this.displayWith || this.config.displayWith;
1106
+ this.equals = this.equals || this.config.equals;
1112
1107
  this.mobile = toBoolean(this.mobile, this.config.mobile);
1113
1108
  this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
1114
1109
  this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
@@ -1123,20 +1118,18 @@ class MatAutocompleteField {
1123
1118
  this.displayColumnSizes = this.displayColumnSizes
1124
1119
  // if only column: auto size
1125
1120
  || (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
1121
+ // Computed column size:
1122
+ // - if label then col size = 2
1123
+ // - if rankOrder then col size = 1
1124
+ // - ese => auto size
1125
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
1126
+ : ((attr && attr.endsWith('rankOrder')) ? 1
1132
1127
  : undefined));
1133
1128
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
1134
1129
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
1135
1130
  this.mobile = toBoolean(this.mobile, false);
1136
1131
  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;
1132
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
1140
1133
  const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
1141
1134
  this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
1142
1135
  || (classList.includes('min-width-large') && '400px')
@@ -1151,7 +1144,7 @@ class MatAutocompleteField {
1151
1144
  if (!this.suggestFn) {
1152
1145
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
1153
1146
  // DEBUG
1154
- //console.debug(this.logPrefix + " Calling suggestFromArray with value=", value);
1147
+ //console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
1155
1148
  return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
1156
1149
  });
1157
1150
  // Wait (once) that items are loaded, then call suggest from array fn
@@ -1169,7 +1162,7 @@ class MatAutocompleteField {
1169
1162
  }
1170
1163
  else if (!this.searchable) {
1171
1164
  // 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)
1165
+ this._subscription.add(this._$filter.pipe(startWith(this.filter), debounceTime(250), takeWhile((_) => !this.searchable), // Close subscription, when enabling search (no more mat-select)
1173
1166
  switchMap((filter) => this.suggest('*', filter)))
1174
1167
  .subscribe(items => this.$inputItems.next(items)));
1175
1168
  }
@@ -1180,11 +1173,7 @@ class MatAutocompleteField {
1180
1173
  this.formControl.valueChanges
1181
1174
  .pipe(filter(value => this.enabled && this.searchable && isNotNilString(value)),
1182
1175
  // 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
- }),
1176
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
1188
1177
  // Filter if not enough character
1189
1178
  filter(value => value.length >= this.suggestLengthThreshold),
1190
1179
  // Wait a debounce time
@@ -1213,12 +1202,12 @@ class MatAutocompleteField {
1213
1202
  // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
1214
1203
  merge(
1215
1204
  // Focus or click => Load all
1216
- merge(this.onFocus, this.onClick)
1205
+ merge(this.focused, this.clicked)
1217
1206
  .pipe(filter(_ => this.enabled && this.searchable), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)),
1218
1207
  // 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)))
1208
+ this.dropButtonClick
1209
+ .pipe(filter(event => this.searchable && this.enabled && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)), merge(this.$inputItems, this._$filter)
1210
+ .pipe(filter(_ => this.searchable && this.enabled), map(_ => this.formControl.value)))
1222
1211
  .pipe(
1223
1212
  // Start to suggest when :
1224
1213
  // - '*'
@@ -1257,7 +1246,7 @@ class MatAutocompleteField {
1257
1246
  // Make sure control value is inside (mat-select)
1258
1247
  if (items && !this.searchable) {
1259
1248
  const value = this.formControl.value;
1260
- if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
1249
+ if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.equals(item, value)) === -1) {
1261
1250
  // If form value is missing: add it to the list
1262
1251
  items = items.concat(value);
1263
1252
  }
@@ -1266,7 +1255,7 @@ class MatAutocompleteField {
1266
1255
  this.$filteredItems.next(items);
1267
1256
  }));
1268
1257
  // Applying implicit value, on blur
1269
- this._subscription.add(this.onBlur
1258
+ this._subscription.add(this.blurred
1270
1259
  .pipe(
1271
1260
  // Skip if not searchable
1272
1261
  filter(_ => this.searchable), map(_ => this.formControl.value))
@@ -1296,7 +1285,7 @@ class MatAutocompleteField {
1296
1285
  distinctUntilChanged())
1297
1286
  // Update component state
1298
1287
  .subscribe(() => this.checkIfTouched()));
1299
- this._subscription.add(this.onKeydownEscape.subscribe(event => {
1288
+ this._subscription.add(this.keydownEscape.subscribe(event => {
1300
1289
  if (this.isOpen) {
1301
1290
  event === null || event === void 0 ? void 0 : event.stopPropagation();
1302
1291
  event === null || event === void 0 ? void 0 : event.preventDefault();
@@ -1317,18 +1306,18 @@ class MatAutocompleteField {
1317
1306
  this.$inputItems.complete();
1318
1307
  this.$filteredItems.complete();
1319
1308
  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();
1309
+ this._$filter.complete();
1310
+ this.dropButtonClick.complete();
1311
+ this.clicked.complete();
1312
+ this.blurred.complete();
1313
+ this.focused.complete();
1314
+ this.keydownEscape.complete();
1315
+ this.keyupEnter.complete();
1327
1316
  this._fetchMore$.complete();
1328
1317
  this._implicitValue = undefined;
1329
1318
  this.formControl = null;
1330
1319
  this.displayWith = null;
1331
- this.compareWith = null;
1320
+ this.equals = null;
1332
1321
  this.suggestFn = null;
1333
1322
  this.$filteredItems = null;
1334
1323
  this.matSelectItems$ = null;
@@ -1338,8 +1327,8 @@ class MatAutocompleteField {
1338
1327
  this._onTouchedCallback = null;
1339
1328
  this._onFetchMoreCallback = null;
1340
1329
  // Destroy the 'autocomplete' instance, to avoid memory leak
1341
- if (this.matAutocomplete) {
1342
- this.matAutocomplete.ngOnDestroy();
1330
+ if (this.autocomplete) {
1331
+ this.autocomplete.ngOnDestroy();
1343
1332
  }
1344
1333
  }
1345
1334
  /**
@@ -1365,7 +1354,7 @@ class MatAutocompleteField {
1365
1354
  }
1366
1355
  setDisabledState(isDisabled) {
1367
1356
  var _a;
1368
- if (isDisabled && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1357
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1369
1358
  this.closePanel();
1370
1359
  }
1371
1360
  this.cd.markForCheck();
@@ -1379,7 +1368,7 @@ class MatAutocompleteField {
1379
1368
  (_a = this.matSelect) === null || _a === void 0 ? void 0 : _a.focus();
1380
1369
  }
1381
1370
  }
1382
- filterInputTextFocusInEvent(event) {
1371
+ filterInputTextFocusEvent(event) {
1383
1372
  if (!event || event.defaultPrevented)
1384
1373
  return;
1385
1374
  // Ignore event from mat-option
@@ -1388,7 +1377,7 @@ class MatAutocompleteField {
1388
1377
  if (event.stopPropagation)
1389
1378
  event.stopPropagation();
1390
1379
  event.returnValue = false;
1391
- //DEBUG console.debug(this.logPrefix + " Cancelling focus event");
1380
+ //DEBUG console.debug(this.logPrefix + ' Cancelling focus event');
1392
1381
  return false;
1393
1382
  }
1394
1383
  const hasContent = selectInputContent(event);
@@ -1396,7 +1385,7 @@ class MatAutocompleteField {
1396
1385
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
1397
1386
  // DEBUG
1398
1387
  //console.debug(this.logPrefix + ' Emit focus event');
1399
- this.onFocus.emit(event);
1388
+ this.focused.emit(event);
1400
1389
  return true;
1401
1390
  }
1402
1391
  return false;
@@ -1417,7 +1406,7 @@ class MatAutocompleteField {
1417
1406
  //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
1418
1407
  // Move caret to the beginning (fix issue IMAGINE-469)
1419
1408
  selectInputRange(event.target, 0, 0);
1420
- this.onBlur.emit(event);
1409
+ this.blurred.emit(event);
1421
1410
  return true;
1422
1411
  }
1423
1412
  filterMatSelectFocusEvent(event) {
@@ -1425,7 +1414,7 @@ class MatAutocompleteField {
1425
1414
  return;
1426
1415
  // DEBUG
1427
1416
  // console.debug(this.logPrefix + " Received <mat-select> focus event", event);
1428
- this.onFocus.emit(event);
1417
+ this.focused.emit(event);
1429
1418
  }
1430
1419
  filterMatSelectBlurEvent(event) {
1431
1420
  if (!event || event.defaultPrevented)
@@ -1442,7 +1431,7 @@ class MatAutocompleteField {
1442
1431
  // console.debug(this.logPrefix + " Cancelling <mat-select> blur event");
1443
1432
  return false;
1444
1433
  }
1445
- this.onBlur.emit(event);
1434
+ this.blurred.emit(event);
1446
1435
  return true;
1447
1436
  }
1448
1437
  toggleSearch(event) {
@@ -1464,7 +1453,7 @@ class MatAutocompleteField {
1464
1453
  event.stopPropagation();
1465
1454
  }
1466
1455
  _initAutocompleteInfiniteScroll(threshold) {
1467
- if (!this.matAutocomplete)
1456
+ if (!this.autocomplete)
1468
1457
  return; // Skip
1469
1458
  // DEBUG
1470
1459
  //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
@@ -1472,12 +1461,12 @@ class MatAutocompleteField {
1472
1461
  this._openedSubscription.unsubscribe();
1473
1462
  this._subscription.remove(this._openedSubscription);
1474
1463
  }
1475
- const opened$ = this.matAutocomplete._isOpen
1464
+ const opened$ = this.autocomplete._isOpen
1476
1465
  ? timer(0)
1477
- : this.matAutocomplete.opened.pipe(debounceTime(200));
1466
+ : this.autocomplete.opened.pipe(debounceTime(200));
1478
1467
  const scrollEnd$ = opened$
1479
1468
  .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
1480
- .pipe(takeUntil(this.matAutocomplete.closed))));
1469
+ .pipe(takeUntil(this.autocomplete.closed))));
1481
1470
  // Trigger fetch more, end end scroll reached
1482
1471
  this._openedSubscription = scrollEnd$
1483
1472
  .subscribe(() => this._fetchMore$.emit());
@@ -1508,14 +1497,14 @@ class MatAutocompleteField {
1508
1497
  }
1509
1498
  get isOpen() {
1510
1499
  var _a, _b;
1511
- return (this.searchable && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1500
+ return (this.searchable && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1512
1501
  || ((_b = this.matSelect) === null || _b === void 0 ? void 0 : _b.panelOpen)
1513
1502
  || false;
1514
1503
  }
1515
1504
  closePanel() {
1516
1505
  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();
1506
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1507
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1519
1508
  return;
1520
1509
  }
1521
1510
  if ((_c = this.matSelect) === null || _c === void 0 ? void 0 : _c.panelOpen) {
@@ -1582,7 +1571,7 @@ class MatAutocompleteField {
1582
1571
  }
1583
1572
  getAutocompletePanel() {
1584
1573
  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;
1574
+ 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
1575
  if (!ele) {
1587
1576
  // DEBUG
1588
1577
  console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
@@ -1616,8 +1605,12 @@ MatAutocompleteField.decorators = [
1616
1605
  { type: Component, args: [{
1617
1606
  // eslint-disable-next-line @angular-eslint/component-selector
1618
1607
  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],
1608
+ 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)=\"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)=\"!autocomplete.opened && dropButtonClick.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><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",
1609
+ providers: [{
1610
+ provide: NG_VALUE_ACCESSOR,
1611
+ multi: true,
1612
+ useExisting: forwardRef(() => MatAutocompleteField)
1613
+ }],
1621
1614
  changeDetection: ChangeDetectionStrategy.OnPush,
1622
1615
  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}"]
1623
1616
  },] }
@@ -1627,7 +1620,7 @@ MatAutocompleteField.ctorParameters = () => [
1627
1620
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
1628
1621
  ];
1629
1622
  MatAutocompleteField.propDecorators = {
1630
- compareWith: [{ type: Input }],
1623
+ equals: [{ type: Input }],
1631
1624
  logPrefix: [{ type: Input }],
1632
1625
  formControl: [{ type: Input }],
1633
1626
  formControlName: [{ type: Input }],
@@ -1657,21 +1650,21 @@ MatAutocompleteField.propDecorators = {
1657
1650
  fetchMoreThreshold: [{ type: Input }],
1658
1651
  suggestLengthThreshold: [{ type: Input }],
1659
1652
  showLoadingSpinner: [{ type: Input }],
1653
+ clicked: [{ type: Output, args: ['click',] }],
1654
+ blurred: [{ type: Output, args: ['blur',] }],
1655
+ focused: [{ type: Output, args: ['focus',] }],
1656
+ dropButtonClick: [{ type: Output }],
1657
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
1658
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
1659
+ matSelect: [{ type: ViewChild, args: ['matSelect',] }],
1660
+ matInputText: [{ type: ViewChild, args: ['matInputText',] }],
1661
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
1662
+ autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
1660
1663
  animationsDisabled: [{ type: HostBinding, args: ['@.disabled',] }],
1661
1664
  filter: [{ type: Input }],
1662
1665
  readonly: [{ type: Input }],
1663
1666
  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,] }]
1667
+ items: [{ type: Input }]
1675
1668
  };
1676
1669
 
1677
1670
  class DateFormatPipe {
@@ -6552,7 +6545,7 @@ class MatSwipeField {
6552
6545
  // Default values
6553
6546
  this.displayAttributes = this.displayAttributes || ['label', 'name'];
6554
6547
  this.displayWith = this.displayWith || ((obj) => obj && joinPropertiesPath(obj, this.displayAttributes));
6555
- this.compareWithFn = this.compareWithFn || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6548
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6556
6549
  this.mobile = toBoolean(this.mobile, false);
6557
6550
  }
6558
6551
  ngOnDestroy() {
@@ -6612,7 +6605,7 @@ class MatSwipeField {
6612
6605
  if (this._writing)
6613
6606
  return;
6614
6607
  this._writing = true;
6615
- if (!this.compareWithFn(value, this._value)) {
6608
+ if (!this.equals(value, this._value)) {
6616
6609
  this._value = value;
6617
6610
  this.showSlides = isNotNil(this._value);
6618
6611
  this.updateSlides();
@@ -6696,7 +6689,7 @@ class MatSwipeField {
6696
6689
  }
6697
6690
  updateSlides() {
6698
6691
  if (this.showSlides) {
6699
- const itemIndex = (this.$items.getValue() || []).findIndex(value => this.compareWithFn(value, this._value));
6692
+ const itemIndex = (this.$items.getValue() || []).findIndex(value => this.equals(value, this._value));
6700
6693
  if (itemIndex >= 0) {
6701
6694
  this.slideTo(itemIndex);
6702
6695
  }
@@ -6760,7 +6753,7 @@ MatSwipeField.propDecorators = {
6760
6753
  required: [{ type: Input }],
6761
6754
  mobile: [{ type: Input }],
6762
6755
  clearable: [{ type: Input }],
6763
- compareWithFn: [{ type: Input }],
6756
+ equals: [{ type: Input }],
6764
6757
  displayWith: [{ type: Input }],
6765
6758
  displayAttributes: [{ type: Input }],
6766
6759
  appAutofocus: [{ type: Input }],
@@ -6809,33 +6802,43 @@ class MatChipsField {
6809
6802
  this.required = false;
6810
6803
  this.readonly = false;
6811
6804
  this.clearable = false;
6812
- this.debounceTime = 250;
6813
- this.highlightAccent = false;
6805
+ this.debounceTime = null;
6814
6806
  this.i18nPrefix = 'REFERENTIAL.';
6815
6807
  this.noResultMessage = 'COMMON.NO_RESULT';
6816
6808
  this.debug = false;
6809
+ this.itemSize = '48px';
6810
+ this.fetchMoreThreshold = '15%';
6811
+ this.suggestLengthThreshold = null;
6812
+ this.showLoadingSpinner = true;
6817
6813
  this.clicked = new EventEmitter();
6818
6814
  this.blurred = new EventEmitter();
6819
6815
  this.focused = new EventEmitter();
6816
+ this.dropButtonClick = new EventEmitter(true);
6817
+ this.keydownEscape = new EventEmitter();
6818
+ this.keyupEnter = new EventEmitter();
6820
6819
  this.$inputItems = new BehaviorSubject(undefined);
6821
- this.onDropButtonClick = new EventEmitter(true);
6820
+ this.$filteredItems = new BehaviorSubject(undefined);
6822
6821
  this.inputControl = new FormControl();
6823
- this.getPropertyByPath = getPropertyByPath;
6822
+ this._fetchMore$ = new EventEmitter();
6824
6823
  this._subscription = new Subscription();
6825
6824
  this._$filter = new BehaviorSubject(undefined);
6825
+ this._reload$ = new Subject();
6826
6826
  }
6827
6827
  get itemCount() {
6828
6828
  return this._itemCount;
6829
6829
  }
6830
+ get canFetchMore() {
6831
+ return this._onFetchMoreCallback && this._moreItemsCount > 0;
6832
+ }
6830
6833
  set filter(value) {
6831
- if (this.debug)
6832
- console.debug(this.logPrefix + ' Setting filter:', value);
6833
- if (value !== this._$filter.getValue()) {
6834
+ if (value !== this._$filter.value) {
6835
+ // DEBUG
6836
+ //console.debug(this.logPrefix + " Setting filter:", value);
6834
6837
  this._$filter.next(value);
6835
6838
  }
6836
6839
  }
6837
6840
  get filter() {
6838
- return this._$filter.getValue();
6841
+ return this._$filter.value;
6839
6842
  }
6840
6843
  set tabindex(value) {
6841
6844
  this._tabindex = value;
@@ -6847,19 +6850,25 @@ class MatChipsField {
6847
6850
  set items(value) {
6848
6851
  // Remove previous subscription on items, (if exits)
6849
6852
  if (this._itemsSubscription) {
6850
- console.warn('Items received twice !');
6853
+ console.warn(this.logPrefix + ' Items received twice !');
6851
6854
  this._subscription.remove(this._itemsSubscription);
6852
6855
  this._itemsSubscription.unsubscribe();
6853
6856
  }
6854
6857
  if (isObservable(value)) {
6855
- this._itemsSubscription = this._subscription.add(value.subscribe(v => this.$inputItems.next(v)));
6858
+ this._itemsSubscription = this._subscription.add(value.subscribe(v => {
6859
+ //console.warn(this.logPrefix + " Received items: ", v);
6860
+ this.$inputItems.next(v);
6861
+ }));
6856
6862
  }
6857
6863
  else {
6858
- if (value !== this.$inputItems.getValue()) {
6864
+ if (value !== this.$inputItems.value) {
6859
6865
  this.$inputItems.next(value);
6860
6866
  }
6861
6867
  }
6862
6868
  }
6869
+ get items() {
6870
+ return this.$inputItems;
6871
+ }
6863
6872
  get value() {
6864
6873
  return this.formControl.value || [];
6865
6874
  }
@@ -6870,6 +6879,7 @@ class MatChipsField {
6870
6879
  return !this.readonly && this.formControl.enabled;
6871
6880
  }
6872
6881
  ngOnInit() {
6882
+ var _a, _b;
6873
6883
  this.formControl = this.formControl || this.formControlName && this.formGroupDir && this.formGroupDir.form.get(this.formControlName);
6874
6884
  if (!this.formControl)
6875
6885
  throw new Error('Missing mandatory attribute \'formControl\' or \'formControlName\' in <mat-chips-field>.');
@@ -6884,85 +6894,201 @@ class MatChipsField {
6884
6894
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
6885
6895
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
6886
6896
  this.displayWith = this.displayWith || this.config.displayWith;
6887
- this.compareWith = this.compareWith || this.config.compareWith;
6897
+ this.equals = this.equals || this.config.equals;
6888
6898
  this.mobile = toBoolean(this.mobile, this.config.mobile);
6889
- this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, true));
6899
+ this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
6900
+ this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
6890
6901
  this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus, true));
6891
- this.class = this.class || this.config.class;
6902
+ this.classList = this.classList || this.config.class;
6903
+ this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
6904
+ this.highlightAccent = toBoolean(this.highlightAccent, toBoolean(this.config.highlightAccent));
6892
6905
  }
6893
6906
  // Default values
6894
- this.displayAttributes = this.displayAttributes || (this.filter && this.filter.attributes) || ['label', 'name'];
6907
+ this.displayAttributes = this.displayAttributes || ((_a = this.filter) === null || _a === void 0 ? void 0 : _a.attributes) || ['label', 'name'];
6895
6908
  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));
6909
+ this.displayColumnSizes = this.displayColumnSizes
6910
+ // if only column: auto size
6911
+ || (this.displayAttributes.length === 1 && [undefined])
6912
+ // Computed column size:
6913
+ // - if label then col size = 2
6914
+ // - if rankOrder then col size = 1
6915
+ // - ese => auto size
6916
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
6917
+ : ((attr && attr.endsWith('rankOrder')) ? 1
6918
+ : undefined));
6904
6919
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
6905
6920
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
6906
6921
  this.mobile = toBoolean(this.mobile, false);
6922
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6923
+ const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
6924
+ this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
6925
+ || (classList.includes('min-width-large') && '400px')
6926
+ || (classList.includes('min-width-xlarge') && '450px')
6927
+ || (classList.includes('mat-autocomplete-panel-full-size') && '100vw')));
6928
+ this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, true);
6929
+ this.debounceTime = toNumber(this.debounceTime, 250);
6930
+ this.highlightAccent = toBoolean(this.highlightAccent, false);
6931
+ this.suggestLengthThreshold = this.suggestLengthThreshold || 0;
6932
+ this.matAutocompletePosition = this.matAutocompletePosition || (this.mobile ? 'above' : 'auto'); // On mobile, set position to above (because of bottom keyboard)
6907
6933
  // No suggestFn: filter on the given items
6908
6934
  if (!this.suggestFn) {
6909
6935
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6910
6936
  if (this.debug)
6911
6937
  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 };
6938
+ return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
6915
6939
  });
6916
6940
  // Wait (once) that items are loaded, then call suggest from array fn
6917
6941
  this.suggestFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6918
- if (isNil(this.$inputItems.getValue())) {
6942
+ if (isNil(this.$inputItems.value)) {
6919
6943
  if (this.debug)
6920
6944
  console.debug('[mat-chips] Waiting items to be set...');
6921
6945
  yield firstNotNilPromise(this.$inputItems);
6922
6946
  if (this.debug)
6923
- console.debug('[mat-chips] Received items:', this.$inputItems.getValue());
6947
+ console.debug('[mat-chips] Received items:', this.$inputItems.value);
6924
6948
  }
6925
6949
  this.suggestFn = suggestFromArrayFn;
6926
- return this.suggest(value, filter); // Loop
6950
+ return this.suggestFn(value, filter); // Loop
6927
6951
  });
6928
6952
  }
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(
6953
+ const filteredItemsChanges = merge(
6954
+ // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
6955
+ this._reload$, this.inputControl.valueChanges
6956
+ .pipe(filter(value => this.enabled && isNotNilString(value)),
6957
+ // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
6958
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
6959
+ // Filter if not enough character
6960
+ filter(value => value.length >= this.suggestLengthThreshold),
6961
+ // Wait a debounce time
6962
+ debounceTime(this.debounceTime)
6963
+ // DEBUG
6964
+ //tap((value) => console.debug(this.logPrefix + ' Search text changes:', value))
6965
+ ),
6966
+ // Object set: use it (no distinctUntilChanged() pipe need)
6967
+ this.inputControl.valueChanges
6968
+ .pipe(startWith(this.inputControl.value), filter(value => isNotNil(value) && typeof value === 'object'), tap(value => {
6969
+ // DEBUG
6970
+ //console.debug(this.logPrefix + ' object changes:', value)
6971
+ // Update the display value (if need)
6972
+ const displayValue = this.displayWith(value) || '';
6973
+ // Apply to the input[type=text] field
6974
+ if (this.matInputText && this.matInputText.nativeElement) {
6975
+ this.matInputText.nativeElement.value = displayValue;
6976
+ }
6977
+ })
6978
+ // DEBUG
6979
+ //tap(value => console.debug(this.logPrefix + ' Received an object value: ', value))
6980
+ ),
6981
+ // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
6982
+ merge(
6949
6983
  // Focus or click => Load all
6950
6984
  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)));
6985
+ .pipe(filter(_ => this.enabled), map((_) => this.showAllOnFocus ? '*' : this.inputControl.value)),
6986
+ // On dropdown button click
6987
+ this.dropButtonClick
6988
+ .pipe(filter(event => this.enabled && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.inputControl.value)), merge(this.$inputItems, this._$filter)
6989
+ .pipe(filter(_ => this.enabled), map(_ => this.inputControl.value)))
6990
+ .pipe(
6991
+ // Start to suggest when :
6992
+ // - '*'
6993
+ // - is nil, and there is no min length threshold
6994
+ // - is not nil and not a string type (e.g. object)
6995
+ // - is not nil and is a string, and has enough character
6996
+ filter(value => value === '*'
6997
+ || (isNil(value)
6998
+ ? (this.suggestLengthThreshold <= 0)
6999
+ : (!(typeof value === 'string')
7000
+ || value.length >= this.suggestLengthThreshold))),
7001
+ // Distinguish changes
7002
+ distinctUntilChanged()))
7003
+ .pipe(
7004
+ // Suggest values
7005
+ mergeMap(value => this.suggest(value, this.filter)),
7006
+ // DEBUG
7007
+ //tap(items => console.debug(this.logPrefix + ' Received from suggest: ', items)),
7008
+ // Store implicit value (will use it onBlur if not other value selected)
7009
+ tap(items => this.updateImplicitValue(items)));
7010
+ // Fetch more events
7011
+ const fetchMoreItemsChanges = this._fetchMore$
7012
+ .pipe(tap(event => event === null || event === void 0 ? void 0 : event.preventDefault()), // Avoid to close the mat-select
7013
+ filter(() => this.canFetchMore), mergeMap(() => this.fetchMore()),
7014
+ // DEBUG
7015
+ //tap(moreItems => console.debug(this.logPrefix + ' Received from fetch More: ', moreItems)),
7016
+ // Concat to existing items
7017
+ map(moreItems => (this.$filteredItems.value || []).concat(moreItems)));
7018
+ // Update filtered items
7019
+ this._subscription.add(merge(this.$inputItems,
7020
+ // Update items events
7021
+ filteredItemsChanges,
7022
+ // Fetch more events
7023
+ fetchMoreItemsChanges)
7024
+ // Emit items
7025
+ .subscribe(items => this.$filteredItems.next(items)));
7026
+ // Applying implicit value, on blur
7027
+ this._subscription.add(this.blurred
7028
+ .pipe(map(_ => this.formControl.value))
7029
+ .subscribe(value => {
7030
+ // When leave component without object, use implicit value if :
7031
+ // - an explicit value
7032
+ // - field is not empty (user fill something)
7033
+ // - OR field empty but is required
7034
+ if (this._implicitValue && ((this.required && isNilOrBlank(value)) || (isNotNilOrBlank(value) && typeof value !== 'object'))) {
7035
+ this.writeValue(this._implicitValue);
7036
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7037
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7038
+ this.checkIfTouched();
7039
+ }
7040
+ else if (isNilOrBlank(value)) {
7041
+ this.writeValue(null);
7042
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7043
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7044
+ this.checkIfTouched();
7045
+ }
7046
+ this._implicitValue = null; // reset the implicit value
7047
+ }));
7048
+ this._subscription.add(this.keydownEscape.subscribe(event => {
7049
+ if (this.isOpen) {
7050
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
7051
+ event === null || event === void 0 ? void 0 : event.preventDefault();
7052
+ // DEBUG
7053
+ //console.debug(this.logPrefix + " Closing the panel (keydown.escape)");
7054
+ this.closePanel();
7055
+ }
7056
+ }));
6962
7057
  }
6963
7058
  ngOnDestroy() {
6964
7059
  this._subscription.unsubscribe();
7060
+ if (this._itemsSubscription) {
7061
+ this._itemsSubscription.unsubscribe();
7062
+ }
7063
+ if (this._openedSubscription) {
7064
+ this._openedSubscription.unsubscribe();
7065
+ }
6965
7066
  this.$inputItems.complete();
7067
+ this.$filteredItems.complete();
7068
+ this._reload$.complete();
7069
+ this._$filter.complete();
7070
+ this.dropButtonClick.complete();
7071
+ this.clicked.complete();
7072
+ this.blurred.complete();
7073
+ this.focused.complete();
7074
+ this.keydownEscape.complete();
7075
+ this.keyupEnter.complete();
7076
+ this._fetchMore$.complete();
7077
+ this._implicitValue = undefined;
7078
+ this.formControl = null;
7079
+ this.displayWith = null;
7080
+ this.equals = null;
7081
+ this.suggestFn = null;
7082
+ this.$filteredItems = null;
7083
+ this.config = null;
7084
+ this.classList = null;
7085
+ this._onChangeCallback = null;
7086
+ this._onTouchedCallback = null;
7087
+ this._onFetchMoreCallback = null;
7088
+ // Destroy the 'autocomplete' instance, to avoid memory leak
7089
+ if (this.autocomplete) {
7090
+ this.autocomplete.ngOnDestroy();
7091
+ }
6966
7092
  }
6967
7093
  /**
6968
7094
  * Add item to chips
@@ -6983,19 +7109,21 @@ class MatChipsField {
6983
7109
  * @param item
6984
7110
  */
6985
7111
  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('');
7112
+ const value = this.value;
7113
+ const index = value ? value.indexOf(item) : -1;
7114
+ if (index !== -1) {
7115
+ const newValue = value.slice();
7116
+ newValue.splice(index, 1);
7117
+ this.writeValue(newValue);
7118
+ this.closePanel();
6992
7119
  }
6993
7120
  }
6994
7121
  /**
6995
7122
  * Allow to reload content. Useful when filter has been changed but not detected
6996
7123
  */
6997
- reloadItems() {
6998
- this.onDropButtonClick.emit();
7124
+ reloadItems(value) {
7125
+ // Force a refresh
7126
+ this._reload$.next(value || '*');
6999
7127
  }
7000
7128
  writeValue(value) {
7001
7129
  if (this.debug)
@@ -7012,6 +7140,10 @@ class MatChipsField {
7012
7140
  this._onTouchedCallback = fn;
7013
7141
  }
7014
7142
  setDisabledState(isDisabled) {
7143
+ var _a;
7144
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
7145
+ this.closePanel();
7146
+ }
7015
7147
  this.cd.markForCheck();
7016
7148
  }
7017
7149
  focus() {
@@ -7031,6 +7163,7 @@ class MatChipsField {
7031
7163
  return false;
7032
7164
  }
7033
7165
  const hasContent = selectInputContent(event);
7166
+ // If combo is empty, or if has content but should force to show panel on focus
7034
7167
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
7035
7168
  if (this.debug)
7036
7169
  console.debug(this.logPrefix + ' Emit focus event');
@@ -7039,10 +7172,67 @@ class MatChipsField {
7039
7172
  }
7040
7173
  return false;
7041
7174
  }
7175
+ filterInputTextBlurEvent(event) {
7176
+ if (!event || event.defaultPrevented)
7177
+ return;
7178
+ // Ignore event from mat-option
7179
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.tagName === 'MAT-OPTION') {
7180
+ event.preventDefault();
7181
+ if (event.stopPropagation)
7182
+ event.stopPropagation();
7183
+ event.returnValue = false;
7184
+ //DEBUG console.debug(this.logPrefix + " Cancelling blur event");
7185
+ return false;
7186
+ }
7187
+ //DEBUG
7188
+ //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
7189
+ // Move caret to the beginning (fix issue IMAGINE-469)
7190
+ selectInputRange(event.target, 0, 0);
7191
+ this.blurred.emit(event);
7192
+ return true;
7193
+ }
7194
+ filterChipsFocusEvent(event) {
7195
+ // DEBUG
7196
+ if (this.debug)
7197
+ console.debug(this.logPrefix + ' Focus in chips list', event);
7198
+ this.matInputText.nativeElement.focus();
7199
+ }
7042
7200
  clearValue(event) {
7043
7201
  this.writeValue(null);
7044
7202
  event.stopPropagation();
7045
7203
  }
7204
+ _initAutocompleteInfiniteScroll(threshold) {
7205
+ if (!this.autocomplete)
7206
+ return; // Skip
7207
+ // DEBUG
7208
+ //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
7209
+ if (this._openedSubscription) {
7210
+ this._openedSubscription.unsubscribe();
7211
+ this._subscription.remove(this._openedSubscription);
7212
+ }
7213
+ const opened$ = this.autocomplete._isOpen
7214
+ ? timer(0)
7215
+ : this.autocomplete.opened.pipe(debounceTime(200));
7216
+ const scrollEnd$ = opened$
7217
+ .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
7218
+ .pipe(takeUntil(this.autocomplete.closed))));
7219
+ // Trigger fetch more, end end scroll reached
7220
+ this._openedSubscription = scrollEnd$
7221
+ .subscribe(() => this._fetchMore$.emit());
7222
+ // Register subscription
7223
+ this._subscription.add(this._openedSubscription);
7224
+ }
7225
+ get isOpen() {
7226
+ var _a;
7227
+ return ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
7228
+ }
7229
+ closePanel() {
7230
+ var _a, _b;
7231
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
7232
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
7233
+ return;
7234
+ }
7235
+ }
7046
7236
  /* -- private method -- */
7047
7237
  suggest(value, filter) {
7048
7238
  return __awaiter(this, void 0, void 0, function* () {
@@ -7064,7 +7254,7 @@ class MatChipsField {
7064
7254
  this._itemCount = toNumber(total, data && data.length || 0);
7065
7255
  }
7066
7256
  // Filter out existing items
7067
- const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.compareWith(item1, item2)) === -1);
7257
+ const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.equals(item1, item2)) === -1);
7068
7258
  // Update items count
7069
7259
  this._itemCount = this._itemCount - (((res === null || res === void 0 ? void 0 : res.length) || 0) - ((filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) || 0));
7070
7260
  if (this.debug)
@@ -7072,15 +7262,64 @@ class MatChipsField {
7072
7262
  return res;
7073
7263
  });
7074
7264
  }
7265
+ fetchMore() {
7266
+ return __awaiter(this, void 0, void 0, function* () {
7267
+ if (!this._onFetchMoreCallback)
7268
+ return [];
7269
+ // Save then remove fetch function (to avoid multi-call)
7270
+ const fetchMoreFn = this._onFetchMoreCallback;
7271
+ this._onFetchMoreCallback = undefined;
7272
+ const { data, total, fetchMore } = yield fetchMoreFn();
7273
+ this._itemCount = total || this._itemCount;
7274
+ this._onFetchMoreCallback = fetchMore;
7275
+ this._moreItemsCount = Math.max(0, this._moreItemsCount - data.length); // Borne min = 0
7276
+ return data;
7277
+ });
7278
+ }
7279
+ updateImplicitValue(items) {
7280
+ // Store implicit value (will use it onBlur if not other value selected)
7281
+ if (items && items.length === 1) {
7282
+ this._implicitValue = items[0];
7283
+ //this.formControl.setErrors(null);
7284
+ }
7285
+ else {
7286
+ this._implicitValue = undefined;
7287
+ }
7288
+ }
7289
+ getAutocompletePanel() {
7290
+ var _a, _b;
7291
+ const ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
7292
+ if (!ele) {
7293
+ // DEBUG
7294
+ console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
7295
+ }
7296
+ return ele;
7297
+ }
7298
+ checkIfTouched() {
7299
+ if (this.formControl.touched) {
7300
+ this.cd.markForCheck();
7301
+ this._onTouchedCallback();
7302
+ }
7303
+ }
7075
7304
  markForCheck() {
7076
7305
  this.cd.markForCheck();
7077
7306
  }
7307
+ markAsLoading() {
7308
+ if (this.$filteredItems.value) {
7309
+ this.$filteredItems.next(undefined);
7310
+ this._itemCount = undefined;
7311
+ this._implicitValue = undefined;
7312
+ this._onFetchMoreCallback = undefined;
7313
+ this._moreItemsCount = 0;
7314
+ this.markForCheck();
7315
+ }
7316
+ }
7078
7317
  }
7079
7318
  MatChipsField.decorators = [
7080
7319
  { type: Component, args: [{
7081
7320
  // eslint-disable-next-line @angular-eslint/component-selector
7082
7321
  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",
7322
+ 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 (focus)=\"matInputText.focus()\">\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]=\"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\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 matSuffix 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 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",
7084
7323
  providers: [
7085
7324
  {
7086
7325
  provide: NG_VALUE_ACCESSOR,
@@ -7097,7 +7336,7 @@ MatChipsField.ctorParameters = () => [
7097
7336
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
7098
7337
  ];
7099
7338
  MatChipsField.propDecorators = {
7100
- compareWith: [{ type: Input }],
7339
+ equals: [{ type: Input }],
7101
7340
  logPrefix: [{ type: Input }],
7102
7341
  formControl: [{ type: Input }],
7103
7342
  formControlName: [{ type: Input }],
@@ -7116,16 +7355,26 @@ MatChipsField.propDecorators = {
7116
7355
  highlightAccent: [{ type: Input }],
7117
7356
  showAllOnFocus: [{ type: Input }],
7118
7357
  showPanelOnFocus: [{ type: Input }],
7119
- appAutofocus: [{ type: Input }],
7358
+ autofocus: [{ type: Input }],
7120
7359
  config: [{ type: Input }],
7121
7360
  i18nPrefix: [{ type: Input }],
7122
7361
  noResultMessage: [{ type: Input }],
7123
- class: [{ type: Input }],
7362
+ classList: [{ type: Input, args: ['class',] }],
7363
+ panelWidth: [{ type: Input }],
7364
+ matAutocompletePosition: [{ type: Input }],
7124
7365
  debug: [{ type: Input }],
7366
+ itemSize: [{ type: Input }],
7367
+ fetchMoreThreshold: [{ type: Input }],
7368
+ suggestLengthThreshold: [{ type: Input }],
7369
+ showLoadingSpinner: [{ type: Input }],
7125
7370
  clicked: [{ type: Output }],
7126
7371
  blurred: [{ type: Output }],
7127
7372
  focused: [{ type: Output }],
7373
+ dropButtonClick: [{ type: Output }],
7374
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
7375
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
7128
7376
  matInputText: [{ type: ViewChild, args: ['matInputText',] }],
7377
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
7129
7378
  autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
7130
7379
  filter: [{ type: Input }],
7131
7380
  tabindex: [{ type: Input }],
@@ -16367,7 +16616,7 @@ class AutocompleteTestPage {
16367
16616
  AutocompleteTestPage.decorators = [
16368
16617
  { type: Component, args: [{
16369
16618
  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"
16619
+ 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]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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"
16371
16620
  },] }
16372
16621
  ];
16373
16622
  AutocompleteTestPage.ctorParameters = () => [
@@ -16522,7 +16771,7 @@ class SwipeTestPage {
16522
16771
  SwipeTestPage.decorators = [
16523
16772
  { type: Component, args: [{
16524
16773
  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"
16774
+ 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
16775
  },] }
16527
16776
  ];
16528
16777
  SwipeTestPage.ctorParameters = () => [
@@ -16651,13 +16900,15 @@ class ChipsTestPage {
16651
16900
  this.autocompleteFields.add('entity-suggestFn', {
16652
16901
  suggestFn: (value, filter) => this.suggest(value, filter),
16653
16902
  attributes: ['label', 'name'],
16654
- displayWith: this.entityToString
16903
+ displayWith: this.entityToString,
16904
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16655
16905
  });
16656
16906
  // From items
16657
16907
  this.autocompleteFields.add('entity-items', {
16658
16908
  items: FAKE_ENTITIES.slice(),
16659
16909
  attributes: ['label', 'name'],
16660
- displayWith: this.entityToString
16910
+ displayWith: this.entityToString,
16911
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16661
16912
  });
16662
16913
  // From items
16663
16914
  this.autocompleteFields.add('entity-$items', {
@@ -16709,7 +16960,7 @@ class ChipsTestPage {
16709
16960
  ChipsTestPage.decorators = [
16710
16961
  { type: Component, args: [{
16711
16962
  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"
16963
+ 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 [equals]=\"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 [equals]=\"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 [equals]=\"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 [equals]=\"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"
16713
16964
  },] }
16714
16965
  ];
16715
16966
  ChipsTestPage.ctorParameters = () => [
@@ -17253,11 +17504,18 @@ AuthForm.propDecorators = {
17253
17504
  };
17254
17505
 
17255
17506
  class AuthModal {
17256
- constructor(accountService, viewCtrl, changeDetectorRef) {
17507
+ constructor(accountService, viewCtrl, cd) {
17257
17508
  this.accountService = accountService;
17258
17509
  this.viewCtrl = viewCtrl;
17259
- this.changeDetectorRef = changeDetectorRef;
17260
- this.loading = false;
17510
+ this.cd = cd;
17511
+ }
17512
+ get loading() {
17513
+ var _a;
17514
+ return (_a = this.form) === null || _a === void 0 ? void 0 : _a.loading;
17515
+ }
17516
+ ngOnInit() {
17517
+ this.form.markAsReady({ emitEvent: false });
17518
+ this.form.markAsLoaded();
17261
17519
  }
17262
17520
  cancel() {
17263
17521
  this.viewCtrl.dismiss();
@@ -17267,10 +17525,10 @@ class AuthModal {
17267
17525
  if (this.form.disabled)
17268
17526
  return;
17269
17527
  if (!this.form.valid) {
17270
- this.form.markAsTouched();
17528
+ this.form.markAllAsTouched();
17271
17529
  return;
17272
17530
  }
17273
- this.loading = true;
17531
+ this.markAsLoading();
17274
17532
  try {
17275
17533
  const data = this.form.value;
17276
17534
  // Disable the form
@@ -17279,9 +17537,8 @@ class AuthModal {
17279
17537
  return this.viewCtrl.dismiss(account);
17280
17538
  }
17281
17539
  catch (err) {
17282
- this.loading = false;
17283
17540
  this.form.error = err && err.message || err;
17284
- this.markForCheck();
17541
+ this.markAsLoaded();
17285
17542
  // Enable the form
17286
17543
  this.form.enable();
17287
17544
  // Reset form error on next changes
@@ -17294,7 +17551,13 @@ class AuthModal {
17294
17551
  });
17295
17552
  }
17296
17553
  markForCheck() {
17297
- this.changeDetectorRef.markForCheck();
17554
+ this.cd.markForCheck();
17555
+ }
17556
+ markAsLoading(opts) {
17557
+ this.form.markAsLoading(opts);
17558
+ }
17559
+ markAsLoaded(opts) {
17560
+ this.form.markAsLoaded(opts);
17298
17561
  }
17299
17562
  }
17300
17563
  AuthModal.decorators = [