@sankhyalabs/core 5.20.0-dev.7 → 5.20.0-dev.71

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 (183) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/ColumnFilterManager.md +145 -0
  3. package/.docs/classes/DataUnit.md +405 -137
  4. package/.docs/classes/DataUnitInMemoryLoader.md +303 -0
  5. package/.docs/classes/DataUnitLoaderUtils.md +151 -0
  6. package/.docs/classes/DateUtils.md +8 -8
  7. package/.docs/classes/FieldComparator.md +2 -2
  8. package/.docs/classes/IDBRepository.md +22 -0
  9. package/.docs/classes/LockManager.md +249 -0
  10. package/.docs/classes/MaskFormatter.md +66 -14
  11. package/.docs/classes/ObjectUtils.md +141 -0
  12. package/.docs/classes/OverflowWatcher.md +533 -0
  13. package/.docs/classes/SelectionInfo.md +25 -11
  14. package/.docs/classes/ServiceCanceledException.md +193 -0
  15. package/.docs/classes/ServiceUtils.md +67 -0
  16. package/.docs/classes/SilentException.md +193 -0
  17. package/.docs/classes/UserAgentUtils.md +15 -1
  18. package/.docs/enumerations/Action.md +41 -21
  19. package/.docs/enumerations/ChangeOperation.md +4 -4
  20. package/.docs/enumerations/LockManagerOperation.md +33 -0
  21. package/.docs/enumerations/OverflowDirection.md +29 -0
  22. package/.docs/enumerations/RECORD_DATE_FORMAT.md +27 -0
  23. package/.docs/enumerations/SelectionMode.md +2 -2
  24. package/.docs/enumerations/StorageType.md +37 -0
  25. package/.docs/globals.md +24 -0
  26. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  27. package/.docs/interfaces/DataUnitInMemoryLoaderConfig.md +37 -0
  28. package/.docs/interfaces/IRepository.md +18 -0
  29. package/.docs/interfaces/LoadDataRequest.md +1 -1
  30. package/.docs/interfaces/OverFlowWatcherParams.md +67 -0
  31. package/.docs/interfaces/PageRequest.md +3 -3
  32. package/.docs/interfaces/PaginationInfo.md +25 -0
  33. package/.docs/interfaces/PaginationInfoBuilderParams.md +37 -0
  34. package/.docs/interfaces/QuickFilter.md +3 -3
  35. package/.docs/interfaces/Record.md +4 -4
  36. package/.docs/interfaces/SavedRecord.md +5 -5
  37. package/.docs/interfaces/WaitingChange.md +3 -3
  38. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +1 -1
  39. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +1 -1
  40. package/.docs/type-aliases/DataUnitEventOptions.md +17 -0
  41. package/.docs/type-aliases/OnOverflowCallBack.md +25 -0
  42. package/.docs/variables/OVERFLOWED_CLASS_NAME.md +13 -0
  43. package/.releaserc +1 -0
  44. package/bun.lockb +0 -0
  45. package/dist/dataunit/DataUnit.d.ts +82 -12
  46. package/dist/dataunit/DataUnit.js +209 -67
  47. package/dist/dataunit/DataUnit.js.map +1 -1
  48. package/dist/dataunit/DataUnitHelper.js +6 -5
  49. package/dist/dataunit/DataUnitHelper.js.map +1 -1
  50. package/dist/dataunit/formatting/PrettyFormatter.js +14 -6
  51. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  52. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.d.ts +9 -0
  53. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.js +6 -0
  54. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.js.map +1 -0
  55. package/dist/dataunit/loader/dataUnitInMemoryLoader.d.ts +25 -0
  56. package/dist/dataunit/loader/dataUnitInMemoryLoader.js +131 -0
  57. package/dist/dataunit/loader/dataUnitInMemoryLoader.js.map +1 -0
  58. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.d.ts +20 -0
  59. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.js +62 -0
  60. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.js.map +1 -0
  61. package/dist/dataunit/loading/LoadDataRequest.d.ts +1 -1
  62. package/dist/dataunit/loading/PaginationInfo.d.ts +8 -0
  63. package/dist/dataunit/metadata/DataType.js +3 -0
  64. package/dist/dataunit/metadata/DataType.js.map +1 -1
  65. package/dist/dataunit/sorting/FieldComparator.d.ts +2 -2
  66. package/dist/dataunit/sorting/FieldComparator.js +4 -9
  67. package/dist/dataunit/sorting/FieldComparator.js.map +1 -1
  68. package/dist/dataunit/state/action/DataUnitAction.d.ts +2 -0
  69. package/dist/dataunit/state/action/DataUnitAction.js +2 -0
  70. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  71. package/dist/dataunit/state/slice/InvalidFieldsSlice.js +2 -0
  72. package/dist/dataunit/state/slice/InvalidFieldsSlice.js.map +1 -1
  73. package/dist/dataunit/state/slice/LoadingControlSlice.js +16 -0
  74. package/dist/dataunit/state/slice/LoadingControlSlice.js.map +1 -1
  75. package/dist/dataunit/state/slice/RecordsSlice.js +1 -1
  76. package/dist/dataunit/state/slice/RecordsSlice.js.map +1 -1
  77. package/dist/dataunit/state/slice/SelectionSlice.js +4 -4
  78. package/dist/dataunit/state/slice/SelectionSlice.js.map +1 -1
  79. package/dist/exceptions/ServiceCanceledException.d.ts +14 -0
  80. package/dist/exceptions/ServiceCanceledException.js +13 -0
  81. package/dist/exceptions/ServiceCanceledException.js.map +1 -0
  82. package/dist/exceptions/SilentException.d.ts +14 -0
  83. package/dist/exceptions/SilentException.js +13 -0
  84. package/dist/exceptions/SilentException.js.map +1 -0
  85. package/dist/index.d.ts +12 -2
  86. package/dist/index.js +11 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/repository/IRepository.d.ts +6 -0
  89. package/dist/repository/indexeddb/IDBRepository.d.ts +1 -0
  90. package/dist/repository/indexeddb/IDBRepository.js +3 -0
  91. package/dist/repository/indexeddb/IDBRepository.js.map +1 -1
  92. package/dist/utils/CacheManager/index.d.ts +52 -0
  93. package/dist/utils/CacheManager/index.js +101 -0
  94. package/dist/utils/CacheManager/index.js.map +1 -0
  95. package/dist/utils/CacheManager/interfaces/index.d.ts +5 -0
  96. package/dist/utils/CacheManager/interfaces/index.js +7 -0
  97. package/dist/utils/CacheManager/interfaces/index.js.map +1 -0
  98. package/dist/utils/ColumnFilterManager.d.ts +19 -0
  99. package/dist/utils/ColumnFilterManager.js +73 -0
  100. package/dist/utils/ColumnFilterManager.js.map +1 -0
  101. package/dist/utils/DateUtils.js +3 -0
  102. package/dist/utils/DateUtils.js.map +1 -1
  103. package/dist/utils/ElementUtils.d.ts +2 -0
  104. package/dist/utils/ElementUtils.js +9 -0
  105. package/dist/utils/ElementUtils.js.map +1 -0
  106. package/dist/utils/LockManager.d.ts +58 -0
  107. package/dist/utils/LockManager.js +191 -0
  108. package/dist/utils/LockManager.js.map +1 -0
  109. package/dist/utils/MaskFormatter.d.ts +16 -1
  110. package/dist/utils/MaskFormatter.js +82 -2
  111. package/dist/utils/MaskFormatter.js.map +1 -1
  112. package/dist/utils/ObjectUtils.d.ts +38 -0
  113. package/dist/utils/ObjectUtils.js +51 -0
  114. package/dist/utils/ObjectUtils.js.map +1 -1
  115. package/dist/utils/OnboardingUtils.js +1 -1
  116. package/dist/utils/OnboardingUtils.js.map +1 -1
  117. package/dist/utils/OverflowWatcher/index.d.ts +59 -0
  118. package/dist/utils/OverflowWatcher/index.js +188 -0
  119. package/dist/utils/OverflowWatcher/index.js.map +1 -0
  120. package/dist/utils/OverflowWatcher/types/overflow-callback.d.ts +6 -0
  121. package/dist/utils/OverflowWatcher/types/overflow-callback.js +2 -0
  122. package/dist/utils/OverflowWatcher/types/overflow-callback.js.map +1 -0
  123. package/dist/utils/OverflowWatcher/types/overflow-direction.d.ts +7 -0
  124. package/dist/utils/OverflowWatcher/types/overflow-direction.js +9 -0
  125. package/dist/utils/OverflowWatcher/types/overflow-direction.js.map +1 -0
  126. package/dist/utils/ServiceUtils.d.ts +24 -0
  127. package/dist/utils/ServiceUtils.js +40 -0
  128. package/dist/utils/ServiceUtils.js.map +1 -0
  129. package/dist/utils/SortingUtils.d.ts +9 -0
  130. package/dist/utils/SortingUtils.js +24 -0
  131. package/dist/utils/SortingUtils.js.map +1 -0
  132. package/dist/utils/UserAgentUtils/index.d.ts +1 -0
  133. package/dist/utils/UserAgentUtils/index.js +5 -0
  134. package/dist/utils/UserAgentUtils/index.js.map +1 -1
  135. package/jest.config.ts +2 -0
  136. package/package.json +2 -1
  137. package/reports/test-report.xml +215 -0
  138. package/setupTests.js +7 -0
  139. package/sonar-project.properties +6 -3
  140. package/src/dataunit/DataUnit.ts +250 -82
  141. package/src/dataunit/DataUnitHelper.ts +6 -5
  142. package/src/dataunit/formatting/PrettyFormatter.ts +15 -6
  143. package/src/dataunit/loader/DataUnitInMemoryLoaderConfig.ts +10 -0
  144. package/src/dataunit/loader/dataUnitInMemoryLoader.ts +176 -0
  145. package/src/dataunit/loader/utils/dataUnitLoaderUtils.ts +86 -0
  146. package/src/dataunit/loading/LoadDataRequest.ts +1 -1
  147. package/src/dataunit/loading/PaginationInfo.ts +10 -0
  148. package/src/dataunit/metadata/DataType.ts +3 -0
  149. package/src/dataunit/sorting/FieldComparator.ts +18 -32
  150. package/src/dataunit/state/action/DataUnitAction.ts +2 -0
  151. package/src/dataunit/state/slice/InvalidFieldsSlice.ts +2 -0
  152. package/src/dataunit/state/slice/LoadingControlSlice.ts +42 -0
  153. package/src/dataunit/state/slice/RecordsSlice.ts +1 -1
  154. package/src/dataunit/state/slice/SelectionSlice.ts +4 -4
  155. package/src/dataunit/state/slice/test/RecordsSlice.spec.ts +45 -0
  156. package/src/dataunit/test/DataUnit.spec.ts +44 -0
  157. package/src/exceptions/ServiceCanceledException.ts +25 -0
  158. package/src/exceptions/SilentException.ts +25 -0
  159. package/src/index.ts +29 -1
  160. package/src/repository/IRepository.ts +7 -0
  161. package/src/repository/indexeddb/IDBRepository.ts +4 -0
  162. package/src/utils/CacheManager/index.ts +103 -0
  163. package/src/utils/CacheManager/interfaces/index.ts +5 -0
  164. package/src/utils/ColumnFilterManager.ts +104 -0
  165. package/src/utils/DateUtils.ts +3 -0
  166. package/src/utils/ElementUtils.ts +10 -0
  167. package/src/utils/LockManager.ts +213 -0
  168. package/src/utils/MaskFormatter.ts +93 -2
  169. package/src/utils/ObjectUtils.ts +56 -0
  170. package/src/utils/OnboardingUtils.ts +1 -1
  171. package/src/utils/OverflowWatcher/index.ts +243 -0
  172. package/src/utils/OverflowWatcher/types/overflow-callback.ts +6 -0
  173. package/src/utils/OverflowWatcher/types/overflow-direction.ts +7 -0
  174. package/src/utils/ServiceUtils.ts +36 -0
  175. package/src/utils/SortingUtils.ts +30 -0
  176. package/src/utils/UserAgentUtils/index.ts +6 -1
  177. package/src/utils/test/objectUtils.spec.ts +109 -0
  178. package/test/dataunit/formatting/PrettyFormatter.spec.ts +177 -0
  179. package/test/dataunit/loader/dataUnitInMemoryLoader.spec.ts +221 -0
  180. package/test/dataunit/loader/utils/dataUnitLoaderUtils.spec.ts +158 -0
  181. package/test/util/ColumnFilterManager.spec.ts +133 -0
  182. package/test/util/ElementUtils.spec.ts +34 -0
  183. package/test/util/OverflowWatcher.spec.ts +152 -0
@@ -26,6 +26,9 @@ 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.
@@ -33,27 +36,32 @@ import { getLoadingProperties, LoadingPropertiesReducer } from "./state/slice/Lo
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;
57
+ private _cancelPagination: boolean = false;
51
58
 
52
59
  public metadataLoader?: (dataUnit: DataUnit) => Promise<UnitMetadata>;
53
60
  public dataLoader?: (dataUnit: DataUnit, request: LoadDataRequest) => Promise<LoadDataResponse>;
54
61
  public saveLoader?: (dataUnit: DataUnit, changes: Array<Change>) => Promise<Array<SavedRecord>>;
55
62
  public removeLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<string>>;
56
63
  public recordLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<Record>>;
64
+ public allRecordsLoader?: (dataUnit: DataUnit) => Array<Record> | undefined;
57
65
 
58
66
  constructor(name: string = DataUnit.DEFAULT_DATAUNIT_NAME, parentDataUnit?: DataUnit) {
59
67
  this._uuid = uuid()
@@ -75,11 +83,12 @@ export default class DataUnit {
75
83
  SnapshotReducer
76
84
  ]
77
85
  );
78
- this._observers = [];
86
+ this._observers = new Map();
79
87
  this._filterProviders = new Map<string, FilterProvider>();
80
88
  this._sortingProvider = undefined;
81
89
  this._defaultSorting = [];
82
- this._interceptors = [];
90
+ this._allowReleaseCallbacks = true;
91
+ this._interceptors = new Map();
83
92
  this._parentDataUnit = parentDataUnit;
84
93
  this._parentDataUnit?.subscribe(this.onDataUnitParentEvent);
85
94
  this._loadingLockers = [];
@@ -110,10 +119,12 @@ export default class DataUnit {
110
119
  * - Sorting Providers
111
120
  */
112
121
  public releaseCallbacks(){
113
- this._observers = [];
122
+ if(!this._allowReleaseCallbacks) return;
123
+
124
+ this._observers = new Map();
114
125
  this._filterProviders = new Map<string, FilterProvider>();
115
126
  this._sortingProvider = undefined;
116
- this._interceptors = [];
127
+ this._interceptors = new Map();
117
128
  }
118
129
 
119
130
  /**
@@ -142,6 +153,36 @@ export default class DataUnit {
142
153
  return this._uuid;
143
154
  }
144
155
 
156
+
157
+ /**
158
+ * Retorna se o dataUnit está com recarregamento pendente.
159
+ */
160
+ public isWaitingToReload(): boolean {
161
+ return this._waitingToReload;
162
+ }
163
+
164
+ /**
165
+ * Define se o dataUnit tem um recarregamento pendente.
166
+ * @param isWaiting
167
+ */
168
+ public setWaitingToReload(isWaiting: boolean){
169
+ this._waitingToReload = isWaiting;
170
+ }
171
+
172
+ /**
173
+ * Informa se a paginação deve ser cancelada.
174
+ */
175
+ public get cancelPagination(): boolean {
176
+ return this._cancelPagination;
177
+ }
178
+
179
+ /**
180
+ * Informa se a paginação deve ser cancelada.
181
+ */
182
+ public set cancelPagination(cancelPagination: boolean) {
183
+ this._cancelPagination = cancelPagination;
184
+ }
185
+
145
186
  /**
146
187
  *
147
188
  * Obtém o nome de identificação do DataUnit (geralmente em formato de URI - Uniform Resource Identifier).
@@ -180,6 +221,8 @@ export default class DataUnit {
180
221
  case Action.NEXT_SELECTED:
181
222
  case Action.PREVIOUS_SELECTED:
182
223
  case Action.DATA_LOADED:
224
+ case Action.RECORDS_ADDED:
225
+ case Action.EDITION_CANCELED:
183
226
  this.clearDataUnit();
184
227
  const selectedRecord = this._parentDataUnit?.getSelectedRecord();
185
228
  if(selectedRecord != undefined && !this.isNewRecord(selectedRecord.__record__id__)){
@@ -241,10 +284,10 @@ export default class DataUnit {
241
284
  async response => {
242
285
  await this.dispatchAction(
243
286
  Action.DATA_LOADED,
244
- {...response, keepSelection: request.keepSelection, filters: request.filters},
287
+ {...response, keepSelection: request.keepSelection, filters: request.filters, selectFirstRecord},
245
288
  executionCtx
246
289
  );
247
-
290
+
248
291
  await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
249
292
 
250
293
  if(selectFirstRecord){
@@ -317,13 +360,16 @@ export default class DataUnit {
317
360
 
318
361
  await this.processLoadingLockers();
319
362
 
320
- if (this._parentDataUnit && !this._parentDataUnit.getSelectedRecord()) {
363
+ if (this._parentDataUnit) {
364
+ const parentRecord = this._parentDataUnit.getSelectedRecord();
365
+ if(parentRecord == undefined || this._parentDataUnit.isNewRecord(parentRecord.__record__id__)){
321
366
  if(this.records){
322
367
  this.clearDataUnit();
323
368
  }
324
369
  return Promise.resolve({
325
370
  records: []
326
371
  });
372
+ }
327
373
  }
328
374
 
329
375
  const loadDataRequest = this.getLoadDataRequest(quickFilter, source);
@@ -336,11 +382,14 @@ export default class DataUnit {
336
382
  *
337
383
  * @param page - Número da página desejada.
338
384
  * @param executionCtx - Contexto de execução do carregamento dos registros do DataUnit.
339
- *
385
+ *
340
386
  * @returns - Registros da página desejada.
341
387
  *
342
388
  */
343
389
  public async gotoPage(page: number, executionCtx?: ExecutionContext): Promise<LoadDataResponse | void> {
390
+ if(this._pageSize === 0) {
391
+ return;
392
+ }
344
393
 
345
394
  let request = getCurrentRequest(this._stateManager);
346
395
 
@@ -471,7 +520,16 @@ export default class DataUnit {
471
520
  Promise.all(dispatchPromisses).then(() => resolve());
472
521
  }).catch(cause => {
473
522
  const {errorCode} = cause;
523
+ this.dispatchAction(Action.SAVING_ERROR);
474
524
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
525
+ if(cause instanceof ServiceCanceledException){
526
+ console.debug("Service canceled: " + cause.message);
527
+ resolve();
528
+ }
529
+ if(cause instanceof SilentException){
530
+ fail(cause);
531
+ return;
532
+ }
475
533
  fail(new ErrorException("Erro ao salvar alterações", cause, errorCode));
476
534
  });
477
535
  } else {
@@ -541,8 +599,8 @@ export default class DataUnit {
541
599
  if(selection.isAllRecords()){
542
600
  throw new Error("Exclusão remota não implementada.");
543
601
  }
544
- const records = selection?.records || [];
545
- const recordIds = selection?.recordIds || [];
602
+ const records = selection?.records as Array<Record> || [];
603
+ const recordIds = selection?.recordIds as Array<string> || [];
546
604
  return this.removeRecords(recordIds, records, buffered, undefined, silent);
547
605
  }
548
606
  return Promise.resolve([]);
@@ -576,10 +634,11 @@ export default class DataUnit {
576
634
  const currentRecordsKeys = Array.from(getCurrentRecords(this._stateManager).keys());
577
635
  const removedIndex: Array<number> = recordIds
578
636
  .map(id => currentRecordsKeys.indexOf(id))
579
- .filter(index => index > -1);
637
+ .filter(index => index > -1)
638
+ .sort((a, b) => a - b);
580
639
 
581
640
  const nextIndex = Math.min(removedIndex.slice(-1)[0] + 1, currentRecordsKeys.length);
582
- const selectionAfterRemove = [currentRecordsKeys[nextIndex]];
641
+ const selectionAfterRemove = currentRecordsKeys[nextIndex] ? [currentRecordsKeys[nextIndex]] : [];
583
642
 
584
643
  this.dispatchAction(Action.RECORDS_REMOVED, { records: removedIds, cachedRecords, removedIndex, buffered: false, selectionAfterRemove }, executionCtx);
585
644
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
@@ -643,7 +702,6 @@ export default class DataUnit {
643
702
  *
644
703
  */
645
704
  public getFormattedValue(fieldName: string, value?: any): string {
646
-
647
705
  const descriptor = this.getField(fieldName);
648
706
  if (value == undefined) {
649
707
  value = this.getFieldValue(fieldName);
@@ -666,7 +724,7 @@ export default class DataUnit {
666
724
  *
667
725
  */
668
726
  public addInterceptor(interceptor: DUActionInterceptor): void {
669
- this._interceptors.push(interceptor);
727
+ this._interceptors.set(interceptor.interceptAction.toString(), interceptor);
670
728
  }
671
729
 
672
730
  /**
@@ -677,7 +735,7 @@ export default class DataUnit {
677
735
  *
678
736
  */
679
737
  public removeInterceptor(interceptor: DUActionInterceptor) {
680
- this._interceptors = this._interceptors.filter(i => i !== interceptor);
738
+ this._interceptors.delete(interceptor.interceptAction.toString())
681
739
  }
682
740
 
683
741
  /**
@@ -722,6 +780,7 @@ export default class DataUnit {
722
780
  return this.dispatchAction(Action.PAGINATION_UPDATED, info, undefined);
723
781
  }
724
782
 
783
+
725
784
  /**
726
785
  *
727
786
  * Define a lógica de ordenação dos registros.
@@ -786,7 +845,8 @@ export default class DataUnit {
786
845
  *
787
846
  */
788
847
  public set records(records: Array<Record>) {
789
- this.dispatchAction(Action.DATA_LOADED, { records }, undefined);
848
+ const paginationInfo = this.getPaginationInfo();
849
+ this.dispatchAction(Action.DATA_LOADED, { records, paginationInfo }, undefined);
790
850
  }
791
851
 
792
852
  /**
@@ -879,8 +939,12 @@ export default class DataUnit {
879
939
  public copySelected(executionCtx?: ExecutionContext): void {
880
940
  const selectionInfo = this.getSelectionInfo();
881
941
  if (selectionInfo) {
882
- const selectedRecords = selectionInfo.records;
942
+ if(selectionInfo.isAllRecords()){
943
+ throw new Error("Erro interno: Impossível copiar os registros selecionados pois a seleção atual é virtual.");
944
+ }
945
+ const selectedRecords = selectionInfo.records as Array<Record>;
883
946
  if(selectedRecords){
947
+
884
948
  this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.getParentRecordId()), executionCtx);
885
949
  }
886
950
  }
@@ -946,21 +1010,31 @@ export default class DataUnit {
946
1010
  * @param fieldName -Identificador do campo a ser modificado.
947
1011
  * @param newValue - Valor a ser inserido no campo.
948
1012
  * @param records - Indica quais registros foram afetados pela alteração no valor do campo.
1013
+ * @param options - Configurações do evento
949
1014
  * @returns - Promise que será resolvida quando o novo valor for persistido no state.
950
- *
951
- */
952
- public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>): Promise<boolean> {
1015
+ *
1016
+ */
1017
+ public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>, options?:DataUnitEventOptions): Promise<boolean> {
1018
+ const noRecordSelected = !this.hasNewRecord() && !this.getSelectedRecord();
1019
+
1020
+ const suppressCreateNewRecord = options?.suppressCreateNewRecord;
1021
+
1022
+ if(noRecordSelected && suppressCreateNewRecord) return Promise.resolve(false);
1023
+
1024
+ if(noRecordSelected) await this.addRecord();
953
1025
 
954
- if(!this.hasNewRecord() && !this.getSelectedRecord()) await this.addRecord();
955
-
956
1026
  const typedValue = this.validateAndTypeValue(fieldName, newValue);
957
1027
  const currentValue = this.getFieldValue(fieldName);
958
-
1028
+
1029
+ if(!(newValue instanceof Promise) && ObjectUtils.hasEquivalentProps(currentValue, typedValue)){
1030
+ return Promise.resolve(false);
1031
+ }
1032
+
959
1033
  if(newValue instanceof Promise){
960
1034
  const promise:Promise<boolean> = new Promise(accept => {
961
1035
  newValue.then(resolvedValue => {
962
1036
  this.dispatchAction(Action.DATA_RESOLVED, { [fieldName]: resolvedValue, records }, undefined);
963
- accept(this.setFieldValue(fieldName, resolvedValue, records));
1037
+ accept(this.setFieldValue(fieldName, resolvedValue, records, options));
964
1038
  });
965
1039
  });
966
1040
  this._savingLockers.push(promise);
@@ -968,7 +1042,7 @@ export default class DataUnit {
968
1042
  }
969
1043
 
970
1044
  if (currentValue !== typedValue) {
971
- const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined);
1045
+ const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined, options);
972
1046
  this._savingLockers.push(promise);
973
1047
  return promise;
974
1048
  }
@@ -989,6 +1063,18 @@ export default class DataUnit {
989
1063
  this.dispatchAction(Action.FIELD_INVALIDATED, { fieldName, message, recordId }, undefined);
990
1064
  }
991
1065
 
1066
+ /**
1067
+ *
1068
+ * Cancela o saving exibindo os campos invalidos.
1069
+ *
1070
+ * @param filds - Lista dos campos
1071
+ * @param recordId - Indica qual registro está com os campos inválido.
1072
+ *
1073
+ */
1074
+ public savingCanceled(fields: Array<{name: string, message: string}>, recordId: string): void {
1075
+ this.dispatchAction(Action.SAVING_CANCELED, { fields, recordId }, undefined);
1076
+ }
1077
+
992
1078
  /**
993
1079
  *
994
1080
  * Limpa campos inválidos.
@@ -1096,7 +1182,7 @@ export default class DataUnit {
1096
1182
  public setSelection(selection: Array<string> | SelectionMode.ALL_RECORDS, executionCtx?: ExecutionContext): Promise<SelectionInfo> {
1097
1183
  return new Promise(resolve => {
1098
1184
  this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection }, executionCtx)
1099
- .then(()=>resolve(getSelectionInfo(this._stateManager)));
1185
+ .then(()=>resolve(this.getSelectionInfo()));
1100
1186
  });
1101
1187
  }
1102
1188
 
@@ -1117,35 +1203,53 @@ export default class DataUnit {
1117
1203
  *
1118
1204
  * @param selection - IDs dos registros selecionados no snapshot atual
1119
1205
  * @param executionCtx - Contexto de execução da seleção dos registros do DataUnit.
1120
- *
1121
1206
  * @returns - Informações sobre a seleção.
1122
1207
  */
1123
1208
  public updatePageSelection(selection: Array<string>, executionCtx?: ExecutionContext): Promise<SelectionInfo|undefined>{
1124
-
1125
- if(!selection){
1126
- return Promise.resolve(this.getSelectionInfo());
1127
- }
1209
+ if(!selection) return Promise.resolve(this.getSelectionInfo());
1128
1210
 
1129
1211
  return new Promise(resolve => {
1130
- const newSelection: Set<string> = new Set(getSelection(this._stateManager));
1131
- const currentRecords = getCurrentRecords(this._stateManager) || new Map();
1132
- Array.from(currentRecords.keys()).forEach(
1133
- recordId => {
1134
- if(selection.includes(recordId)){
1135
- newSelection.add(recordId);
1136
- } else {
1137
- newSelection.delete(recordId);
1138
- }
1139
- }
1140
- );
1212
+ const newSelection: Set<string> = new Set(selection);
1141
1213
 
1142
1214
  this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection: Array.from(newSelection) }, executionCtx)
1143
- .then(()=>{
1144
- resolve(this.getSelectionInfo())
1145
- });
1215
+ .then(()=> resolve(this.getSelectionInfo()));
1216
+ });
1217
+ }
1218
+
1219
+
1220
+ private updatePageSelectionAll(addRecords: boolean): Promise<SelectionInfo|undefined>{
1221
+ return new Promise(resolve => {
1222
+ const newSelection: Set<string> = new Set<string>();
1223
+
1224
+ const cachedSelection = getSelection(this._stateManager);
1225
+ cachedSelection.forEach(item => newSelection.add(item));
1226
+
1227
+ const recordsIds = this.records.map(r => r.__record__id__);
1228
+ recordsIds.forEach(id => addRecords ? newSelection.add(id): newSelection.delete(id));
1229
+
1230
+ this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection: Array.from(newSelection) })
1231
+ .then(()=> resolve(this.getSelectionInfo()));
1146
1232
  });
1147
1233
  }
1148
1234
 
1235
+ /**
1236
+ * Seleciona todos os registros da página.
1237
+ *
1238
+ * @returns - Informações sobre a seleção.
1239
+ */
1240
+ public selectAllRecords(): Promise<SelectionInfo|undefined>{
1241
+ return this.updatePageSelectionAll(true);
1242
+ }
1243
+
1244
+ /**
1245
+ * Desseleciona todos os registros da página.
1246
+ *
1247
+ * @returns - Informações sobre a seleção.
1248
+ */
1249
+ public unSelectAllRecords(): Promise<SelectionInfo|undefined>{
1250
+ return this.updatePageSelectionAll(false);
1251
+ }
1252
+
1149
1253
  /**
1150
1254
  *
1151
1255
  * Obtém informações sobre a seleção atual.
@@ -1153,8 +1257,16 @@ export default class DataUnit {
1153
1257
  * @returns - Objeto com informações como registros selecionados e seleção por critério.
1154
1258
  *
1155
1259
  **/
1156
- public getSelectionInfo(): SelectionInfo | undefined {
1157
- return getSelectionInfo(this._stateManager)
1260
+ public getSelectionInfo(): SelectionInfo{
1261
+ const selectionInfo: SelectionInfo = getSelectionInfo(this._stateManager);
1262
+ selectionInfo.getAllRecords = () => this.allRecordsLoader?.(this);
1263
+
1264
+ if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1265
+ const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
1266
+ selectionInfo.records?.sort(sortingFunction)
1267
+ }
1268
+
1269
+ return selectionInfo;
1158
1270
  }
1159
1271
 
1160
1272
  /**
@@ -1168,6 +1280,15 @@ export default class DataUnit {
1168
1280
  return getSelectedRecord(this._stateManager);
1169
1281
  }
1170
1282
 
1283
+ /**
1284
+ * Retorna o DataUnit pai
1285
+ *
1286
+ * @returns DataUnit pai ou undefined
1287
+ */
1288
+ public getParentDataUnit(): DataUnit | undefined {
1289
+ return this._parentDataUnit;
1290
+ }
1291
+
1171
1292
  /**
1172
1293
  *
1173
1294
  * Limpa todos os registros do DataUnit
@@ -1255,14 +1376,28 @@ export default class DataUnit {
1255
1376
  /**
1256
1377
  *
1257
1378
  * Retorna se existe algum tipo de alteração pendente.
1258
- *
1379
+ *
1380
+ * @param ignoreChildren: Define se deverá ignorar alterações pendentes no DataUnit filho.
1259
1381
  * @returns Verdadeiro se existir alterações pendentes.
1260
1382
  *
1261
1383
  */
1262
- public isDirty(): boolean {
1384
+ public isDirty(ignoreChildren?: boolean): boolean {
1385
+ if(ignoreChildren) return isDirty(this._stateManager);
1386
+
1263
1387
  return isDirty(this._stateManager) || this.childrenIsDirty();
1264
1388
  }
1265
1389
 
1390
+ /**
1391
+ *
1392
+ * Retorna se existe alterações pendentes no DataUnit pai.
1393
+ *
1394
+ * @returns Verdadeiro se existir alterações pendentes e Falso caso não exista alterações ou não exista DataUnit pai.
1395
+ *
1396
+ */
1397
+ public isParentDirty(): boolean {
1398
+ return this._parentDataUnit ? this._parentDataUnit.isDirty(true) : false;
1399
+ }
1400
+
1266
1401
  /**
1267
1402
  *
1268
1403
  * Retorna se existe algum DataUnit detail com alterações pendentes.
@@ -1435,17 +1570,17 @@ export default class DataUnit {
1435
1570
  * @returns - Verdadeiro se ação iniciada.
1436
1571
  *
1437
1572
  */
1438
- private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext): Promise<boolean> {
1573
+ private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext, options?:DataUnitEventOptions): Promise<boolean> {
1439
1574
  return new Promise(async resolve => {
1440
1575
  let action = new DataUnitAction(actionType, payload);
1441
1576
  if (executionCtx && executionCtx.before) {
1442
1577
  action = executionCtx.before(action);
1443
1578
  }
1444
- if (action && this._interceptors && this._interceptors.length > 0) {
1579
+ if (action && this._interceptors && this._interceptors.size > 0) {
1445
1580
  action = await this.intercept(action, this._interceptors.values());
1446
1581
  }
1447
1582
  if (action) {
1448
- this.doDispatchAction(action);
1583
+ this.doDispatchAction(action, options);
1449
1584
  if (executionCtx && executionCtx.after) {
1450
1585
  executionCtx.after(action)
1451
1586
  }
@@ -1483,16 +1618,13 @@ export default class DataUnit {
1483
1618
  * @param action - Ações em execução no DataUnit.
1484
1619
  *
1485
1620
  */
1486
- private doDispatchAction(action: DataUnitAction): void {
1621
+ private doDispatchAction(action: DataUnitAction, options:DataUnitEventOptions = {}): void {
1487
1622
  this._stateManager.process(action);
1488
1623
  this?._parentDataUnit?.dispatchAction(Action.CHILD_CHANGED, { srcAction: action, srcDataUnit: this });
1489
1624
  this._observers.forEach(f => {
1490
- /*
1491
- if some observer throws exceptions,
1492
- should be continued
1493
- */
1625
+ //if some observer throws exceptions, should be continued
1494
1626
  try {
1495
- f(action);
1627
+ f(action, options);
1496
1628
  } catch (e) {
1497
1629
  console.warn("[DataUnit] error while call observer", e);
1498
1630
  }
@@ -1529,10 +1661,17 @@ export default class DataUnit {
1529
1661
  * Ela vai ser chamada sempre que uma ação for despachada (dispatchAction()).
1530
1662
  *
1531
1663
  * @param observer - Função que recebe como parâmetro as ações que serão monitoradas.
1532
- *
1664
+ * @param uuid - Identificador do observer. Quando não informado, será gerado um identificador aleatório.
1533
1665
  */
1534
- public subscribe(observer: (action: DataUnitAction) => void) {
1535
- this._observers.push(observer);
1666
+ public subscribe(observer: (action: DataUnitAction, options?: DataUnitEventOptions) => void | Promise<void>, uuid?: string): string {
1667
+ if (uuid) {
1668
+ this._observers.set(uuid, observer);
1669
+ } else {
1670
+ uuid = StringUtils.generateUUID();
1671
+ this._observers.set(uuid, observer);
1672
+ }
1673
+
1674
+ return uuid;
1536
1675
  }
1537
1676
 
1538
1677
  /**
@@ -1540,10 +1679,18 @@ export default class DataUnit {
1540
1679
  * Remove um observer existente.
1541
1680
  *
1542
1681
  * @param observer - Observer que se deseja remover.
1543
- *
1682
+ * @param uuid - Identificador do observer. Quando não informado o delete removera com base no equals do observer.
1544
1683
  */
1545
- public unsubscribe(observer: Function) {
1546
- this._observers = this._observers.filter(f => f !== observer);
1684
+ public unsubscribe(observer: Function, uuid?: string) {
1685
+ if (uuid) {
1686
+ this._observers.delete(uuid);
1687
+ } else {
1688
+ this._observers.forEach((valor, chave) => {
1689
+ if (valor == observer) {
1690
+ this._observers.delete(chave);
1691
+ }
1692
+ });
1693
+ }
1547
1694
  }
1548
1695
 
1549
1696
  /**
@@ -1556,18 +1703,21 @@ export default class DataUnit {
1556
1703
  public reloadCurrentRecord(): Promise<Array<Record>>{
1557
1704
  return new Promise(async (resolve, fail) => {
1558
1705
  const selection = getSelection(this._stateManager);
1559
- this.dispatchAction(Action.LOADING_RECORD, selection);
1706
+ await this.dispatchAction(Action.LOADING_RECORD, selection);
1560
1707
 
1561
1708
  if(!this.recordLoader || !this.dataLoader) {
1562
- this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1709
+ await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1710
+ resolve([]);
1563
1711
  return;
1564
1712
  }
1565
1713
 
1566
- this.recordLoader(this, selection).then(response => {
1567
- this.dispatchAction(Action.RECORD_LOADED, response)
1568
- this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1569
- }).catch(cause => {
1570
- this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1714
+ this.recordLoader(this, selection).then(async response => {
1715
+ await this.dispatchAction(Action.RECORD_LOADED, response)
1716
+ await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1717
+
1718
+ resolve(response);
1719
+ }).catch(async cause => {
1720
+ await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1571
1721
  const {errorCode} = cause;
1572
1722
  fail(new ErrorException("Erro ao recarregar registro", cause, errorCode));
1573
1723
  });
@@ -1662,11 +1812,12 @@ export default class DataUnit {
1662
1812
  * @param fieldName - nome do campo para ficar invisível.
1663
1813
  *
1664
1814
  */
1665
- public hideField(fieldName:string) {
1815
+ public hideField(fieldName:string, options:HideFieldOptions) {
1666
1816
  const fieldDescriptor = this.getField(fieldName);
1667
1817
 
1668
1818
  if(fieldDescriptor?.visible === true){
1669
1819
  fieldDescriptor.visible = false;
1820
+ fieldDescriptor.properties = {...fieldDescriptor.properties, visibleOnConfig: options.visibleOnConfig };
1670
1821
  this.metadata = {...this.metadata};
1671
1822
  }
1672
1823
  }
@@ -1706,7 +1857,7 @@ export default class DataUnit {
1706
1857
  }
1707
1858
 
1708
1859
  if (selection) {
1709
- return selection.records;
1860
+ return selection.records as Array<Record>;
1710
1861
  }
1711
1862
  }
1712
1863
 
@@ -1724,12 +1875,15 @@ export default class DataUnit {
1724
1875
  public getSelection(): Array<string> {
1725
1876
  console.warn("DataUnit: O método `getSelection` foi descontinuado. Use o método `getSelectionInfo`.");
1726
1877
  const selection = this.getSelectionInfo();
1878
+ if(selection == undefined){
1879
+ return [];
1880
+ }
1727
1881
 
1728
- if(selection != undefined && selection.isAllRecords()){
1882
+ if(selection.isAllRecords()){
1729
1883
  throw new Error("Erro interno: Impossível retornar os registros selecionados. A seleção atual é virtual. Use o método `getSelectionInfo`.");
1730
1884
  }
1731
1885
 
1732
- return selection?.recordIds || [];
1886
+ return selection?.recordIds as Array<string>;
1733
1887
  }
1734
1888
 
1735
1889
  /**
@@ -1765,6 +1919,10 @@ export default class DataUnit {
1765
1919
  return loadingLockerResolver || Promise.resolve;
1766
1920
  }
1767
1921
 
1922
+ public set allowReleaseCallbacks(allow: boolean){
1923
+ this._allowReleaseCallbacks = allow;
1924
+ }
1925
+
1768
1926
  private async processLoadingLockers(){
1769
1927
  if(this._loadingLockers.length) {
1770
1928
  await Promise.all(this._loadingLockers);
@@ -1906,12 +2064,13 @@ export enum SelectionMode{
1906
2064
 
1907
2065
  export class SelectionInfo{
1908
2066
 
1909
- private _records: Array<Record>;
1910
2067
  public mode: SelectionMode;
1911
- private _total?: number;
1912
2068
  public filters?: Array<Filter>;
1913
2069
  public sort?: Array<Sort>;
1914
-
2070
+ public getAllRecords?: () => Array<Record> | undefined;
2071
+ private _records: Array<Record>;
2072
+ private _total?: number;
2073
+
1915
2074
  constructor(records: Array<Record>, mode: SelectionMode = SelectionMode.SOME_RECORDS, total?:number, filters?: Array<Filter>, sort?: Array<Sort>){
1916
2075
  this._records = records;
1917
2076
  this._total = total;
@@ -1922,7 +2081,10 @@ export class SelectionInfo{
1922
2081
 
1923
2082
  public get records(): Array<Record> | undefined{
1924
2083
  if(this.isAllRecords()){
1925
- throw new Error("Erro interno: [ALL_RECORDS] - Impossível retornar os registros selecionados numa seleção virtual.")
2084
+ if(this.getAllRecords != undefined){
2085
+ return this.getAllRecords();
2086
+ }
2087
+ throw new Error("Erro interno: Impossível retornar os registros selecionados numa seleção virtual.");
1926
2088
  }
1927
2089
  return this._records;
1928
2090
  }
@@ -1941,7 +2103,7 @@ export class SelectionInfo{
1941
2103
  if(this.isAllRecords()){
1942
2104
  return this._total || 0;
1943
2105
  }
1944
- return this.records == undefined ? 0: this.records.length;
2106
+ return this.records == undefined ? 0 : (this.records as Array<Record>).length;
1945
2107
  }
1946
2108
 
1947
2109
  public isAllRecords(): boolean{
@@ -1952,3 +2114,9 @@ export class SelectionInfo{
1952
2114
  return this.length === 0;
1953
2115
  }
1954
2116
  }
2117
+
2118
+ export type DataUnitEventOptions = {[key:string]: any};
2119
+
2120
+ export type HideFieldOptions = {
2121
+ visibleOnConfig: boolean
2122
+ };