@sumaris-net/ngx-components 2.0.0-rc3 → 2.0.0-rc4

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 (39) hide show
  1. package/doc/changelog.md +13 -0
  2. package/esm2020/src/app/core/form/editor.class.mjs +4 -1
  3. package/esm2020/src/app/core/form/entity-editor-modal.class.mjs +19 -5
  4. package/esm2020/src/app/core/form/form.class.mjs +4 -1
  5. package/esm2020/src/app/core/form/form.utils.mjs +3 -3
  6. package/esm2020/src/app/core/icon/icon.component.mjs +2 -2
  7. package/esm2020/src/app/core/services/model/peer.model.mjs +3 -3
  8. package/esm2020/src/app/core/services/model/referential.model.mjs +11 -3
  9. package/esm2020/src/app/core/table/entities-table-datasource.class.mjs +1 -1
  10. package/esm2020/src/app/core/table/table.class.mjs +162 -159
  11. package/esm2020/src/app/shared/events.mjs +2 -2
  12. package/esm2020/src/app/shared/graph/graph-colors.mjs +17 -63
  13. package/esm2020/src/app/shared/material/autocomplete/material.autocomplete.mjs +3 -5
  14. package/esm2020/src/app/shared/material/chips/material.chips.mjs +2 -3
  15. package/esm2020/src/app/shared/material/material.animations.mjs +1 -1
  16. package/esm2020/src/app/shared/material/material.testing.module.mjs +18 -6
  17. package/esm2020/src/app/shared/observables.mjs +29 -22
  18. package/esm2020/src/app/shared/services/memory-entity-service.class.mjs +12 -6
  19. package/esm2020/src/app/shared/testing/observable.test.mjs +96 -0
  20. package/fesm2015/sumaris-net.ngx-components.mjs +489 -422
  21. package/fesm2015/sumaris-net.ngx-components.mjs.map +1 -1
  22. package/fesm2020/sumaris-net.ngx-components.mjs +359 -266
  23. package/fesm2020/sumaris-net.ngx-components.mjs.map +1 -1
  24. package/package.json +1 -1
  25. package/src/app/core/form/editor.class.d.ts +1 -0
  26. package/src/app/core/form/entity-editor-modal.class.d.ts +13 -4
  27. package/src/app/core/form/form.class.d.ts +1 -0
  28. package/src/app/core/form/form.utils.d.ts +2 -2
  29. package/src/app/core/services/model/referential.model.d.ts +6 -1
  30. package/src/app/core/table/table.class.d.ts +34 -47
  31. package/src/app/shared/events.d.ts +4 -4
  32. package/src/app/shared/graph/graph-colors.d.ts +8 -8
  33. package/src/app/shared/material/material.testing.module.d.ts +9 -8
  34. package/src/app/shared/observables.d.ts +1 -1
  35. package/src/app/shared/services/memory-entity-service.class.d.ts +5 -0
  36. package/src/app/shared/testing/observable.test.d.ts +25 -0
  37. package/src/theme/_ionic.globals.scss +4 -0
  38. package/src/theme/_material.globals.scss +3 -1
  39. package/src/theme/_mixins.scss +3 -3
@@ -57,8 +57,8 @@ import { MatAutocomplete, MatAutocompleteTrigger, MatAutocompleteModule, MAT_AUT
57
57
  import { __awaiter, __decorate, __param } from 'tslib';
58
58
  import * as i1$2 from '@angular/forms';
59
59
  import { Validators, NG_VALUE_ACCESSOR, AbstractControl, UntypedFormGroup, UntypedFormArray, UntypedFormControl, ReactiveFormsModule } from '@angular/forms';
60
- import { timer, merge, fromEvent, BehaviorSubject, Subscription, Subject, isObservable, from, of, noop as noop$9, Observable, forkJoin, defer, interval, combineLatest, EMPTY } from 'rxjs';
61
- import { filter, takeUntil, map, first, switchMap, tap, debounceTime, startWith, distinctUntilChanged, mergeMap, catchError, throttleTime, skip, bufferWhen, take } from 'rxjs/operators';
60
+ import { timer, merge, fromEvent, BehaviorSubject, Subscription, Subject, isObservable, from, of, noop as noop$9, Observable, forkJoin, defer, interval, EMPTY } from 'rxjs';
61
+ import { filter, map, takeUntil, first, switchMap, tap, debounceTime, startWith, distinctUntilChanged, mergeMap, catchError, throttleTime, skip, bufferWhen, take } from 'rxjs/operators';
62
62
  import { trigger, transition, style, animate, state } from '@angular/animations';
63
63
  import * as i2 from '@ionic/angular';
64
64
  import { IonicModule, Platform, ToastController, IonicSafeString, createAnimation, IonicRouteStrategy, ModalController, AlertController, IonContent, IonItem, IonInfiniteScroll } from '@ionic/angular';
@@ -824,32 +824,40 @@ function filterTrue(obs) {
824
824
  function filterFalse(obs, opts) {
825
825
  return obs.pipe(filter((v) => v === false));
826
826
  }
827
- function decorateForFirst(obs, opts) {
827
+ function decorateWithTakeUntil(obs, opts) {
828
828
  // Set take until notifier
829
- const takeUntil$ = (opts === null || opts === void 0 ? void 0 : opts.stop) || ((opts === null || opts === void 0 ? void 0 : opts.timeout) && timer(opts.timeout));
830
- if (takeUntil$) {
831
- // When stop, throw an error (useful when used with a toPromise())
832
- if (opts.stopError) {
833
- return merge(obs.pipe(takeUntil(takeUntil$)), takeUntil$
834
- .pipe(map(() => {
835
- throw new Error(opts.timeout ? `Timeout ${opts.timeout}ms` : `Stop`);
836
- })));
829
+ let takeUntil$ = (opts === null || opts === void 0 ? void 0 : opts.stop) || ((opts === null || opts === void 0 ? void 0 : opts.timeout) && timer(opts.timeout));
830
+ if (!takeUntil$)
831
+ return obs; // Skip
832
+ // When stop, throw an error (useful when used with a toPromise())
833
+ if (opts.stopError) {
834
+ let error;
835
+ if (opts.stopError === true) {
836
+ error = new Error(opts.timeout ? `Timeout ${opts.timeout}ms` : `stop`);
837
+ }
838
+ else if (typeof opts.stopError === 'string') {
839
+ error = new Error(opts.stopError);
840
+ }
841
+ else {
842
+ error = opts.stopError;
837
843
  }
838
- // Add a takeUntil
839
- return obs.pipe(takeUntil(takeUntil$));
844
+ takeUntil$ = takeUntil$.pipe(map(() => {
845
+ throw error;
846
+ }));
840
847
  }
841
- return obs;
848
+ return obs
849
+ .pipe(takeUntil(takeUntil$));
842
850
  }
843
851
  function firstNotNil(obs, opts) {
844
- return decorateForFirst(obs, opts).pipe(first(isNotNil));
852
+ return decorateWithTakeUntil(obs.pipe(first(isNotNil)), opts);
845
853
  }
846
854
  function firstTrue(obs, opts) {
847
- return decorateForFirst(obs, opts).pipe(first((v) => v === true), map(_ => { }) // Convert to void
848
- );
855
+ return decorateWithTakeUntil(obs.pipe(first((v) => v === true), map(_ => { }) // Convert to void
856
+ ), opts);
849
857
  }
850
858
  function firstFalse(obs, opts) {
851
- return decorateForFirst(obs, opts).pipe(first((v) => v === false), map(_ => { }) // Convert to void
852
- );
859
+ return decorateWithTakeUntil(obs.pipe(first((v) => v === false), map(_ => { }) // Convert to void
860
+ ), opts);
853
861
  }
854
862
  function firstTruePromise(obs, opts) {
855
863
  return firstTrue(obs, Object.assign({ stopError: true }, opts)).toPromise();
@@ -898,9 +906,8 @@ function waitForTrue(observable, opts) {
898
906
  opts = Object.assign({ stopError: true }, opts);
899
907
  // dueTime (without timeout)
900
908
  if (opts && opts.dueTime > 0) {
901
- return timer(opts.dueTime)
902
- .pipe(switchMap(() => firstTrue(observable, opts)))
903
- .toPromise();
909
+ observable = timer(opts.dueTime)
910
+ .pipe(switchMap(() => observable));
904
911
  }
905
912
  return firstTrue(observable, opts).toPromise();
906
913
  }
@@ -914,7 +921,7 @@ function createPromiseEventEmitter() {
914
921
  return new EventEmitter(true);
915
922
  }
916
923
  function createPromiseEvent(eventType, promise, initArg) {
917
- const detail = Object.assign(Object.assign({}, (initArg && initArg.detail || {})), promise);
924
+ const detail = Object.assign(Object.assign({}, ((initArg === null || initArg === void 0 ? void 0 : initArg.detail) || {})), promise);
918
925
  return new CustomEvent(eventType, { detail });
919
926
  }
920
927
  function emitPromiseEvent(emitter, eventType, initArg) {
@@ -2268,8 +2275,7 @@ class MatAutocompleteField {
2268
2275
  ? timer(0)
2269
2276
  : this.autocomplete.opened.pipe(debounceTime(200));
2270
2277
  const scrollEnd$ = opened$
2271
- .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
2272
- .pipe(takeUntil(this.autocomplete.closed))));
2278
+ .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)), takeUntil(this.autocomplete.closed));
2273
2279
  // Trigger fetch more, end end scroll reached
2274
2280
  this._openedSubscription = scrollEnd$
2275
2281
  .subscribe(() => this._fetchMore$.emit());
@@ -2289,8 +2295,7 @@ class MatAutocompleteField {
2289
2295
  ? timer(0)
2290
2296
  : this.matSelect._openedStream.pipe(debounceTime(200));
2291
2297
  const scrollEnd$ = opened$
2292
- .pipe(map((_) => this.getMatSelectPanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
2293
- .pipe(takeUntil(this.matSelect._closedStream))));
2298
+ .pipe(map((_) => this.getMatSelectPanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)), takeUntil(this.matSelect._closedStream));
2294
2299
  // Trigger fetch more, end end scroll reached
2295
2300
  this._openedSubscription = scrollEnd$
2296
2301
  .pipe(filter(_ => this._moreItemsCount > 0))
@@ -4537,7 +4542,7 @@ AppFormUtils.waitIdle = waitIdle;
4537
4542
  AppFormUtils.updateValueAndValidity = updateValueAndValidity;
4538
4543
  AppFormUtils.getFormErrors = getFormErrors;
4539
4544
  // ArrayForm
4540
- AppFormUtils.addValueInArray = pushValueInArray;
4545
+ AppFormUtils.pushValueInArray = pushValueInArray;
4541
4546
  AppFormUtils.removeValueInArray = removeValueInArray;
4542
4547
  AppFormUtils.resizeArray = resizeArray;
4543
4548
  AppFormUtils.clearValueInArray = clearValueInArray;
@@ -8866,8 +8871,7 @@ class MatChipsField {
8866
8871
  ? timer(0)
8867
8872
  : this.autocomplete.opened.pipe(debounceTime(200));
8868
8873
  const scrollEnd$ = opened$
8869
- .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
8870
- .pipe(takeUntil(this.autocomplete.closed))));
8874
+ .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)), takeUntil(this.autocomplete.closed));
8871
8875
  // Trigger fetch more, end end scroll reached
8872
8876
  this._openedSubscription = scrollEnd$
8873
8877
  .subscribe(() => this._fetchMore$.emit());
@@ -13047,7 +13051,7 @@ let Peer = Peer_1 = class Peer extends Entity {
13047
13051
  }
13048
13052
  static parseUrl(peerUrl) {
13049
13053
  const url = new URL(peerUrl);
13050
- return this.fromObject({
13054
+ return Peer_1.fromObject({
13051
13055
  dns: url.hostname,
13052
13056
  port: isNilOrBlank(url.port) ? undefined : url.port,
13053
13057
  useSsl: url.protocol && (url.protocol.startsWith('https') || url.protocol.startsWith('wss')),
@@ -13065,7 +13069,7 @@ let Peer = Peer_1 = class Peer extends Entity {
13065
13069
  return path.substring(1);
13066
13070
  return path;
13067
13071
  }).filter(isNotNilOrBlank);
13068
- return [noTrailingSlash(this.fromObject(peer).url)].concat(...paths).join('/');
13072
+ return [noTrailingSlash(Peer_1.fromObject(peer).url)].concat(...paths).join('/');
13069
13073
  }
13070
13074
  asObject(options) {
13071
13075
  return super.asObject(options);
@@ -14711,6 +14715,8 @@ class BaseReferential extends Entity {
14711
14715
  target.creationDate = toDateISOString(this.creationDate);
14712
14716
  if (opts && opts.keepTypename === false)
14713
14717
  delete target.entityName;
14718
+ if (opts && opts.keepIcon === false)
14719
+ delete target.icon;
14714
14720
  return target;
14715
14721
  }
14716
14722
  fromObject(source, opts) {
@@ -14725,6 +14731,7 @@ class BaseReferential extends Entity {
14725
14731
  this.parentId = source.parentId;
14726
14732
  this.creationDate = fromDateISOString(source.creationDate);
14727
14733
  this.entityName = source.entityName;
14734
+ this.icon = source.icon;
14728
14735
  }
14729
14736
  equals(other) {
14730
14737
  return super.equals(other) && this.entityName === other.entityName;
@@ -14763,7 +14770,8 @@ const MINIFY_ENTITY_FOR_LOCAL_STORAGE = Object.freeze({
14763
14770
  minify: true,
14764
14771
  keepTypename: true,
14765
14772
  keepEntityName: true,
14766
- keepLocalId: true
14773
+ keepLocalId: true,
14774
+ keepIcon: true
14767
14775
  });
14768
14776
  /**
14769
14777
  * Used to send an entity to a pod
@@ -14772,7 +14780,8 @@ const MINIFY_ENTITY_FOR_POD = Object.freeze({
14772
14780
  minify: true,
14773
14781
  keepTypename: false,
14774
14782
  keepEntityName: false,
14775
- keepLocalId: false
14783
+ keepLocalId: false,
14784
+ keepIcon: false
14776
14785
  });
14777
14786
  // @dynamic
14778
14787
  let ReferentialRef = ReferentialRef_1 = class ReferentialRef extends Entity {
@@ -14790,6 +14799,8 @@ let ReferentialRef = ReferentialRef_1 = class ReferentialRef extends Entity {
14790
14799
  const target = super.asObject(opts);
14791
14800
  if (opts && opts.keepEntityName === false)
14792
14801
  delete target.entityName;
14802
+ if (opts && opts.keepIcon === false)
14803
+ delete target.icon;
14793
14804
  return target;
14794
14805
  }
14795
14806
  fromObject(source, opts) {
@@ -14800,6 +14811,7 @@ let ReferentialRef = ReferentialRef_1 = class ReferentialRef extends Entity {
14800
14811
  this.description = source.description;
14801
14812
  this.statusId = source.statusId;
14802
14813
  this.levelId = source.levelId;
14814
+ this.icon = source.icon;
14803
14815
  }
14804
14816
  };
14805
14817
  ReferentialRef = ReferentialRef_1 = __decorate([
@@ -19698,8 +19710,6 @@ class InMemoryEntitiesService extends StartableObservableService {
19698
19710
  return __awaiter(this, void 0, void 0, function* () {
19699
19711
  if (this.debug)
19700
19712
  console.debug('[memory-data-service] Stopping...');
19701
- this.dataSubject.complete();
19702
- this.dataSubject.unsubscribe();
19703
19713
  this._hiddenData = null;
19704
19714
  this.markAsSaved();
19705
19715
  this.markAsPristine();
@@ -19829,10 +19839,10 @@ class InMemoryEntitiesService extends StartableObservableService {
19829
19839
  offset = offset >= 0 ? offset : 0;
19830
19840
  size = size >= 0 ? size : -1;
19831
19841
  return this.dataSubject
19832
- .pipe(takeUntil(this.stopSubject),
19842
+ .pipe(
19833
19843
  // Warn if waiting value to be set
19834
- tap((data) => this.debug && !data && console.debug('[memory-data-service] Waiting value to be set...')), filter(isNotNil), mergeMap(_ => this.loadAll(offset, size, sortBy, sortDirection, filterData, options)), filter(isNotNil) // Skip no result (e.g. when stopped)
19835
- );
19844
+ tap((data) => this.debug && !data && console.debug('[memory-data-service] Waiting value to be set...')), filter(isNotNil), mergeMap(_ => this.loadAll(offset, size, sortBy, sortDirection, filterData, options)), filter(isNotNil), // Skip no result (e.g. when stopped)
19845
+ takeUntil(this.stopSubject));
19836
19846
  }
19837
19847
  saveAll(data, options) {
19838
19848
  return __awaiter(this, void 0, void 0, function* () {
@@ -19903,6 +19913,14 @@ class InMemoryEntitiesService extends StartableObservableService {
19903
19913
  * Count data stored by the service (without hidden data).
19904
19914
  * @param opts
19905
19915
  */
19916
+ // get visibleCount(): number {
19917
+ // return (this.dataSubject.value?.length || 0);
19918
+ // }
19919
+ /**
19920
+ * Count data stored by the service (without hidden data).
19921
+ * @param opts
19922
+ * @deprecated TODO Please rename this getter into visibleCount (and create count = hiddenCount + visibleCount)
19923
+ */
19906
19924
  get count() {
19907
19925
  var _a;
19908
19926
  return (((_a = this.dataSubject.value) === null || _a === void 0 ? void 0 : _a.length) || 0);
@@ -20212,14 +20230,13 @@ Color.transparent = function () {
20212
20230
  // @dynamic
20213
20231
  class ColorScale {
20214
20232
  constructor(colorArray, options) {
20215
- this.colorArray = colorArray;
20216
20233
  options = options || {};
20217
20234
  // reserved last colors for value > max
20218
20235
  const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : (colorArray.length - 1);
20219
20236
  this._min = options.min || 0;
20220
20237
  this._max = options.max || nbIntervalBeforeUpper;
20221
20238
  this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
20222
- this._legendItems = this.computeLegend();
20239
+ this._legendItems = this.computeLegendItems(colorArray);
20223
20240
  }
20224
20241
  static default() {
20225
20242
  return ColorScale.custom(25);
@@ -20243,15 +20260,21 @@ class ColorScale {
20243
20260
  };
20244
20261
  }
20245
20262
  getValueColor(value) {
20246
- const index = Math.floor(value * (this.colorArray.length - 1) / this._max);
20247
- return this.colorArray[index];
20263
+ return this.getLegendForValue(value).color;
20264
+ }
20265
+ getLegendForValue(value) {
20266
+ const index = Math.floor(value * (this._legendItems.length - 1) / this._max);
20267
+ return this._legendItems[index];
20248
20268
  }
20249
- computeLegend() {
20250
- return this.colorArray.map((color, index) => {
20269
+ getLegendAtIndex(index) {
20270
+ return this._legendItems[index];
20271
+ }
20272
+ computeLegendItems(colorArray) {
20273
+ return colorArray.map((color, index) => {
20251
20274
  const start = index * this._rangeSize;
20252
20275
  const end = start + this._rangeSize;
20253
20276
  return {
20254
- color,
20277
+ color: new Color(color),
20255
20278
  label: (end < this._max) ? `${start.toLocaleString()} - ${end.toLocaleString()}` : ` >= ${start}`
20256
20279
  };
20257
20280
  });
@@ -20260,12 +20283,10 @@ class ColorScale {
20260
20283
  ColorScale.custom = (count, options) => {
20261
20284
  options = options || {};
20262
20285
  return new ColorScale(linearColorGradientWithIntermediate(count, {
20263
- opacity: options.opacity,
20264
20286
  startColor: options.startColor || undefined,
20265
20287
  mainColor: options.mainColor || undefined,
20266
20288
  mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
20267
- endColor: options.endColor || undefined,
20268
- format: options.format
20289
+ endColor: options.endColor || undefined
20269
20290
  }), options);
20270
20291
  };
20271
20292
  // Fill colorsMap
@@ -20273,28 +20294,6 @@ Object.getOwnPropertyNames(rgbArrayMap)
20273
20294
  .forEach((key) => {
20274
20295
  colorsMap[key] = new Color(rgbArrayMap[key], 1, key);
20275
20296
  });
20276
- // Internal function
20277
- function state2side(state) {
20278
- switch (state) {
20279
- case 0:
20280
- return 0;
20281
- case 1:
20282
- return -1;
20283
- case 2:
20284
- return 0;
20285
- case 3:
20286
- return 1;
20287
- }
20288
- }
20289
- const SCALE_OPTIONS_DEFAULT = {
20290
- startColor: rgbArrayMap.red,
20291
- startStates: [0, 2, 3],
20292
- startStepsFn: (defaultStateSize) => [
20293
- Math.round((rgbArrayMap.red[0] - 50) / defaultStateSize),
20294
- Math.round((255 - rgbArrayMap.red[1]) / defaultStateSize),
20295
- Math.round((255 - rgbArrayMap.red[2]) / defaultStateSize)
20296
- ]
20297
- };
20298
20297
  /**
20299
20298
  * Internal function, that create a colors scale, using iteration
20300
20299
  *
@@ -20307,34 +20306,26 @@ const SCALE_OPTIONS_DEFAULT = {
20307
20306
  function linearColorGradientWithIntermediate(count, options) {
20308
20307
  options = options || {};
20309
20308
  // From [0,1]
20310
- options.opacity = (options.opacity > 0 && options.opacity < 1) ? options.opacity : 1;
20311
20309
  options.startColor = options.startColor || [255, 255, 190]; // default start = creme
20312
20310
  options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
20313
20311
  options.endColor = options.endColor || [255, 0, 0]; // default main = red
20314
- options.format = options.format || 'rgb';
20315
20312
  if (!options.mainColor) {
20316
20313
  return linearColorGradient(count, {
20317
- opacity: options.opacity,
20318
20314
  startColor: options.startColor,
20319
- endColor: options.endColor,
20320
- format: options.format
20315
+ endColor: options.endColor
20321
20316
  });
20322
20317
  }
20323
20318
  else {
20324
20319
  // Step 1: startColor -> mainColor
20325
20320
  const result = linearColorGradient(options.mainColorIndex + 1, {
20326
- opacity: options.opacity,
20327
20321
  startColor: options.startColor,
20328
- endColor: options.mainColor,
20329
- format: options.format
20322
+ endColor: options.mainColor
20330
20323
  });
20331
20324
  // Step 2: mainColor -> endColor
20332
20325
  if (options.mainColorIndex < count - 1) {
20333
20326
  return result.concat(linearColorGradient(count - options.mainColorIndex, {
20334
- opacity: options.opacity,
20335
20327
  startColor: options.mainColor,
20336
- endColor: options.endColor,
20337
- format: options.format
20328
+ endColor: options.endColor
20338
20329
  }));
20339
20330
  }
20340
20331
  else {
@@ -20345,10 +20336,8 @@ function linearColorGradientWithIntermediate(count, options) {
20345
20336
  function linearColorGradient(count, options) {
20346
20337
  options = options || {};
20347
20338
  // From [0,1]
20348
- options.opacity = (options.opacity > 0 && options.opacity < 1) ? options.opacity : 1;
20349
20339
  options.startColor = options.startColor || [255, 255, 255]; // default start = white
20350
20340
  options.endColor = options.endColor || [255, 0, 0]; // default end = red
20351
- options.format = options.format || 'rgb';
20352
20341
  const result = [];
20353
20342
  const color = options.startColor.slice(); // copy the start color
20354
20343
  const delta = [
@@ -20365,23 +20354,6 @@ function linearColorGradient(count, options) {
20365
20354
  // Force last color = end color
20366
20355
  result.push(options.endColor.slice());
20367
20356
  // Output as array
20368
- if (options.format === 'array') {
20369
- return result;
20370
- }
20371
- // Output as rgb(r,g,b) string
20372
- if (options.format === 'rgb') {
20373
- if (options.opacity >= 1) {
20374
- return result.map(color => 'rgb(' + color.join(',') + ')');
20375
- }
20376
- else {
20377
- return result.map(color => 'rgba(' + color.concat(options.opacity).join(',') + ')');
20378
- }
20379
- }
20380
- // Output as hex
20381
- // TODO
20382
- // return result.map(color => {
20383
- // return "rgb(" + color.join(',') + ")";
20384
- // });
20385
20357
  return result;
20386
20358
  }
20387
20359
 
@@ -20857,6 +20829,10 @@ class AppForm {
20857
20829
  this.markForCheck();
20858
20830
  }
20859
20831
  }
20832
+ get destroyed() {
20833
+ var _a;
20834
+ return ((_a = this.destroySubject) === null || _a === void 0 ? void 0 : _a.closed) !== false;
20835
+ }
20860
20836
  set form(value) {
20861
20837
  this.setForm(value);
20862
20838
  }
@@ -23759,7 +23735,7 @@ class AppIconComponent {
23759
23735
  this.icon = value.icon;
23760
23736
  this.matIcon = value.matIcon;
23761
23737
  this.matSvgIcon = value.matSvgIcon;
23762
- this.color = value.color;
23738
+ this.color = value.color || this.color; // Keep existing, if not defined in the reference
23763
23739
  }
23764
23740
  ;
23765
23741
  }
@@ -25114,6 +25090,7 @@ class AppTable {
25114
25090
  this.defaultPageSize = DEFAULT_PAGE_SIZE;
25115
25091
  this.defaultPageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS;
25116
25092
  this.onRefresh = new EventEmitter();
25093
+ this.onOpenIdAndRow = new EventEmitter();
25117
25094
  this.onOpenRow = new EventEmitter();
25118
25095
  this.onNewRow = new EventEmitter();
25119
25096
  this.onStartEditingRow = new EventEmitter();
@@ -25260,7 +25237,7 @@ class AppTable {
25260
25237
  if (this.dirty || (this.editedRow && this.editedRow.id !== this.previouslyEditedRowId)) {
25261
25238
  // Cancel the current editing row only if editing and if it was not previously saved
25262
25239
  if ((_a = this.editedRow) === null || _a === void 0 ? void 0 : _a.editing) {
25263
- this.editedRow.cancelOrDelete();
25240
+ this.dataSource.cancelOrDelete(this.editedRow);
25264
25241
  // Mark row as pristine
25265
25242
  this.checkIfRowPristine(this.editedRow, { emitEvent: false });
25266
25243
  }
@@ -25364,6 +25341,10 @@ class AppTable {
25364
25341
  get paginator() {
25365
25342
  return this._paginator || this.childPaginator;
25366
25343
  }
25344
+ get destroyed() {
25345
+ var _a;
25346
+ return ((_a = this.destroySubject) === null || _a === void 0 ? void 0 : _a.closed) !== false;
25347
+ }
25367
25348
  ngOnInit() {
25368
25349
  var _a;
25369
25350
  if (this._initialized)
@@ -25399,17 +25380,11 @@ class AppTable {
25399
25380
  // Propagate row dirty state to table
25400
25381
  this.registerSubscription(this.onStartEditingRow
25401
25382
  .pipe(filter(row => (row === null || row === void 0 ? void 0 : row.validator) && true), mergeMap(row => row.validator.valueChanges
25402
- .pipe(takeUntil(
25403
- // Stop if next another row, or destroying
25404
- combineLatest([
25405
- this.onStartEditingRow,
25406
- this.destroySubject
25407
- ])
25383
+ .pipe(filter(row => row.dirty), first(),
25408
25384
  // DEBUG
25409
- //.pipe(
25410
- // tap(() => console.debug('TODO stopping previous row listener'))
25411
- //)
25412
- ), map(() => row.dirty), filter(dirty => dirty === true), first())))
25385
+ //tap(() => console.debug("Propagate row's dirty to table..."))
25386
+ // Stop if next another row, or destroying
25387
+ takeUntil(this.onStartEditingRow), takeUntil(this.destroySubject))))
25413
25388
  .subscribe(() => this.markAsDirty()));
25414
25389
  // Call datasource refresh, on each refresh events
25415
25390
  this.registerSubscription(this.onRefresh
@@ -25541,41 +25516,40 @@ class AppTable {
25541
25516
  }
25542
25517
  }
25543
25518
  confirmAndAdd(event, row) {
25544
- return __awaiter(this, void 0, void 0, function* () {
25545
- const confirmed = yield this.confirmEditCreate(event, row);
25546
- if (!confirmed)
25547
- return false;
25548
- // Add row
25549
- return this.addRow(event);
25550
- });
25519
+ if (row && !this.confirmEditCreate(event, row)) {
25520
+ return false;
25521
+ }
25522
+ // Add row
25523
+ return this.addRow(event);
25551
25524
  }
25552
25525
  confirmAndBackward(event, row) {
25553
- var _a;
25554
- return __awaiter(this, void 0, void 0, function* () {
25555
- // Deleting edited row, if empty and not dirty
25556
- const previousRow = this.editedRow;
25557
- if (previousRow && (previousRow.id === -1) && ((_a = previousRow.validator) === null || _a === void 0 ? void 0 : _a.invalid) && !(previousRow.dirty)) {
25558
- const deleted = this.deleteNew(event, previousRow);
25559
- if (!deleted)
25560
- return false;
25561
- // Wait deletion is done, then edit previous row (by id, because of reloading)
25562
- yield this.waitIdle();
25563
- }
25564
- // Edit previous row
25565
- return this.startEditRow(event, row, { focusColumn: this.lastUserColumn });
25566
- });
25526
+ var _a, _b;
25527
+ // Deleting edited row, if empty and not dirty
25528
+ const previousRow = this.editedRow;
25529
+ if (previousRow && (previousRow.id === -1) && ((_a = previousRow.validator) === null || _a === void 0 ? void 0 : _a.invalid) && !((_b = previousRow.validator) === null || _b === void 0 ? void 0 : _b.dirty)) {
25530
+ this.deleteNewRow(event, previousRow);
25531
+ // Wait deletion is done, then edit previous row (by id, because of reloading)
25532
+ this.waitIdle()
25533
+ .then(() => this.editRowById(event, row.id, { focusColumn: this.lastUserColumn }));
25534
+ return true;
25535
+ }
25536
+ // Edit previous row
25537
+ this.editRow(event, row, { focusColumn: this.lastUserColumn });
25538
+ return true;
25567
25539
  }
25568
25540
  confirmAndForward(event, row) {
25569
- return __awaiter(this, void 0, void 0, function* () {
25570
- row = row || this.editedRow;
25571
- const confirmed = yield this.confirmEditCreate(event, row);
25572
- // Cannot confirm previous row: skip
25573
- if (!confirmed)
25574
- return false;
25575
- // Edit next row
25576
- this.startEditRowById(null, row.id + 1, { focusColumn: this.firstUserColumn });
25577
- return true;
25578
- });
25541
+ var _a;
25542
+ row = row || this.editedRow;
25543
+ if (!this.confirmEditCreate(event, row)) {
25544
+ // If pending: Wait end of validation, then loop
25545
+ if ((_a = row.validator) === null || _a === void 0 ? void 0 : _a.pending) {
25546
+ return AppFormUtils.waitWhilePending(row.validator).then(() => this.confirmAndForward(event, row));
25547
+ }
25548
+ return false;
25549
+ }
25550
+ // Edit next row
25551
+ this.editRowById(event, row.id + 1, { focusColumn: this.firstUserColumn });
25552
+ return true;
25579
25553
  }
25580
25554
  /**
25581
25555
  * Confirm the creation of the given row, or if not specified the currently edited row
@@ -25584,64 +25558,65 @@ class AppTable {
25584
25558
  * @param row
25585
25559
  */
25586
25560
  confirmEditCreate(event, row) {
25587
- return __awaiter(this, void 0, void 0, function* () {
25588
- row = row || this.editedRow;
25589
- if (!row || !row.editing)
25590
- return true; // no row to confirm
25591
- // Stop event
25592
- event === null || event === void 0 ? void 0 : event.preventDefault();
25593
- event === null || event === void 0 ? void 0 : event.stopPropagation();
25594
- // Confirmation edition or creation
25595
- const confirmed = yield row.confirmEditCreate();
25596
- if (confirmed) {
25597
- // If edit finished, forget edited row
25598
- if (row === this.editedRow) {
25599
- this.editedRow = undefined;
25600
- }
25601
- // Mark table as dirty (if row is dirty)
25602
- if (row.dirty) {
25603
- this.markAsDirty({ emitEvent: false /* because of resetError() */ });
25604
- }
25605
- // Clear error
25606
- this.resetError();
25607
- // Emit the confirm event
25608
- this.onConfirmEditCreateRow.next(row);
25609
- return true; // Continue
25561
+ row = row || this.editedRow;
25562
+ if (!row || !row.editing)
25563
+ return true; // no row to confirm
25564
+ // Stop event
25565
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
25566
+ // Confirmation edition or creation
25567
+ // TODO: add await ?
25568
+ const confirmed = row.confirmEditCreate();
25569
+ if (confirmed) {
25570
+ // If edit finished, forget edited row
25571
+ if (row === this.editedRow) {
25572
+ this.editedRow = undefined;
25610
25573
  }
25611
- // Not conformed + validator
25612
- else if (row.validator) {
25613
- // NOT confirmed = row has error
25614
- if (this.debug) {
25615
- console.warn('[table] Cannot confirm row, because invalid');
25616
- AppFormUtils.logFormErrors(row.validator, '[table] ');
25617
- }
25618
- // fix: mark all controls as touched to show errors
25619
- row.validator.markAllAsTouched();
25620
- // Compute row error, and propagate to table's error
25621
- if (this.propagateRowError) {
25622
- const error = this.getRowError(row);
25623
- this.setError(error);
25624
- }
25574
+ // Mark table as dirty (if row is dirty)
25575
+ if (row.dirty) {
25576
+ this.markAsDirty({ emitEvent: false /* because of resetError() */ });
25625
25577
  }
25626
- // Not confirmed
25627
- return false;
25628
- });
25629
- }
25630
- cancelOrDelete(event, row, opts) {
25631
- return __awaiter(this, void 0, void 0, function* () {
25632
- // Delete new row
25633
- if (row.id === -1) {
25634
- return this.deleteNew(event, row);
25578
+ // Clear error
25579
+ this.resetError();
25580
+ // Emit the confirm event
25581
+ this.onConfirmEditCreateRow.next(row);
25582
+ return true; // Continue
25583
+ }
25584
+ if (row.validator) {
25585
+ // If pending: Wait end of validation, then loop
25586
+ // TODO: remove when using async isValid() function
25587
+ if (row.validator.pending) {
25588
+ AppFormUtils.waitWhilePending(row.validator).then(() => this.confirmEditCreate(event, row));
25589
+ return false;
25635
25590
  }
25636
- // Delete existing (but not editing) row
25637
- else if (!row.editing) {
25638
- return this.deleteExisting(event, row, opts);
25591
+ // NOT confirmed = row has error
25592
+ if (this.debug) {
25593
+ console.warn('[table] Cannot confirm row, because invalid');
25594
+ AppFormUtils.logFormErrors(row.validator, '[table] ');
25639
25595
  }
25640
- // Cancel existing (and editing) row
25641
- else {
25642
- return this.cancelExisting(event, row, opts);
25596
+ // fix: mark all controls as touched to show errors
25597
+ row.validator.markAllAsTouched();
25598
+ // Compute row error, and propagate to table's error
25599
+ if (this.propagateRowError) {
25600
+ const error = this.getRowError(row);
25601
+ this.setError(error);
25643
25602
  }
25644
- });
25603
+ }
25604
+ // Not confirmed
25605
+ return false;
25606
+ }
25607
+ cancelOrDelete(event, row, opts) {
25608
+ // Delete new row
25609
+ if (row.id === -1) {
25610
+ this.deleteNewRow(event, row);
25611
+ }
25612
+ // Delete existing (bu not editing) row
25613
+ else if (!row.editing) {
25614
+ this.deleteExistingRow(event, row, opts);
25615
+ }
25616
+ // Cancel existing (and editing) row
25617
+ else {
25618
+ this.cancelExistingRow(event, row, opts);
25619
+ }
25645
25620
  }
25646
25621
  addRow(event, insertAt, opts) {
25647
25622
  return __awaiter(this, void 0, void 0, function* () {
@@ -25655,11 +25630,11 @@ class AppTable {
25655
25630
  return false;
25656
25631
  }
25657
25632
  // Try to finish edited row first
25658
- const confirmed = yield this.confirmEditCreate();
25659
- if (!confirmed)
25633
+ if (!this.confirmEditCreate()) {
25660
25634
  return false;
25661
- // Create a new row
25662
- const row = yield this.createNewRow(insertAt, opts);
25635
+ }
25636
+ // Add new row
25637
+ const row = yield this.addRowToTable(insertAt, opts);
25663
25638
  return !!row;
25664
25639
  });
25665
25640
  }
@@ -25702,30 +25677,29 @@ class AppTable {
25702
25677
  });
25703
25678
  }
25704
25679
  cancel(event, opts) {
25705
- return __awaiter(this, void 0, void 0, function* () {
25706
- // Check confirmation
25707
- if ((!opts || opts.interactive !== false) && this.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
25708
- event === null || event === void 0 ? void 0 : event.stopPropagation();
25709
- const canCancel = yield this.canCancelRows();
25710
- if (!canCancel)
25711
- return false;
25712
- }
25713
- this.onRefresh.emit();
25714
- return true;
25715
- });
25680
+ // Check confirmation
25681
+ if ((!opts || opts.interactive !== false) && this.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
25682
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
25683
+ this.canCancelRows().then(confirm => {
25684
+ // If confirmed, loop
25685
+ if (confirm)
25686
+ this.cancel(null, { interactive: false });
25687
+ });
25688
+ return;
25689
+ }
25690
+ this.onRefresh.emit();
25716
25691
  }
25717
25692
  duplicateRow(event, row, opts) {
25718
25693
  var _a;
25719
25694
  return __awaiter(this, void 0, void 0, function* () {
25720
25695
  event === null || event === void 0 ? void 0 : event.stopPropagation();
25721
25696
  row = row || this.singleSelectedRow;
25722
- if (!row)
25723
- return false; // No row, or too many row
25724
- // Confirm the source row
25725
- const confirmed = yield this.confirmEditCreate(event, row);
25726
- if (!confirmed)
25697
+ if (!row || !this.confirmEditCreate(event, row)) {
25727
25698
  return false;
25699
+ }
25728
25700
  const newRow = yield this.addRowToTable(row.id + 1);
25701
+ if (!newRow)
25702
+ throw new Error('Cannot add new row to table');
25729
25703
  const json = Object.assign(Object.assign({}, row.currentData), { id: null });
25730
25704
  // Reset some properties (e.g. rankOrder, etc)
25731
25705
  if (opts && opts.skipProperties) {
@@ -25796,8 +25770,9 @@ class AppTable {
25796
25770
  deleteRows(event, rows, opts) {
25797
25771
  var _a;
25798
25772
  return __awaiter(this, void 0, void 0, function* () {
25799
- if (this.readOnly)
25773
+ if (this.readOnly) {
25800
25774
  throw { code: ErrorCodes.TABLE_READ_ONLY, message: 'ERROR.TABLE_READ_ONLY' };
25775
+ }
25801
25776
  if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
25802
25777
  return 0; // SKip
25803
25778
  event === null || event === void 0 ? void 0 : event.preventDefault();
@@ -25833,7 +25808,7 @@ class AppTable {
25833
25808
  if (row.id === -1 || (!row.editing && row.invalid /*do not use !valid because if row is disabled, it will be always !valid */)) {
25834
25809
  if (this.debug)
25835
25810
  console.debug(`[table] Delete row #${row.id}`);
25836
- row.delete(); // TODO: add await ?
25811
+ row.delete();
25837
25812
  this.visibleRowCount--;
25838
25813
  this.totalRowCount--;
25839
25814
  deletedRows.push(row);
@@ -25843,7 +25818,7 @@ class AppTable {
25843
25818
  }
25844
25819
  // Cancel the row (and mark as pristine), when not valid but in edition
25845
25820
  else if (row.editing && !row.valid) {
25846
- row.cancel(); // TODO: add await ?
25821
+ row.cancel();
25847
25822
  // Mark row as pristine (if possible)
25848
25823
  this.checkIfRowPristine(row, { emitEvent: false, onlySelf: true /*avoid propagation to table*/ });
25849
25824
  return true; // Keep the row. the row will be deleted after the save
@@ -25855,8 +25830,10 @@ class AppTable {
25855
25830
  if (deletedRows.length || tempRows.length) {
25856
25831
  // Save data (e.g. when using memory service)
25857
25832
  const saved = yield this.saveBeforeAction('delete');
25858
- if (!saved)
25859
- return 0; // Stop if save cancelled or save failed
25833
+ if (!saved) {
25834
+ // Stop if save cancelled or save failed
25835
+ return;
25836
+ }
25860
25837
  }
25861
25838
  }
25862
25839
  try {
@@ -25870,7 +25847,7 @@ class AppTable {
25870
25847
  // DO not update manually, because watchALl().subscribe() will update this count
25871
25848
  //this.totalRowCount -= deleteCount;
25872
25849
  //this.visibleRowCount -= deleteCount;
25873
- this.selection.deselect(...rows);
25850
+ this.selection.clear();
25874
25851
  this.editedRow = undefined;
25875
25852
  this.markAsDirty({ emitEvent: false /*markForCheck() is called just after*/ });
25876
25853
  this.markForCheck();
@@ -25911,43 +25888,37 @@ class AppTable {
25911
25888
  });
25912
25889
  }
25913
25890
  clickRow(event, row) {
25914
- return __awaiter(this, void 0, void 0, function* () {
25915
- // Wait while loading
25916
- if (this.loading) {
25917
- if (this.debug)
25918
- console.debug("[table] Waiting before apply clickRow() (datasource is busy)...");
25919
- yield this.waitIdle();
25920
- }
25921
- // DEBUG
25922
- //console.debug("[table] Detect click on row");
25923
- if (row.id === -1 || row.editing)
25924
- return true; // Already in edition
25925
- if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
25926
- return false; // Cancelled by event
25927
- // Start editing row
25928
- if (this.inlineEdition) {
25929
- return this.editRow(event, row, { focusColumn: undefined /*force to use the click target*/ });
25930
- }
25931
- // Open the detail page
25932
- else {
25933
- if (event) {
25934
- event.stopPropagation();
25935
- event.preventDefault();
25936
- }
25937
- this.markAsLoading();
25938
- try {
25939
- return this.openRowDetail(row);
25940
- }
25941
- finally {
25942
- this.markAsLoaded();
25943
- }
25891
+ if (this.loading) {
25892
+ // Wait while loading, and loop
25893
+ if (this.debug)
25894
+ console.debug("[table] Waiting before apply clickRow() (datasource is busy)...");
25895
+ this.waitIdle().then(() => this.clickRow(event, row));
25896
+ return false;
25897
+ }
25898
+ // DEBUG
25899
+ //console.debug("[table] Detect click on row");
25900
+ if (row.id === -1 || row.editing)
25901
+ return true; // Already in edition
25902
+ if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
25903
+ return false; // Cancelled by event
25904
+ // Open the detail page (if not inline editing)
25905
+ if (!this.inlineEdition) {
25906
+ if (event) {
25907
+ event.stopPropagation();
25908
+ event.preventDefault();
25944
25909
  }
25945
- });
25910
+ this.markAsLoading();
25911
+ this.selection.clear();
25912
+ this.openRow(row.currentData.id, row)
25913
+ .then(() => this.markAsLoaded())
25914
+ .catch(() => this.markAsLoaded());
25915
+ return true;
25916
+ }
25917
+ // Start editing row
25918
+ return this.editRow(event, row, { focusColumn: undefined /*force to use the click target*/ });
25946
25919
  }
25947
25920
  moveRow(id, direction) {
25948
- return __awaiter(this, void 0, void 0, function* () {
25949
- return this.dataSource.move(id, direction);
25950
- });
25921
+ this.dataSource.move(id, direction);
25951
25922
  }
25952
25923
  ready(opts) {
25953
25924
  return waitForTrue(this.readySubject, opts);
@@ -26007,35 +25978,41 @@ class AppTable {
26007
25978
  }));
26008
25979
  }
26009
25980
  escapeEditingRow(event, row) {
26010
- return __awaiter(this, void 0, void 0, function* () {
26011
- row = row || this.editedRow;
26012
- if (!row || !row.editing)
26013
- return;
26014
- // DEBUG
26015
- //console.debug('[app-table] Cancel the row (keydown.escape)');
26016
- if (event) {
26017
- // Avoid to cancel the editor
26018
- event.preventDefault();
26019
- event.stopPropagation();
26020
- }
26021
- // If new row (no id)
26022
- if (row.id === -1) {
26023
- const valid = yield row.isValid();
25981
+ row = row || this.editedRow;
25982
+ if (!row || !row.editing)
25983
+ return;
25984
+ // DEBUG
25985
+ //console.debug('[app-table] Cancel the row (keydown.escape)');
25986
+ if (event) {
25987
+ // Avoid to cancel the editor
25988
+ event.preventDefault();
25989
+ event.stopPropagation();
25990
+ }
25991
+ // If new row (no id)
25992
+ if (row.id === -1) {
25993
+ if (row.validator) {
25994
+ // If pending: Wait end of validation, then loop
25995
+ if (row.validator.pending) {
25996
+ AppFormUtils.waitWhilePending(row.validator).then(() => this.escapeEditingRow(event, row));
25997
+ return;
25998
+ }
26024
25999
  // Row is invalid: delete the row
26025
- if (!valid) {
26026
- return this.deleteNew(event, row);
26000
+ if (row.validator.invalid) {
26001
+ this.deleteNewRow(event, row);
26002
+ return;
26027
26003
  }
26028
26004
  }
26029
- // If the row exists (has an id)
26030
- else {
26031
- // need to call cancel function (if confirmation will be call before before)
26032
- if (this.confirmBeforeCancel) {
26033
- return this.cancelExisting(event, row, { keepEditing: false });
26034
- }
26005
+ }
26006
+ // If the row exists (has an id)
26007
+ else {
26008
+ // need to call cancel function (if confirmation will be call before before)
26009
+ if (this.confirmBeforeCancel) {
26010
+ this.cancelExistingRow(event, row, { keepEditing: false });
26011
+ return;
26035
26012
  }
26036
- // By default, try to confirm the row
26037
- return this.confirmEditCreate(event, row);
26038
- });
26013
+ }
26014
+ // By default, try to confirm the row
26015
+ this.confirmEditCreate(event, row);
26039
26016
  }
26040
26017
  translateControlPath(path) {
26041
26018
  var _a;
@@ -26045,27 +26022,23 @@ class AppTable {
26045
26022
  return ((_a = this.translate) === null || _a === void 0 ? void 0 : _a.instant(i18nColumnKey)) || i18nColumnKey;
26046
26023
  }
26047
26024
  /* -- protected method -- */
26048
- startEditRow(event, row, opts) {
26049
- return __awaiter(this, void 0, void 0, function* () {
26050
- if (!this._enabled)
26051
- return false;
26052
- if (this.editedRow === row)
26053
- return true; // Already the edited row
26054
- if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
26055
- return false;
26056
- const confirmed = yield this.confirmEditCreate();
26057
- if (!confirmed)
26058
- return false;
26059
- if (!row.editing && !this.loading) {
26060
- this.focusColumn = opts && opts.focusColumn || this.focusColumn;
26061
- const started = yield row.startEdit();
26062
- if (!started)
26063
- return false;
26064
- }
26065
- this.editedRow = row;
26066
- this.onStartEditingRow.emit(row);
26067
- return true;
26068
- });
26025
+ editRow(event, row, opts) {
26026
+ if (!this._enabled)
26027
+ return false;
26028
+ if (this.editedRow === row)
26029
+ return true; // Already the edited row
26030
+ if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
26031
+ return false;
26032
+ if (!this.confirmEditCreate()) {
26033
+ return false;
26034
+ }
26035
+ if (!row.editing && !this.loading) {
26036
+ this.focusColumn = opts && opts.focusColumn || this.focusColumn;
26037
+ this._dataSource.startEdit(row);
26038
+ }
26039
+ this.editedRow = row;
26040
+ this.onStartEditingRow.emit(row);
26041
+ return true;
26069
26042
  }
26070
26043
  /**
26071
26044
  * Try to select row, by row.id, or by data. WIll use EntityUtils.equals()
@@ -26135,7 +26108,7 @@ class AppTable {
26135
26108
  canCancelRows(rows, opts) {
26136
26109
  return __awaiter(this, void 0, void 0, function* () {
26137
26110
  // Get dirty rows
26138
- rows = rows || this.dataSource.getRows().filter(row => row.dirty);
26111
+ rows = rows || this.dataSource.getRows().filter(row => { var _a; return (_a = row.validator) === null || _a === void 0 ? void 0 : _a.dirty; });
26139
26112
  if (isEmptyArray(rows))
26140
26113
  return true; // No dirty: OK
26141
26114
  // Check using emitter
@@ -26220,27 +26193,32 @@ class AppTable {
26220
26193
  * @param row
26221
26194
  * @protected
26222
26195
  */
26223
- openRowDetail(row) {
26196
+ openRow(id, row) {
26224
26197
  return __awaiter(this, void 0, void 0, function* () {
26225
- if (!this.allowRowDetail)
26226
- return false;
26227
- if (this.debug && this.dirty) {
26228
- console.warn('[table] Opening row details, but table has unsaved changes!');
26229
- }
26230
- if (this.onOpenRow.observers.length > 0) {
26231
- this.onOpenRow.emit(row);
26232
- return true;
26233
- }
26234
- // No ID defined: unable to open details
26235
- const id = row.currentData.id;
26236
- if (isNil(id)) {
26237
- console.warn('[table] Opening row details, but data has no id!');
26238
- return false;
26198
+ if (this.allowRowDetail) {
26199
+ if (this.debug && this.dirty) {
26200
+ console.warn('[table] Opening row details, but table has unsaved changes!');
26201
+ }
26202
+ if (this.onOpenIdAndRow.observers.length) {
26203
+ console.warn('[table] Detected deprecated use of (onOpenIdAndRow). Please use (onOpenRow) instead');
26204
+ this.onOpenIdAndRow.emit({ id, row });
26205
+ return true;
26206
+ }
26207
+ if (this.onOpenRow.observers.length) {
26208
+ this.onOpenRow.emit(row);
26209
+ return true;
26210
+ }
26211
+ // No ID defined: unable to open details
26212
+ if (isNil(id)) {
26213
+ console.warn('[table] Opening row details, but data has no id!');
26214
+ return false;
26215
+ }
26216
+ return this.router.navigate(['.', id.toString()], {
26217
+ relativeTo: this.route,
26218
+ queryParams: {}
26219
+ });
26239
26220
  }
26240
- return this.router.navigate(['.', id.toString()], {
26241
- relativeTo: this.route,
26242
- queryParams: {}
26243
- });
26221
+ return false;
26244
26222
  });
26245
26223
  }
26246
26224
  openNewRowDetail(event) {
@@ -26332,9 +26310,14 @@ class AppTable {
26332
26310
  //if (this.debug) console.debug("[table] id = " + id);
26333
26311
  return id;
26334
26312
  }
26335
- createNewRow(insertAt, opts) {
26313
+ addRowToTable(insertAt, opts) {
26336
26314
  return __awaiter(this, void 0, void 0, function* () {
26337
- const row = yield this._dataSource.createNew(insertAt, opts && { editing: opts.editing });
26315
+ // Try to finish edited row first
26316
+ if (!this.confirmEditCreate()) {
26317
+ console.warn('[table] Cannot add new row, because the previous edited row cannot be confirmed');
26318
+ return undefined;
26319
+ }
26320
+ const row = yield this._dataSource.createNew(insertAt, { editing: opts === null || opts === void 0 ? void 0 : opts.editing });
26338
26321
  if (!row)
26339
26322
  return undefined;
26340
26323
  if (row.editing) {
@@ -26465,28 +26448,6 @@ class AppTable {
26465
26448
  (_a = row.validator) === null || _a === void 0 ? void 0 : _a.markAsDirty(opts);
26466
26449
  this.markAsDirty(opts);
26467
26450
  }
26468
- /**
26469
- * @deprecated Use startEditRow instead
26470
- */
26471
- editRow(event, row, opts) {
26472
- return this.startEditRow(event, row, opts);
26473
- }
26474
- /**
26475
- * @deprecated use openRowDetail() instead)
26476
- */
26477
- openRow(id, row) {
26478
- return __awaiter(this, void 0, void 0, function* () {
26479
- return this.openRowDetail(row);
26480
- });
26481
- }
26482
- /**
26483
- * @deprecated use createNewRow() instead
26484
- */
26485
- addRowToTable(insertAt, opts) {
26486
- return __awaiter(this, void 0, void 0, function* () {
26487
- return this.createNewRow(insertAt, opts);
26488
- });
26489
- }
26490
26451
  /* -- private method -- */
26491
26452
  listenSortAndPaginationEvents() {
26492
26453
  return __awaiter(this, void 0, void 0, function* () {
@@ -26526,18 +26487,16 @@ class AppTable {
26526
26487
  }
26527
26488
  });
26528
26489
  }
26529
- startEditRowById(event, id, opts) {
26490
+ editRowById(event, id, opts) {
26530
26491
  return __awaiter(this, void 0, void 0, function* () {
26531
26492
  if (id < 0)
26532
- return false; // Invalid row id
26493
+ return;
26533
26494
  if (id >= this.visibleRowCount) {
26534
- return this.addRow(event, undefined, Object.assign(Object.assign({}, opts), { editing: true }));
26495
+ this.addRow(event, undefined, Object.assign(Object.assign({}, opts), { editing: true }));
26535
26496
  }
26536
26497
  else {
26537
- const row = this.dataSource.getRow(id);
26538
- if (!row)
26539
- return false;
26540
- return this.startEditRow(event, row, opts);
26498
+ const row = yield this.dataSource.getRow(id);
26499
+ this.editRow(event, row, opts);
26541
26500
  }
26542
26501
  });
26543
26502
  }
@@ -26549,63 +26508,53 @@ class AppTable {
26549
26508
  }
26550
26509
  }
26551
26510
  }
26552
- deleteNew(event, row) {
26553
- return __awaiter(this, void 0, void 0, function* () {
26554
- // Make sure row will be cancelled, and NOT deleted
26555
- if (row.id !== -1)
26556
- throw new Error('Row must have id = -1');
26557
- event === null || event === void 0 ? void 0 : event.preventDefault();
26558
- event === null || event === void 0 ? void 0 : event.stopPropagation();
26559
- const deleted = yield this._dataSource.cancelOrDelete(row);
26560
- if (!deleted)
26561
- return false;
26562
- this.selection.clear();
26563
- this.editedRow = undefined; // unselect row
26564
- this.onCancelOrDeleteRow.next(row);
26565
- this.resetError();
26566
- this.totalRowCount--;
26567
- this.visibleRowCount--;
26568
- return true;
26569
- });
26570
- }
26571
- deleteExisting(event, row, opts) {
26572
- return __awaiter(this, void 0, void 0, function* () {
26573
- // Make sure row will be cancelled, and NOT deleted
26574
- if (row.id === -1)
26575
- throw new Error('Row must have an id');
26576
- event === null || event === void 0 ? void 0 : event.preventDefault();
26577
- event === null || event === void 0 ? void 0 : event.stopPropagation();
26578
- return this.deleteRow(null, row, opts);
26579
- });
26580
- }
26581
- cancelExisting(event, row, opts) {
26582
- return __awaiter(this, void 0, void 0, function* () {
26583
- // Make sure row will be cancelled, and NOT deleted
26584
- if (row.id === -1 || !row.editing)
26585
- throw new Error('Row cannot be canceling, but only deleting');
26586
- const confirmed = (!opts || opts.interactive !== false);
26587
- // Ask user confirmation, if cancel
26588
- if (!confirmed && row.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
26589
- event === null || event === void 0 ? void 0 : event.preventDefault();
26590
- event === null || event === void 0 ? void 0 : event.stopPropagation();
26591
- const canCancel = yield this.canCancelRows([row], opts);
26592
- if (!canCancel)
26593
- return false;
26594
- }
26595
- const keepEditing = row.editing && (!opts || opts.keepEditing !== false);
26596
- this.editedRow = undefined; // unselect row
26597
- const cancelled = yield row.cancel(); // TODO check this
26598
- if (!cancelled)
26599
- return false;
26600
- this.onCancelOrDeleteRow.next(row);
26601
- // Mark row as pristine
26602
- yield this.checkIfRowPristine(row);
26603
- // Restore editing state
26604
- if (keepEditing) {
26605
- this.editRow(undefined, row);
26606
- }
26607
- return true;
26608
- });
26511
+ deleteNewRow(event, row) {
26512
+ if (row.id !== -1)
26513
+ throw new Error('Row must have id = -1');
26514
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
26515
+ this.selection.clear();
26516
+ this.editedRow = undefined; // unselect row
26517
+ this._dataSource.cancelOrDelete(row);
26518
+ this.onCancelOrDeleteRow.next(row);
26519
+ this.resetError();
26520
+ this.totalRowCount--;
26521
+ this.visibleRowCount--;
26522
+ }
26523
+ deleteExistingRow(event, row, opts) {
26524
+ // Make sure row will be cancelled, and NOT deleted
26525
+ if (row.id === -1)
26526
+ throw new Error('Row must have an id');
26527
+ if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
26528
+ return 0; // SKip
26529
+ event === null || event === void 0 ? void 0 : event.preventDefault();
26530
+ this.deleteRow(null, row, opts);
26531
+ }
26532
+ cancelExistingRow(event, row, opts) {
26533
+ // Make sure row will be cancelled, and NOT deleted
26534
+ if (row.id === -1 || !row.editing)
26535
+ throw new Error('Row cannot be canceling, but only deleting');
26536
+ const confirmed = (!opts || opts.interactive !== false);
26537
+ // Ask user confirmation, if cancel
26538
+ if (!confirmed && row.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
26539
+ event.stopPropagation();
26540
+ this.canCancelRows([row], opts)
26541
+ .then(confirm => {
26542
+ // If confirmed, loop
26543
+ if (confirm)
26544
+ this.cancelExistingRow(event, row, { interactive: false });
26545
+ });
26546
+ return;
26547
+ }
26548
+ const keepEditing = row.editing && (!opts || opts.keepEditing !== false);
26549
+ this.editedRow = undefined; // unselect row
26550
+ this._dataSource.cancelOrDelete(row);
26551
+ this.onCancelOrDeleteRow.next(row);
26552
+ // Mark row as pristine
26553
+ this.checkIfRowPristine(row);
26554
+ // Restore editing state
26555
+ if (keepEditing) {
26556
+ this.editRow(undefined, row);
26557
+ }
26609
26558
  }
26610
26559
  checkIfRowPristine(row, opts) {
26611
26560
  var _a, _b;
@@ -26711,7 +26660,7 @@ class AppTable {
26711
26660
  }
26712
26661
  }
26713
26662
  AppTable.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AppTable, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
26714
- AppTable.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.6", type: AppTable, inputs: { settingsId: "settingsId", debug: "debug", i18nColumnPrefix: "i18nColumnPrefix", i18nColumnSuffix: "i18nColumnSuffix", autoLoad: "autoLoad", readOnly: "readOnly", inlineEdition: "inlineEdition", focusFirstColumn: "focusFirstColumn", confirmBeforeDelete: "confirmBeforeDelete", confirmBeforeCancel: "confirmBeforeCancel", undoableDeletion: "undoableDeletion", saveBeforeDelete: "saveBeforeDelete", keepEditedRowOnSave: "keepEditedRowOnSave", saveBeforeSort: "saveBeforeSort", saveBeforeFilter: "saveBeforeFilter", propagateRowError: "propagateRowError", defaultSortBy: "defaultSortBy", defaultSortDirection: "defaultSortDirection", defaultPageSize: "defaultPageSize", defaultPageSizeOptions: "defaultPageSizeOptions", focusColumn: "focusColumn", dataSource: "dataSource", filter: "filter", disabled: "disabled", paginator: "paginator" }, outputs: { onRefresh: "onRefresh", onOpenRow: "onOpenRow", onNewRow: "onNewRow", onStartEditingRow: "onStartEditingRow", onConfirmEditCreateRow: "onConfirmEditCreateRow", onCancelOrDeleteRow: "onCancelOrDeleteRow", onBeforeDeleteRows: "onBeforeDeleteRows", onBeforeCancelRows: "onBeforeCancelRows", onBeforeSave: "onBeforeSave", onAfterDeletedRows: "onAfterDeletedRows", onSort: "onSort", onDirty: "onDirty", onError: "onError" }, viewQueries: [{ propertyName: "table", first: true, predicate: MatTable, descendants: true }, { propertyName: "childPaginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0 });
26663
+ AppTable.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.6", type: AppTable, inputs: { settingsId: "settingsId", debug: "debug", i18nColumnPrefix: "i18nColumnPrefix", i18nColumnSuffix: "i18nColumnSuffix", autoLoad: "autoLoad", readOnly: "readOnly", inlineEdition: "inlineEdition", focusFirstColumn: "focusFirstColumn", confirmBeforeDelete: "confirmBeforeDelete", confirmBeforeCancel: "confirmBeforeCancel", undoableDeletion: "undoableDeletion", saveBeforeDelete: "saveBeforeDelete", keepEditedRowOnSave: "keepEditedRowOnSave", saveBeforeSort: "saveBeforeSort", saveBeforeFilter: "saveBeforeFilter", propagateRowError: "propagateRowError", defaultSortBy: "defaultSortBy", defaultSortDirection: "defaultSortDirection", defaultPageSize: "defaultPageSize", defaultPageSizeOptions: "defaultPageSizeOptions", focusColumn: "focusColumn", dataSource: "dataSource", filter: "filter", disabled: "disabled", paginator: "paginator" }, outputs: { onRefresh: "onRefresh", onOpenIdAndRow: "onOpenIdAndRow", onOpenRow: "onOpenRow", onNewRow: "onNewRow", onStartEditingRow: "onStartEditingRow", onConfirmEditCreateRow: "onConfirmEditCreateRow", onCancelOrDeleteRow: "onCancelOrDeleteRow", onBeforeDeleteRows: "onBeforeDeleteRows", onBeforeCancelRows: "onBeforeCancelRows", onBeforeSave: "onBeforeSave", onAfterDeletedRows: "onAfterDeletedRows", onSort: "onSort", onDirty: "onDirty", onError: "onError" }, viewQueries: [{ propertyName: "table", first: true, predicate: MatTable, descendants: true }, { propertyName: "childPaginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0 });
26715
26664
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AppTable, decorators: [{
26716
26665
  type: Directive
26717
26666
  }], ctorParameters: function () { return [{ type: i0.Injector }, { type: undefined }, { type: EntitiesTableDataSource }, { type: undefined }]; }, propDecorators: { settingsId: [{
@@ -26762,6 +26711,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
26762
26711
  type: Input
26763
26712
  }], onRefresh: [{
26764
26713
  type: Output
26714
+ }], onOpenIdAndRow: [{
26715
+ type: Output
26765
26716
  }], onOpenRow: [{
26766
26717
  type: Output
26767
26718
  }], onNewRow: [{
@@ -26858,6 +26809,10 @@ class AppEditor {
26858
26809
  var _a;
26859
26810
  return this.savingSubject.value || ((_a = this.editors) === null || _a === void 0 ? void 0 : _a.some(e => e.saving));
26860
26811
  }
26812
+ get destroyed() {
26813
+ var _a;
26814
+ return ((_a = this.destroySubject) === null || _a === void 0 ? void 0 : _a.closed) !== false;
26815
+ }
26861
26816
  get tables() {
26862
26817
  return this._children && this._children.filter(c => c instanceof AppTable);
26863
26818
  }
@@ -28173,12 +28128,24 @@ class AppEntityEditorModal extends AppTabEditor {
28173
28128
  // FOR DEV ONLY ----
28174
28129
  //this.debug = !environment.production;
28175
28130
  }
28176
- set isNewData(value) {
28177
- this._isNewData = value;
28131
+ set isNew(value) {
28132
+ this._isNew = value;
28178
28133
  }
28179
- get isNewData() {
28134
+ get isNew() {
28180
28135
  var _a;
28181
- return isNotNil(this._isNewData) ? this._isNewData : isNil((_a = this.data) === null || _a === void 0 ? void 0 : _a.id);
28136
+ return isNotNil(this._isNew) ? this._isNew : isNil((_a = this.data) === null || _a === void 0 ? void 0 : _a.id);
28137
+ }
28138
+ /**
28139
+ * @deprecated use isNew instead
28140
+ */
28141
+ get isNewData() {
28142
+ return this.isNew;
28143
+ }
28144
+ /**
28145
+ * @deprecated use isNew instead
28146
+ */
28147
+ set isNewData(value) {
28148
+ this.isNew = value;
28182
28149
  }
28183
28150
  set disabled(value) {
28184
28151
  this._enabled = !value;
@@ -28532,7 +28499,7 @@ class AppEntityEditorModal extends AppTabEditor {
28532
28499
  }
28533
28500
  }
28534
28501
  AppEntityEditorModal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AppEntityEditorModal, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
28535
- AppEntityEditorModal.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.6", type: AppEntityEditorModal, inputs: { data: "data", mobile: "mobile", usageMode: "usageMode", onAfterModalInit: "onAfterModalInit", onDelete: "onDelete", i18nSuffix: "i18nSuffix", isNewData: "isNewData", disabled: "disabled" }, usesInheritance: true, ngImport: i0 });
28502
+ AppEntityEditorModal.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.6", type: AppEntityEditorModal, inputs: { data: "data", mobile: "mobile", usageMode: "usageMode", onAfterModalInit: "onAfterModalInit", onDelete: "onDelete", i18nSuffix: "i18nSuffix", isNew: "isNew", isNewData: "isNewData", disabled: "disabled" }, usesInheritance: true, ngImport: i0 });
28536
28503
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AppEntityEditorModal, decorators: [{
28537
28504
  type: Directive
28538
28505
  }], ctorParameters: function () {
@@ -28551,6 +28518,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
28551
28518
  type: Input
28552
28519
  }], i18nSuffix: [{
28553
28520
  type: Input
28521
+ }], isNew: [{
28522
+ type: Input
28554
28523
  }], isNewData: [{
28555
28524
  type: Input
28556
28525
  }], disabled: [{
@@ -31328,6 +31297,93 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
31328
31297
  args: [{ selector: 'app-data-test', 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>Date 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\n <ion-grid>\n\n <!-- debugging memory leak -->\n <ion-row><ion-col><ion-text><h4>Debug memory leak</h4></ion-text></ion-col></ion-row>\n <ion-row>\n <ion-col size=\"auto\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\">Start timer</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\">Stop timer</ion-button>\n </ion-col>\n <ion-col size=\"4\">\n <mat-form-field floatLabel=\"never\">\n <input matInput type=\"text\" hidden>\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n Mobile ?\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n <ion-col>\n <mat-date-field formControlName=\"empty\"\n *ngIf=\"!memoryHide\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"memoryMobile\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-col>\n </ion-row>\n\n <!-- Mobile mode -->\n <ion-row><ion-col><ion-text><h4>Mobile mode</h4></ion-text></ion-col></ion-row>\n <ion-row>\n <ion-col>\n\n <!-- Empty value -->\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.empty.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"empty\"\n placeholder=\"Date\"\n [mobile]=\"true\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n\n <!-- Empty value -->\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty value (required)\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.emptyRequired.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"emptyRequired\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"true\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Enable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n With value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.enable.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"enable\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"true\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.disable.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"disable\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- TimeZone -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n TimeZone\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>timezone=\"{{timezone}}\" value=\"{{stringify(form.controls.timezone.value)}}\"</pre></small>\n <pre>Should display using the browser TZ,<br/>but serialize/deserialize for the given TZ</pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"timezone\"\n placeholder=\"Date\"\n [mobile]=\"true\"\n [clearable]=\"true\"\n [timezone]=\"timezone\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Readonly -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly toggle\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.readonly.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-checkbox (change)=\"mobileReadonlyField.readonly=$event.checked\" [checked]=\"mobileReadonlyField.readonly\">\n </mat-checkbox>\n\n <mat-date-field #mobileReadonlyField formControlName=\"readonly\"\n placeholder=\"Date\"\n [readonly]=\"true\"\n [mobile]=\"true\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <!-- debug console -->\n <ion-row>\n <!-- buttons -->\n <ion-col size=\"2\">\n <!-- submit form -->\n <ion-button (click)=\"doSubmit($event)\"\n fill=\"outline\">\n <ion-icon name=\"checkmark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n\n <!-- clear log -->\n <ion-button (click)=\"clearLogPanel()\"\n fill=\"outline\">\n <ion-icon name=\"trash\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </ion-col>\n <ion-col size=\"10\" *ngIf=\"showLogPanel\">\n <ion-text color=\"primary\">Log:<br/></ion-text>\n <div class=\"ion-padding-start\">\n <ion-text color=\"medium\">\n <small [innerHTML]=\"logContent\"></small>\n </ion-text>\n </div>\n </ion-col>\n </ion-row>\n\n <hr/>\n\n <!-- Desktop mode -->\n <ion-row><ion-col><ion-text><h4>Desktop mode</h4></ion-text></ion-col></ion-row>\n <ion-row>\n <ion-col>\n\n <!-- Empty value -->\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.empty.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"empty\"\n placeholder=\"Date\"\n [mobile]=\"false\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n\n <!-- Empty value -->\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty value (required)\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.emptyRequired.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"emptyRequired\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"false\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Enable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n With value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.enable.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"enable\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"false\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.disable.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"disable\"\n placeholder=\"Date\"\n [required]=\"true\"\n [mobile]=\"false\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- TimeZone -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n TimeZone\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>timezone=\"{{timezone}}\" value={{stringify(form.controls.timezone.value)}}</pre></small>\n <pre>Should display using the browser TZ,<br/>but serialize/deserialize for the given TZ</pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-date-field formControlName=\"timezone\"\n placeholder=\"Date\"\n [mobile]=\"false\"\n [clearable]=\"true\"\n [timezone]=\"timezone\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- Readonly -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly toggle\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>{{stringify(form.controls.readonly.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-checkbox (change)=\"desktopReadonlyField.readonly=$event.checked\" [checked]=\"desktopReadonlyField.readonly\">\n </mat-checkbox>\n\n <mat-date-field #desktopReadonlyField formControlName=\"readonly\"\n placeholder=\"Date\"\n [readonly]=\"true\"\n [mobile]=\"false\"\n [clearable]=\"true\">\n <ion-icon matPrefix name=\"calendar-outline\"></ion-icon>\n </mat-date-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n </ion-grid>\n\n </form>\n</ion-content>\n" }]
31329
31298
  }], ctorParameters: function () { return [{ type: i1$2.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }]; } });
31330
31299
 
31300
+ class ObservableTestPage {
31301
+ constructor() {
31302
+ this._stopSubject = new Subject();
31303
+ this.$value = new Subject();
31304
+ this.runningTaskCount = 0;
31305
+ this.taskId = 0;
31306
+ this.options = {
31307
+ stop: this._stopSubject
31308
+ };
31309
+ }
31310
+ ngOnDestroy() {
31311
+ this._stopSubject.next();
31312
+ }
31313
+ setOptions(opts) {
31314
+ var _a;
31315
+ this.options = Object.assign(Object.assign({}, this.options), opts);
31316
+ this.log('Options changes to: ', Object.assign(Object.assign({}, this.options), { stop: ((_a = this.options) === null || _a === void 0 ? void 0 : _a.stop) ? '$stop' : 'null' }));
31317
+ }
31318
+ setOptionsError(errorMessage) {
31319
+ this.setOptions({ stopError: new Error(errorMessage) });
31320
+ }
31321
+ firstNotNilPromise(opts) {
31322
+ return __awaiter(this, void 0, void 0, function* () {
31323
+ const logPrefix = `[firstNotNilPromise() #${++this.taskId}] `;
31324
+ yield this.execute(firstNotNilPromise, opts, logPrefix);
31325
+ });
31326
+ }
31327
+ waitForTrue(opts) {
31328
+ return __awaiter(this, void 0, void 0, function* () {
31329
+ const logPrefix = `[waitForTrue() #${++this.taskId}] `;
31330
+ yield this.execute(waitForTrue, opts, logPrefix);
31331
+ });
31332
+ }
31333
+ execute(fn, opts, logPrefix) {
31334
+ return __awaiter(this, void 0, void 0, function* () {
31335
+ opts = Object.assign(Object.assign(Object.assign({}, this.options), { stop: this._stopSubject }), opts);
31336
+ logPrefix = logPrefix || `[#${++this.taskId}] `;
31337
+ const value$ = this.$value.pipe(tap(value => this.log(logPrefix + `Received value: ${value}`)));
31338
+ try {
31339
+ this.runningTaskCount++;
31340
+ this.log(logPrefix + 'Waiting...');
31341
+ // Execute the async function
31342
+ const result = yield fn(value$, opts);
31343
+ // Dump result
31344
+ if (isNotNil(result))
31345
+ this.log(logPrefix + '[OK] with result:', result);
31346
+ else
31347
+ this.log(logPrefix + '[OK]');
31348
+ }
31349
+ catch (err) {
31350
+ const errorMsg = logPrefix + 'Error: ' + ((err === null || err === void 0 ? void 0 : err.message) || '');
31351
+ console.error(errorMsg, err);
31352
+ this.log(errorMsg
31353
+ + ((err === null || err === void 0 ? void 0 : err.originalStack) ? `<pre>${err.originalStack}</pre>` : ''));
31354
+ }
31355
+ finally {
31356
+ this.runningTaskCount--;
31357
+ }
31358
+ });
31359
+ }
31360
+ emit(value) {
31361
+ this.$value.next(value);
31362
+ }
31363
+ stop() {
31364
+ this._stopSubject.next();
31365
+ }
31366
+ log(message, ...args) {
31367
+ let fullMessage = `${toDateISOString(moment$7())} - ${message}`;
31368
+ if (args === null || args === void 0 ? void 0 : args.length) {
31369
+ fullMessage += ' - <small>' + args.map(JSON.stringify).join(', ') + '</small>';
31370
+ }
31371
+ this.appendLog(fullMessage);
31372
+ }
31373
+ appendLog(message) {
31374
+ this.resultDiv.nativeElement.innerHTML += message + '<br/>';
31375
+ }
31376
+ }
31377
+ ObservableTestPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: ObservableTestPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
31378
+ ObservableTestPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: ObservableTestPage, selector: "app-observable-test", viewQueries: [{ propertyName: "resultDiv", first: true, predicate: ["result"], descendants: true }], ngImport: i0, template: "\n<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>Observables test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <ion-card>\n <ion-card-header>\n <ion-card-title>waitFor, waitForTrue, waitForFalse</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <div style=\"display: inline-block\">\n <p>Set options: </p>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: false})\">stopError: false</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: true})\">stopError: true</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: 'my error'})\">stopError: 'my error'</ion-button>\n\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({timeout: 2000})\">timeout: 2000</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({timeout: null})\">timeout: null</ion-button>\n <br/>\n <p>Call await function: </p>\n <ion-button (click)=\"waitForTrue()\">waitForTrue</ion-button>\n <ion-button (click)=\"firstNotNilPromise()\">firstNotNilPromise</ion-button>\n <br/>\n <p>Send event to source: </p>\n <ion-button (click)=\"emit(true)\">Emit 'true'</ion-button>\n <ion-button (click)=\"emit(false)\">Emit 'false'</ion-button>\n <br/>\n <p>Stop: </p>\n <ion-button color=\"danger\" (click)=\"stop()\">Stop</ion-button>\n </div>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-toolbar>\n <ion-title>Running job count: {{runningTaskCount}}</ion-title>\n <ion-button fill=\"outline\" slot=\"end\" (click)=\"result.innerHTML=''\">Clear log</ion-button>\n </ion-toolbar>\n <ion-card-content>\n <div #result style=\"color: var(--ion-color-medium)\">\n </div>\n </ion-card-content>\n </ion-card>\n</ion-content>\n", styles: ["ion-button{text-transform:none!important}\n"], dependencies: [{ kind: "component", type: i2.IonBackButton, selector: "ion-back-button", inputs: ["color", "defaultHref", "disabled", "icon", "mode", "routerAnimation", "text", "type"] }, { kind: "component", type: i2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i2.IonBackButtonDelegate, selector: "ion-back-button", inputs: ["defaultHref", "routerAnimation"] }] });
31379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: ObservableTestPage, decorators: [{
31380
+ type: Component,
31381
+ args: [{ selector: 'app-observable-test', template: "\n<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>Observables test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <ion-card>\n <ion-card-header>\n <ion-card-title>waitFor, waitForTrue, waitForFalse</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <div style=\"display: inline-block\">\n <p>Set options: </p>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: false})\">stopError: false</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: true})\">stopError: true</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({stopError: 'my error'})\">stopError: 'my error'</ion-button>\n\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({timeout: 2000})\">timeout: 2000</ion-button>\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"setOptions({timeout: null})\">timeout: null</ion-button>\n <br/>\n <p>Call await function: </p>\n <ion-button (click)=\"waitForTrue()\">waitForTrue</ion-button>\n <ion-button (click)=\"firstNotNilPromise()\">firstNotNilPromise</ion-button>\n <br/>\n <p>Send event to source: </p>\n <ion-button (click)=\"emit(true)\">Emit 'true'</ion-button>\n <ion-button (click)=\"emit(false)\">Emit 'false'</ion-button>\n <br/>\n <p>Stop: </p>\n <ion-button color=\"danger\" (click)=\"stop()\">Stop</ion-button>\n </div>\n </ion-card-content>\n </ion-card>\n\n <ion-card>\n <ion-toolbar>\n <ion-title>Running job count: {{runningTaskCount}}</ion-title>\n <ion-button fill=\"outline\" slot=\"end\" (click)=\"result.innerHTML=''\">Clear log</ion-button>\n </ion-toolbar>\n <ion-card-content>\n <div #result style=\"color: var(--ion-color-medium)\">\n </div>\n </ion-card-content>\n </ion-card>\n</ion-content>\n", styles: ["ion-button{text-transform:none!important}\n"] }]
31382
+ }], ctorParameters: function () { return []; }, propDecorators: { resultDiv: [{
31383
+ type: ViewChild,
31384
+ args: ['result']
31385
+ }] } });
31386
+
31331
31387
  const SHARED_MATERIAL_TESTING_PAGES = [
31332
31388
  { label: 'Shared Material components', divider: true },
31333
31389
  { label: 'Date/Time field', page: '/testing/shared/datetime' },
@@ -31338,7 +31394,9 @@ const SHARED_MATERIAL_TESTING_PAGES = [
31338
31394
  { label: 'Swipe component', page: '/testing/shared/swipe' },
31339
31395
  { label: 'Chips component', page: '/testing/shared/chips' },
31340
31396
  { label: 'Shared directives', divider: true },
31341
- { label: 'Badge icon directive', page: '/testing/shared/badge-icon' }
31397
+ { label: 'Badge directive', page: '/testing/shared/badge' },
31398
+ { label: 'Shared utils', divider: true },
31399
+ { label: 'Observable', page: '/testing/shared/observable' }
31342
31400
  ];
31343
31401
  const routes$7 = [
31344
31402
  {
@@ -31346,6 +31404,11 @@ const routes$7 = [
31346
31404
  pathMatch: 'full',
31347
31405
  component: SharedTestsPage
31348
31406
  },
31407
+ {
31408
+ path: 'observable',
31409
+ pathMatch: 'full',
31410
+ component: ObservableTestPage
31411
+ },
31349
31412
  {
31350
31413
  path: 'autocomplete',
31351
31414
  pathMatch: 'full',
@@ -31397,7 +31460,8 @@ MaterialTestingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", v
31397
31460
  NumpadTestPage,
31398
31461
  SwipeTestPage,
31399
31462
  ChipsTestPage,
31400
- MatBadgeTestPage], imports: [CommonModule,
31463
+ MatBadgeTestPage,
31464
+ ObservableTestPage], imports: [CommonModule,
31401
31465
  IonicModule,
31402
31466
  ReactiveFormsModule,
31403
31467
  SharedMaterialModule, i1$1.TranslateModule, i1$5.RouterModule, SharedPipesModule], exports: [SharedMaterialModule,
@@ -31408,7 +31472,8 @@ MaterialTestingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", v
31408
31472
  LatLongTestPage,
31409
31473
  NumpadTestPage,
31410
31474
  SwipeTestPage,
31411
- ChipsTestPage] });
31475
+ ChipsTestPage,
31476
+ ObservableTestPage] });
31412
31477
  MaterialTestingModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: MaterialTestingModule, imports: [CommonModule,
31413
31478
  IonicModule,
31414
31479
  ReactiveFormsModule,
@@ -31437,7 +31502,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
31437
31502
  NumpadTestPage,
31438
31503
  SwipeTestPage,
31439
31504
  ChipsTestPage,
31440
- MatBadgeTestPage
31505
+ MatBadgeTestPage,
31506
+ ObservableTestPage
31441
31507
  ],
31442
31508
  exports: [
31443
31509
  SharedMaterialModule,
@@ -31448,7 +31514,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
31448
31514
  LatLongTestPage,
31449
31515
  NumpadTestPage,
31450
31516
  SwipeTestPage,
31451
- ChipsTestPage
31517
+ ChipsTestPage,
31518
+ ObservableTestPage
31452
31519
  ]
31453
31520
  }]
31454
31521
  }] });