@sankhyalabs/core 5.20.0-dev.6 → 5.20.0-dev.60

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 (144) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/DataUnit.md +317 -137
  3. package/.docs/classes/DateUtils.md +8 -8
  4. package/.docs/classes/IDBRepository.md +22 -0
  5. package/.docs/classes/LockManager.md +191 -0
  6. package/.docs/classes/MaskFormatter.md +66 -14
  7. package/.docs/classes/ObjectUtils.md +141 -0
  8. package/.docs/classes/OverflowWatcher.md +533 -0
  9. package/.docs/classes/SelectionInfo.md +25 -11
  10. package/.docs/classes/ServiceCanceledException.md +193 -0
  11. package/.docs/classes/ServiceUtils.md +67 -0
  12. package/.docs/classes/SilentException.md +193 -0
  13. package/.docs/enumerations/Action.md +41 -21
  14. package/.docs/enumerations/ChangeOperation.md +4 -4
  15. package/.docs/enumerations/LockManagerOperation.md +21 -0
  16. package/.docs/enumerations/OverflowDirection.md +29 -0
  17. package/.docs/enumerations/SelectionMode.md +2 -2
  18. package/.docs/enumerations/StorageType.md +37 -0
  19. package/.docs/globals.md +18 -0
  20. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  21. package/.docs/interfaces/IRepository.md +18 -0
  22. package/.docs/interfaces/LoadDataRequest.md +1 -1
  23. package/.docs/interfaces/OverFlowWatcherParams.md +67 -0
  24. package/.docs/interfaces/PageRequest.md +3 -3
  25. package/.docs/interfaces/PaginationInfo.md +13 -0
  26. package/.docs/interfaces/QuickFilter.md +3 -3
  27. package/.docs/interfaces/Record.md +4 -4
  28. package/.docs/interfaces/SavedRecord.md +5 -5
  29. package/.docs/interfaces/WaitingChange.md +3 -3
  30. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +1 -1
  31. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +1 -1
  32. package/.docs/type-aliases/DataUnitEventOptions.md +17 -0
  33. package/.docs/type-aliases/OnOverflowCallBack.md +25 -0
  34. package/.docs/variables/OVERFLOWED_CLASS_NAME.md +13 -0
  35. package/bun.lockb +0 -0
  36. package/dist/dataunit/DataUnit.d.ts +60 -11
  37. package/dist/dataunit/DataUnit.js +158 -46
  38. package/dist/dataunit/DataUnit.js.map +1 -1
  39. package/dist/dataunit/DataUnitHelper.js +6 -5
  40. package/dist/dataunit/DataUnitHelper.js.map +1 -1
  41. package/dist/dataunit/formatting/PrettyFormatter.js +10 -6
  42. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  43. package/dist/dataunit/loading/LoadDataRequest.d.ts +1 -1
  44. package/dist/dataunit/loading/PaginationInfo.d.ts +4 -0
  45. package/dist/dataunit/metadata/DataType.js +3 -0
  46. package/dist/dataunit/metadata/DataType.js.map +1 -1
  47. package/dist/dataunit/state/action/DataUnitAction.d.ts +2 -0
  48. package/dist/dataunit/state/action/DataUnitAction.js +2 -0
  49. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  50. package/dist/dataunit/state/slice/InvalidFieldsSlice.js +2 -0
  51. package/dist/dataunit/state/slice/InvalidFieldsSlice.js.map +1 -1
  52. package/dist/dataunit/state/slice/RecordsSlice.js +1 -1
  53. package/dist/dataunit/state/slice/RecordsSlice.js.map +1 -1
  54. package/dist/dataunit/state/slice/SelectionSlice.js +4 -4
  55. package/dist/dataunit/state/slice/SelectionSlice.js.map +1 -1
  56. package/dist/exceptions/ServiceCanceledException.d.ts +14 -0
  57. package/dist/exceptions/ServiceCanceledException.js +13 -0
  58. package/dist/exceptions/ServiceCanceledException.js.map +1 -0
  59. package/dist/exceptions/SilentException.d.ts +14 -0
  60. package/dist/exceptions/SilentException.js +13 -0
  61. package/dist/exceptions/SilentException.js.map +1 -0
  62. package/dist/index.d.ts +8 -2
  63. package/dist/index.js +7 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/repository/IRepository.d.ts +6 -0
  66. package/dist/repository/indexeddb/IDBRepository.d.ts +1 -0
  67. package/dist/repository/indexeddb/IDBRepository.js +3 -0
  68. package/dist/repository/indexeddb/IDBRepository.js.map +1 -1
  69. package/dist/utils/CacheManager/index.d.ts +52 -0
  70. package/dist/utils/CacheManager/index.js +101 -0
  71. package/dist/utils/CacheManager/index.js.map +1 -0
  72. package/dist/utils/CacheManager/interfaces/index.d.ts +5 -0
  73. package/dist/utils/CacheManager/interfaces/index.js +7 -0
  74. package/dist/utils/CacheManager/interfaces/index.js.map +1 -0
  75. package/dist/utils/DateUtils.js +3 -0
  76. package/dist/utils/DateUtils.js.map +1 -1
  77. package/dist/utils/ElementUtils.d.ts +2 -0
  78. package/dist/utils/ElementUtils.js +9 -0
  79. package/dist/utils/ElementUtils.js.map +1 -0
  80. package/dist/utils/LockManager.d.ts +46 -0
  81. package/dist/utils/LockManager.js +141 -0
  82. package/dist/utils/LockManager.js.map +1 -0
  83. package/dist/utils/MaskFormatter.d.ts +16 -1
  84. package/dist/utils/MaskFormatter.js +82 -2
  85. package/dist/utils/MaskFormatter.js.map +1 -1
  86. package/dist/utils/ObjectUtils.d.ts +38 -0
  87. package/dist/utils/ObjectUtils.js +51 -0
  88. package/dist/utils/ObjectUtils.js.map +1 -1
  89. package/dist/utils/OnboardingUtils.js +1 -1
  90. package/dist/utils/OnboardingUtils.js.map +1 -1
  91. package/dist/utils/OverflowWatcher/index.d.ts +59 -0
  92. package/dist/utils/OverflowWatcher/index.js +188 -0
  93. package/dist/utils/OverflowWatcher/index.js.map +1 -0
  94. package/dist/utils/OverflowWatcher/types/overflow-callback.d.ts +6 -0
  95. package/dist/utils/OverflowWatcher/types/overflow-callback.js +2 -0
  96. package/dist/utils/OverflowWatcher/types/overflow-callback.js.map +1 -0
  97. package/dist/utils/OverflowWatcher/types/overflow-direction.d.ts +7 -0
  98. package/dist/utils/OverflowWatcher/types/overflow-direction.js +9 -0
  99. package/dist/utils/OverflowWatcher/types/overflow-direction.js.map +1 -0
  100. package/dist/utils/ServiceUtils.d.ts +24 -0
  101. package/dist/utils/ServiceUtils.js +40 -0
  102. package/dist/utils/ServiceUtils.js.map +1 -0
  103. package/dist/utils/SortingUtils.d.ts +9 -0
  104. package/dist/utils/SortingUtils.js +24 -0
  105. package/dist/utils/SortingUtils.js.map +1 -0
  106. package/jest.config.ts +2 -0
  107. package/package.json +2 -1
  108. package/reports/test-report.xml +175 -0
  109. package/setupTests.js +7 -0
  110. package/sonar-project.properties +6 -3
  111. package/src/dataunit/DataUnit.ts +189 -59
  112. package/src/dataunit/DataUnitHelper.ts +6 -5
  113. package/src/dataunit/formatting/PrettyFormatter.ts +10 -6
  114. package/src/dataunit/loading/LoadDataRequest.ts +1 -1
  115. package/src/dataunit/loading/PaginationInfo.ts +5 -0
  116. package/src/dataunit/metadata/DataType.ts +3 -0
  117. package/src/dataunit/state/action/DataUnitAction.ts +2 -0
  118. package/src/dataunit/state/slice/InvalidFieldsSlice.ts +2 -0
  119. package/src/dataunit/state/slice/RecordsSlice.ts +1 -1
  120. package/src/dataunit/state/slice/SelectionSlice.ts +4 -4
  121. package/src/dataunit/state/slice/test/RecordsSlice.spec.ts +45 -0
  122. package/src/dataunit/test/DataUnit.spec.ts +44 -0
  123. package/src/exceptions/ServiceCanceledException.ts +25 -0
  124. package/src/exceptions/SilentException.ts +25 -0
  125. package/src/index.ts +19 -1
  126. package/src/repository/IRepository.ts +7 -0
  127. package/src/repository/indexeddb/IDBRepository.ts +4 -0
  128. package/src/utils/CacheManager/index.ts +103 -0
  129. package/src/utils/CacheManager/interfaces/index.ts +5 -0
  130. package/src/utils/DateUtils.ts +3 -0
  131. package/src/utils/ElementUtils.ts +10 -0
  132. package/src/utils/LockManager.ts +157 -0
  133. package/src/utils/MaskFormatter.ts +93 -2
  134. package/src/utils/ObjectUtils.ts +56 -0
  135. package/src/utils/OnboardingUtils.ts +1 -1
  136. package/src/utils/OverflowWatcher/index.ts +243 -0
  137. package/src/utils/OverflowWatcher/types/overflow-callback.ts +6 -0
  138. package/src/utils/OverflowWatcher/types/overflow-direction.ts +7 -0
  139. package/src/utils/ServiceUtils.ts +36 -0
  140. package/src/utils/SortingUtils.ts +30 -0
  141. package/src/utils/test/objectUtils.spec.ts +109 -0
  142. package/test/dataunit/formatting/PrettyFormatter.spec.ts +177 -0
  143. package/test/util/ElementUtils.spec.ts +34 -0
  144. package/test/util/OverflowWatcher.spec.ts +152 -0
@@ -26,34 +26,41 @@ import { v4 as uuid } from "uuid";
26
26
  import { DataUnitStorage } from "./DataUnitStorage.js";
27
27
  import { getInvalidFieldMessage, InvalidFieldsReducer } from "./state/slice/InvalidFieldsSlice.js";
28
28
  import { getLoadingProperties, LoadingPropertiesReducer } from "./state/slice/LoadingProperties.js";
29
+ import SortingUtils from "../utils/SortingUtils.js";
30
+ import ServiceCanceledException from "../exceptions/ServiceCanceledException.js";
31
+ import SilentException from "../exceptions/SilentException.js";
29
32
 
30
33
  /***
31
34
  * `DataUnit`: Atua como uma camada de abstração entre o back-end e a interface do usuário.
32
35
  */
33
36
  export default class DataUnit {
34
-
37
+
35
38
  public static CHANGING_PAGE_LOADING_SOURCE = "CHANGING_PAGE_LOADING_SOURCE";
39
+ public static ALL_RECORDS_SELECTION_SOURCE = "ALL_RECORDS_SELECTION_SOURCE";
36
40
  public static DEFAULT_DATAUNIT_NAME = "dataunit";
37
41
 
38
42
  private _uuid: string;
39
43
  private _name: string;
40
- private _observers: Array<(action: DataUnitAction) => void>;
44
+ private _observers: Map<string, (action: DataUnitAction, options?: DataUnitEventOptions) => void>;
41
45
  private _sortingProvider?: SortingProvider;
42
46
  private _filterProviders: Map<string, FilterProvider>;
43
47
  private _stateManager: StateManager;
44
- private _interceptors: Array<DUActionInterceptor>;
48
+ private _interceptors: Map<string, DUActionInterceptor>;
45
49
  private _pageSize: number;
46
50
  private _childByName = new Map<string, DataUnit>();
47
51
  private _parentDataUnit: DataUnit | undefined;
48
52
  private _loadingLockers: Promise<void>[];
49
53
  private _savingLockers: Promise<any>[] = [];
50
54
  private _defaultSorting: Array<Sort>;
55
+ private _allowReleaseCallbacks: boolean;
56
+ private _waitingToReload: boolean = false;
51
57
 
52
58
  public metadataLoader?: (dataUnit: DataUnit) => Promise<UnitMetadata>;
53
59
  public dataLoader?: (dataUnit: DataUnit, request: LoadDataRequest) => Promise<LoadDataResponse>;
54
60
  public saveLoader?: (dataUnit: DataUnit, changes: Array<Change>) => Promise<Array<SavedRecord>>;
55
61
  public removeLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<string>>;
56
62
  public recordLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<Record>>;
63
+ public allRecordsLoader?: (dataUnit: DataUnit) => Array<Record> | undefined;
57
64
 
58
65
  constructor(name: string = DataUnit.DEFAULT_DATAUNIT_NAME, parentDataUnit?: DataUnit) {
59
66
  this._uuid = uuid()
@@ -75,11 +82,12 @@ export default class DataUnit {
75
82
  SnapshotReducer
76
83
  ]
77
84
  );
78
- this._observers = [];
85
+ this._observers = new Map();
79
86
  this._filterProviders = new Map<string, FilterProvider>();
80
87
  this._sortingProvider = undefined;
81
88
  this._defaultSorting = [];
82
- this._interceptors = [];
89
+ this._allowReleaseCallbacks = true;
90
+ this._interceptors = new Map();
83
91
  this._parentDataUnit = parentDataUnit;
84
92
  this._parentDataUnit?.subscribe(this.onDataUnitParentEvent);
85
93
  this._loadingLockers = [];
@@ -110,10 +118,12 @@ export default class DataUnit {
110
118
  * - Sorting Providers
111
119
  */
112
120
  public releaseCallbacks(){
113
- this._observers = [];
121
+ if(!this._allowReleaseCallbacks) return;
122
+
123
+ this._observers = new Map();
114
124
  this._filterProviders = new Map<string, FilterProvider>();
115
125
  this._sortingProvider = undefined;
116
- this._interceptors = [];
126
+ this._interceptors = new Map();
117
127
  }
118
128
 
119
129
  /**
@@ -142,6 +152,21 @@ export default class DataUnit {
142
152
  return this._uuid;
143
153
  }
144
154
 
155
+ /**
156
+ * Retorna se o dataUnit está com recarregamento pendente.
157
+ */
158
+ public isWaitingToReload(): boolean {
159
+ return this._waitingToReload;
160
+ }
161
+
162
+ /**
163
+ * Define se o dataUnit tem um recarregamento pendente.
164
+ * @param isWaiting
165
+ */
166
+ public setWaitingToReload(isWaiting: boolean){
167
+ this._waitingToReload = isWaiting;
168
+ }
169
+
145
170
  /**
146
171
  *
147
172
  * Obtém o nome de identificação do DataUnit (geralmente em formato de URI - Uniform Resource Identifier).
@@ -180,6 +205,8 @@ export default class DataUnit {
180
205
  case Action.NEXT_SELECTED:
181
206
  case Action.PREVIOUS_SELECTED:
182
207
  case Action.DATA_LOADED:
208
+ case Action.RECORDS_ADDED:
209
+ case Action.EDITION_CANCELED:
183
210
  this.clearDataUnit();
184
211
  const selectedRecord = this._parentDataUnit?.getSelectedRecord();
185
212
  if(selectedRecord != undefined && !this.isNewRecord(selectedRecord.__record__id__)){
@@ -238,13 +265,14 @@ export default class DataUnit {
238
265
  if (await this.dispatchAction(Action.LOADING_DATA, request, executionCtx)) {
239
266
  if (this.dataLoader) {
240
267
  this.dataLoader(this, request).then(
241
- response => {
242
- this.dispatchAction(
268
+ async response => {
269
+ await this.dispatchAction(
243
270
  Action.DATA_LOADED,
244
- {...response, keepSelection: request.keepSelection, filters: request.filters},
271
+ {...response, keepSelection: request.keepSelection, filters: request.filters, selectFirstRecord},
245
272
  executionCtx
246
273
  );
247
- this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
274
+
275
+ await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
248
276
 
249
277
  if(selectFirstRecord){
250
278
  this.requestSelectFirst(executionCtx);
@@ -316,13 +344,16 @@ export default class DataUnit {
316
344
 
317
345
  await this.processLoadingLockers();
318
346
 
319
- if (this._parentDataUnit && !this._parentDataUnit.getSelectedRecord()) {
347
+ if (this._parentDataUnit) {
348
+ const parentRecord = this._parentDataUnit.getSelectedRecord();
349
+ if(parentRecord == undefined || this._parentDataUnit.isNewRecord(parentRecord.__record__id__)){
320
350
  if(this.records){
321
351
  this.clearDataUnit();
322
352
  }
323
353
  return Promise.resolve({
324
354
  records: []
325
355
  });
356
+ }
326
357
  }
327
358
 
328
359
  const loadDataRequest = this.getLoadDataRequest(quickFilter, source);
@@ -335,11 +366,14 @@ export default class DataUnit {
335
366
  *
336
367
  * @param page - Número da página desejada.
337
368
  * @param executionCtx - Contexto de execução do carregamento dos registros do DataUnit.
338
- *
369
+ *
339
370
  * @returns - Registros da página desejada.
340
371
  *
341
372
  */
342
373
  public async gotoPage(page: number, executionCtx?: ExecutionContext): Promise<LoadDataResponse | void> {
374
+ if(this._pageSize === 0) {
375
+ return;
376
+ }
343
377
 
344
378
  let request = getCurrentRequest(this._stateManager);
345
379
 
@@ -470,7 +504,15 @@ export default class DataUnit {
470
504
  Promise.all(dispatchPromisses).then(() => resolve());
471
505
  }).catch(cause => {
472
506
  const {errorCode} = cause;
507
+ this.dispatchAction(Action.SAVING_ERROR);
473
508
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
509
+ if(cause instanceof ServiceCanceledException){
510
+ console.debug("Service canceled: " + cause.message);
511
+ resolve();
512
+ }
513
+ if(cause instanceof SilentException){
514
+ fail();
515
+ }
474
516
  fail(new ErrorException("Erro ao salvar alterações", cause, errorCode));
475
517
  });
476
518
  } else {
@@ -540,8 +582,8 @@ export default class DataUnit {
540
582
  if(selection.isAllRecords()){
541
583
  throw new Error("Exclusão remota não implementada.");
542
584
  }
543
- const records = selection?.records || [];
544
- const recordIds = selection?.recordIds || [];
585
+ const records = selection?.records as Array<Record> || [];
586
+ const recordIds = selection?.recordIds as Array<string> || [];
545
587
  return this.removeRecords(recordIds, records, buffered, undefined, silent);
546
588
  }
547
589
  return Promise.resolve([]);
@@ -575,10 +617,11 @@ export default class DataUnit {
575
617
  const currentRecordsKeys = Array.from(getCurrentRecords(this._stateManager).keys());
576
618
  const removedIndex: Array<number> = recordIds
577
619
  .map(id => currentRecordsKeys.indexOf(id))
578
- .filter(index => index > -1);
620
+ .filter(index => index > -1)
621
+ .sort((a, b) => a - b);
579
622
 
580
623
  const nextIndex = Math.min(removedIndex.slice(-1)[0] + 1, currentRecordsKeys.length);
581
- const selectionAfterRemove = [currentRecordsKeys[nextIndex]];
624
+ const selectionAfterRemove = currentRecordsKeys[nextIndex] ? [currentRecordsKeys[nextIndex]] : [];
582
625
 
583
626
  this.dispatchAction(Action.RECORDS_REMOVED, { records: removedIds, cachedRecords, removedIndex, buffered: false, selectionAfterRemove }, executionCtx);
584
627
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
@@ -642,7 +685,6 @@ export default class DataUnit {
642
685
  *
643
686
  */
644
687
  public getFormattedValue(fieldName: string, value?: any): string {
645
-
646
688
  const descriptor = this.getField(fieldName);
647
689
  if (value == undefined) {
648
690
  value = this.getFieldValue(fieldName);
@@ -665,7 +707,7 @@ export default class DataUnit {
665
707
  *
666
708
  */
667
709
  public addInterceptor(interceptor: DUActionInterceptor): void {
668
- this._interceptors.push(interceptor);
710
+ this._interceptors.set(interceptor.interceptAction.toString(), interceptor);
669
711
  }
670
712
 
671
713
  /**
@@ -676,7 +718,7 @@ export default class DataUnit {
676
718
  *
677
719
  */
678
720
  public removeInterceptor(interceptor: DUActionInterceptor) {
679
- this._interceptors = this._interceptors.filter(i => i !== interceptor);
721
+ this._interceptors.delete(interceptor.interceptAction.toString())
680
722
  }
681
723
 
682
724
  /**
@@ -785,7 +827,8 @@ export default class DataUnit {
785
827
  *
786
828
  */
787
829
  public set records(records: Array<Record>) {
788
- this.dispatchAction(Action.DATA_LOADED, { records }, undefined);
830
+ const paginationInfo = this.getPaginationInfo();
831
+ this.dispatchAction(Action.DATA_LOADED, { records, paginationInfo }, undefined);
789
832
  }
790
833
 
791
834
  /**
@@ -878,8 +921,12 @@ export default class DataUnit {
878
921
  public copySelected(executionCtx?: ExecutionContext): void {
879
922
  const selectionInfo = this.getSelectionInfo();
880
923
  if (selectionInfo) {
881
- const selectedRecords = selectionInfo.records;
924
+ if(selectionInfo.isAllRecords()){
925
+ throw new Error("Erro interno: Impossível copiar os registros selecionados pois a seleção atual é virtual.");
926
+ }
927
+ const selectedRecords = selectionInfo.records as Array<Record>;
882
928
  if(selectedRecords){
929
+
883
930
  this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.getParentRecordId()), executionCtx);
884
931
  }
885
932
  }
@@ -945,21 +992,31 @@ export default class DataUnit {
945
992
  * @param fieldName -Identificador do campo a ser modificado.
946
993
  * @param newValue - Valor a ser inserido no campo.
947
994
  * @param records - Indica quais registros foram afetados pela alteração no valor do campo.
995
+ * @param options - Configurações do evento
948
996
  * @returns - Promise que será resolvida quando o novo valor for persistido no state.
949
- *
950
- */
951
- public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>): Promise<boolean> {
997
+ *
998
+ */
999
+ public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>, options?:DataUnitEventOptions): Promise<boolean> {
1000
+ const noRecordSelected = !this.hasNewRecord() && !this.getSelectedRecord();
1001
+
1002
+ const suppressCreateNewRecord = options?.suppressCreateNewRecord;
1003
+
1004
+ if(noRecordSelected && suppressCreateNewRecord) return Promise.resolve(false);
1005
+
1006
+ if(noRecordSelected) await this.addRecord();
952
1007
 
953
- if(!this.hasNewRecord() && !this.getSelectedRecord()) await this.addRecord();
954
-
955
1008
  const typedValue = this.validateAndTypeValue(fieldName, newValue);
956
1009
  const currentValue = this.getFieldValue(fieldName);
957
-
1010
+
1011
+ if(!(newValue instanceof Promise) && ObjectUtils.hasEquivalentProps(currentValue, typedValue)){
1012
+ return Promise.resolve(false);
1013
+ }
1014
+
958
1015
  if(newValue instanceof Promise){
959
1016
  const promise:Promise<boolean> = new Promise(accept => {
960
1017
  newValue.then(resolvedValue => {
961
1018
  this.dispatchAction(Action.DATA_RESOLVED, { [fieldName]: resolvedValue, records }, undefined);
962
- accept(this.setFieldValue(fieldName, resolvedValue, records));
1019
+ accept(this.setFieldValue(fieldName, resolvedValue, records, options));
963
1020
  });
964
1021
  });
965
1022
  this._savingLockers.push(promise);
@@ -967,7 +1024,7 @@ export default class DataUnit {
967
1024
  }
968
1025
 
969
1026
  if (currentValue !== typedValue) {
970
- const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined);
1027
+ const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined, options);
971
1028
  this._savingLockers.push(promise);
972
1029
  return promise;
973
1030
  }
@@ -988,6 +1045,18 @@ export default class DataUnit {
988
1045
  this.dispatchAction(Action.FIELD_INVALIDATED, { fieldName, message, recordId }, undefined);
989
1046
  }
990
1047
 
1048
+ /**
1049
+ *
1050
+ * Cancela o saving exibindo os campos invalidos.
1051
+ *
1052
+ * @param filds - Lista dos campos
1053
+ * @param recordId - Indica qual registro está com os campos inválido.
1054
+ *
1055
+ */
1056
+ public savingCanceled(fields: Array<{name: string, message: string}>, recordId: string): void {
1057
+ this.dispatchAction(Action.SAVING_CANCELED, { fields, recordId }, undefined);
1058
+ }
1059
+
991
1060
  /**
992
1061
  *
993
1062
  * Limpa campos inválidos.
@@ -1095,7 +1164,7 @@ export default class DataUnit {
1095
1164
  public setSelection(selection: Array<string> | SelectionMode.ALL_RECORDS, executionCtx?: ExecutionContext): Promise<SelectionInfo> {
1096
1165
  return new Promise(resolve => {
1097
1166
  this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection }, executionCtx)
1098
- .then(()=>resolve(getSelectionInfo(this._stateManager)));
1167
+ .then(()=>resolve(this.getSelectionInfo()));
1099
1168
  });
1100
1169
  }
1101
1170
 
@@ -1152,8 +1221,16 @@ export default class DataUnit {
1152
1221
  * @returns - Objeto com informações como registros selecionados e seleção por critério.
1153
1222
  *
1154
1223
  **/
1155
- public getSelectionInfo(): SelectionInfo | undefined {
1156
- return getSelectionInfo(this._stateManager)
1224
+ public getSelectionInfo(): SelectionInfo{
1225
+ const selectionInfo: SelectionInfo = getSelectionInfo(this._stateManager);
1226
+ selectionInfo.getAllRecords = () => this.allRecordsLoader?.(this);
1227
+
1228
+ if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1229
+ const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
1230
+ selectionInfo.records?.sort(sortingFunction)
1231
+ }
1232
+
1233
+ return selectionInfo;
1157
1234
  }
1158
1235
 
1159
1236
  /**
@@ -1167,6 +1244,15 @@ export default class DataUnit {
1167
1244
  return getSelectedRecord(this._stateManager);
1168
1245
  }
1169
1246
 
1247
+ /**
1248
+ * Retorna o DataUnit pai
1249
+ *
1250
+ * @returns DataUnit pai ou undefined
1251
+ */
1252
+ public getParentDataUnit(): DataUnit | undefined {
1253
+ return this._parentDataUnit;
1254
+ }
1255
+
1170
1256
  /**
1171
1257
  *
1172
1258
  * Limpa todos os registros do DataUnit
@@ -1254,14 +1340,28 @@ export default class DataUnit {
1254
1340
  /**
1255
1341
  *
1256
1342
  * Retorna se existe algum tipo de alteração pendente.
1257
- *
1343
+ *
1344
+ * @param ignoreChildren: Define se deverá ignorar alterações pendentes no DataUnit filho.
1258
1345
  * @returns Verdadeiro se existir alterações pendentes.
1259
1346
  *
1260
1347
  */
1261
- public isDirty(): boolean {
1348
+ public isDirty(ignoreChildren?: boolean): boolean {
1349
+ if(ignoreChildren) return isDirty(this._stateManager);
1350
+
1262
1351
  return isDirty(this._stateManager) || this.childrenIsDirty();
1263
1352
  }
1264
1353
 
1354
+ /**
1355
+ *
1356
+ * Retorna se existe alterações pendentes no DataUnit pai.
1357
+ *
1358
+ * @returns Verdadeiro se existir alterações pendentes e Falso caso não exista alterações ou não exista DataUnit pai.
1359
+ *
1360
+ */
1361
+ public isParentDirty(): boolean {
1362
+ return this._parentDataUnit ? this._parentDataUnit.isDirty(true) : false;
1363
+ }
1364
+
1265
1365
  /**
1266
1366
  *
1267
1367
  * Retorna se existe algum DataUnit detail com alterações pendentes.
@@ -1434,17 +1534,17 @@ export default class DataUnit {
1434
1534
  * @returns - Verdadeiro se ação iniciada.
1435
1535
  *
1436
1536
  */
1437
- private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext): Promise<boolean> {
1537
+ private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext, options?:DataUnitEventOptions): Promise<boolean> {
1438
1538
  return new Promise(async resolve => {
1439
1539
  let action = new DataUnitAction(actionType, payload);
1440
1540
  if (executionCtx && executionCtx.before) {
1441
1541
  action = executionCtx.before(action);
1442
1542
  }
1443
- if (action && this._interceptors && this._interceptors.length > 0) {
1543
+ if (action && this._interceptors && this._interceptors.size > 0) {
1444
1544
  action = await this.intercept(action, this._interceptors.values());
1445
1545
  }
1446
1546
  if (action) {
1447
- this.doDispatchAction(action);
1547
+ this.doDispatchAction(action, options);
1448
1548
  if (executionCtx && executionCtx.after) {
1449
1549
  executionCtx.after(action)
1450
1550
  }
@@ -1482,16 +1582,13 @@ export default class DataUnit {
1482
1582
  * @param action - Ações em execução no DataUnit.
1483
1583
  *
1484
1584
  */
1485
- private doDispatchAction(action: DataUnitAction): void {
1585
+ private doDispatchAction(action: DataUnitAction, options:DataUnitEventOptions = {}): void {
1486
1586
  this._stateManager.process(action);
1487
1587
  this?._parentDataUnit?.dispatchAction(Action.CHILD_CHANGED, { srcAction: action, srcDataUnit: this });
1488
1588
  this._observers.forEach(f => {
1489
- /*
1490
- if some observer throws exceptions,
1491
- should be continued
1492
- */
1589
+ //if some observer throws exceptions, should be continued
1493
1590
  try {
1494
- f(action);
1591
+ f(action, options);
1495
1592
  } catch (e) {
1496
1593
  console.warn("[DataUnit] error while call observer", e);
1497
1594
  }
@@ -1528,10 +1625,17 @@ export default class DataUnit {
1528
1625
  * Ela vai ser chamada sempre que uma ação for despachada (dispatchAction()).
1529
1626
  *
1530
1627
  * @param observer - Função que recebe como parâmetro as ações que serão monitoradas.
1531
- *
1628
+ * @param uuid - Identificador do observer. Quando não informado, será gerado um identificador aleatório.
1532
1629
  */
1533
- public subscribe(observer: (action: DataUnitAction) => void) {
1534
- this._observers.push(observer);
1630
+ public subscribe(observer: (action: DataUnitAction, options?: DataUnitEventOptions) => void | Promise<void>, uuid?: string): string {
1631
+ if (uuid) {
1632
+ this._observers.set(uuid, observer);
1633
+ } else {
1634
+ uuid = StringUtils.generateUUID();
1635
+ this._observers.set(uuid, observer);
1636
+ }
1637
+
1638
+ return uuid;
1535
1639
  }
1536
1640
 
1537
1641
  /**
@@ -1539,10 +1643,18 @@ export default class DataUnit {
1539
1643
  * Remove um observer existente.
1540
1644
  *
1541
1645
  * @param observer - Observer que se deseja remover.
1542
- *
1646
+ * @param uuid - Identificador do observer. Quando não informado o delete removera com base no equals do observer.
1543
1647
  */
1544
- public unsubscribe(observer: Function) {
1545
- this._observers = this._observers.filter(f => f !== observer);
1648
+ public unsubscribe(observer: Function, uuid?: string) {
1649
+ if (uuid) {
1650
+ this._observers.delete(uuid);
1651
+ } else {
1652
+ this._observers.forEach((valor, chave) => {
1653
+ if (valor == observer) {
1654
+ this._observers.delete(chave);
1655
+ }
1656
+ });
1657
+ }
1546
1658
  }
1547
1659
 
1548
1660
  /**
@@ -1661,11 +1773,12 @@ export default class DataUnit {
1661
1773
  * @param fieldName - nome do campo para ficar invisível.
1662
1774
  *
1663
1775
  */
1664
- public hideField(fieldName:string) {
1776
+ public hideField(fieldName:string, options:HideFieldOptions) {
1665
1777
  const fieldDescriptor = this.getField(fieldName);
1666
1778
 
1667
1779
  if(fieldDescriptor?.visible === true){
1668
1780
  fieldDescriptor.visible = false;
1781
+ fieldDescriptor.properties = {...fieldDescriptor.properties, visibleOnConfig: options.visibleOnConfig };
1669
1782
  this.metadata = {...this.metadata};
1670
1783
  }
1671
1784
  }
@@ -1705,7 +1818,7 @@ export default class DataUnit {
1705
1818
  }
1706
1819
 
1707
1820
  if (selection) {
1708
- return selection.records;
1821
+ return selection.records as Array<Record>;
1709
1822
  }
1710
1823
  }
1711
1824
 
@@ -1723,12 +1836,15 @@ export default class DataUnit {
1723
1836
  public getSelection(): Array<string> {
1724
1837
  console.warn("DataUnit: O método `getSelection` foi descontinuado. Use o método `getSelectionInfo`.");
1725
1838
  const selection = this.getSelectionInfo();
1839
+ if(selection == undefined){
1840
+ return [];
1841
+ }
1726
1842
 
1727
- if(selection != undefined && selection.isAllRecords()){
1843
+ if(selection.isAllRecords()){
1728
1844
  throw new Error("Erro interno: Impossível retornar os registros selecionados. A seleção atual é virtual. Use o método `getSelectionInfo`.");
1729
1845
  }
1730
1846
 
1731
- return selection?.recordIds || [];
1847
+ return selection?.recordIds as Array<string>;
1732
1848
  }
1733
1849
 
1734
1850
  /**
@@ -1764,6 +1880,10 @@ export default class DataUnit {
1764
1880
  return loadingLockerResolver || Promise.resolve;
1765
1881
  }
1766
1882
 
1883
+ public set allowReleaseCallbacks(allow: boolean){
1884
+ this._allowReleaseCallbacks = allow;
1885
+ }
1886
+
1767
1887
  private async processLoadingLockers(){
1768
1888
  if(this._loadingLockers.length) {
1769
1889
  await Promise.all(this._loadingLockers);
@@ -1905,12 +2025,13 @@ export enum SelectionMode{
1905
2025
 
1906
2026
  export class SelectionInfo{
1907
2027
 
1908
- private _records: Array<Record>;
1909
2028
  public mode: SelectionMode;
1910
- private _total?: number;
1911
2029
  public filters?: Array<Filter>;
1912
2030
  public sort?: Array<Sort>;
1913
-
2031
+ public getAllRecords?: () => Array<Record> | undefined;
2032
+ private _records: Array<Record>;
2033
+ private _total?: number;
2034
+
1914
2035
  constructor(records: Array<Record>, mode: SelectionMode = SelectionMode.SOME_RECORDS, total?:number, filters?: Array<Filter>, sort?: Array<Sort>){
1915
2036
  this._records = records;
1916
2037
  this._total = total;
@@ -1921,7 +2042,10 @@ export class SelectionInfo{
1921
2042
 
1922
2043
  public get records(): Array<Record> | undefined{
1923
2044
  if(this.isAllRecords()){
1924
- throw new Error("Erro interno: [ALL_RECORDS] - Impossível retornar os registros selecionados numa seleção virtual.")
2045
+ if(this.getAllRecords != undefined){
2046
+ return this.getAllRecords();
2047
+ }
2048
+ throw new Error("Erro interno: Impossível retornar os registros selecionados numa seleção virtual.");
1925
2049
  }
1926
2050
  return this._records;
1927
2051
  }
@@ -1940,7 +2064,7 @@ export class SelectionInfo{
1940
2064
  if(this.isAllRecords()){
1941
2065
  return this._total || 0;
1942
2066
  }
1943
- return this.records == undefined ? 0: this.records.length;
2067
+ return this.records == undefined ? 0 : (this.records as Array<Record>).length;
1944
2068
  }
1945
2069
 
1946
2070
  public isAllRecords(): boolean{
@@ -1951,3 +2075,9 @@ export class SelectionInfo{
1951
2075
  return this.length === 0;
1952
2076
  }
1953
2077
  }
2078
+
2079
+ export type DataUnitEventOptions = {[key:string]: any};
2080
+
2081
+ export type HideFieldOptions = {
2082
+ visibleOnConfig: boolean
2083
+ };
@@ -31,17 +31,18 @@ export function defaultDataLoader(dataUnit: DataUnit, request: LoadDataRequest,
31
31
  }
32
32
 
33
33
  function buildPaginationInfo({ offset, limit }: LoadDataRequest, records: Array<Record>): PaginationInfo | undefined {
34
- if (offset === undefined || limit === undefined) {
34
+ if (offset === undefined || limit === undefined || records === undefined) {
35
35
  return undefined;
36
36
  }
37
37
 
38
- const lastRecord = Math.min(offset + limit, records.length);
38
+ const total = records?.length || 0;
39
+ const lastRecord = Math.min(offset + limit, total);
39
40
  return {
40
- currentPage: Math.ceil(offset / limit),
41
+ currentPage: limit === 0 ? 0 : Math.ceil(offset / limit),
41
42
  firstRecord: offset,
42
43
  lastRecord: lastRecord,
43
- total: records.length,
44
- hasMore: !!(records.length - lastRecord),
44
+ total: total,
45
+ hasMore: !!(total - lastRecord),
45
46
  };
46
47
  }
47
48
 
@@ -10,10 +10,13 @@ export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
10
10
  }
11
11
 
12
12
  if(descriptor?.dataType === DataType.OBJECT){
13
- if(Object.hasOwn(value, "value")){
13
+ if(value && Object.hasOwn(value, "value")){
14
14
  return getSearchFormat(value, descriptor);
15
15
  }
16
- return value == undefined ? "" : value.toString();
16
+
17
+ if(value === null || value === undefined) return "";
18
+
19
+ return value.toString();
17
20
  }
18
21
 
19
22
  if(descriptor?.userInterface === UserInterface.OPTIONSELECTOR){
@@ -43,7 +46,7 @@ export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
43
46
  const mask = getMask(value, descriptor);
44
47
  if(mask != undefined){
45
48
  try{
46
- return new MaskFormatter(mask).format(value);
49
+ return new MaskFormatter(mask).format(value, true);
47
50
  } catch(error){
48
51
  console.warn(`Erro ao formatar valor: ${value}. Mascara: ${mask}. Erro: ${error}`);
49
52
  }
@@ -53,6 +56,7 @@ export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
53
56
  }
54
57
 
55
58
  const getNumberFormat = (value: any, descriptor: FieldDescriptor) => {
59
+ if(value === undefined || value === null) return "";
56
60
  if(descriptor.userInterface === UserInterface.INTEGERNUMBER){
57
61
  return value;
58
62
  }
@@ -84,7 +88,7 @@ const getOptionFormat = (value: any, descriptor: FieldDescriptor) => {
84
88
  }
85
89
  }
86
90
 
87
- if(typeof value === "object"){
91
+ if(value && typeof value === "object" ){
88
92
  value = value.value
89
93
  }
90
94
 
@@ -92,7 +96,7 @@ const getOptionFormat = (value: any, descriptor: FieldDescriptor) => {
92
96
  return options[value];
93
97
  }
94
98
 
95
- return value;
99
+ return value ?? "";
96
100
  }
97
101
 
98
102
 
@@ -103,7 +107,7 @@ const getMask = (value: string, descriptor: FieldDescriptor | undefined): string
103
107
  }
104
108
 
105
109
  const mask = descriptor.properties?.mask;
106
- if(mask == undefined){
110
+ if(mask == undefined || !value){
107
111
  return;
108
112
  }
109
113
 
@@ -4,7 +4,7 @@ import { Filter, Sort } from "../metadata/UnitMetadata.js";
4
4
  /** Atributos enviados na requisição de carregamento dos registros */
5
5
  export interface LoadDataRequest {
6
6
  /** De onde partiu o refresh. Por padrão a fonte não é identificada */
7
- source?: String;
7
+ source?: string;
8
8
 
9
9
  /** Indice inicial dos registros que será retornado */
10
10
  offset?: number;
@@ -18,4 +18,9 @@ export interface PaginationInfo {
18
18
 
19
19
  /** Se ainda existem mais registros */
20
20
  hasMore: boolean;
21
+
22
+ /** Informa se o carregamento de dados em background está sendo executado
23
+ * Caso o dataunit não tenha carga paralela o valor será indefinido
24
+ */
25
+ loadingInProgress?: boolean;
21
26
  }
@@ -119,6 +119,9 @@ export const toString = ( dataType: DataType|undefined, value: any): string => {
119
119
  } else if (dataType === DataType.DATE) {
120
120
  if (typeof value === "string") {
121
121
  value = DateUtils.strToDate(value);
122
+ if (value == undefined) {
123
+ return value;
124
+ }
122
125
  }
123
126
  return DateUtils.formatRfc3339(value as Date);
124
127
  } else {