@sankhyalabs/core 0.0.0-bugfix-dev-KB-77217.0 → 0.0.0-bugfix-dev-KB-87855.0

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 (69) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/DataUnit.md +230 -122
  3. package/.docs/classes/FieldComparator.md +6 -2
  4. package/.docs/classes/KeyboardManager.md +26 -6
  5. package/.docs/classes/LockManager.md +4 -4
  6. package/.docs/classes/ObjectUtils.md +50 -2
  7. package/.docs/classes/SelectionInfo.md +12 -12
  8. package/.docs/classes/StringUtils.md +10 -10
  9. package/.docs/enumerations/Action.md +41 -31
  10. package/.docs/enumerations/ChangeOperation.md +4 -4
  11. package/.docs/enumerations/SelectionMode.md +2 -2
  12. package/.docs/enumerations/UserInterface.md +15 -5
  13. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  14. package/.docs/interfaces/PageRequest.md +3 -3
  15. package/.docs/interfaces/QuickFilter.md +3 -3
  16. package/.docs/interfaces/Record.md +4 -4
  17. package/.docs/interfaces/SavedRecord.md +5 -5
  18. package/.docs/interfaces/WaitingChange.md +3 -3
  19. package/.docs/type-aliases/DataUnitEventOptions.md +1 -1
  20. package/dist/dataunit/DataUnit.d.ts +19 -0
  21. package/dist/dataunit/DataUnit.js +43 -3
  22. package/dist/dataunit/DataUnit.js.map +1 -1
  23. package/dist/dataunit/formatting/PrettyFormatter.js +3 -0
  24. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  25. package/dist/dataunit/metadata/DataType.d.ts +1 -1
  26. package/dist/dataunit/metadata/DataType.js +7 -4
  27. package/dist/dataunit/metadata/DataType.js.map +1 -1
  28. package/dist/dataunit/metadata/UnitMetadata.d.ts +1 -0
  29. package/dist/dataunit/metadata/UnitMetadata.js +1 -0
  30. package/dist/dataunit/metadata/UnitMetadata.js.map +1 -1
  31. package/dist/dataunit/sorting/FieldComparator.d.ts +2 -2
  32. package/dist/dataunit/sorting/FieldComparator.js +4 -4
  33. package/dist/dataunit/sorting/FieldComparator.js.map +1 -1
  34. package/dist/dataunit/state/action/DataUnitAction.d.ts +1 -0
  35. package/dist/dataunit/state/action/DataUnitAction.js +1 -0
  36. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  37. package/dist/utils/KeyboardManager/index.d.ts +5 -0
  38. package/dist/utils/KeyboardManager/index.js +8 -0
  39. package/dist/utils/KeyboardManager/index.js.map +1 -1
  40. package/dist/utils/LockManager.js +1 -4
  41. package/dist/utils/LockManager.js.map +1 -1
  42. package/dist/utils/ObjectUtils.d.ts +14 -0
  43. package/dist/utils/ObjectUtils.js +20 -0
  44. package/dist/utils/ObjectUtils.js.map +1 -1
  45. package/dist/utils/SortingUtils.js +9 -1
  46. package/dist/utils/SortingUtils.js.map +1 -1
  47. package/dist/utils/StringUtils.js +12 -6
  48. package/dist/utils/StringUtils.js.map +1 -1
  49. package/package.json +1 -1
  50. package/reports/test-report.xml +665 -120
  51. package/src/dataunit/DataUnit.ts +51 -3
  52. package/src/dataunit/formatting/PrettyFormatter.ts +4 -0
  53. package/src/dataunit/metadata/DataType.ts +8 -4
  54. package/src/dataunit/metadata/UnitMetadata.ts +1 -0
  55. package/src/dataunit/sorting/FieldComparator.ts +4 -4
  56. package/src/dataunit/state/action/DataUnitAction.ts +2 -0
  57. package/src/utils/KeyboardManager/index.ts +10 -0
  58. package/src/utils/LockManager.ts +59 -65
  59. package/src/utils/ObjectUtils.ts +21 -0
  60. package/src/utils/SortingUtils.ts +10 -1
  61. package/src/utils/StringUtils.ts +10 -6
  62. package/test/testCases/NumberUtilsTestCases.ts +190 -0
  63. package/test/testCases/StringUtilsTestCases.ts +435 -0
  64. package/test/testCases/TimeFormatterTestUtils.ts +43 -0
  65. package/test/util/NumberUtils.spec.ts +72 -150
  66. package/test/util/ObjectUtils.spec.ts +572 -0
  67. package/test/util/StringUtils.spec.ts +260 -36
  68. package/test/util/TimeFormatter.spec.ts +65 -18
  69. package/src/utils/test/objectUtils.spec.ts +0 -109
@@ -55,6 +55,8 @@ export default class DataUnit {
55
55
  private _allowReleaseCallbacks: boolean;
56
56
  private _waitingToReload: boolean = false;
57
57
  private _cancelPagination: boolean = false;
58
+ private _isMultipleEdition: boolean = false;
59
+ private _fieldSourceValue: Map<string, string> = new Map<string, string>();
58
60
 
59
61
  public metadataLoader?: (dataUnit: DataUnit) => Promise<UnitMetadata>;
60
62
  public dataLoader?: (dataUnit: DataUnit, request: LoadDataRequest) => Promise<LoadDataResponse>;
@@ -183,6 +185,21 @@ export default class DataUnit {
183
185
  this._cancelPagination = cancelPagination;
184
186
  }
185
187
 
188
+ /**
189
+ * Informa se o DataUnit está no modo de edição de múltiplos registros.
190
+ */
191
+ public get isMultipleEdition(): boolean {
192
+ return this._isMultipleEdition;
193
+ }
194
+
195
+ /**
196
+ * Define se o DataUnit está no modo de edição de múltiplos registros.
197
+ */
198
+ public set isMultipleEdition(isMultipleEdition) {
199
+ this._isMultipleEdition = isMultipleEdition;
200
+ this.dispatchAction(Action.MULTIPLE_EDITION_CHANGED, {isMultipleEdition});
201
+ }
202
+
186
203
  /**
187
204
  *
188
205
  * Obtém o nome de identificação do DataUnit (geralmente em formato de URI - Uniform Resource Identifier).
@@ -1031,7 +1048,7 @@ export default class DataUnit {
1031
1048
  const typedValue = this.validateAndTypeValue(fieldName, newValue);
1032
1049
  const currentValue = this.getFieldValue(fieldName);
1033
1050
 
1034
- if(!(newValue instanceof Promise) && ObjectUtils.hasEquivalentProps(currentValue, typedValue)){
1051
+ if(this.areEquivalentValues(newValue, currentValue, typedValue)) {
1035
1052
  return Promise.resolve(false);
1036
1053
  }
1037
1054
 
@@ -1046,7 +1063,7 @@ export default class DataUnit {
1046
1063
  return promise;
1047
1064
  }
1048
1065
 
1049
- if (currentValue !== typedValue) {
1066
+ if (this.isMultipleEdition || currentValue !== typedValue) {
1050
1067
  const promise = this.dispatchAction(Action.DATA_CHANGED, { [fieldName]: typedValue, records }, undefined, options);
1051
1068
  this._savingLockers.push(promise);
1052
1069
  return promise;
@@ -1055,6 +1072,12 @@ export default class DataUnit {
1055
1072
  return Promise.resolve(false);
1056
1073
  }
1057
1074
 
1075
+ private areEquivalentValues(newValue: any, currentValue: any, typedValue: any) {
1076
+ return !(newValue instanceof Promise)
1077
+ && ObjectUtils.hasEquivalentProps(currentValue, typedValue)
1078
+ && !this.isMultipleEdition;
1079
+ }
1080
+
1058
1081
  /**
1059
1082
  *
1060
1083
  * Marca campos como inválidos.
@@ -1264,7 +1287,17 @@ export default class DataUnit {
1264
1287
  **/
1265
1288
  public getSelectionInfo(): SelectionInfo{
1266
1289
  const selectionInfo: SelectionInfo = getSelectionInfo(this._stateManager);
1267
- selectionInfo.getAllRecords = () => this.allRecordsLoader?.(this);
1290
+ selectionInfo.getAllRecords = () => {
1291
+
1292
+ let records = this.allRecordsLoader?.(this) ?? [];
1293
+
1294
+ if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1295
+ const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
1296
+ records = [...records].sort(sortingFunction);
1297
+ }
1298
+
1299
+ return records;
1300
+ }
1268
1301
 
1269
1302
  if (selectionInfo.sort != undefined && selectionInfo.sort.length > 0) {
1270
1303
  const sortingFunction = SortingUtils.getSortingFunction(this, selectionInfo.sort)
@@ -1928,6 +1961,21 @@ export default class DataUnit {
1928
1961
  this._allowReleaseCallbacks = allow;
1929
1962
  }
1930
1963
 
1964
+
1965
+ /**
1966
+ * Adiciona um mapeamento de origem dos dados de um determinado campo
1967
+ */
1968
+ public addSourceFieldValue(sourceFieldName:string, targetFieldName:string): void{
1969
+ this._fieldSourceValue.set(sourceFieldName, targetFieldName);
1970
+ }
1971
+
1972
+ /**
1973
+ * Retornar o campo de origem dos dados caso exista mapeamento
1974
+ */
1975
+ public getSourceFieldValue(targetFieldName:string): string | undefined{
1976
+ return this._fieldSourceValue.get(targetFieldName);
1977
+ }
1978
+
1931
1979
  private async processLoadingLockers(){
1932
1980
  if(this._loadingLockers.length) {
1933
1981
  await Promise.all(this._loadingLockers);
@@ -77,6 +77,10 @@ const getSearchFormat = (value: any, descriptor: FieldDescriptor) => {
77
77
  return label ? `${codeValue} - ${label}` : codeValue;
78
78
  }
79
79
 
80
+ if(descriptor.userInterface === UserInterface.SEARCHPLUS){
81
+ return codeValue;
82
+ }
83
+
80
84
  return label ? label : codeValue;
81
85
  }
82
86
 
@@ -144,16 +144,20 @@ export const toString = ( dataType: DataType|undefined, value: any): string => {
144
144
  }
145
145
  }
146
146
 
147
- export const compareValues = (valueA: any, valueB: any, descriptor: FieldDescriptor): number => {
147
+ const isSearchField = (userInterface:UserInterface | undefined): boolean => {
148
+ return userInterface === UserInterface.SEARCH || userInterface === UserInterface.SEARCHPLUS;
149
+ }
150
+
151
+ export const compareValues = (valueA: any, valueB: any, descriptor: FieldDescriptor, onlyLabel:boolean = false): number => {
148
152
 
149
153
  if(useStringForComparison(descriptor)){
150
154
  return (getFormattedValue(valueA, descriptor) as string).localeCompare(getFormattedValue(valueB, descriptor));
151
155
  }
152
156
 
153
157
  const {dataType, userInterface} = descriptor;
154
- if(userInterface === UserInterface.SEARCH || userInterface === UserInterface.OPTIONSELECTOR){
155
- const optionValueA = valueA?.value || valueA;
156
- const optionValueB = valueB?.value || valueB;
158
+ if(isSearchField(userInterface) || userInterface === UserInterface.OPTIONSELECTOR){
159
+ const optionValueA = (onlyLabel ? valueA?.label : valueA?.value) || valueA;
160
+ const optionValueB = (onlyLabel ? valueB?.label : valueB?.value) || valueB;
157
161
  if(isNaN(optionValueA) || isNaN(optionValueB)){
158
162
  return (optionValueA as string).localeCompare(optionValueB);
159
163
  }
@@ -91,6 +91,7 @@ export enum UserInterface {
91
91
  DECIMALNUMBER = "DECIMALNUMBER",
92
92
  INTEGERNUMBER = "INTEGERNUMBER",
93
93
  SEARCH = "SEARCH",
94
+ SEARCHPLUS = "SEARCHPLUS",
94
95
  SHORTTEXT = "SHORTTEXT",
95
96
  PASSWORD = "PASSWORD",
96
97
  MASKEDTEXT = "MASKEDTEXT",
@@ -4,16 +4,16 @@ import { FieldDescriptor } from '../metadata/UnitMetadata.js';
4
4
 
5
5
  export class FieldComparator {
6
6
 
7
- public static compare(field: FieldDescriptor, recordA: Record, recordB: Record, asc: boolean = true): number {
7
+ public static compare(field: FieldDescriptor, recordA: Record, recordB: Record, asc: boolean = true, onlyLabel: boolean = false): number {
8
8
  const valueA = (asc ? recordA : recordB)[field.name];
9
9
  const valueB = (asc ? recordB : recordA)[field.name];
10
10
 
11
- return FieldComparator.compareValues(field, valueA, valueB);
11
+ return FieldComparator.compareValues(field, valueA, valueB, onlyLabel);
12
12
  }
13
13
 
14
- public static compareValues(descriptor: FieldDescriptor, valueA: any, valueB: any): number {
14
+ public static compareValues(descriptor: FieldDescriptor, valueA: any, valueB: any, onlyLabel: boolean = false): number {
15
15
  const undefinedComparison = this.compareUndefined(valueA == undefined, valueB == undefined);
16
- return (undefinedComparison != undefined) ? undefinedComparison : compareValues(valueA, valueB, descriptor);
16
+ return (undefinedComparison != undefined) ? undefinedComparison : compareValues(valueA, valueB, descriptor, onlyLabel);
17
17
  }
18
18
 
19
19
  public static compareUndefined(isUndefinedA: boolean, isUndefinedB: boolean): number | undefined {
@@ -26,6 +26,8 @@ export interface ExecutionContext{
26
26
 
27
27
  export enum Action{
28
28
 
29
+ MULTIPLE_EDITION_CHANGED = "multipleEditionChanged",
30
+
29
31
  LOADING_METADATA = "loadingMetadata",
30
32
  METADATA_LOADED = "metadataLoaded",
31
33
 
@@ -107,6 +107,16 @@ export class KeyboardManager {
107
107
  return this;
108
108
  }
109
109
 
110
+
111
+ /**
112
+ * Remove todos os eventos de teclado ou de uma lista informada
113
+ *
114
+ */
115
+ public unbindAllShortcutKeys(listShortcutKeys?: string[]): void {
116
+ const mappedKeys = [...Object.keys(listShortcutKeys || this._mappedElements)];
117
+ mappedKeys?.forEach(keyMap => this.unbind(keyMap));
118
+ }
119
+
110
120
  /**
111
121
  * Remove um evento de teclado
112
122
  *
@@ -1,7 +1,7 @@
1
1
  import { StringUtils } from "./StringUtils.js";
2
- /**
3
- * Define os tipos de operação que o locker pode controlar
4
- */
2
+ /**
3
+ * Define os tipos de operação que o locker pode controlar
4
+ */
5
5
  export enum LockManagerOperation {
6
6
  /**
7
7
  Operação de lock utilizada para controlar cliques nos botoes da taskbar.
@@ -19,7 +19,7 @@ type Lock = {
19
19
  done: boolean
20
20
  }
21
21
 
22
- export class LockManager{
22
+ export class LockManager {
23
23
  private static _locks = new Map<string, Array<Lock>>();
24
24
 
25
25
  /**
@@ -27,18 +27,18 @@ export class LockManager{
27
27
  */
28
28
  public static ATTRIBUTE_NAME = "data-locker-manger-context-id";
29
29
 
30
- private static buildContextID(): string{
30
+ private static buildContextID(): string {
31
31
  return StringUtils.generateUUID();
32
32
  }
33
33
 
34
- private static buildLockerID(ctxId:string|HTMLElement, operation: LockManagerOperation): string | undefined{
35
- if(ctxId == undefined) return undefined;
34
+ private static buildLockerID(ctxId: string | HTMLElement, operation: LockManagerOperation): string | undefined {
35
+ if (ctxId == undefined) return undefined;
36
36
 
37
- let resolvedID:any = ctxId;
37
+ let resolvedID: any = ctxId;
38
38
 
39
- if(resolvedID instanceof HTMLElement){
39
+ if (resolvedID instanceof HTMLElement) {
40
40
  resolvedID = (ctxId as HTMLElement).getAttribute(LockManager.ATTRIBUTE_NAME);
41
- if(!resolvedID) return undefined;
41
+ if (!resolvedID) return undefined;
42
42
  }
43
43
 
44
44
  return `${resolvedID}_${operation}`;
@@ -46,23 +46,23 @@ export class LockManager{
46
46
 
47
47
  private static findExistingCtxId = (element: HTMLElement): string | null => {
48
48
  let currentElement: HTMLElement | null = element;
49
-
49
+
50
50
  while (currentElement) {
51
- if (currentElement.hasAttribute(LockManager.ATTRIBUTE_NAME)) {
52
- return currentElement.getAttribute(LockManager.ATTRIBUTE_NAME);
53
- }
54
-
55
- const childWithCtxId = Array.from(currentElement.children).find(child =>
56
- (child instanceof HTMLElement) && child.hasAttribute(LockManager.ATTRIBUTE_NAME)
57
- ) as HTMLElement | undefined;
58
-
59
- if (childWithCtxId) {
60
- return childWithCtxId.getAttribute(LockManager.ATTRIBUTE_NAME);
61
- }
62
-
63
- currentElement = currentElement.parentElement;
51
+ if (currentElement.hasAttribute(LockManager.ATTRIBUTE_NAME)) {
52
+ return currentElement.getAttribute(LockManager.ATTRIBUTE_NAME);
53
+ }
54
+
55
+ const childWithCtxId = Array.from(currentElement.children).find(child =>
56
+ (child instanceof HTMLElement) && child.hasAttribute(LockManager.ATTRIBUTE_NAME)
57
+ ) as HTMLElement | undefined;
58
+
59
+ if (childWithCtxId) {
60
+ return childWithCtxId.getAttribute(LockManager.ATTRIBUTE_NAME);
61
+ }
62
+
63
+ currentElement = currentElement.parentElement;
64
64
  }
65
-
65
+
66
66
  return null;
67
67
  }
68
68
 
@@ -76,37 +76,31 @@ export class LockManager{
76
76
  }
77
77
  });
78
78
  };
79
-
80
- /**
81
- * Cria um contexto de locker, caso nao exista, para todos elementos pais iniciados com ez- ou snk-.
82
- *
83
- * @param startElement - Elemento de de onde o lock deve começar.
84
- *
85
- * @returns - O id do locker, que pode ser usado para iniciar ou aguardar um lock do contexto.
86
- */
79
+
80
+ /**
81
+ * Cria um contexto de locker, caso nao exista, para todos elementos pais iniciados com ez- ou snk-.
82
+ *
83
+ * @param startElement - Elemento de de onde o lock deve começar.
84
+ *
85
+ * @returns - O id do locker, que pode ser usado para iniciar ou aguardar um lock do contexto.
86
+ */
87
87
  public static addLockManagerCtxId(startElement: HTMLElement): string {
88
- try{
88
+ try {
89
89
  if (!startElement) {
90
90
  console.error("Elemento inicial não fornecido.");
91
91
  return "";
92
92
  }
93
93
 
94
94
  const ctxId = LockManager.findExistingCtxId(startElement) ?? LockManager.buildContextID();
95
-
96
95
  let currentElement: HTMLElement | null = startElement;
97
-
98
- while (currentElement && currentElement.tagName != 'BODY') {
99
- LockManager.traverseAndAddAttr(currentElement, ctxId);
100
- currentElement = currentElement.parentElement;
101
- }
102
-
96
+ LockManager.traverseAndAddAttr(currentElement, ctxId);
103
97
  return ctxId;
104
- }catch(err){
98
+ } catch (err) {
105
99
  console.warn(`Erro ao registrar locks para o elemento: ${startElement?.tagName}`, err);
106
100
  return "";
107
101
  }
108
102
  }
109
-
103
+
110
104
  /**
111
105
  * Reseta todos os locks existentes para um determinado contexto e operação de forma assíncrona
112
106
  * @param id - ID do contexto ou elemento HTML contendo contexto
@@ -116,7 +110,7 @@ export class LockManager{
116
110
  public static async resetLocks(id: string | HTMLElement, operation: LockManagerOperation): Promise<void> {
117
111
  const lockerId = this.buildLockerID(id, operation);
118
112
  if (!lockerId) return;
119
-
113
+
120
114
  const currentLocks = this._locks.get(lockerId);
121
115
 
122
116
  if (currentLocks?.length) {
@@ -126,11 +120,11 @@ export class LockManager{
126
120
  return lock.promise;
127
121
  }));
128
122
 
129
- this._locks.delete(lockerId);
123
+ this._locks.delete(lockerId);
130
124
  }
131
125
  }
132
-
133
-
126
+
127
+
134
128
  /**
135
129
  * Inicia um locker baseado em um contexto e uma operação.
136
130
  *
@@ -139,12 +133,12 @@ export class LockManager{
139
133
  *
140
134
  * @returns - Uma função que fara a liberação do lock.
141
135
  */
142
- public static lock(id:string|HTMLElement, operation:LockManagerOperation): () => void {
136
+ public static lock(id: string | HTMLElement, operation: LockManagerOperation): () => void {
143
137
  const lockerId = LockManager.buildLockerID(id, operation);
144
138
 
145
- if(!lockerId) return () => {};
139
+ if (!lockerId) return () => { };
146
140
 
147
- const lock:Lock = { done: false };
141
+ const lock: Lock = { done: false };
148
142
  const promise = new Promise<void>(resolve => lock.resolve = resolve);
149
143
  lock.promise = promise;
150
144
 
@@ -166,42 +160,42 @@ export class LockManager{
166
160
  *
167
161
  * @returns - Promise que será resolvida quando todos lockers forem finalizados.
168
162
  */
169
- public static async whenResolve(id:string|HTMLElement, operation:LockManagerOperation, debounce?: number, timeOut?: number): Promise<void> {
163
+ public static async whenResolve(id: string | HTMLElement, operation: LockManagerOperation, debounce?: number, timeOut?: number): Promise<void> {
170
164
  const lockerId = LockManager.buildLockerID(id, operation);
171
165
 
172
- if(!lockerId) return;
166
+ if (!lockerId) return;
167
+
168
+ if (debounce) await new Promise(resolve => setTimeout(resolve, debounce));
173
169
 
174
- if(debounce) await new Promise(resolve => setTimeout(resolve, debounce));
175
-
176
170
  const startTime = Date.now();
177
-
171
+
178
172
  while (LockManager._locks.get(lockerId)?.length) {
179
-
173
+
180
174
  if (timeOut && Date.now() - startTime >= timeOut) {
181
175
  await this.resetLocks(id, operation);
182
176
  return;
183
177
  }
184
178
 
185
- const locks:Array<Lock> = LockManager._locks.get(lockerId) ?? [];
179
+ const locks: Array<Lock> = LockManager._locks.get(lockerId) ?? [];
186
180
  await Promise.all(locks.map(lock => lock.promise));
187
-
181
+
188
182
  //Aguarda listeners da tela reagirem as mudancas de estado do dataunit
189
183
  await new Promise(resolve => setTimeout(resolve, debounce || 200));
190
-
184
+
191
185
  LockManager._locks.set(lockerId, locks.filter(lock => !lock.done));
192
- }
193
-
186
+ }
187
+
194
188
  }
195
-
196
- public static async whenHasLock(id:string|HTMLElement, operation:LockManagerOperation, timeOut?: number): Promise<void> {
189
+
190
+ public static async whenHasLock(id: string | HTMLElement, operation: LockManagerOperation, timeOut?: number): Promise<void> {
197
191
  const lockerId = LockManager.buildLockerID(id, operation);
198
192
 
199
- if(!lockerId) return;
193
+ if (!lockerId) return;
200
194
 
201
195
  const startTime = Date.now();
202
196
 
203
197
  while (!LockManager._locks.get(lockerId)?.length) {
204
-
198
+
205
199
  if (timeOut && Date.now() - startTime >= timeOut) {
206
200
  await this.resetLocks(id, operation);
207
201
  return;
@@ -120,6 +120,17 @@ export default class ObjectUtils{
120
120
  return Object.keys(obj).length === 0 && obj.constructor === Object;
121
121
  }
122
122
 
123
+ /**
124
+ * Verifica se o objeto está vazio (sem atributos) e retorna true caso seja undefined ou null.
125
+ *
126
+ * @param obj - Objeto a ser verificado.
127
+ * @returns - True caso o objeto esteja vazio.
128
+ */
129
+ public static isEmptySafetyCheck(obj: object): boolean{
130
+ if(obj === null || obj === undefined) return true;
131
+ return Object.keys(obj).length === 0 && obj.constructor === Object;
132
+ }
133
+
123
134
  /**
124
135
  * Verifica se o objeto NÃO está vazio (sem atributos).
125
136
  *
@@ -130,6 +141,16 @@ export default class ObjectUtils{
130
141
  return !this.isEmpty(obj);
131
142
  }
132
143
 
144
+ /**
145
+ * Verifica se o objeto NÃO está vazio (sem atributos) e retorna false caso objeto seja null ou undefined.
146
+ *
147
+ * @param obj - Objeto a ser verificado.
148
+ * @returns - True caso o objeto NÃO esteja vazio
149
+ */
150
+ public static isNotEmptySafetyCheck(obj: object): boolean{
151
+ return !this.isEmptySafetyCheck(obj);
152
+ }
153
+
133
154
  /**
134
155
  * Busca a propriedade de um objeto baseado em seu caminho.
135
156
  *
@@ -17,7 +17,16 @@ export default class SortingUtils {
17
17
  return (recordA, recordB) => {
18
18
  for (const sort of sorting) {
19
19
  if (sort.field){
20
- const result = FieldComparator.compare(dataUnit.getField(sort.field) as FieldDescriptor, recordA, recordB, sort.mode === SortMode.ASC);
20
+ let field = dataUnit.getField(sort.field) as FieldDescriptor;
21
+ let onlyLabel = false;
22
+ if(!field){
23
+ const sourceFieldName = dataUnit.getSourceFieldValue(sort.field) ?? "";
24
+
25
+ field = dataUnit.getField(sourceFieldName) as FieldDescriptor;
26
+ onlyLabel = true;
27
+ }
28
+
29
+ const result = FieldComparator.compare(field, recordA, recordB, sort.mode === SortMode.ASC, onlyLabel);
21
30
  if (result != 0) {
22
31
  return result;
23
32
  }
@@ -289,7 +289,7 @@ export class StringUtils {
289
289
  * @returns String convertida em PascalCase.
290
290
  */
291
291
  static toPascalCase(value: string): string{
292
- return value
292
+ return (value || "")
293
293
  .toLowerCase()
294
294
  .replace(/(?:^|\s)\w/g, (match: string) => match.toUpperCase())
295
295
  .replace(/[\s-_]/g,'');
@@ -301,7 +301,7 @@ export class StringUtils {
301
301
  * @returns String convertida em snake_case.
302
302
  */
303
303
  static toSnakeCase(value: string): string{
304
- return value
304
+ return (value || "")
305
305
  .toLowerCase()
306
306
  .replace(/[A-Z]/g, (match: string) => `_${match.toLowerCase()}`)
307
307
  .replace(/[\s-]/g, '_')
@@ -314,7 +314,7 @@ export class StringUtils {
314
314
  * @returns String convertida em KebabCase.
315
315
  */
316
316
  static toKebabCase(value: string): string{
317
- return value.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
317
+ return (value || "").replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
318
318
  }
319
319
 
320
320
  /**
@@ -336,13 +336,13 @@ export class StringUtils {
336
336
  const units = ["B", "KB", "MB", "GB"];
337
337
 
338
338
  if (bytes < 1024) {
339
- return `${bytes.toString()}B`;
339
+ return `${bytes?.toString()}B`;
340
340
  }
341
341
 
342
342
  const base = Math.log(bytes) / Math.log(1024);
343
343
  const offSet = Math.floor(base);
344
344
  if (offSet >= units.length) {
345
- return `${bytes.toString()}B`;
345
+ return `${bytes?.toString()}B`;
346
346
  }
347
347
 
348
348
  const value = this.prettyPrecision(Math.pow(1024, base - offSet).toFixed(2).toString());
@@ -389,6 +389,8 @@ export class StringUtils {
389
389
  * @return {boolean} Se a string pode ser convertida
390
390
  */
391
391
  static isCaseable(original: string): boolean {
392
+ if(original == null) return false;
393
+
392
394
  const uppercase = original.toUpperCase()
393
395
  const lowercase = original.toLowerCase()
394
396
 
@@ -402,6 +404,7 @@ export class StringUtils {
402
404
  * @return {boolean} Se a string é minúscula
403
405
  */
404
406
  static isLowerCase(original: string): boolean {
407
+ if(original == null) return false;
405
408
  const uppercase = original.toUpperCase()
406
409
 
407
410
  return this.isCaseable(original) && uppercase !== original
@@ -414,6 +417,7 @@ export class StringUtils {
414
417
  * @return {string} A string invertida
415
418
  */
416
419
  static getOppositeCase(original: string): string {
420
+ if(original == null) return "";
417
421
  return this.isLowerCase(original) ? original.toUpperCase() : original.toLowerCase()
418
422
  }
419
423
 
@@ -487,7 +491,7 @@ export class StringUtils {
487
491
  }
488
492
 
489
493
  public static highlightValue(argument: String, matchFields: any, value: string, fieldMD: any, forceMatch: boolean) {
490
- const startHighlightTag = "<span class='card-item__highlight'>";
494
+ const startHighlightTag = "<span class='card-item__highlight'>"; //FIXME: valor hardcoded, não podemos reaproveitar para outros cenários, dessa forma.
491
495
  const endHighlightTag = "</span>";
492
496
  let valueAux = JSUtils.replaceHtmlEntities(value);
493
497