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

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 +118 -31
  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 +141 -41
  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)) {
@@ -547,15 +589,18 @@ export default class DataUnit {
547
589
  const currentRecordsKeys = Array.from(getCurrentRecords(this._stateManager).keys());
548
590
  const removedIndex: Array<number> = recordIds
549
591
  .map(id => currentRecordsKeys.indexOf(id))
550
- .filter(index => index > -1);
592
+ .filter(index => index > -1)
593
+ .sort();
551
594
 
552
595
  const nextIndex = Math.min(removedIndex.slice(-1)[0] + 1, currentRecordsKeys.length);
553
- const selectionAfterRemove = [currentRecordsKeys[nextIndex]];
596
+ const selectionAfterRemove = currentRecordsKeys[nextIndex] ? [currentRecordsKeys[nextIndex]] : [];
554
597
 
555
598
  this.dispatchAction(Action.RECORDS_REMOVED, { records: removedIds, cachedRecords, removedIndex, buffered: false, selectionAfterRemove }, executionCtx);
599
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
556
600
  resolve(removedIds);
557
601
  }
558
602
  ).catch(error => {
603
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
559
604
  const {errorCode} = error;
560
605
  fail(new ErrorException("Erro ao remover registros", error, errorCode))
561
606
  });
@@ -612,7 +657,6 @@ export default class DataUnit {
612
657
  *
613
658
  */
614
659
  public getFormattedValue(fieldName: string, value?: any): string {
615
-
616
660
  const descriptor = this.getField(fieldName);
617
661
  if (value == undefined) {
618
662
  value = this.getFieldValue(fieldName);
@@ -635,7 +679,7 @@ export default class DataUnit {
635
679
  *
636
680
  */
637
681
  public addInterceptor(interceptor: DUActionInterceptor): void {
638
- this._interceptors.push(interceptor);
682
+ this._interceptors.set(interceptor.interceptAction.toString(), interceptor);
639
683
  }
640
684
 
641
685
  /**
@@ -646,7 +690,7 @@ export default class DataUnit {
646
690
  *
647
691
  */
648
692
  public removeInterceptor(interceptor: DUActionInterceptor) {
649
- this._interceptors = this._interceptors.filter(i => i !== interceptor);
693
+ this._interceptors.delete(interceptor.interceptAction.toString())
650
694
  }
651
695
 
652
696
  /**
@@ -848,8 +892,12 @@ export default class DataUnit {
848
892
  public copySelected(executionCtx?: ExecutionContext): void {
849
893
  const selectionInfo = this.getSelectionInfo();
850
894
  if (selectionInfo) {
851
- const selectedRecords = selectionInfo.records;
895
+ if(selectionInfo.isAllRecords()){
896
+ throw new Error("Erro interno: Impossível copiar os registros selecionados pois a seleção atual é virtual.");
897
+ }
898
+ const selectedRecords = selectionInfo.records as Array<Record>;
852
899
  if(selectedRecords){
900
+
853
901
  this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.getParentRecordId()), executionCtx);
854
902
  }
855
903
  }
@@ -918,12 +966,16 @@ export default class DataUnit {
918
966
  * @returns - Promise que será resolvida quando o novo valor for persistido no state.
919
967
  *
920
968
  */
921
- public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>): Promise<boolean> {
969
+ public async setFieldValue(fieldName: string, newValue: any, records?: Array<string>, options?:DataUnitEventOptions): Promise<boolean> {
922
970
 
923
971
  if(!this.hasNewRecord() && !this.getSelectedRecord()) await this.addRecord();
924
972
 
925
973
  const typedValue = this.validateAndTypeValue(fieldName, newValue);
926
974
  const currentValue = this.getFieldValue(fieldName);
975
+
976
+ if(ObjectUtils.hasEquivalentProps(currentValue, typedValue)){
977
+ return Promise.resolve(false);
978
+ }
927
979
 
928
980
  if(newValue instanceof Promise){
929
981
  const promise:Promise<boolean> = new Promise(accept => {
@@ -937,7 +989,7 @@ export default class DataUnit {
937
989
  }
938
990
 
939
991
  if (currentValue !== typedValue) {
940
- const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined);
992
+ const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined, options);
941
993
  this._savingLockers.push(promise);
942
994
  return promise;
943
995
  }
@@ -1065,7 +1117,7 @@ export default class DataUnit {
1065
1117
  public setSelection(selection: Array<string> | SelectionMode.ALL_RECORDS, executionCtx?: ExecutionContext): Promise<SelectionInfo> {
1066
1118
  return new Promise(resolve => {
1067
1119
  this.dispatchAction(Action.SELECTION_CHANGED, { type: "id", selection }, executionCtx)
1068
- .then(()=>resolve(getSelectionInfo(this._stateManager)));
1120
+ .then(()=>resolve(this.getSelectionInfo()));
1069
1121
  });
1070
1122
  }
1071
1123
 
@@ -1122,8 +1174,16 @@ export default class DataUnit {
1122
1174
  * @returns - Objeto com informações como registros selecionados e seleção por critério.
1123
1175
  *
1124
1176
  **/
1125
- public getSelectionInfo(): SelectionInfo | undefined {
1126
- return getSelectionInfo(this._stateManager)
1177
+ public getSelectionInfo(): SelectionInfo{
1178
+ const selectionInfo: SelectionInfo = getSelectionInfo(this._stateManager);
1179
+ selectionInfo.getAllRecords = () => this.allRecordsLoader?.(this);
1180
+
1181
+ if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1182
+ const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
1183
+ selectionInfo.records?.sort(sortingFunction)
1184
+ }
1185
+
1186
+ return selectionInfo;
1127
1187
  }
1128
1188
 
1129
1189
  /**
@@ -1137,6 +1197,15 @@ export default class DataUnit {
1137
1197
  return getSelectedRecord(this._stateManager);
1138
1198
  }
1139
1199
 
1200
+ /**
1201
+ * Retorna o DataUnit pai
1202
+ *
1203
+ * @returns DataUnit pai ou undefined
1204
+ */
1205
+ public getParentDataUnit(): DataUnit | undefined {
1206
+ return this._parentDataUnit;
1207
+ }
1208
+
1140
1209
  /**
1141
1210
  *
1142
1211
  * Limpa todos os registros do DataUnit
@@ -1224,14 +1293,28 @@ export default class DataUnit {
1224
1293
  /**
1225
1294
  *
1226
1295
  * Retorna se existe algum tipo de alteração pendente.
1227
- *
1296
+ *
1297
+ * @param ignoreChildren: Define se deverá ignorar alterações pendentes no DataUnit filho.
1228
1298
  * @returns Verdadeiro se existir alterações pendentes.
1229
1299
  *
1230
1300
  */
1231
- public isDirty(): boolean {
1301
+ public isDirty(ignoreChildren?: boolean): boolean {
1302
+ if(ignoreChildren) return isDirty(this._stateManager);
1303
+
1232
1304
  return isDirty(this._stateManager) || this.childrenIsDirty();
1233
1305
  }
1234
1306
 
1307
+ /**
1308
+ *
1309
+ * Retorna se existe alterações pendentes no DataUnit pai.
1310
+ *
1311
+ * @returns Verdadeiro se existir alterações pendentes e Falso caso não exista alterações ou não exista DataUnit pai.
1312
+ *
1313
+ */
1314
+ public isParentDirty(): boolean {
1315
+ return this._parentDataUnit ? this._parentDataUnit.isDirty(true) : false;
1316
+ }
1317
+
1235
1318
  /**
1236
1319
  *
1237
1320
  * Retorna se existe algum DataUnit detail com alterações pendentes.
@@ -1404,17 +1487,17 @@ export default class DataUnit {
1404
1487
  * @returns - Verdadeiro se ação iniciada.
1405
1488
  *
1406
1489
  */
1407
- private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext): Promise<boolean> {
1490
+ private async dispatchAction(actionType: Action, payload?: any, executionCtx?: ExecutionContext, options?:DataUnitEventOptions): Promise<boolean> {
1408
1491
  return new Promise(async resolve => {
1409
1492
  let action = new DataUnitAction(actionType, payload);
1410
1493
  if (executionCtx && executionCtx.before) {
1411
1494
  action = executionCtx.before(action);
1412
1495
  }
1413
- if (action && this._interceptors && this._interceptors.length > 0) {
1496
+ if (action && this._interceptors && this._interceptors.size > 0) {
1414
1497
  action = await this.intercept(action, this._interceptors.values());
1415
1498
  }
1416
1499
  if (action) {
1417
- this.doDispatchAction(action);
1500
+ this.doDispatchAction(action, options);
1418
1501
  if (executionCtx && executionCtx.after) {
1419
1502
  executionCtx.after(action)
1420
1503
  }
@@ -1452,7 +1535,7 @@ export default class DataUnit {
1452
1535
  * @param action - Ações em execução no DataUnit.
1453
1536
  *
1454
1537
  */
1455
- private doDispatchAction(action: DataUnitAction): void {
1538
+ private doDispatchAction(action: DataUnitAction, options:DataUnitEventOptions = {}): void {
1456
1539
  this._stateManager.process(action);
1457
1540
  this?._parentDataUnit?.dispatchAction(Action.CHILD_CHANGED, { srcAction: action, srcDataUnit: this });
1458
1541
  this._observers.forEach(f => {
@@ -1461,7 +1544,7 @@ export default class DataUnit {
1461
1544
  should be continued
1462
1545
  */
1463
1546
  try {
1464
- f(action);
1547
+ f(action, options);
1465
1548
  } catch (e) {
1466
1549
  console.warn("[DataUnit] error while call observer", e);
1467
1550
  }
@@ -1500,7 +1583,7 @@ export default class DataUnit {
1500
1583
  * @param observer - Função que recebe como parâmetro as ações que serão monitoradas.
1501
1584
  *
1502
1585
  */
1503
- public subscribe(observer: (action: DataUnitAction) => void) {
1586
+ public subscribe(observer: (action: DataUnitAction, options?:DataUnitEventOptions) => void | Promise<void>) {
1504
1587
  this._observers.push(observer);
1505
1588
  }
1506
1589
 
@@ -1527,12 +1610,16 @@ export default class DataUnit {
1527
1610
  const selection = getSelection(this._stateManager);
1528
1611
  this.dispatchAction(Action.LOADING_RECORD, selection);
1529
1612
 
1530
- if(!this.dataLoader) return;
1531
- if(!this.recordLoader) return;
1613
+ if(!this.recordLoader || !this.dataLoader) {
1614
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1615
+ return;
1616
+ }
1532
1617
 
1533
1618
  this.recordLoader(this, selection).then(response => {
1534
1619
  this.dispatchAction(Action.RECORD_LOADED, response)
1620
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1535
1621
  }).catch(cause => {
1622
+ this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
1536
1623
  const {errorCode} = cause;
1537
1624
  fail(new ErrorException("Erro ao recarregar registro", cause, errorCode));
1538
1625
  });
@@ -1671,7 +1758,7 @@ export default class DataUnit {
1671
1758
  }
1672
1759
 
1673
1760
  if (selection) {
1674
- return selection.records;
1761
+ return selection.records as Array<Record>;
1675
1762
  }
1676
1763
  }
1677
1764
 
@@ -1689,12 +1776,15 @@ export default class DataUnit {
1689
1776
  public getSelection(): Array<string> {
1690
1777
  console.warn("DataUnit: O método `getSelection` foi descontinuado. Use o método `getSelectionInfo`.");
1691
1778
  const selection = this.getSelectionInfo();
1779
+ if(selection == undefined){
1780
+ return [];
1781
+ }
1692
1782
 
1693
- if(selection != undefined && selection.isAllRecords()){
1783
+ if(selection.isAllRecords()){
1694
1784
  throw new Error("Erro interno: Impossível retornar os registros selecionados. A seleção atual é virtual. Use o método `getSelectionInfo`.");
1695
1785
  }
1696
1786
 
1697
- return selection?.recordIds || [];
1787
+ return selection?.recordIds as Array<string>;
1698
1788
  }
1699
1789
 
1700
1790
  /**
@@ -1730,6 +1820,10 @@ export default class DataUnit {
1730
1820
  return loadingLockerResolver || Promise.resolve;
1731
1821
  }
1732
1822
 
1823
+ public set allowReleaseCallbacks(allow: boolean){
1824
+ this._allowReleaseCallbacks = allow;
1825
+ }
1826
+
1733
1827
  private async processLoadingLockers(){
1734
1828
  if(this._loadingLockers.length) {
1735
1829
  await Promise.all(this._loadingLockers);
@@ -1871,12 +1965,13 @@ export enum SelectionMode{
1871
1965
 
1872
1966
  export class SelectionInfo{
1873
1967
 
1874
- private _records: Array<Record>;
1875
1968
  public mode: SelectionMode;
1876
- private _total?: number;
1877
1969
  public filters?: Array<Filter>;
1878
1970
  public sort?: Array<Sort>;
1879
-
1971
+ public getAllRecords?: () => Array<Record> | undefined;
1972
+ private _records: Array<Record>;
1973
+ private _total?: number;
1974
+
1880
1975
  constructor(records: Array<Record>, mode: SelectionMode = SelectionMode.SOME_RECORDS, total?:number, filters?: Array<Filter>, sort?: Array<Sort>){
1881
1976
  this._records = records;
1882
1977
  this._total = total;
@@ -1887,7 +1982,10 @@ export class SelectionInfo{
1887
1982
 
1888
1983
  public get records(): Array<Record> | undefined{
1889
1984
  if(this.isAllRecords()){
1890
- throw new Error("Erro interno: [ALL_RECORDS] - Impossível retornar os registros selecionados numa seleção virtual.")
1985
+ if(this.getAllRecords != undefined){
1986
+ return this.getAllRecords();
1987
+ }
1988
+ throw new Error("Erro interno: Impossível retornar os registros selecionados numa seleção virtual.");
1891
1989
  }
1892
1990
  return this._records;
1893
1991
  }
@@ -1906,7 +2004,7 @@ export class SelectionInfo{
1906
2004
  if(this.isAllRecords()){
1907
2005
  return this._total || 0;
1908
2006
  }
1909
- return this.records == undefined ? 0: this.records.length;
2007
+ return this.records == undefined ? 0 : (this.records as Array<Record>).length;
1910
2008
  }
1911
2009
 
1912
2010
  public isAllRecords(): boolean{
@@ -1917,3 +2015,5 @@ export class SelectionInfo{
1917
2015
  return this.length === 0;
1918
2016
  }
1919
2017
  }
2018
+
2019
+ 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;