@sankhyalabs/core 5.20.0-dev.4 → 5.20.0-dev.40

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 (163) hide show
  1. package/.docs/README.md +3 -1
  2. package/.docs/classes/ApplicationContext.md +31 -32
  3. package/.docs/classes/ArrayUtils.md +95 -83
  4. package/.docs/classes/AuthorizedServiceCaller.md +25 -34
  5. package/.docs/classes/Change.md +59 -74
  6. package/.docs/classes/DataUnit.md +1110 -1086
  7. package/.docs/classes/DataUnitAction.md +25 -42
  8. package/.docs/classes/DataUnitStorage.md +40 -43
  9. package/.docs/classes/DateUtils.md +133 -126
  10. package/.docs/classes/ElementIDUtils.md +123 -122
  11. package/.docs/classes/ErrorException.md +67 -88
  12. package/.docs/classes/ErrorTracking.md +20 -23
  13. package/.docs/classes/FieldComparator.md +35 -39
  14. package/.docs/classes/FloatingManager.md +195 -198
  15. package/.docs/classes/HTMLBuilder.md +14 -20
  16. package/.docs/classes/HttpProvider.md +45 -41
  17. package/.docs/classes/IDBRepository.md +201 -196
  18. package/.docs/classes/JSUtils.md +65 -66
  19. package/.docs/classes/KeyboardManager.md +95 -87
  20. package/.docs/classes/{MaskFormatter-1.md → MaskFormatter.md} +93 -128
  21. package/.docs/classes/NumberUtils.md +163 -152
  22. package/.docs/classes/ObjectUtils.md +206 -70
  23. package/.docs/classes/OnboardingUtils.md +36 -51
  24. package/.docs/classes/OverflowWatcher.md +533 -0
  25. package/.docs/classes/PromiseSync.md +25 -42
  26. package/.docs/classes/ReadyUtil.md +31 -41
  27. package/.docs/classes/RequestMetadata.md +29 -30
  28. package/.docs/classes/SearchUtils.md +18 -20
  29. package/.docs/classes/SelectionInfo.md +73 -74
  30. package/.docs/classes/SkwHttpProvider.md +33 -45
  31. package/.docs/classes/StringUtils.md +297 -322
  32. package/.docs/classes/TimeFormatter.md +43 -44
  33. package/.docs/classes/UserAgentUtils.md +17 -20
  34. package/.docs/classes/VersionUtils.md +15 -18
  35. package/.docs/classes/WaitingChangeException.md +63 -84
  36. package/.docs/classes/WarningException.md +67 -88
  37. package/.docs/enumerations/Action.md +307 -0
  38. package/.docs/enumerations/ChangeOperation.md +47 -0
  39. package/.docs/enumerations/DataType.md +57 -0
  40. package/.docs/enumerations/DependencyType.md +37 -0
  41. package/.docs/enumerations/OverflowDirection.md +29 -0
  42. package/.docs/enumerations/SelectionMode.md +27 -0
  43. package/.docs/enumerations/SortMode.md +27 -0
  44. package/.docs/enumerations/UserInterface.md +177 -0
  45. package/.docs/functions/defaultDataLoader.md +25 -0
  46. package/.docs/{modules.md → globals.md} +26 -36
  47. package/.docs/interfaces/ChildDescriptor.md +12 -16
  48. package/.docs/interfaces/ChildLink.md +9 -12
  49. package/.docs/interfaces/DUActionInterceptor.md +10 -14
  50. package/.docs/interfaces/ExecutionContext.md +17 -32
  51. package/.docs/interfaces/FieldDescriptor.md +52 -66
  52. package/.docs/interfaces/Filter.md +13 -17
  53. package/.docs/interfaces/IElementIDInfo.md +11 -14
  54. package/.docs/interfaces/ILoadResult.md +11 -16
  55. package/.docs/interfaces/IRepository.md +106 -93
  56. package/.docs/interfaces/IRepositoryIndex.md +23 -30
  57. package/.docs/interfaces/LoadDataRequest.md +36 -45
  58. package/.docs/interfaces/LoadDataResponse.md +11 -14
  59. package/.docs/interfaces/OverFlowWatcherParams.md +67 -0
  60. package/.docs/interfaces/PageRequest.md +16 -20
  61. package/.docs/interfaces/PaginationInfo.md +24 -31
  62. package/.docs/interfaces/PromiseSyncCallback.md +13 -17
  63. package/.docs/interfaces/QuickFilter.md +17 -21
  64. package/.docs/interfaces/Record.md +26 -33
  65. package/.docs/interfaces/SavedRecord.md +33 -41
  66. package/.docs/interfaces/Sort.md +12 -16
  67. package/.docs/interfaces/SortingProvider.md +10 -13
  68. package/.docs/interfaces/UnitMetadata.md +16 -21
  69. package/.docs/interfaces/WaitingChange.md +16 -20
  70. package/.docs/namespaces/MaskFormatter/README.md +17 -0
  71. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +13 -0
  72. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +13 -0
  73. package/.docs/type-aliases/DataUnitEventOptions.md +17 -0
  74. package/.docs/type-aliases/OnOverflowCallBack.md +25 -0
  75. package/.docs/variables/OVERFLOWED_CLASS_NAME.md +13 -0
  76. package/dist/dataunit/DataUnit.d.ts +45 -6
  77. package/dist/dataunit/DataUnit.js +116 -30
  78. package/dist/dataunit/DataUnit.js.map +1 -1
  79. package/dist/dataunit/formatting/PrettyFormatter.js +11 -7
  80. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  81. package/dist/dataunit/loading/LoadDataRequest.d.ts +1 -1
  82. package/dist/dataunit/metadata/DataType.js +13 -1
  83. package/dist/dataunit/metadata/DataType.js.map +1 -1
  84. package/dist/dataunit/state/action/DataUnitAction.d.ts +4 -1
  85. package/dist/dataunit/state/action/DataUnitAction.js +3 -0
  86. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  87. package/dist/dataunit/state/slice/ChangesSlice.js +12 -11
  88. package/dist/dataunit/state/slice/ChangesSlice.js.map +1 -1
  89. package/dist/dataunit/state/slice/InvalidFieldsSlice.js +2 -0
  90. package/dist/dataunit/state/slice/InvalidFieldsSlice.js.map +1 -1
  91. package/dist/dataunit/state/slice/LoadingProperties.d.ts +9 -0
  92. package/dist/dataunit/state/slice/LoadingProperties.js +31 -0
  93. package/dist/dataunit/state/slice/LoadingProperties.js.map +1 -0
  94. package/dist/dataunit/state/slice/SelectionSlice.js +4 -4
  95. package/dist/dataunit/state/slice/SelectionSlice.js.map +1 -1
  96. package/dist/index.d.ts +3 -2
  97. package/dist/index.js +2 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/repository/IRepository.d.ts +6 -0
  100. package/dist/repository/indexeddb/IDBRepository.d.ts +1 -0
  101. package/dist/repository/indexeddb/IDBRepository.js +3 -0
  102. package/dist/repository/indexeddb/IDBRepository.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/MaskFormatter.d.ts +2 -1
  107. package/dist/utils/MaskFormatter.js +5 -1
  108. package/dist/utils/MaskFormatter.js.map +1 -1
  109. package/dist/utils/ObjectUtils.d.ts +38 -0
  110. package/dist/utils/ObjectUtils.js +51 -0
  111. package/dist/utils/ObjectUtils.js.map +1 -1
  112. package/dist/utils/OnboardingUtils.js +1 -1
  113. package/dist/utils/OnboardingUtils.js.map +1 -1
  114. package/dist/utils/OverflowWatcher/index.d.ts +59 -0
  115. package/dist/utils/OverflowWatcher/index.js +188 -0
  116. package/dist/utils/OverflowWatcher/index.js.map +1 -0
  117. package/dist/utils/OverflowWatcher/types/overflow-callback.d.ts +6 -0
  118. package/dist/utils/OverflowWatcher/types/overflow-callback.js +2 -0
  119. package/dist/utils/OverflowWatcher/types/overflow-callback.js.map +1 -0
  120. package/dist/utils/OverflowWatcher/types/overflow-direction.d.ts +7 -0
  121. package/dist/utils/OverflowWatcher/types/overflow-direction.js +9 -0
  122. package/dist/utils/OverflowWatcher/types/overflow-direction.js.map +1 -0
  123. package/dist/utils/SortingUtils.d.ts +9 -0
  124. package/dist/utils/SortingUtils.js +24 -0
  125. package/dist/utils/SortingUtils.js.map +1 -0
  126. package/jest.config.ts +3 -1
  127. package/package.json +12 -4
  128. package/reports/test-report.xml +166 -0
  129. package/setupTests.js +7 -0
  130. package/sonar-project.properties +10 -0
  131. package/src/dataunit/DataUnit.ts +139 -40
  132. package/src/dataunit/formatting/PrettyFormatter.ts +10 -7
  133. package/src/dataunit/loading/LoadDataRequest.ts +1 -1
  134. package/src/dataunit/metadata/DataType.ts +13 -1
  135. package/src/dataunit/state/action/DataUnitAction.ts +4 -1
  136. package/src/dataunit/state/slice/ChangesSlice.ts +15 -14
  137. package/src/dataunit/state/slice/InvalidFieldsSlice.ts +2 -0
  138. package/src/dataunit/state/slice/LoadingProperties.ts +37 -0
  139. package/src/dataunit/state/slice/SelectionSlice.ts +4 -4
  140. package/src/index.ts +8 -1
  141. package/src/repository/IRepository.ts +7 -0
  142. package/src/repository/indexeddb/IDBRepository.ts +4 -0
  143. package/src/utils/ElementUtils.ts +10 -0
  144. package/src/utils/MaskFormatter.ts +5 -1
  145. package/src/utils/ObjectUtils.ts +56 -0
  146. package/src/utils/OnboardingUtils.ts +1 -1
  147. package/src/utils/OverflowWatcher/index.ts +243 -0
  148. package/src/utils/OverflowWatcher/types/overflow-callback.ts +6 -0
  149. package/src/utils/OverflowWatcher/types/overflow-direction.ts +7 -0
  150. package/src/utils/SortingUtils.ts +30 -0
  151. package/src/utils/test/objectUtils.spec.ts +109 -0
  152. package/test/dataunit/formatting/PrettyFormatter.spec.ts +177 -0
  153. package/test/util/ElementUtils.spec.ts +34 -0
  154. package/test/util/OverflowWatcher.spec.ts +152 -0
  155. package/.docs/.nojekyll +0 -1
  156. package/.docs/enums/Action.md +0 -305
  157. package/.docs/enums/ChangeOperation.md +0 -52
  158. package/.docs/enums/DataType.md +0 -63
  159. package/.docs/enums/DependencyType.md +0 -41
  160. package/.docs/enums/SelectionMode.md +0 -30
  161. package/.docs/enums/SortMode.md +0 -30
  162. package/.docs/enums/UserInterface.md +0 -195
  163. package/.docs/modules/MaskFormatter.md +0 -37
@@ -25,6 +25,8 @@ import { getFormattedValue } from "./formatting/PrettyFormatter.js";
25
25
  import { v4 as uuid } from "uuid";
26
26
  import { DataUnitStorage } from "./DataUnitStorage.js";
27
27
  import { getInvalidFieldMessage, InvalidFieldsReducer } from "./state/slice/InvalidFieldsSlice.js";
28
+ import { getLoadingProperties, LoadingPropertiesReducer } from "./state/slice/LoadingProperties.js";
29
+ import SortingUtils from "../utils/SortingUtils.js";
28
30
 
29
31
  /***
30
32
  * `DataUnit`: Atua como uma camada de abstração entre o back-end e a interface do usuário.
@@ -32,27 +34,30 @@ import { getInvalidFieldMessage, InvalidFieldsReducer } from "./state/slice/Inva
32
34
  export default class DataUnit {
33
35
 
34
36
  public static CHANGING_PAGE_LOADING_SOURCE = "CHANGING_PAGE_LOADING_SOURCE";
37
+ public static ALL_RECORDS_SELECTION_SOURCE = "ALL_RECORDS_SELECTION_SOURCE";
35
38
  public static DEFAULT_DATAUNIT_NAME = "dataunit";
36
39
 
37
40
  private _uuid: string;
38
41
  private _name: string;
39
- private _observers: Array<(action: DataUnitAction) => void>;
42
+ private _observers: Array<(action: DataUnitAction, options?:DataUnitEventOptions) => void>;
40
43
  private _sortingProvider?: SortingProvider;
41
44
  private _filterProviders: Map<string, FilterProvider>;
42
45
  private _stateManager: StateManager;
43
- private _interceptors: Array<DUActionInterceptor>;
46
+ private _interceptors: Map<string, DUActionInterceptor>;
44
47
  private _pageSize: number;
45
48
  private _childByName = new Map<string, DataUnit>();
46
49
  private _parentDataUnit: DataUnit | undefined;
47
50
  private _loadingLockers: Promise<void>[];
48
51
  private _savingLockers: Promise<any>[] = [];
49
52
  private _defaultSorting: Array<Sort>;
53
+ private _allowReleaseCallbacks: boolean;
50
54
 
51
55
  public metadataLoader?: (dataUnit: DataUnit) => Promise<UnitMetadata>;
52
56
  public dataLoader?: (dataUnit: DataUnit, request: LoadDataRequest) => Promise<LoadDataResponse>;
53
57
  public saveLoader?: (dataUnit: DataUnit, changes: Array<Change>) => Promise<Array<SavedRecord>>;
54
58
  public removeLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<string>>;
55
59
  public recordLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<Record>>;
60
+ public allRecordsLoader?: (dataUnit: DataUnit) => Array<Record> | undefined;
56
61
 
57
62
  constructor(name: string = DataUnit.DEFAULT_DATAUNIT_NAME, parentDataUnit?: DataUnit) {
58
63
  this._uuid = uuid()
@@ -61,6 +66,7 @@ export default class DataUnit {
61
66
  this._stateManager = new StateManager(
62
67
  [
63
68
  HistReducer,
69
+ LoadingPropertiesReducer,
64
70
  UnitMetadataReducer,
65
71
  LoadingControlReducer,
66
72
  RecordsReducer,
@@ -77,7 +83,8 @@ export default class DataUnit {
77
83
  this._filterProviders = new Map<string, FilterProvider>();
78
84
  this._sortingProvider = undefined;
79
85
  this._defaultSorting = [];
80
- this._interceptors = [];
86
+ this._allowReleaseCallbacks = true;
87
+ this._interceptors = new Map();
81
88
  this._parentDataUnit = parentDataUnit;
82
89
  this._parentDataUnit?.subscribe(this.onDataUnitParentEvent);
83
90
  this._loadingLockers = [];
@@ -108,12 +115,35 @@ export default class DataUnit {
108
115
  * - Sorting Providers
109
116
  */
110
117
  public releaseCallbacks(){
118
+ if(!this._allowReleaseCallbacks) return;
119
+
111
120
  this._observers = [];
112
121
  this._filterProviders = new Map<string, FilterProvider>();
113
122
  this._sortingProvider = undefined;
114
- this._interceptors = [];
123
+ this._interceptors = new Map();
115
124
  }
116
125
 
126
+ /**
127
+ * Adiciona uma propriedade transacional que será envida aos
128
+ * loaders (dataLoader, saveLoader, removeLoader e recordLoader) na chamada.
129
+ * Essas propriedades serão limpas ao final da execução de cada método.
130
+ *
131
+ * @param name - Nome da propriedade
132
+ * @param value - Valor da propriedade
133
+ *
134
+ */
135
+ public addGlobalLoaderProp(name: string, value: string):void{
136
+ this.dispatchAction(Action.LOADING_PROPERTY_ADDED, {[name]: value});
137
+ }
138
+
139
+ /**
140
+ * Retorna as propriedades transacionais adicionados anteriores à chamada.
141
+ *
142
+ * @returns - Todas as propriedades desde o final do último loader.
143
+ */
144
+ public getGlobalLoaderProps(): Map<string, string>{
145
+ return getLoadingProperties(this._stateManager) || new Map();
146
+ }
117
147
 
118
148
  public get dataUnitId(): string{
119
149
  return this._uuid;
@@ -157,6 +187,8 @@ export default class DataUnit {
157
187
  case Action.NEXT_SELECTED:
158
188
  case Action.PREVIOUS_SELECTED:
159
189
  case Action.DATA_LOADED:
190
+ case Action.RECORDS_ADDED:
191
+ case Action.EDITION_CANCELED:
160
192
  this.clearDataUnit();
161
193
  const selectedRecord = this._parentDataUnit?.getSelectedRecord();
162
194
  if(selectedRecord != undefined && !this.isNewRecord(selectedRecord.__record__id__)){
@@ -215,22 +247,24 @@ export default class DataUnit {
215
247
  if (await this.dispatchAction(Action.LOADING_DATA, request, executionCtx)) {
216
248
  if (this.dataLoader) {
217
249
  this.dataLoader(this, request).then(
218
- response => {
219
- this.dispatchAction(
250
+ async response => {
251
+ await this.dispatchAction(
220
252
  Action.DATA_LOADED,
221
- {...response, keepSelection: request.keepSelection, filters: request.filters},
253
+ {...response, keepSelection: request.keepSelection, filters: request.filters, selectFirstRecord},
222
254
  executionCtx
223
255
  );
224
256
 
257
+ await this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
258
+
225
259
  if(selectFirstRecord){
226
260
  this.requestSelectFirst(executionCtx);
227
261
  }
228
-
229
262
  resolve(response);
230
263
  }
231
264
  ).catch(error => {
232
265
  console.error(error);
233
266
  const {errorCode} = error;
267
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
234
268
  fail(new ErrorException("Erro ao carregar registros", error, errorCode))
235
269
  });
236
270
  }
@@ -292,13 +326,16 @@ export default class DataUnit {
292
326
 
293
327
  await this.processLoadingLockers();
294
328
 
295
- if (this._parentDataUnit && !this._parentDataUnit.getSelectedRecord()) {
329
+ if (this._parentDataUnit) {
330
+ const parentRecord = this._parentDataUnit.getSelectedRecord();
331
+ if(parentRecord == undefined || this._parentDataUnit.isNewRecord(parentRecord.__record__id__)){
296
332
  if(this.records){
297
333
  this.clearDataUnit();
298
334
  }
299
335
  return Promise.resolve({
300
336
  records: []
301
337
  });
338
+ }
302
339
  }
303
340
 
304
341
  const loadDataRequest = this.getLoadDataRequest(quickFilter, source);
@@ -311,7 +348,7 @@ export default class DataUnit {
311
348
  *
312
349
  * @param page - Número da página desejada.
313
350
  * @param executionCtx - Contexto de execução do carregamento dos registros do DataUnit.
314
- *
351
+ *
315
352
  * @returns - Registros da página desejada.
316
353
  *
317
354
  */
@@ -431,6 +468,7 @@ export default class DataUnit {
431
468
  const changes: Array<Change> = this.getAllChangesToSave();
432
469
 
433
470
  this.saveLoader(this, changes).then((records) => {
471
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
434
472
  const recordsByDataUnit = this.getRecordsByDataUnit(records);
435
473
 
436
474
  const dispatchPromisses = [];
@@ -445,9 +483,12 @@ export default class DataUnit {
445
483
  Promise.all(dispatchPromisses).then(() => resolve());
446
484
  }).catch(cause => {
447
485
  const {errorCode} = cause;
486
+ this.dispatchAction(Action.SAVING_ERROR);
487
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
448
488
  fail(new ErrorException("Erro ao salvar alterações", cause, errorCode));
449
489
  });
450
490
  } else {
491
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
451
492
  resolve();
452
493
  }
453
494
  });
@@ -513,8 +554,8 @@ export default class DataUnit {
513
554
  if(selection.isAllRecords()){
514
555
  throw new Error("Exclusão remota não implementada.");
515
556
  }
516
- const records = selection?.records || [];
517
- const recordIds = selection?.recordIds || [];
557
+ const records = selection?.records as Array<Record> || [];
558
+ const recordIds = selection?.recordIds as Array<string> || [];
518
559
  return this.removeRecords(recordIds, records, buffered, undefined, silent);
519
560
  }
520
561
  return Promise.resolve([]);
@@ -536,6 +577,7 @@ export default class DataUnit {
536
577
  public async removeRecords(recordIds: Array<string>, cachedRecords: Array<Record>, buffered: boolean = false, executionCtx?: ExecutionContext, silent: boolean = false): Promise<Array<string>> {
537
578
  if (recordIds) {
538
579
  if (buffered || !this.removeLoader) {
580
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
539
581
  this.dispatchAction(Action.RECORDS_REMOVED, { records: recordIds, cachedRecords, buffered: true }, executionCtx);
540
582
  } else {
541
583
  if (await this.dispatchAction(Action.REMOVING_RECORDS, {silent}, executionCtx)) {
@@ -550,12 +592,14 @@ export default class DataUnit {
550
592
  .filter(index => index > -1);
551
593
 
552
594
  const nextIndex = Math.min(removedIndex.slice(-1)[0] + 1, currentRecordsKeys.length);
553
- const selectionAfterRemove = [currentRecordsKeys[nextIndex]];
595
+ const selectionAfterRemove = currentRecordsKeys[nextIndex] ? [currentRecordsKeys[nextIndex]] : [];
554
596
 
555
597
  this.dispatchAction(Action.RECORDS_REMOVED, { records: removedIds, cachedRecords, removedIndex, buffered: false, selectionAfterRemove }, executionCtx);
598
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
556
599
  resolve(removedIds);
557
600
  }
558
601
  ).catch(error => {
602
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
559
603
  const {errorCode} = error;
560
604
  fail(new ErrorException("Erro ao remover registros", error, errorCode))
561
605
  });
@@ -612,7 +656,6 @@ export default class DataUnit {
612
656
  *
613
657
  */
614
658
  public getFormattedValue(fieldName: string, value?: any): string {
615
-
616
659
  const descriptor = this.getField(fieldName);
617
660
  if (value == undefined) {
618
661
  value = this.getFieldValue(fieldName);
@@ -635,7 +678,7 @@ export default class DataUnit {
635
678
  *
636
679
  */
637
680
  public addInterceptor(interceptor: DUActionInterceptor): void {
638
- this._interceptors.push(interceptor);
681
+ this._interceptors.set(interceptor.interceptAction.toString(), interceptor);
639
682
  }
640
683
 
641
684
  /**
@@ -646,7 +689,7 @@ export default class DataUnit {
646
689
  *
647
690
  */
648
691
  public removeInterceptor(interceptor: DUActionInterceptor) {
649
- this._interceptors = this._interceptors.filter(i => i !== interceptor);
692
+ this._interceptors.delete(interceptor.interceptAction.toString())
650
693
  }
651
694
 
652
695
  /**
@@ -848,8 +891,12 @@ export default class DataUnit {
848
891
  public copySelected(executionCtx?: ExecutionContext): void {
849
892
  const selectionInfo = this.getSelectionInfo();
850
893
  if (selectionInfo) {
851
- const selectedRecords = selectionInfo.records;
894
+ if(selectionInfo.isAllRecords()){
895
+ throw new Error("Erro interno: Impossível copiar os registros selecionados pois a seleção atual é virtual.");
896
+ }
897
+ const selectedRecords = selectionInfo.records as Array<Record>;
852
898
  if(selectedRecords){
899
+
853
900
  this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.getParentRecordId()), executionCtx);
854
901
  }
855
902
  }
@@ -918,12 +965,16 @@ export default class DataUnit {
918
965
  * @returns - Promise que será resolvida quando o novo valor for persistido no state.
919
966
  *
920
967
  */
921
- public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>): Promise<boolean> {
968
+ public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>, options?:DataUnitEventOptions): Promise<boolean> {
922
969
 
923
970
  if(!this.hasNewRecord() && !this.getSelectedRecord()) await this.addRecord();
924
971
 
925
972
  const typedValue = this.validateAndTypeValue(fieldName, newValue);
926
973
  const currentValue = this.getFieldValue(fieldName);
974
+
975
+ if(ObjectUtils.hasEquivalentProps(currentValue, typedValue)){
976
+ return Promise.resolve(false);
977
+ }
927
978
 
928
979
  if(newValue instanceof Promise){
929
980
  const promise:Promise<boolean> = new Promise(accept => {
@@ -937,7 +988,7 @@ export default class DataUnit {
937
988
  }
938
989
 
939
990
  if (currentValue !== typedValue) {
940
- const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined);
991
+ const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined, options);
941
992
  this._savingLockers.push(promise);
942
993
  return promise;
943
994
  }
@@ -1065,7 +1116,7 @@ export default class DataUnit {
1065
1116
  public setSelection(selection: Array<string> | SelectionMode.ALL_RECORDS, executionCtx?: ExecutionContext): Promise<SelectionInfo> {
1066
1117
  return new Promise(resolve => {
1067
1118
  this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection }, executionCtx)
1068
- .then(()=>resolve(getSelectionInfo(this._stateManager)));
1119
+ .then(()=>resolve(this.getSelectionInfo()));
1069
1120
  });
1070
1121
  }
1071
1122
 
@@ -1122,8 +1173,16 @@ export default class DataUnit {
1122
1173
  * @returns - Objeto com informações como registros selecionados e seleção por critério.
1123
1174
  *
1124
1175
  **/
1125
- public getSelectionInfo(): SelectionInfo | undefined {
1126
- return getSelectionInfo(this._stateManager)
1176
+ public getSelectionInfo(): SelectionInfo{
1177
+ const selectionInfo: SelectionInfo = getSelectionInfo(this._stateManager);
1178
+ selectionInfo.getAllRecords = () => this.allRecordsLoader?.(this);
1179
+
1180
+ if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1181
+ const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
1182
+ selectionInfo.records?.sort(sortingFunction)
1183
+ }
1184
+
1185
+ return selectionInfo;
1127
1186
  }
1128
1187
 
1129
1188
  /**
@@ -1137,6 +1196,15 @@ export default class DataUnit {
1137
1196
  return getSelectedRecord(this._stateManager);
1138
1197
  }
1139
1198
 
1199
+ /**
1200
+ * Retorna o DataUnit pai
1201
+ *
1202
+ * @returns DataUnit pai ou undefined
1203
+ */
1204
+ public getParentDataUnit(): DataUnit | undefined {
1205
+ return this._parentDataUnit;
1206
+ }
1207
+
1140
1208
  /**
1141
1209
  *
1142
1210
  * Limpa todos os registros do DataUnit
@@ -1224,14 +1292,28 @@ export default class DataUnit {
1224
1292
  /**
1225
1293
  *
1226
1294
  * Retorna se existe algum tipo de alteração pendente.
1227
- *
1295
+ *
1296
+ * @param ignoreChildren: Define se deverá ignorar alterações pendentes no DataUnit filho.
1228
1297
  * @returns Verdadeiro se existir alterações pendentes.
1229
1298
  *
1230
1299
  */
1231
- public isDirty(): boolean {
1300
+ public isDirty(ignoreChildren?: boolean): boolean {
1301
+ if(ignoreChildren) return isDirty(this._stateManager);
1302
+
1232
1303
  return isDirty(this._stateManager) || this.childrenIsDirty();
1233
1304
  }
1234
1305
 
1306
+ /**
1307
+ *
1308
+ * Retorna se existe alterações pendentes no DataUnit pai.
1309
+ *
1310
+ * @returns Verdadeiro se existir alterações pendentes e Falso caso não exista alterações ou não exista DataUnit pai.
1311
+ *
1312
+ */
1313
+ public isParentDirty(): boolean {
1314
+ return this._parentDataUnit ? this._parentDataUnit.isDirty(true) : false;
1315
+ }
1316
+
1235
1317
  /**
1236
1318
  *
1237
1319
  * Retorna se existe algum DataUnit detail com alterações pendentes.
@@ -1404,17 +1486,17 @@ export default class DataUnit {
1404
1486
  * @returns - Verdadeiro se ação iniciada.
1405
1487
  *
1406
1488
  */
1407
- private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext): Promise<boolean> {
1489
+ private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext, options?:DataUnitEventOptions): Promise<boolean> {
1408
1490
  return new Promise(async resolve => {
1409
1491
  let action = new DataUnitAction(actionType, payload);
1410
1492
  if (executionCtx && executionCtx.before) {
1411
1493
  action = executionCtx.before(action);
1412
1494
  }
1413
- if (action && this._interceptors && this._interceptors.length > 0) {
1495
+ if (action && this._interceptors && this._interceptors.size > 0) {
1414
1496
  action = await this.intercept(action, this._interceptors.values());
1415
1497
  }
1416
1498
  if (action) {
1417
- this.doDispatchAction(action);
1499
+ this.doDispatchAction(action, options);
1418
1500
  if (executionCtx && executionCtx.after) {
1419
1501
  executionCtx.after(action)
1420
1502
  }
@@ -1452,7 +1534,7 @@ export default class DataUnit {
1452
1534
  * @param action - Ações em execução no DataUnit.
1453
1535
  *
1454
1536
  */
1455
- private doDispatchAction(action: DataUnitAction): void {
1537
+ private doDispatchAction(action: DataUnitAction, options:DataUnitEventOptions = {}): void {
1456
1538
  this._stateManager.process(action);
1457
1539
  this?._parentDataUnit?.dispatchAction(Action.CHILD_CHANGED, { srcAction: action, srcDataUnit: this });
1458
1540
  this._observers.forEach(f => {
@@ -1461,7 +1543,7 @@ export default class DataUnit {
1461
1543
  should be continued
1462
1544
  */
1463
1545
  try {
1464
- f(action);
1546
+ f(action, options);
1465
1547
  } catch (e) {
1466
1548
  console.warn("[DataUnit] error while call observer", e);
1467
1549
  }
@@ -1500,7 +1582,7 @@ export default class DataUnit {
1500
1582
  * @param observer - Função que recebe como parâmetro as ações que serão monitoradas.
1501
1583
  *
1502
1584
  */
1503
- public subscribe(observer: (action: DataUnitAction) => void) {
1585
+ public subscribe(observer: (action: DataUnitAction, options?:DataUnitEventOptions) => void | Promise<void>) {
1504
1586
  this._observers.push(observer);
1505
1587
  }
1506
1588
 
@@ -1527,12 +1609,16 @@ export default class DataUnit {
1527
1609
  const selection = getSelection(this._stateManager);
1528
1610
  this.dispatchAction(Action.LOADING_RECORD, selection);
1529
1611
 
1530
- if(!this.dataLoader) return;
1531
- if(!this.recordLoader) return;
1612
+ if(!this.recordLoader || !this.dataLoader) {
1613
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1614
+ return;
1615
+ }
1532
1616
 
1533
1617
  this.recordLoader(this, selection).then(response => {
1534
1618
  this.dispatchAction(Action.RECORD_LOADED, response)
1619
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1535
1620
  }).catch(cause => {
1621
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1536
1622
  const {errorCode} = cause;
1537
1623
  fail(new ErrorException("Erro ao recarregar registro", cause, errorCode));
1538
1624
  });
@@ -1671,7 +1757,7 @@ export default class DataUnit {
1671
1757
  }
1672
1758
 
1673
1759
  if (selection) {
1674
- return selection.records;
1760
+ return selection.records as Array<Record>;
1675
1761
  }
1676
1762
  }
1677
1763
 
@@ -1689,12 +1775,15 @@ export default class DataUnit {
1689
1775
  public getSelection(): Array<string> {
1690
1776
  console.warn("DataUnit: O método `getSelection` foi descontinuado. Use o método `getSelectionInfo`.");
1691
1777
  const selection = this.getSelectionInfo();
1778
+ if(selection == undefined){
1779
+ return [];
1780
+ }
1692
1781
 
1693
- if(selection != undefined && selection.isAllRecords()){
1782
+ if(selection.isAllRecords()){
1694
1783
  throw new Error("Erro interno: Impossível retornar os registros selecionados. A seleção atual é virtual. Use o método `getSelectionInfo`.");
1695
1784
  }
1696
1785
 
1697
- return selection?.recordIds || [];
1786
+ return selection?.recordIds as Array<string>;
1698
1787
  }
1699
1788
 
1700
1789
  /**
@@ -1730,6 +1819,10 @@ export default class DataUnit {
1730
1819
  return loadingLockerResolver || Promise.resolve;
1731
1820
  }
1732
1821
 
1822
+ public set allowReleaseCallbacks(allow: boolean){
1823
+ this._allowReleaseCallbacks = allow;
1824
+ }
1825
+
1733
1826
  private async processLoadingLockers(){
1734
1827
  if(this._loadingLockers.length) {
1735
1828
  await Promise.all(this._loadingLockers);
@@ -1871,12 +1964,13 @@ export enum SelectionMode{
1871
1964
 
1872
1965
  export class SelectionInfo{
1873
1966
 
1874
- private _records: Array<Record>;
1875
1967
  public mode: SelectionMode;
1876
- private _total?: number;
1877
1968
  public filters?: Array<Filter>;
1878
1969
  public sort?: Array<Sort>;
1879
-
1970
+ public getAllRecords?: () => Array<Record> | undefined;
1971
+ private _records: Array<Record>;
1972
+ private _total?: number;
1973
+
1880
1974
  constructor(records: Array<Record>, mode: SelectionMode = SelectionMode.SOME_RECORDS, total?:number, filters?: Array<Filter>, sort?: Array<Sort>){
1881
1975
  this._records = records;
1882
1976
  this._total = total;
@@ -1887,7 +1981,10 @@ export class SelectionInfo{
1887
1981
 
1888
1982
  public get records(): Array<Record> | undefined{
1889
1983
  if(this.isAllRecords()){
1890
- throw new Error("Erro interno: [ALL_RECORDS] - Impossível retornar os registros selecionados numa seleção virtual.")
1984
+ if(this.getAllRecords != undefined){
1985
+ return this.getAllRecords();
1986
+ }
1987
+ throw new Error("Erro interno: Impossível retornar os registros selecionados numa seleção virtual.");
1891
1988
  }
1892
1989
  return this._records;
1893
1990
  }
@@ -1906,7 +2003,7 @@ export class SelectionInfo{
1906
2003
  if(this.isAllRecords()){
1907
2004
  return this._total || 0;
1908
2005
  }
1909
- return this.records == undefined ? 0: this.records.length;
2006
+ return this.records == undefined ? 0 : (this.records as Array<Record>).length;
1910
2007
  }
1911
2008
 
1912
2009
  public isAllRecords(): boolean{
@@ -1917,3 +2014,5 @@ export class SelectionInfo{
1917
2014
  return this.length === 0;
1918
2015
  }
1919
2016
  }
2017
+
2018
+ export type DataUnitEventOptions = {[key:string]: any};
@@ -10,10 +10,10 @@ 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
+ return !value ? "" : value.toString();
17
17
  }
18
18
 
19
19
  if(descriptor?.userInterface === UserInterface.OPTIONSELECTOR){
@@ -43,7 +43,7 @@ export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
43
43
  const mask = getMask(value, descriptor);
44
44
  if(mask != undefined){
45
45
  try{
46
- return new MaskFormatter(mask).format(value);
46
+ return new MaskFormatter(mask).format(value, true);
47
47
  } catch(error){
48
48
  console.warn(`Erro ao formatar valor: ${value}. Mascara: ${mask}. Erro: ${error}`);
49
49
  }
@@ -53,6 +53,7 @@ export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
53
53
  }
54
54
 
55
55
  const getNumberFormat = (value: any, descriptor: FieldDescriptor) => {
56
+ if(value === undefined || value === null) return "";
56
57
  if(descriptor.userInterface === UserInterface.INTEGERNUMBER){
57
58
  return value;
58
59
  }
@@ -79,10 +80,12 @@ const getOptionFormat = (value: any, descriptor: FieldDescriptor) => {
79
80
  options = JSON.parse(prop);
80
81
  } else {
81
82
  options = {};
82
- prop.forEach(opt => options[opt.label] = opt.value);
83
+ if(prop != undefined){
84
+ prop.forEach(opt => options[opt.label] = opt.value);
85
+ }
83
86
  }
84
87
 
85
- if(typeof value === "object"){
88
+ if(value && typeof value === "object" ){
86
89
  value = value.value
87
90
  }
88
91
 
@@ -90,7 +93,7 @@ const getOptionFormat = (value: any, descriptor: FieldDescriptor) => {
90
93
  return options[value];
91
94
  }
92
95
 
93
- return value;
96
+ return value ?? "";
94
97
  }
95
98
 
96
99
 
@@ -101,7 +104,7 @@ const getMask = (value: string, descriptor: FieldDescriptor | undefined): string
101
104
  }
102
105
 
103
106
  const mask = descriptor.properties?.mask;
104
- if(mask == undefined){
107
+ if(mask == undefined || !value){
105
108
  return;
106
109
  }
107
110
 
@@ -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;
@@ -67,7 +67,16 @@ export const getConvertedValue = (dataType: DataType, value: any): any => {
67
67
  case DataType.NUMBER:
68
68
  return value === "" || isNaN(Number(value)) ? null : Number(value);
69
69
  case DataType.OBJECT:
70
- return typeof value === "string" ? JSON.parse(value) : value;
70
+ //Caso de uso campo DataType do tipo STRING e UserInterface do tipo TEXT, que tem relacionamento (TGFPRO.GRUPOPIS)
71
+ if(typeof value === "string") {
72
+ try{
73
+ return JSON.parse(value);
74
+ }catch(e){
75
+ console.warn(`Erro ao fazer parse do valor ${value} para objeto, dataType: ${dataType}`);
76
+ }
77
+ }
78
+
79
+ return value;
71
80
  case DataType.BOOLEAN:
72
81
  if (typeof value === "string") {
73
82
  return Boolean(value === 'true');
@@ -110,6 +119,9 @@ export const toString = ( dataType: DataType|undefined, value: any): string => {
110
119
  } else if (dataType === DataType.DATE) {
111
120
  if (typeof value === "string") {
112
121
  value = DateUtils.strToDate(value);
122
+ if (value == undefined) {
123
+ return value;
124
+ }
113
125
  }
114
126
  return DateUtils.formatRfc3339(value as Date);
115
127
  } else {
@@ -35,6 +35,7 @@ export enum Action{
35
35
 
36
36
  SAVING_DATA = "savingData",
37
37
  DATA_SAVED = "dataSaved",
38
+ SAVING_ERROR = "savingError",
38
39
 
39
40
  REMOVING_RECORDS = "removingRecords",
40
41
  RECORDS_REMOVED = "recordsRemoved",
@@ -63,6 +64,8 @@ export enum Action{
63
64
  CHILD_CHANGED = "childChanged",
64
65
 
65
66
  FIELD_INVALIDATED = "fieldInvalidated",
66
- INVALIDATE_CLEAN = "invalidateClean"
67
+ INVALIDATE_CLEAN = "invalidateClean",
67
68
 
69
+ LOADING_PROPERTY_ADDED = "loadingPropertyAdded",
70
+ LOADING_PROPERTIES_CLEANED = "loadingPropertiesCleaned"
68
71
  }
@@ -94,27 +94,28 @@ export const getChangedFieldValue = (fieldName: string, record: Record, stateMan
94
94
  }
95
95
 
96
96
  export const getChangesToSave = (dataUnit: string, stateManager: StateManager): Array<Change> => {
97
- const result: Array<Change> = [];
98
-
97
+
99
98
  const changes = getChanges(stateManager);
100
99
  const selectedRecords = getSelectionRecords(stateManager) || [];
101
- const records: Set<Record> = new Set(
100
+ const records: Map<string, Record> = new Map(
102
101
  getRecords(stateManager)
103
102
  .concat(
104
- selectedRecords
105
- .filter(record => !isAddedRecord(record.__record__id__, stateManager)
106
- )
103
+ selectedRecords.filter(record => !isAddedRecord(record.__record__id__, stateManager))
107
104
  )
105
+ .map(r => [r.__record__id__, r])
108
106
  );
109
-
110
- records?.forEach(r => {
111
- if(changes){
112
- const c = changes.get(r.__record__id__);
113
- if (c) {
114
- result.push(new Change(dataUnit, r, c, ChangeOperation.UPDATE, r.__record__source__id__));
107
+
108
+ const result: Array<Change> = [];
109
+
110
+ if(changes != undefined){
111
+ Array.from(changes.entries())
112
+ .forEach(([recordId, change]) =>{
113
+ const record = records.get(recordId);
114
+ if(record != undefined){
115
+ result.push(new Change(dataUnit, record, change, ChangeOperation.UPDATE, record.__record__source__id__));
115
116
  }
116
- }
117
- });
117
+ });
118
+ }
118
119
 
119
120
  const addedRecords = getAddedRecords(stateManager);
120
121
  if (addedRecords) {
@@ -15,6 +15,8 @@ class InvalidFieldsReducerImpl implements ActionReducer{
15
15
  return makeInvalid(currentState, action.payload);
16
16
  case Action.INVALIDATE_CLEAN:
17
17
  return clearRecord(currentState, action.payload);
18
+ case Action.EDITION_CANCELED:
19
+ return undefined;
18
20
  }
19
21
 
20
22
  return currentState;