@sumaris-net/ngx-components 1.5.11 → 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.
Files changed (30) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +568 -216
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +7 -0
  6. package/esm2015/src/app/core/auth/modal/modal-auth.js +21 -9
  7. package/esm2015/src/app/core/form/editor.class.js +17 -3
  8. package/esm2015/src/app/core/form/form.class.js +6 -5
  9. package/esm2015/src/app/core/table/entities-table-datasource.class.js +35 -1
  10. package/esm2015/src/app/core/table/table.class.js +7 -2
  11. package/esm2015/src/app/shared/form/field.model.js +1 -1
  12. package/esm2015/src/app/shared/material/autocomplete/material.autocomplete.js +81 -88
  13. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +2 -2
  14. package/esm2015/src/app/shared/material/chips/material.chips.js +336 -79
  15. package/esm2015/src/app/shared/material/chips/testing/chips.test.js +7 -4
  16. package/esm2015/src/app/shared/material/swipe/material.swipe.js +5 -5
  17. package/esm2015/src/app/shared/material/swipe/testing/swipe.test.js +2 -2
  18. package/esm2015/src/app/shared/observables.js +10 -3
  19. package/fesm2015/sumaris-net.ngx-components.js +512 -188
  20. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/app/core/auth/modal/modal-auth.d.ts +12 -5
  23. package/src/app/core/form/editor.class.d.ts +9 -1
  24. package/src/app/core/table/entities-table-datasource.class.d.ts +6 -1
  25. package/src/app/shared/form/field.model.d.ts +2 -1
  26. package/src/app/shared/material/autocomplete/material.autocomplete.d.ts +35 -35
  27. package/src/app/shared/material/chips/material.chips.d.ts +35 -10
  28. package/src/app/shared/material/swipe/material.swipe.d.ts +2 -2
  29. package/src/app/shared/observables.d.ts +2 -0
  30. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -1117,6 +1117,14 @@
1117
1117
  .then(function (jobRes) { return finalResult.concat(jobRes); }); });
1118
1118
  }, null);
1119
1119
  }
1120
+ function firstTrue(obs) {
1121
+ return obs.pipe(operators.first(function (v) { return v === true; }), operators.map(function (_) { }) // Convert to void
1122
+ );
1123
+ }
1124
+ function firstFalse(obs) {
1125
+ return obs.pipe(operators.first(function (v) { return v === false; }), operators.map(function (_) { }) // Convert to void
1126
+ );
1127
+ }
1120
1128
  /**
1121
1129
  * Wait form a predicate return true. This need to implement AppFormUtils.waitWhilePending(), AppFormUtils.waitIdle()
1122
1130
  * @param predicate
@@ -1157,8 +1165,7 @@
1157
1165
  return __awaiter(this, void 0, void 0, function () {
1158
1166
  var firstTrueObservable, $timeout;
1159
1167
  return __generator(this, function (_a) {
1160
- firstTrueObservable = observable.pipe(operators.first(function (v) { return v === true; }), operators.map(function () { }) // convert to void
1161
- );
1168
+ firstTrueObservable = firstTrue(observable);
1162
1169
  // Timeout (+ dueTime)
1163
1170
  if (opts && opts.timeout > 0) {
1164
1171
  $timeout = rxjs.timer(opts.timeout);
@@ -1255,11 +1262,10 @@
1255
1262
  var attributesOrFn = attributes.map(function (a, index) { return typeof a === 'function' && options.attributes[index] || a; });
1256
1263
  var filter = Object.assign({ searchAttribute: attributes.length === 1 ? attributes[0] : undefined, searchAttributes: attributes.length > 1 ? attributes : undefined }, options.filter);
1257
1264
  var displayWith = options.displayWith || (function (obj) { return obj && joinPropertiesPath(obj, attributes); });
1258
- var compareWith = options.compareWith || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
1265
+ var compareWith = options.equals || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
1259
1266
  var config = Object.assign(Object.assign({}, options), { attributes: attributesOrFn, suggestFn: suggestFn,
1260
1267
  filter: filter,
1261
- displayWith: displayWith,
1262
- compareWith: compareWith });
1268
+ displayWith: displayWith, equals: compareWith });
1263
1269
  this.fields[fieldName] = config;
1264
1270
  return config;
1265
1271
  };
@@ -1271,11 +1277,6 @@
1271
1277
  };
1272
1278
  return MatAutocompleteConfigHolder;
1273
1279
  }());
1274
- var DEFAULT_VALUE_ACCESSOR$6 = {
1275
- provide: i1.NG_VALUE_ACCESSOR,
1276
- useExisting: i0.forwardRef(function () { return MatAutocompleteField; }),
1277
- multi: true
1278
- };
1279
1280
  var noop$7 = function () { };
1280
1281
  var ɵ0$a = noop$7;
1281
1282
  var MatAutocompleteField = /** @class */ (function () {
@@ -1284,14 +1285,6 @@
1284
1285
  this.formGroupDir = formGroupDir;
1285
1286
  this._onChangeCallback = noop$7;
1286
1287
  this._onTouchedCallback = noop$7;
1287
- this._subscription = new rxjs.Subscription();
1288
- this._filter$ = new rxjs.BehaviorSubject(undefined);
1289
- this._reload$ = new rxjs.Subject();
1290
- this._readonly = false;
1291
- this.$inputItems = new rxjs.BehaviorSubject(undefined);
1292
- this.$filteredItems = new rxjs.BehaviorSubject(undefined);
1293
- this.displayValue = '';
1294
- this._fetchMore$ = new i0.EventEmitter();
1295
1288
  this.logPrefix = '[mat-autocomplete] ';
1296
1289
  this.formControlName = null;
1297
1290
  this.required = false;
@@ -1304,13 +1297,21 @@
1304
1297
  this.fetchMoreThreshold = '15%';
1305
1298
  this.suggestLengthThreshold = null;
1306
1299
  this.showLoadingSpinner = true;
1300
+ this.clicked = new i0.EventEmitter();
1301
+ this.blurred = new i0.EventEmitter();
1302
+ this.focused = new i0.EventEmitter();
1303
+ this.dropButtonClick = new i0.EventEmitter(true);
1304
+ this.keydownEscape = new i0.EventEmitter();
1305
+ this.keyupEnter = new i0.EventEmitter();
1307
1306
  this.animationsDisabled = true;
1308
- this.onClick = new i0.EventEmitter();
1309
- this.onBlur = new i0.EventEmitter();
1310
- this.onFocus = new i0.EventEmitter();
1311
- this.onDropButtonClick = new i0.EventEmitter(true);
1312
- this.onKeydownEscape = new i0.EventEmitter();
1313
- this.onKeyupEnter = new i0.EventEmitter();
1307
+ this._readonly = false;
1308
+ this.$inputItems = new rxjs.BehaviorSubject(undefined);
1309
+ this.$filteredItems = new rxjs.BehaviorSubject(undefined);
1310
+ this.displayValue = '';
1311
+ this._fetchMore$ = new i0.EventEmitter();
1312
+ this._subscription = new rxjs.Subscription();
1313
+ this._$filter = new rxjs.BehaviorSubject(undefined);
1314
+ this._reload$ = new rxjs.Subject();
1314
1315
  }
1315
1316
  Object.defineProperty(MatAutocompleteField.prototype, "itemCount", {
1316
1317
  get: function () {
@@ -1328,13 +1329,13 @@
1328
1329
  });
1329
1330
  Object.defineProperty(MatAutocompleteField.prototype, "filter", {
1330
1331
  get: function () {
1331
- return this._filter$.value;
1332
+ return this._$filter.value;
1332
1333
  },
1333
1334
  set: function (value) {
1334
- if (value !== this._filter$.value) {
1335
+ if (value !== this._$filter.value) {
1335
1336
  // DEBUG
1336
1337
  //console.debug(this.logPrefix + " Setting filter:", value);
1337
- this._filter$.next(value);
1338
+ this._$filter.next(value);
1338
1339
  }
1339
1340
  },
1340
1341
  enumerable: false,
@@ -1427,6 +1428,7 @@
1427
1428
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
1428
1429
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
1429
1430
  this.displayWith = this.displayWith || this.config.displayWith;
1431
+ this.equals = this.equals || this.config.equals;
1430
1432
  this.mobile = toBoolean(this.mobile, this.config.mobile);
1431
1433
  this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
1432
1434
  this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
@@ -1441,21 +1443,18 @@
1441
1443
  this.displayColumnSizes = this.displayColumnSizes
1442
1444
  // if only column: auto size
1443
1445
  || (this.displayAttributes.length === 1 && [undefined])
1444
- || this.displayAttributes.map(function (attr) {
1445
- // If label, set col size = 2
1446
- return (attr && attr.endsWith('label')) ? 2 :
1447
- // If rankOrder => col size = 1
1448
- ((attr && attr.endsWith('rankOrder')) ? 1
1449
- // Else => auto size
1450
- : undefined);
1451
- });
1446
+ // Computed column size:
1447
+ // - if label then col size = 2
1448
+ // - if rankOrder then col size = 1
1449
+ // - ese => auto size
1450
+ || this.displayAttributes.map(function (attr) { return (attr && attr.endsWith('label')) ? 2
1451
+ : ((attr && attr.endsWith('rankOrder')) ? 1
1452
+ : undefined); });
1452
1453
  this.displayColumnNames = this.displayAttributes.map(function (attr, index) { return _this.displayColumnNames && _this.displayColumnNames[index] ||
1453
1454
  (_this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()); });
1454
1455
  this.mobile = toBoolean(this.mobile, false);
1455
1456
  this.searchable = !this.mobile && !this.multiple;
1456
- // Default comparator (only need when using mat-select)
1457
- if (!this.searchable && !this.compareWith)
1458
- this.compareWith = function (o1, o2) { return o1 && o2 && o1.id === o2.id; };
1457
+ this.equals = this.equals || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
1459
1458
  var classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
1460
1459
  this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
1461
1460
  || (classList.includes('min-width-large') && '400px')
@@ -1471,7 +1470,7 @@
1471
1470
  var suggestFromArrayFn_1 = function (value, filter) { return __awaiter(_this, void 0, void 0, function () {
1472
1471
  return __generator(this, function (_e) {
1473
1472
  // DEBUG
1474
- //console.debug(this.logPrefix + " Calling suggestFromArray with value=", value);
1473
+ //console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
1475
1474
  return [2 /*return*/, suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter))];
1476
1475
  });
1477
1476
  }); };
@@ -1498,7 +1497,7 @@
1498
1497
  }
1499
1498
  else if (!this.searchable) {
1500
1499
  // Manually fill input items, from the given suggest function
1501
- this._subscription.add(this._filter$.pipe(operators.startWith(this.filter), operators.debounceTime(250), operators.takeWhile(function (_) { return !_this.searchable; }), // Close subscription, when enabling search (no more mat-select)
1500
+ this._subscription.add(this._$filter.pipe(operators.startWith(this.filter), operators.debounceTime(250), operators.takeWhile(function (_) { return !_this.searchable; }), // Close subscription, when enabling search (no more mat-select)
1502
1501
  operators.switchMap(function (filter) { return _this.suggest('*', filter); }))
1503
1502
  .subscribe(function (items) { return _this.$inputItems.next(items); }));
1504
1503
  }
@@ -1509,11 +1508,7 @@
1509
1508
  this.formControl.valueChanges
1510
1509
  .pipe(operators.filter(function (value) { return _this.enabled && _this.searchable && isNotNilString(value); }),
1511
1510
  // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
1512
- operators.tap(function (value) {
1513
- if (_this.showLoadingSpinner) {
1514
- _this.markAsLoading();
1515
- }
1516
- }),
1511
+ operators.tap(function (_) { return _this.showLoadingSpinner && _this.markAsLoading(); }),
1517
1512
  // Filter if not enough character
1518
1513
  operators.filter(function (value) { return value.length >= _this.suggestLengthThreshold; }),
1519
1514
  // Wait a debounce time
@@ -1542,12 +1537,12 @@
1542
1537
  // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
1543
1538
  rxjs.merge(
1544
1539
  // Focus or click => Load all
1545
- rxjs.merge(this.onFocus, this.onClick)
1540
+ rxjs.merge(this.focused, this.clicked)
1546
1541
  .pipe(operators.filter(function (_) { return _this.enabled && _this.searchable; }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.formControl.value; })),
1547
1542
  // On dropdown button click
1548
- this.onDropButtonClick
1549
- .pipe(operators.filter(function (event) { return _this.enabled && _this.searchable && (!event || !event.defaultPrevented); }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.formControl.value; })), rxjs.merge(this.$inputItems, this._filter$)
1550
- .pipe(operators.filter(function (event) { return _this.enabled && _this.searchable; }), operators.map(function (_) { return _this.formControl.value; })))
1543
+ this.dropButtonClick
1544
+ .pipe(operators.filter(function (event) { return _this.searchable && _this.enabled && (!event || !event.defaultPrevented); }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.formControl.value; })), rxjs.merge(this.$inputItems, this._$filter)
1545
+ .pipe(operators.filter(function (_) { return _this.searchable && _this.enabled; }), operators.map(function (_) { return _this.formControl.value; })))
1551
1546
  .pipe(
1552
1547
  // Start to suggest when :
1553
1548
  // - '*'
@@ -1586,7 +1581,7 @@
1586
1581
  // Make sure control value is inside (mat-select)
1587
1582
  if (items && !_this.searchable) {
1588
1583
  var value_1 = _this.formControl.value;
1589
- if (isNotNil(value_1) && typeof value_1 === 'object' && items.findIndex(function (item) { return _this.compareWith(item, value_1); }) === -1) {
1584
+ if (isNotNil(value_1) && typeof value_1 === 'object' && items.findIndex(function (item) { return _this.equals(item, value_1); }) === -1) {
1590
1585
  // If form value is missing: add it to the list
1591
1586
  items = items.concat(value_1);
1592
1587
  }
@@ -1595,7 +1590,7 @@
1595
1590
  _this.$filteredItems.next(items);
1596
1591
  }));
1597
1592
  // Applying implicit value, on blur
1598
- this._subscription.add(this.onBlur
1593
+ this._subscription.add(this.blurred
1599
1594
  .pipe(
1600
1595
  // Skip if not searchable
1601
1596
  operators.filter(function (_) { return _this.searchable; }), operators.map(function (_) { return _this.formControl.value; }))
@@ -1625,7 +1620,7 @@
1625
1620
  operators.distinctUntilChanged())
1626
1621
  // Update component state
1627
1622
  .subscribe(function () { return _this.checkIfTouched(); }));
1628
- this._subscription.add(this.onKeydownEscape.subscribe(function (event) {
1623
+ this._subscription.add(this.keydownEscape.subscribe(function (event) {
1629
1624
  if (_this.isOpen) {
1630
1625
  event === null || event === void 0 ? void 0 : event.stopPropagation();
1631
1626
  event === null || event === void 0 ? void 0 : event.preventDefault();
@@ -1646,18 +1641,18 @@
1646
1641
  this.$inputItems.complete();
1647
1642
  this.$filteredItems.complete();
1648
1643
  this._reload$.complete();
1649
- this._filter$.complete();
1650
- this.onDropButtonClick.complete();
1651
- this.onClick.complete();
1652
- this.onBlur.complete();
1653
- this.onFocus.complete();
1654
- this.onKeydownEscape.complete();
1655
- this.onKeyupEnter.complete();
1644
+ this._$filter.complete();
1645
+ this.dropButtonClick.complete();
1646
+ this.clicked.complete();
1647
+ this.blurred.complete();
1648
+ this.focused.complete();
1649
+ this.keydownEscape.complete();
1650
+ this.keyupEnter.complete();
1656
1651
  this._fetchMore$.complete();
1657
1652
  this._implicitValue = undefined;
1658
1653
  this.formControl = null;
1659
1654
  this.displayWith = null;
1660
- this.compareWith = null;
1655
+ this.equals = null;
1661
1656
  this.suggestFn = null;
1662
1657
  this.$filteredItems = null;
1663
1658
  this.matSelectItems$ = null;
@@ -1667,8 +1662,8 @@
1667
1662
  this._onTouchedCallback = null;
1668
1663
  this._onFetchMoreCallback = null;
1669
1664
  // Destroy the 'autocomplete' instance, to avoid memory leak
1670
- if (this.matAutocomplete) {
1671
- this.matAutocomplete.ngOnDestroy();
1665
+ if (this.autocomplete) {
1666
+ this.autocomplete.ngOnDestroy();
1672
1667
  }
1673
1668
  };
1674
1669
  /**
@@ -1694,7 +1689,7 @@
1694
1689
  };
1695
1690
  MatAutocompleteField.prototype.setDisabledState = function (isDisabled) {
1696
1691
  var _a;
1697
- if (isDisabled && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1692
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1698
1693
  this.closePanel();
1699
1694
  }
1700
1695
  this.cd.markForCheck();
@@ -1708,7 +1703,7 @@
1708
1703
  (_a = this.matSelect) === null || _a === void 0 ? void 0 : _a.focus();
1709
1704
  }
1710
1705
  };
1711
- MatAutocompleteField.prototype.filterInputTextFocusInEvent = function (event) {
1706
+ MatAutocompleteField.prototype.filterInputTextFocusEvent = function (event) {
1712
1707
  if (!event || event.defaultPrevented)
1713
1708
  return;
1714
1709
  // Ignore event from mat-option
@@ -1717,7 +1712,7 @@
1717
1712
  if (event.stopPropagation)
1718
1713
  event.stopPropagation();
1719
1714
  event.returnValue = false;
1720
- //DEBUG console.debug(this.logPrefix + " Cancelling focus event");
1715
+ //DEBUG console.debug(this.logPrefix + ' Cancelling focus event');
1721
1716
  return false;
1722
1717
  }
1723
1718
  var hasContent = selectInputContent(event);
@@ -1725,7 +1720,7 @@
1725
1720
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
1726
1721
  // DEBUG
1727
1722
  //console.debug(this.logPrefix + ' Emit focus event');
1728
- this.onFocus.emit(event);
1723
+ this.focused.emit(event);
1729
1724
  return true;
1730
1725
  }
1731
1726
  return false;
@@ -1746,7 +1741,7 @@
1746
1741
  //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
1747
1742
  // Move caret to the beginning (fix issue IMAGINE-469)
1748
1743
  selectInputRange(event.target, 0, 0);
1749
- this.onBlur.emit(event);
1744
+ this.blurred.emit(event);
1750
1745
  return true;
1751
1746
  };
1752
1747
  MatAutocompleteField.prototype.filterMatSelectFocusEvent = function (event) {
@@ -1754,7 +1749,7 @@
1754
1749
  return;
1755
1750
  // DEBUG
1756
1751
  // console.debug(this.logPrefix + " Received <mat-select> focus event", event);
1757
- this.onFocus.emit(event);
1752
+ this.focused.emit(event);
1758
1753
  };
1759
1754
  MatAutocompleteField.prototype.filterMatSelectBlurEvent = function (event) {
1760
1755
  if (!event || event.defaultPrevented)
@@ -1771,7 +1766,7 @@
1771
1766
  // console.debug(this.logPrefix + " Cancelling <mat-select> blur event");
1772
1767
  return false;
1773
1768
  }
1774
- this.onBlur.emit(event);
1769
+ this.blurred.emit(event);
1775
1770
  return true;
1776
1771
  };
1777
1772
  MatAutocompleteField.prototype.toggleSearch = function (event) {
@@ -1794,7 +1789,7 @@
1794
1789
  };
1795
1790
  MatAutocompleteField.prototype._initAutocompleteInfiniteScroll = function (threshold) {
1796
1791
  var _this = this;
1797
- if (!this.matAutocomplete)
1792
+ if (!this.autocomplete)
1798
1793
  return; // Skip
1799
1794
  // DEBUG
1800
1795
  //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
@@ -1802,12 +1797,12 @@
1802
1797
  this._openedSubscription.unsubscribe();
1803
1798
  this._subscription.remove(this._openedSubscription);
1804
1799
  }
1805
- var opened$ = this.matAutocomplete._isOpen
1800
+ var opened$ = this.autocomplete._isOpen
1806
1801
  ? rxjs.timer(0)
1807
- : this.matAutocomplete.opened.pipe(operators.debounceTime(200));
1802
+ : this.autocomplete.opened.pipe(operators.debounceTime(200));
1808
1803
  var scrollEnd$ = opened$
1809
1804
  .pipe(operators.map(function (_) { return _this.getAutocompletePanel(); }), operators.filter(isNotNil), operators.switchMap(function (panel) { return fromScrollEndEvent(panel, threshold)
1810
- .pipe(operators.takeUntil(_this.matAutocomplete.closed)); }));
1805
+ .pipe(operators.takeUntil(_this.autocomplete.closed)); }));
1811
1806
  // Trigger fetch more, end end scroll reached
1812
1807
  this._openedSubscription = scrollEnd$
1813
1808
  .subscribe(function () { return _this._fetchMore$.emit(); });
@@ -1840,7 +1835,7 @@
1840
1835
  Object.defineProperty(MatAutocompleteField.prototype, "isOpen", {
1841
1836
  get: function () {
1842
1837
  var _a, _b;
1843
- return (this.searchable && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1838
+ return (this.searchable && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1844
1839
  || ((_b = this.matSelect) === null || _b === void 0 ? void 0 : _b.panelOpen)
1845
1840
  || false;
1846
1841
  },
@@ -1849,8 +1844,8 @@
1849
1844
  });
1850
1845
  MatAutocompleteField.prototype.closePanel = function () {
1851
1846
  var _a, _b, _c, _d;
1852
- if ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1853
- (_b = this.matAutocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1847
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1848
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1854
1849
  return;
1855
1850
  }
1856
1851
  if ((_c = this.matSelect) === null || _c === void 0 ? void 0 : _c.panelOpen) {
@@ -1931,7 +1926,7 @@
1931
1926
  };
1932
1927
  MatAutocompleteField.prototype.getAutocompletePanel = function () {
1933
1928
  var _a, _b;
1934
- var ele = (_b = (_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1929
+ var ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1935
1930
  if (!ele) {
1936
1931
  // DEBUG
1937
1932
  console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
@@ -1966,8 +1961,12 @@
1966
1961
  { type: i0.Component, args: [{
1967
1962
  // eslint-disable-next-line @angular-eslint/component-selector
1968
1963
  selector: 'mat-autocomplete-field',
1969
- 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",
1970
- providers: [DEFAULT_VALUE_ACCESSOR$6],
1964
+ 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",
1965
+ providers: [{
1966
+ provide: i1.NG_VALUE_ACCESSOR,
1967
+ multi: true,
1968
+ useExisting: i0.forwardRef(function () { return MatAutocompleteField; })
1969
+ }],
1971
1970
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
1972
1971
  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}"]
1973
1972
  },] }
@@ -1977,7 +1976,7 @@
1977
1976
  { type: i1.FormGroupDirective, decorators: [{ type: i0.Optional }] }
1978
1977
  ]; };
1979
1978
  MatAutocompleteField.propDecorators = {
1980
- compareWith: [{ type: i0.Input }],
1979
+ equals: [{ type: i0.Input }],
1981
1980
  logPrefix: [{ type: i0.Input }],
1982
1981
  formControl: [{ type: i0.Input }],
1983
1982
  formControlName: [{ type: i0.Input }],
@@ -2007,21 +2006,21 @@
2007
2006
  fetchMoreThreshold: [{ type: i0.Input }],
2008
2007
  suggestLengthThreshold: [{ type: i0.Input }],
2009
2008
  showLoadingSpinner: [{ type: i0.Input }],
2009
+ clicked: [{ type: i0.Output, args: ['click',] }],
2010
+ blurred: [{ type: i0.Output, args: ['blur',] }],
2011
+ focused: [{ type: i0.Output, args: ['focus',] }],
2012
+ dropButtonClick: [{ type: i0.Output }],
2013
+ keydownEscape: [{ type: i0.Output, args: ['keydown.escape',] }],
2014
+ keyupEnter: [{ type: i0.Output, args: ['keyup.enter',] }],
2015
+ matSelect: [{ type: i0.ViewChild, args: ['matSelect',] }],
2016
+ matInputText: [{ type: i0.ViewChild, args: ['matInputText',] }],
2017
+ autocomplete: [{ type: i0.ViewChild, args: [autocomplete.MatAutocomplete,] }],
2018
+ autocompleteTrigger: [{ type: i0.ViewChild, args: [autocomplete.MatAutocompleteTrigger,] }],
2010
2019
  animationsDisabled: [{ type: i0.HostBinding, args: ['@.disabled',] }],
2011
2020
  filter: [{ type: i0.Input }],
2012
2021
  readonly: [{ type: i0.Input }],
2013
2022
  tabindex: [{ type: i0.Input }],
2014
- items: [{ type: i0.Input }],
2015
- onClick: [{ type: i0.Output, args: ['click',] }],
2016
- onBlur: [{ type: i0.Output, args: ['blur',] }],
2017
- onFocus: [{ type: i0.Output, args: ['focus',] }],
2018
- onDropButtonClick: [{ type: i0.Output, args: ['dropButtonClick',] }],
2019
- onKeydownEscape: [{ type: i0.Output, args: ['keydown.escape',] }],
2020
- onKeyupEnter: [{ type: i0.Output, args: ['keyup.enter',] }],
2021
- matSelect: [{ type: i0.ViewChild, args: ['matSelect',] }],
2022
- matInputText: [{ type: i0.ViewChild, args: ['matInputText',] }],
2023
- matAutocomplete: [{ type: i0.ViewChild, args: [autocomplete.MatAutocomplete,] }],
2024
- matAutocompleteTrigger: [{ type: i0.ViewChild, args: [autocomplete.MatAutocompleteTrigger,] }]
2023
+ items: [{ type: i0.Input }]
2025
2024
  };
2026
2025
 
2027
2026
  var DateFormatPipe = /** @class */ (function () {
@@ -7340,7 +7339,7 @@
7340
7339
  // Default values
7341
7340
  this.displayAttributes = this.displayAttributes || ['label', 'name'];
7342
7341
  this.displayWith = this.displayWith || (function (obj) { return obj && joinPropertiesPath(obj, _this.displayAttributes); });
7343
- this.compareWithFn = this.compareWithFn || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
7342
+ this.equals = this.equals || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
7344
7343
  this.mobile = toBoolean(this.mobile, false);
7345
7344
  };
7346
7345
  MatSwipeField.prototype.ngOnDestroy = function () {
@@ -7402,7 +7401,7 @@
7402
7401
  if (this._writing)
7403
7402
  return;
7404
7403
  this._writing = true;
7405
- if (!this.compareWithFn(value, this._value)) {
7404
+ if (!this.equals(value, this._value)) {
7406
7405
  this._value = value;
7407
7406
  this.showSlides = isNotNil(this._value);
7408
7407
  this.updateSlides();
@@ -7491,7 +7490,7 @@
7491
7490
  MatSwipeField.prototype.updateSlides = function () {
7492
7491
  var _this = this;
7493
7492
  if (this.showSlides) {
7494
- var itemIndex = (this.$items.getValue() || []).findIndex(function (value) { return _this.compareWithFn(value, _this._value); });
7493
+ var itemIndex = (this.$items.getValue() || []).findIndex(function (value) { return _this.equals(value, _this._value); });
7495
7494
  if (itemIndex >= 0) {
7496
7495
  this.slideTo(itemIndex);
7497
7496
  }
@@ -7578,7 +7577,7 @@
7578
7577
  required: [{ type: i0.Input }],
7579
7578
  mobile: [{ type: i0.Input }],
7580
7579
  clearable: [{ type: i0.Input }],
7581
- compareWithFn: [{ type: i0.Input }],
7580
+ equals: [{ type: i0.Input }],
7582
7581
  displayWith: [{ type: i0.Input }],
7583
7582
  displayAttributes: [{ type: i0.Input }],
7584
7583
  appAutofocus: [{ type: i0.Input }],
@@ -7630,20 +7629,27 @@
7630
7629
  this.required = false;
7631
7630
  this.readonly = false;
7632
7631
  this.clearable = false;
7633
- this.debounceTime = 250;
7634
- this.highlightAccent = false;
7632
+ this.debounceTime = null;
7635
7633
  this.i18nPrefix = 'REFERENTIAL.';
7636
7634
  this.noResultMessage = 'COMMON.NO_RESULT';
7637
7635
  this.debug = false;
7636
+ this.itemSize = '48px';
7637
+ this.fetchMoreThreshold = '15%';
7638
+ this.suggestLengthThreshold = null;
7639
+ this.showLoadingSpinner = true;
7638
7640
  this.clicked = new i0.EventEmitter();
7639
7641
  this.blurred = new i0.EventEmitter();
7640
7642
  this.focused = new i0.EventEmitter();
7643
+ this.dropButtonClick = new i0.EventEmitter(true);
7644
+ this.keydownEscape = new i0.EventEmitter();
7645
+ this.keyupEnter = new i0.EventEmitter();
7641
7646
  this.$inputItems = new rxjs.BehaviorSubject(undefined);
7642
- this.onDropButtonClick = new i0.EventEmitter(true);
7647
+ this.$filteredItems = new rxjs.BehaviorSubject(undefined);
7643
7648
  this.inputControl = new i1.FormControl();
7644
- this.getPropertyByPath = getPropertyByPath;
7649
+ this._fetchMore$ = new i0.EventEmitter();
7645
7650
  this._subscription = new rxjs.Subscription();
7646
7651
  this._$filter = new rxjs.BehaviorSubject(undefined);
7652
+ this._reload$ = new rxjs.Subject();
7647
7653
  }
7648
7654
  Object.defineProperty(MatChipsField.prototype, "itemCount", {
7649
7655
  get: function () {
@@ -7652,14 +7658,21 @@
7652
7658
  enumerable: false,
7653
7659
  configurable: true
7654
7660
  });
7661
+ Object.defineProperty(MatChipsField.prototype, "canFetchMore", {
7662
+ get: function () {
7663
+ return this._onFetchMoreCallback && this._moreItemsCount > 0;
7664
+ },
7665
+ enumerable: false,
7666
+ configurable: true
7667
+ });
7655
7668
  Object.defineProperty(MatChipsField.prototype, "filter", {
7656
7669
  get: function () {
7657
- return this._$filter.getValue();
7670
+ return this._$filter.value;
7658
7671
  },
7659
7672
  set: function (value) {
7660
- if (this.debug)
7661
- console.debug(this.logPrefix + ' Setting filter:', value);
7662
- if (value !== this._$filter.getValue()) {
7673
+ if (value !== this._$filter.value) {
7674
+ // DEBUG
7675
+ //console.debug(this.logPrefix + " Setting filter:", value);
7663
7676
  this._$filter.next(value);
7664
7677
  }
7665
7678
  },
@@ -7678,19 +7691,25 @@
7678
7691
  configurable: true
7679
7692
  });
7680
7693
  Object.defineProperty(MatChipsField.prototype, "items", {
7694
+ get: function () {
7695
+ return this.$inputItems;
7696
+ },
7681
7697
  set: function (value) {
7682
7698
  var _this = this;
7683
7699
  // Remove previous subscription on items, (if exits)
7684
7700
  if (this._itemsSubscription) {
7685
- console.warn('Items received twice !');
7701
+ console.warn(this.logPrefix + ' Items received twice !');
7686
7702
  this._subscription.remove(this._itemsSubscription);
7687
7703
  this._itemsSubscription.unsubscribe();
7688
7704
  }
7689
7705
  if (rxjs.isObservable(value)) {
7690
- this._itemsSubscription = this._subscription.add(value.subscribe(function (v) { return _this.$inputItems.next(v); }));
7706
+ this._itemsSubscription = this._subscription.add(value.subscribe(function (v) {
7707
+ //console.warn(this.logPrefix + " Received items: ", v);
7708
+ _this.$inputItems.next(v);
7709
+ }));
7691
7710
  }
7692
7711
  else {
7693
- if (value !== this.$inputItems.getValue()) {
7712
+ if (value !== this.$inputItems.value) {
7694
7713
  this.$inputItems.next(value);
7695
7714
  }
7696
7715
  }
@@ -7721,6 +7740,7 @@
7721
7740
  });
7722
7741
  MatChipsField.prototype.ngOnInit = function () {
7723
7742
  var _this = this;
7743
+ var _a, _b;
7724
7744
  this.formControl = this.formControl || this.formControlName && this.formGroupDir && this.formGroupDir.form.get(this.formControlName);
7725
7745
  if (!this.formControl)
7726
7746
  throw new Error('Missing mandatory attribute \'formControl\' or \'formControlName\' in <mat-chips-field>.');
@@ -7735,101 +7755,211 @@
7735
7755
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
7736
7756
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
7737
7757
  this.displayWith = this.displayWith || this.config.displayWith;
7738
- this.compareWith = this.compareWith || this.config.compareWith;
7758
+ this.equals = this.equals || this.config.equals;
7739
7759
  this.mobile = toBoolean(this.mobile, this.config.mobile);
7740
- this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, true));
7760
+ this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
7761
+ this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
7741
7762
  this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus, true));
7742
- this.class = this.class || this.config.class;
7763
+ this.classList = this.classList || this.config.class;
7764
+ this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
7765
+ this.highlightAccent = toBoolean(this.highlightAccent, toBoolean(this.config.highlightAccent));
7743
7766
  }
7744
7767
  // Default values
7745
- this.displayAttributes = this.displayAttributes || (this.filter && this.filter.attributes) || ['label', 'name'];
7768
+ this.displayAttributes = this.displayAttributes || ((_a = this.filter) === null || _a === void 0 ? void 0 : _a.attributes) || ['label', 'name'];
7746
7769
  this.displayWith = this.displayWith || (function (obj) { return obj && joinPropertiesPath(obj, _this.displayAttributes); });
7747
- this.compareWith = this.compareWith || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
7748
- this.displayColumnSizes = this.displayColumnSizes || this.displayAttributes.map(function (attr) { return (
7749
- // If label then col size = 2
7750
- attr && attr.endsWith('label')) ? 2 :
7751
- // If rankOrder then col size = 1
7752
- (attr && attr.endsWith('rankOrder') ? 1 :
7753
- // Else, auto size
7754
- undefined); });
7770
+ this.displayColumnSizes = this.displayColumnSizes
7771
+ // if only column: auto size
7772
+ || (this.displayAttributes.length === 1 && [undefined])
7773
+ // Computed column size:
7774
+ // - if label then col size = 2
7775
+ // - if rankOrder then col size = 1
7776
+ // - ese => auto size
7777
+ || this.displayAttributes.map(function (attr) { return (attr && attr.endsWith('label')) ? 2
7778
+ : ((attr && attr.endsWith('rankOrder')) ? 1
7779
+ : undefined); });
7755
7780
  this.displayColumnNames = this.displayAttributes.map(function (attr, index) { return _this.displayColumnNames && _this.displayColumnNames[index] ||
7756
7781
  (_this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()); });
7757
7782
  this.mobile = toBoolean(this.mobile, false);
7783
+ this.equals = this.equals || (function (o1, o2) { return o1 && o2 && o1.id === o2.id; });
7784
+ var classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
7785
+ this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
7786
+ || (classList.includes('min-width-large') && '400px')
7787
+ || (classList.includes('min-width-xlarge') && '450px')
7788
+ || (classList.includes('mat-autocomplete-panel-full-size') && '100vw')));
7789
+ this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, true);
7790
+ this.debounceTime = toNumber(this.debounceTime, 250);
7791
+ this.highlightAccent = toBoolean(this.highlightAccent, false);
7792
+ this.suggestLengthThreshold = this.suggestLengthThreshold || 0;
7793
+ this.matAutocompletePosition = this.matAutocompletePosition || (this.mobile ? 'above' : 'auto'); // On mobile, set position to above (because of bottom keyboard)
7758
7794
  // No suggestFn: filter on the given items
7759
7795
  if (!this.suggestFn) {
7760
7796
  var suggestFromArrayFn_1 = function (value, filter) { return __awaiter(_this, void 0, void 0, function () {
7761
- var _a, data, total;
7762
- return __generator(this, function (_b) {
7763
- switch (_b.label) {
7764
- case 0:
7765
- if (this.debug)
7766
- console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
7767
- return [4 /*yield*/, suggestFromArray(this.$inputItems.getValue(), value, Object.assign({ searchAttributes: this.displayAttributes }, filter))];
7768
- case 1:
7769
- _a = _b.sent(), data = _a.data, total = _a.total;
7770
- this._itemCount = toNumber(total, data && data.length || 0);
7771
- return [2 /*return*/, { data: data, total: total }];
7772
- }
7797
+ return __generator(this, function (_c) {
7798
+ if (this.debug)
7799
+ console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
7800
+ return [2 /*return*/, suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter))];
7773
7801
  });
7774
7802
  }); };
7775
7803
  // Wait (once) that items are loaded, then call suggest from array fn
7776
7804
  this.suggestFn = function (value, filter) { return __awaiter(_this, void 0, void 0, function () {
7777
- return __generator(this, function (_a) {
7778
- switch (_a.label) {
7805
+ return __generator(this, function (_c) {
7806
+ switch (_c.label) {
7779
7807
  case 0:
7780
- if (!isNil(this.$inputItems.getValue())) return [3 /*break*/, 2];
7808
+ if (!isNil(this.$inputItems.value)) return [3 /*break*/, 2];
7781
7809
  if (this.debug)
7782
7810
  console.debug('[mat-chips] Waiting items to be set...');
7783
7811
  return [4 /*yield*/, firstNotNilPromise(this.$inputItems)];
7784
7812
  case 1:
7785
- _a.sent();
7813
+ _c.sent();
7786
7814
  if (this.debug)
7787
- console.debug('[mat-chips] Received items:', this.$inputItems.getValue());
7788
- _a.label = 2;
7815
+ console.debug('[mat-chips] Received items:', this.$inputItems.value);
7816
+ _c.label = 2;
7789
7817
  case 2:
7790
7818
  this.suggestFn = suggestFromArrayFn_1;
7791
- return [2 /*return*/, this.suggest(value, filter)]; // Loop
7819
+ return [2 /*return*/, this.suggestFn(value, filter)]; // Loop
7792
7820
  }
7793
7821
  });
7794
7822
  }); };
7795
7823
  }
7796
- // this.matSelectItems$ = this.$inputItems
7797
- // .pipe(
7798
- // filter(isNotNil),
7799
- // map((items) => {
7800
- // // Make sure control value is inside
7801
- // const value = this.inputControl.value;
7802
- // // If form value is missing: add it to the list
7803
- // if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
7804
- // // console.debug(this.logPrefix + " Add form value to items, because missed in items", value);
7805
- // return items.concat(value);
7806
- // }
7807
- // return items;
7808
- // })
7809
- // );
7810
- this._subscription.add(this.onDropButtonClick
7811
- .pipe(operators.filter(function (event) { return (!event || !event.defaultPrevented) && _this.formControl.enabled; }))
7812
- .subscribe(function () {
7813
- _this.matInputText.nativeElement.focus();
7814
- }));
7815
- var updateFilteredItemsEvents$ = rxjs.merge(
7824
+ var filteredItemsChanges = rxjs.merge(
7825
+ // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
7826
+ this._reload$, this.inputControl.valueChanges
7827
+ .pipe(operators.filter(function (value) { return _this.enabled && isNotNilString(value); }),
7828
+ // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
7829
+ operators.tap(function (_) { return _this.showLoadingSpinner && _this.markAsLoading(); }),
7830
+ // Filter if not enough character
7831
+ operators.filter(function (value) { return value.length >= _this.suggestLengthThreshold; }),
7832
+ // Wait a debounce time
7833
+ operators.debounceTime(this.debounceTime)
7834
+ // DEBUG
7835
+ //tap((value) => console.debug(this.logPrefix + ' Search text changes:', value))
7836
+ ),
7837
+ // Object set: use it (no distinctUntilChanged() pipe need)
7838
+ this.inputControl.valueChanges
7839
+ .pipe(operators.startWith(this.inputControl.value), operators.filter(function (value) { return isNotNil(value) && typeof value === 'object'; }), operators.tap(function (value) {
7840
+ // DEBUG
7841
+ //console.debug(this.logPrefix + ' object changes:', value)
7842
+ // Update the display value (if need)
7843
+ var displayValue = _this.displayWith(value) || '';
7844
+ // Apply to the input[type=text] field
7845
+ if (_this.matInputText && _this.matInputText.nativeElement) {
7846
+ _this.matInputText.nativeElement.value = displayValue;
7847
+ }
7848
+ })
7849
+ // DEBUG
7850
+ //tap(value => console.debug(this.logPrefix + ' Received an object value: ', value))
7851
+ ),
7852
+ // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
7853
+ rxjs.merge(
7816
7854
  // Focus or click => Load all
7817
7855
  rxjs.merge(this.focused, this.clicked)
7818
- .pipe(operators.filter(function (_) { return _this.enabled; }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.inputControl.value; })), this.inputControl.valueChanges
7819
- .pipe(operators.startWith(this.inputControl.value), operators.filter(function (value) { return (isNotNil(value) && typeof value === 'object') || (isNotNilString(value) && !value.includes('**')); }), operators.tap(function (value) {
7820
- if (_this.debug)
7821
- console.debug(_this.logPrefix + ' valueChanges:', value);
7822
- }), operators.debounceTime(this.debounceTime)), this.$inputItems
7823
- .pipe(operators.map(function () { return _this.formControl.value; })), this._$filter
7824
- .pipe(operators.map(function () { return _this.formControl.value; })));
7825
- this.filteredItems$ = updateFilteredItemsEvents$
7826
- .pipe(operators.distinctUntilChanged(),
7827
- //tap(value => this.debug && console.debug(this.logPrefix + " Received update event: ", value)),
7828
- operators.switchMap(function (value) { return _this.suggest(value, _this.filter); }));
7856
+ .pipe(operators.filter(function (_) { return _this.enabled; }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.inputControl.value; })),
7857
+ // On dropdown button click
7858
+ this.dropButtonClick
7859
+ .pipe(operators.filter(function (event) { return _this.enabled && (!event || !event.defaultPrevented); }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.inputControl.value; })), rxjs.merge(this.$inputItems, this._$filter)
7860
+ .pipe(operators.filter(function (_) { return _this.enabled; }), operators.map(function (_) { return _this.inputControl.value; })))
7861
+ .pipe(
7862
+ // Start to suggest when :
7863
+ // - '*'
7864
+ // - is nil, and there is no min length threshold
7865
+ // - is not nil and not a string type (e.g. object)
7866
+ // - is not nil and is a string, and has enough character
7867
+ operators.filter(function (value) { return value === '*'
7868
+ || (isNil(value)
7869
+ ? (_this.suggestLengthThreshold <= 0)
7870
+ : (!(typeof value === 'string')
7871
+ || value.length >= _this.suggestLengthThreshold)); }),
7872
+ // Distinguish changes
7873
+ operators.distinctUntilChanged()))
7874
+ .pipe(
7875
+ // Suggest values
7876
+ operators.mergeMap(function (value) { return _this.suggest(value, _this.filter); }),
7877
+ // DEBUG
7878
+ //tap(items => console.debug(this.logPrefix + ' Received from suggest: ', items)),
7879
+ // Store implicit value (will use it onBlur if not other value selected)
7880
+ operators.tap(function (items) { return _this.updateImplicitValue(items); }));
7881
+ // Fetch more events
7882
+ var fetchMoreItemsChanges = this._fetchMore$
7883
+ .pipe(operators.tap(function (event) { return event === null || event === void 0 ? void 0 : event.preventDefault(); }), // Avoid to close the mat-select
7884
+ operators.filter(function () { return _this.canFetchMore; }), operators.mergeMap(function () { return _this.fetchMore(); }),
7885
+ // DEBUG
7886
+ //tap(moreItems => console.debug(this.logPrefix + ' Received from fetch More: ', moreItems)),
7887
+ // Concat to existing items
7888
+ operators.map(function (moreItems) { return (_this.$filteredItems.value || []).concat(moreItems); }));
7889
+ // Update filtered items
7890
+ this._subscription.add(rxjs.merge(this.$inputItems,
7891
+ // Update items events
7892
+ filteredItemsChanges,
7893
+ // Fetch more events
7894
+ fetchMoreItemsChanges)
7895
+ // Emit items
7896
+ .subscribe(function (items) { return _this.$filteredItems.next(items); }));
7897
+ // Applying implicit value, on blur
7898
+ this._subscription.add(this.blurred
7899
+ .pipe(operators.map(function (_) { return _this.formControl.value; }))
7900
+ .subscribe(function (value) {
7901
+ // When leave component without object, use implicit value if :
7902
+ // - an explicit value
7903
+ // - field is not empty (user fill something)
7904
+ // - OR field empty but is required
7905
+ if (_this._implicitValue && ((_this.required && isNilOrBlank(value)) || (isNotNilOrBlank(value) && typeof value !== 'object'))) {
7906
+ _this.writeValue(_this._implicitValue);
7907
+ _this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7908
+ _this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7909
+ _this.checkIfTouched();
7910
+ }
7911
+ else if (isNilOrBlank(value)) {
7912
+ _this.writeValue(null);
7913
+ _this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7914
+ _this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7915
+ _this.checkIfTouched();
7916
+ }
7917
+ _this._implicitValue = null; // reset the implicit value
7918
+ }));
7919
+ this._subscription.add(this.keydownEscape.subscribe(function (event) {
7920
+ if (_this.isOpen) {
7921
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
7922
+ event === null || event === void 0 ? void 0 : event.preventDefault();
7923
+ // DEBUG
7924
+ //console.debug(this.logPrefix + " Closing the panel (keydown.escape)");
7925
+ _this.closePanel();
7926
+ }
7927
+ }));
7829
7928
  };
7830
7929
  MatChipsField.prototype.ngOnDestroy = function () {
7831
7930
  this._subscription.unsubscribe();
7931
+ if (this._itemsSubscription) {
7932
+ this._itemsSubscription.unsubscribe();
7933
+ }
7934
+ if (this._openedSubscription) {
7935
+ this._openedSubscription.unsubscribe();
7936
+ }
7832
7937
  this.$inputItems.complete();
7938
+ this.$filteredItems.complete();
7939
+ this._reload$.complete();
7940
+ this._$filter.complete();
7941
+ this.dropButtonClick.complete();
7942
+ this.clicked.complete();
7943
+ this.blurred.complete();
7944
+ this.focused.complete();
7945
+ this.keydownEscape.complete();
7946
+ this.keyupEnter.complete();
7947
+ this._fetchMore$.complete();
7948
+ this._implicitValue = undefined;
7949
+ this.formControl = null;
7950
+ this.displayWith = null;
7951
+ this.equals = null;
7952
+ this.suggestFn = null;
7953
+ this.$filteredItems = null;
7954
+ this.config = null;
7955
+ this.classList = null;
7956
+ this._onChangeCallback = null;
7957
+ this._onTouchedCallback = null;
7958
+ this._onFetchMoreCallback = null;
7959
+ // Destroy the 'autocomplete' instance, to avoid memory leak
7960
+ if (this.autocomplete) {
7961
+ this.autocomplete.ngOnDestroy();
7962
+ }
7833
7963
  };
7834
7964
  /**
7835
7965
  * Add item to chips
@@ -7850,19 +7980,21 @@
7850
7980
  * @param item
7851
7981
  */
7852
7982
  MatChipsField.prototype.remove = function (item) {
7853
- var index = this.value && this.value.indexOf(item);
7854
- if (index >= 0) {
7855
- var value = this.value.slice();
7856
- value.splice(index, 1);
7857
- this.writeValue(value);
7858
- this.inputControl.setValue('');
7983
+ var value = this.value;
7984
+ var index = value ? value.indexOf(item) : -1;
7985
+ if (index !== -1) {
7986
+ var newValue = value.slice();
7987
+ newValue.splice(index, 1);
7988
+ this.writeValue(newValue);
7989
+ this.closePanel();
7859
7990
  }
7860
7991
  };
7861
7992
  /**
7862
7993
  * Allow to reload content. Useful when filter has been changed but not detected
7863
7994
  */
7864
- MatChipsField.prototype.reloadItems = function () {
7865
- this.onDropButtonClick.emit();
7995
+ MatChipsField.prototype.reloadItems = function (value) {
7996
+ // Force a refresh
7997
+ this._reload$.next(value || '*');
7866
7998
  };
7867
7999
  MatChipsField.prototype.writeValue = function (value) {
7868
8000
  if (this.debug)
@@ -7879,6 +8011,10 @@
7879
8011
  this._onTouchedCallback = fn;
7880
8012
  };
7881
8013
  MatChipsField.prototype.setDisabledState = function (isDisabled) {
8014
+ var _a;
8015
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
8016
+ this.closePanel();
8017
+ }
7882
8018
  this.cd.markForCheck();
7883
8019
  };
7884
8020
  MatChipsField.prototype.focus = function () {
@@ -7898,6 +8034,7 @@
7898
8034
  return false;
7899
8035
  }
7900
8036
  var hasContent = selectInputContent(event);
8037
+ // If combo is empty, or if has content but should force to show panel on focus
7901
8038
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
7902
8039
  if (this.debug)
7903
8040
  console.debug(this.logPrefix + ' Emit focus event');
@@ -7906,20 +8043,82 @@
7906
8043
  }
7907
8044
  return false;
7908
8045
  };
8046
+ MatChipsField.prototype.filterInputTextBlurEvent = function (event) {
8047
+ if (!event || event.defaultPrevented)
8048
+ return;
8049
+ // Ignore event from mat-option
8050
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.tagName === 'MAT-OPTION') {
8051
+ event.preventDefault();
8052
+ if (event.stopPropagation)
8053
+ event.stopPropagation();
8054
+ event.returnValue = false;
8055
+ //DEBUG console.debug(this.logPrefix + " Cancelling blur event");
8056
+ return false;
8057
+ }
8058
+ //DEBUG
8059
+ //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
8060
+ // Move caret to the beginning (fix issue IMAGINE-469)
8061
+ selectInputRange(event.target, 0, 0);
8062
+ this.blurred.emit(event);
8063
+ return true;
8064
+ };
8065
+ MatChipsField.prototype.filterChipsFocusEvent = function (event) {
8066
+ // DEBUG
8067
+ if (this.debug)
8068
+ console.debug(this.logPrefix + ' Focus in chips list', event);
8069
+ this.matInputText.nativeElement.focus();
8070
+ };
7909
8071
  MatChipsField.prototype.clearValue = function (event) {
7910
8072
  this.writeValue(null);
7911
8073
  event.stopPropagation();
7912
8074
  };
8075
+ MatChipsField.prototype._initAutocompleteInfiniteScroll = function (threshold) {
8076
+ var _this = this;
8077
+ if (!this.autocomplete)
8078
+ return; // Skip
8079
+ // DEBUG
8080
+ //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
8081
+ if (this._openedSubscription) {
8082
+ this._openedSubscription.unsubscribe();
8083
+ this._subscription.remove(this._openedSubscription);
8084
+ }
8085
+ var opened$ = this.autocomplete._isOpen
8086
+ ? rxjs.timer(0)
8087
+ : this.autocomplete.opened.pipe(operators.debounceTime(200));
8088
+ var scrollEnd$ = opened$
8089
+ .pipe(operators.map(function (_) { return _this.getAutocompletePanel(); }), operators.filter(isNotNil), operators.switchMap(function (panel) { return fromScrollEndEvent(panel, threshold)
8090
+ .pipe(operators.takeUntil(_this.autocomplete.closed)); }));
8091
+ // Trigger fetch more, end end scroll reached
8092
+ this._openedSubscription = scrollEnd$
8093
+ .subscribe(function () { return _this._fetchMore$.emit(); });
8094
+ // Register subscription
8095
+ this._subscription.add(this._openedSubscription);
8096
+ };
8097
+ Object.defineProperty(MatChipsField.prototype, "isOpen", {
8098
+ get: function () {
8099
+ var _a;
8100
+ return ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
8101
+ },
8102
+ enumerable: false,
8103
+ configurable: true
8104
+ });
8105
+ MatChipsField.prototype.closePanel = function () {
8106
+ var _a, _b;
8107
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
8108
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
8109
+ return;
8110
+ }
8111
+ };
7913
8112
  /* -- private method -- */
7914
8113
  MatChipsField.prototype.suggest = function (value, filter) {
7915
8114
  return __awaiter(this, void 0, void 0, function () {
7916
8115
  var res, data, total, filteredData;
7917
8116
  var _this = this;
7918
- return __generator(this, function (_a) {
7919
- switch (_a.label) {
8117
+ return __generator(this, function (_c) {
8118
+ switch (_c.label) {
7920
8119
  case 0: return [4 /*yield*/, this.suggestFn(value, filter)];
7921
8120
  case 1:
7922
- res = _a.sent();
8121
+ res = _c.sent();
7923
8122
  // DEBUG
7924
8123
  // console.debug(this.logPrefix + " Filtered items by suggestFn:", value, res);
7925
8124
  if (!res) {
@@ -7935,7 +8134,7 @@
7935
8134
  res = data;
7936
8135
  this._itemCount = toNumber(total, data && data.length || 0);
7937
8136
  }
7938
- filteredData = (res || []).slice().filter(function (item1) { return _this.value.findIndex(function (item2) { return _this.compareWith(item1, item2); }) === -1; });
8137
+ filteredData = (res || []).slice().filter(function (item1) { return _this.value.findIndex(function (item2) { return _this.equals(item1, item2); }) === -1; });
7939
8138
  // Update items count
7940
8139
  this._itemCount = this._itemCount - (((res === null || res === void 0 ? void 0 : res.length) || 0) - ((filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) || 0));
7941
8140
  if (this.debug)
@@ -7945,16 +8144,72 @@
7945
8144
  });
7946
8145
  });
7947
8146
  };
8147
+ MatChipsField.prototype.fetchMore = function () {
8148
+ return __awaiter(this, void 0, void 0, function () {
8149
+ var fetchMoreFn, _c, data, total, fetchMore;
8150
+ return __generator(this, function (_d) {
8151
+ switch (_d.label) {
8152
+ case 0:
8153
+ if (!this._onFetchMoreCallback)
8154
+ return [2 /*return*/, []];
8155
+ fetchMoreFn = this._onFetchMoreCallback;
8156
+ this._onFetchMoreCallback = undefined;
8157
+ return [4 /*yield*/, fetchMoreFn()];
8158
+ case 1:
8159
+ _c = _d.sent(), data = _c.data, total = _c.total, fetchMore = _c.fetchMore;
8160
+ this._itemCount = total || this._itemCount;
8161
+ this._onFetchMoreCallback = fetchMore;
8162
+ this._moreItemsCount = Math.max(0, this._moreItemsCount - data.length); // Borne min = 0
8163
+ return [2 /*return*/, data];
8164
+ }
8165
+ });
8166
+ });
8167
+ };
8168
+ MatChipsField.prototype.updateImplicitValue = function (items) {
8169
+ // Store implicit value (will use it onBlur if not other value selected)
8170
+ if (items && items.length === 1) {
8171
+ this._implicitValue = items[0];
8172
+ //this.formControl.setErrors(null);
8173
+ }
8174
+ else {
8175
+ this._implicitValue = undefined;
8176
+ }
8177
+ };
8178
+ MatChipsField.prototype.getAutocompletePanel = function () {
8179
+ var _a, _b;
8180
+ var ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
8181
+ if (!ele) {
8182
+ // DEBUG
8183
+ console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
8184
+ }
8185
+ return ele;
8186
+ };
8187
+ MatChipsField.prototype.checkIfTouched = function () {
8188
+ if (this.formControl.touched) {
8189
+ this.cd.markForCheck();
8190
+ this._onTouchedCallback();
8191
+ }
8192
+ };
7948
8193
  MatChipsField.prototype.markForCheck = function () {
7949
8194
  this.cd.markForCheck();
7950
8195
  };
8196
+ MatChipsField.prototype.markAsLoading = function () {
8197
+ if (this.$filteredItems.value) {
8198
+ this.$filteredItems.next(undefined);
8199
+ this._itemCount = undefined;
8200
+ this._implicitValue = undefined;
8201
+ this._onFetchMoreCallback = undefined;
8202
+ this._moreItemsCount = 0;
8203
+ this.markForCheck();
8204
+ }
8205
+ };
7951
8206
  return MatChipsField;
7952
8207
  }());
7953
8208
  MatChipsField.decorators = [
7954
8209
  { type: i0.Component, args: [{
7955
8210
  // eslint-disable-next-line @angular-eslint/component-selector
7956
8211
  selector: 'mat-chips-field',
7957
- 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",
8212
+ 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",
7958
8213
  providers: [
7959
8214
  {
7960
8215
  provide: i1.NG_VALUE_ACCESSOR,
@@ -7971,7 +8226,7 @@
7971
8226
  { type: i1.FormGroupDirective, decorators: [{ type: i0.Optional }] }
7972
8227
  ]; };
7973
8228
  MatChipsField.propDecorators = {
7974
- compareWith: [{ type: i0.Input }],
8229
+ equals: [{ type: i0.Input }],
7975
8230
  logPrefix: [{ type: i0.Input }],
7976
8231
  formControl: [{ type: i0.Input }],
7977
8232
  formControlName: [{ type: i0.Input }],
@@ -7990,16 +8245,26 @@
7990
8245
  highlightAccent: [{ type: i0.Input }],
7991
8246
  showAllOnFocus: [{ type: i0.Input }],
7992
8247
  showPanelOnFocus: [{ type: i0.Input }],
7993
- appAutofocus: [{ type: i0.Input }],
8248
+ autofocus: [{ type: i0.Input }],
7994
8249
  config: [{ type: i0.Input }],
7995
8250
  i18nPrefix: [{ type: i0.Input }],
7996
8251
  noResultMessage: [{ type: i0.Input }],
7997
- class: [{ type: i0.Input }],
8252
+ classList: [{ type: i0.Input, args: ['class',] }],
8253
+ panelWidth: [{ type: i0.Input }],
8254
+ matAutocompletePosition: [{ type: i0.Input }],
7998
8255
  debug: [{ type: i0.Input }],
8256
+ itemSize: [{ type: i0.Input }],
8257
+ fetchMoreThreshold: [{ type: i0.Input }],
8258
+ suggestLengthThreshold: [{ type: i0.Input }],
8259
+ showLoadingSpinner: [{ type: i0.Input }],
7999
8260
  clicked: [{ type: i0.Output }],
8000
8261
  blurred: [{ type: i0.Output }],
8001
8262
  focused: [{ type: i0.Output }],
8263
+ dropButtonClick: [{ type: i0.Output }],
8264
+ keydownEscape: [{ type: i0.Output, args: ['keydown.escape',] }],
8265
+ keyupEnter: [{ type: i0.Output, args: ['keyup.enter',] }],
8002
8266
  matInputText: [{ type: i0.ViewChild, args: ['matInputText',] }],
8267
+ autocomplete: [{ type: i0.ViewChild, args: [autocomplete.MatAutocomplete,] }],
8003
8268
  autocompleteTrigger: [{ type: i0.ViewChild, args: [autocomplete.MatAutocompleteTrigger,] }],
8004
8269
  filter: [{ type: i0.Input }],
8005
8270
  tabindex: [{ type: i0.Input }],
@@ -18814,7 +19079,7 @@
18814
19079
  AutocompleteTestPage.decorators = [
18815
19080
  { type: i0.Component, args: [{
18816
19081
  selector: 'app-autocomplete-test',
18817
- 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"
19082
+ 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"
18818
19083
  },] }
18819
19084
  ];
18820
19085
  AutocompleteTestPage.ctorParameters = function () { return [
@@ -18985,7 +19250,7 @@
18985
19250
  SwipeTestPage.decorators = [
18986
19251
  { type: i0.Component, args: [{
18987
19252
  selector: 'app-swipe-test',
18988
- 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"
19253
+ 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"
18989
19254
  },] }
18990
19255
  ];
18991
19256
  SwipeTestPage.ctorParameters = function () { return [
@@ -19126,13 +19391,15 @@
19126
19391
  this.autocompleteFields.add('entity-suggestFn', {
19127
19392
  suggestFn: function (value, filter) { return _this.suggest(value, filter); },
19128
19393
  attributes: ['label', 'name'],
19129
- displayWith: this.entityToString
19394
+ displayWith: this.entityToString,
19395
+ equals: function (o1, o2) { return EntityUtils.compare(o1, o2, 1, 'id') === 0; }
19130
19396
  });
19131
19397
  // From items
19132
19398
  this.autocompleteFields.add('entity-items', {
19133
19399
  items: FAKE_ENTITIES.slice(),
19134
19400
  attributes: ['label', 'name'],
19135
- displayWith: this.entityToString
19401
+ displayWith: this.entityToString,
19402
+ equals: function (o1, o2) { return EntityUtils.compare(o1, o2, 1, 'id') === 0; }
19136
19403
  });
19137
19404
  // From items
19138
19405
  this.autocompleteFields.add('entity-$items', {
@@ -19194,7 +19461,7 @@
19194
19461
  ChipsTestPage.decorators = [
19195
19462
  { type: i0.Component, args: [{
19196
19463
  selector: 'app-chips-test',
19197
- 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"
19464
+ 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"
19198
19465
  },] }
19199
19466
  ];
19200
19467
  ChipsTestPage.ctorParameters = function () { return [
@@ -19531,6 +19798,9 @@
19531
19798
  this.onSubmit.complete();
19532
19799
  this.onSubmit.unsubscribe();
19533
19800
  this._$ready.complete();
19801
+ this._$ready.unsubscribe();
19802
+ this._$loading.complete();
19803
+ this._$loading.unsubscribe();
19534
19804
  };
19535
19805
  AppForm.prototype.cancel = function () {
19536
19806
  this.onCancel.emit();
@@ -19636,16 +19906,7 @@
19636
19906
  * @param opts
19637
19907
  */
19638
19908
  AppForm.prototype.ready = function (opts) {
19639
- return __awaiter(this, void 0, void 0, function () {
19640
- return __generator(this, function (_b) {
19641
- switch (_b.label) {
19642
- case 0: return [4 /*yield*/, firstTruePromise(this._$ready)];
19643
- case 1:
19644
- _b.sent();
19645
- return [2 /*return*/];
19646
- }
19647
- });
19648
- });
19909
+ return waitForTrue(this._$ready, opts);
19649
19910
  };
19650
19911
  /**
19651
19912
  * Wait form is idle (not loading)
@@ -19834,12 +20095,23 @@
19834
20095
  };
19835
20096
 
19836
20097
  var AuthModal = /** @class */ (function () {
19837
- function AuthModal(accountService, viewCtrl, changeDetectorRef) {
20098
+ function AuthModal(accountService, viewCtrl, cd) {
19838
20099
  this.accountService = accountService;
19839
20100
  this.viewCtrl = viewCtrl;
19840
- this.changeDetectorRef = changeDetectorRef;
19841
- this.loading = false;
20101
+ this.cd = cd;
19842
20102
  }
20103
+ Object.defineProperty(AuthModal.prototype, "loading", {
20104
+ get: function () {
20105
+ var _a;
20106
+ return (_a = this.form) === null || _a === void 0 ? void 0 : _a.loading;
20107
+ },
20108
+ enumerable: false,
20109
+ configurable: true
20110
+ });
20111
+ AuthModal.prototype.ngOnInit = function () {
20112
+ this.form.markAsReady({ emitEvent: false });
20113
+ this.form.markAsLoaded();
20114
+ };
19843
20115
  AuthModal.prototype.cancel = function () {
19844
20116
  this.viewCtrl.dismiss();
19845
20117
  };
@@ -19847,31 +20119,30 @@
19847
20119
  return __awaiter(this, void 0, void 0, function () {
19848
20120
  var data, account, err_1;
19849
20121
  var _this = this;
19850
- return __generator(this, function (_a) {
19851
- switch (_a.label) {
20122
+ return __generator(this, function (_b) {
20123
+ switch (_b.label) {
19852
20124
  case 0:
19853
20125
  if (this.form.disabled)
19854
20126
  return [2 /*return*/];
19855
20127
  if (!this.form.valid) {
19856
- this.form.markAsTouched();
20128
+ this.form.markAllAsTouched();
19857
20129
  return [2 /*return*/];
19858
20130
  }
19859
- this.loading = true;
19860
- _a.label = 1;
20131
+ this.markAsLoading();
20132
+ _b.label = 1;
19861
20133
  case 1:
19862
- _a.trys.push([1, 3, , 4]);
20134
+ _b.trys.push([1, 3, , 4]);
19863
20135
  data = this.form.value;
19864
20136
  // Disable the form
19865
20137
  this.form.disable();
19866
20138
  return [4 /*yield*/, this.accountService.login(data)];
19867
20139
  case 2:
19868
- account = _a.sent();
20140
+ account = _b.sent();
19869
20141
  return [2 /*return*/, this.viewCtrl.dismiss(account)];
19870
20142
  case 3:
19871
- err_1 = _a.sent();
19872
- this.loading = false;
20143
+ err_1 = _b.sent();
19873
20144
  this.form.error = err_1 && err_1.message || err_1;
19874
- this.markForCheck();
20145
+ this.markAsLoaded();
19875
20146
  // Enable the form
19876
20147
  this.form.enable();
19877
20148
  // Reset form error on next changes
@@ -19886,7 +20157,13 @@
19886
20157
  });
19887
20158
  };
19888
20159
  AuthModal.prototype.markForCheck = function () {
19889
- this.changeDetectorRef.markForCheck();
20160
+ this.cd.markForCheck();
20161
+ };
20162
+ AuthModal.prototype.markAsLoading = function (opts) {
20163
+ this.form.markAsLoading(opts);
20164
+ };
20165
+ AuthModal.prototype.markAsLoaded = function (opts) {
20166
+ this.form.markAsLoaded(opts);
19890
20167
  };
19891
20168
  return AuthModal;
19892
20169
  }());
@@ -23672,6 +23949,7 @@
23672
23949
  _this._useValidator = false;
23673
23950
  _this._stopWatching$ = new rxjs.Subject();
23674
23951
  _this._editingRowCount = 0;
23952
+ _this._fetchMoreFn = null;
23675
23953
  _this.loadingSubject = new rxjs.BehaviorSubject(undefined);
23676
23954
  _this._options = Object.assign({ dataServiceOptions: {}, debug: config && config.suppressErrors === false, keepOriginalDataAfterConfirm: false }, config);
23677
23955
  _this._useValidator = isNotNil(validatorService);
@@ -23722,6 +24000,7 @@
23722
24000
  var _this = this;
23723
24001
  this._stopWatching$.next();
23724
24002
  this._editingRowCount = 0;
24003
+ this._fetchMoreFn = null;
23725
24004
  this.markAsLoading();
23726
24005
  return this.dataService.watchAll(offset, size, sortBy, sortDirection, filter, this.serviceOptions)
23727
24006
  .pipe(
@@ -23735,6 +24014,7 @@
23735
24014
  }
23736
24015
  else {
23737
24016
  _this.updateDatasource((res.data || []));
24017
+ _this._fetchMoreFn = res.fetchMore;
23738
24018
  }
23739
24019
  return res;
23740
24020
  }));
@@ -23985,6 +24265,53 @@
23985
24265
  });
23986
24266
  });
23987
24267
  };
24268
+ EntitiesTableDataSource.prototype.getData = function () {
24269
+ return __awaiter(this, void 0, void 0, function () {
24270
+ var rows;
24271
+ return __generator(this, function (_a) {
24272
+ switch (_a.label) {
24273
+ case 0: return [4 /*yield*/, this.getRows()];
24274
+ case 1:
24275
+ rows = _a.sent();
24276
+ return [2 /*return*/, this.getDataFromRows(rows)];
24277
+ }
24278
+ });
24279
+ });
24280
+ };
24281
+ EntitiesTableDataSource.prototype.fetchMore = function (opts) {
24282
+ var _super = Object.create(null, {
24283
+ updateDatasource: { get: function () { return _super_1.prototype.updateDatasource; } }
24284
+ });
24285
+ return __awaiter(this, void 0, void 0, function () {
24286
+ var res, existingData;
24287
+ return __generator(this, function (_a) {
24288
+ switch (_a.label) {
24289
+ case 0:
24290
+ if (!this._fetchMoreFn)
24291
+ return [2 /*return*/, false]; // Avoid multiple call
24292
+ if (this._editingRowCount > 0) {
24293
+ console.warn('Cannot fetch more because still editing row');
24294
+ return [2 /*return*/];
24295
+ }
24296
+ // Fetch next page
24297
+ this._fetchMoreFn = null; // Avoid multiple call
24298
+ return [4 /*yield*/, this._fetchMoreFn()];
24299
+ case 1:
24300
+ res = _a.sent();
24301
+ // Skip if empty (no more data)
24302
+ if (isNotEmptyArray(res === null || res === void 0 ? void 0 : res.data))
24303
+ return [2 /*return*/, false];
24304
+ return [4 /*yield*/, this.getData()];
24305
+ case 2:
24306
+ existingData = _a.sent();
24307
+ _super.updateDatasource.call(this, existingData.concat.apply(existingData, __spread(res.data)), opts);
24308
+ // Remember fetchMore
24309
+ this._fetchMoreFn = res.fetchMore;
24310
+ return [2 /*return*/, true];
24311
+ }
24312
+ });
24313
+ });
24314
+ };
23988
24315
  /* -- protected method -- */
23989
24316
  EntitiesTableDataSource.prototype.markAsLoading = function () {
23990
24317
  if (this.loadingSubject.value !== true) {
@@ -24423,7 +24750,12 @@
24423
24750
  rxjs.combineLatest([
24424
24751
  _this.onStartEditingRow,
24425
24752
  _this._destroy$
24426
- ])), operators.map(function () { var _a; return (_a = row.validator) === null || _a === void 0 ? void 0 : _a.dirty; }), operators.filter(function (dirty) { return dirty === true; }), operators.first()); }))
24753
+ ])
24754
+ // DEBUG
24755
+ //.pipe(
24756
+ // tap(() => console.debug('TODO stopping previous row listener'))
24757
+ //)
24758
+ ), operators.map(function () { var _a; return (_a = row.validator) === null || _a === void 0 ? void 0 : _a.dirty; }), operators.filter(function (dirty) { return dirty === true; }), operators.first()); }))
24427
24759
  .subscribe(function () { return _this.markAsDirty(); }));
24428
24760
  // Call datasource refresh, on each refresh events
24429
24761
  this.registerSubscription(this.onRefresh
@@ -25878,13 +26210,29 @@
25878
26210
  this._subscription = new rxjs.Subscription();
25879
26211
  this._enabled = false;
25880
26212
  this._dirty = false;
25881
- this._loading = true;
25882
26213
  this._$ready = new rxjs.BehaviorSubject(false);
26214
+ this._$loading = new rxjs.BehaviorSubject(true);
25883
26215
  this.debug = false;
25884
26216
  this.submitted = false;
25885
26217
  this.toolbar = null;
25886
26218
  this.formButtonsBar = null;
25887
26219
  }
26220
+ Object.defineProperty(AppEditor.prototype, "_loading", {
26221
+ /**
26222
+ * @deprecated
26223
+ */
26224
+ get: function () {
26225
+ return this._$loading.value;
26226
+ },
26227
+ /**
26228
+ * @deprecated
26229
+ */
26230
+ set: function (value) {
26231
+ this._$loading.next(value);
26232
+ },
26233
+ enumerable: false,
26234
+ configurable: true
26235
+ });
25888
26236
  Object.defineProperty(AppEditor.prototype, "tables", {
25889
26237
  get: function () {
25890
26238
  return this._children && this._children.filter(function (c) { return c instanceof AppTable; });
@@ -25937,7 +26285,7 @@
25937
26285
  Object.defineProperty(AppEditor.prototype, "loading", {
25938
26286
  get: function () {
25939
26287
  var _a;
25940
- return this._loading || (((_a = this._children) === null || _a === void 0 ? void 0 : _a.findIndex(function (c) { return c.enabled && c.loading; })) !== -1) || false;
26288
+ return this._$loading.value || (((_a = this._children) === null || _a === void 0 ? void 0 : _a.findIndex(function (c) { return c.enabled && c.loading; })) !== -1) || false;
25941
26289
  },
25942
26290
  enumerable: false,
25943
26291
  configurable: true
@@ -25983,6 +26331,8 @@
25983
26331
  this._subscription.unsubscribe();
25984
26332
  this._$ready.complete();
25985
26333
  this._$ready.unsubscribe();
26334
+ this._$loading.complete();
26335
+ this._$loading.unsubscribe();
25986
26336
  };
25987
26337
  AppEditor.prototype.addChildForm = function (form) {
25988
26338
  if (!form)
@@ -28747,9 +29097,11 @@
28747
29097
  exports.filterNumberInput = filterNumberInput;
28748
29098
  exports.filterTrue = filterTrue;
28749
29099
  exports.firstArrayValue = firstArrayValue;
29100
+ exports.firstFalse = firstFalse;
28750
29101
  exports.firstFalsePromise = firstFalsePromise;
28751
29102
  exports.firstNotNil = firstNotNil;
28752
29103
  exports.firstNotNilPromise = firstNotNilPromise;
29104
+ exports.firstTrue = firstTrue;
28753
29105
  exports.firstTruePromise = firstTruePromise;
28754
29106
  exports.focusInput = focusInput;
28755
29107
  exports.focusNextInput = focusNextInput;