@sankhyalabs/core 5.17.4 → 5.18.0-ms.1

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 (135) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/ColumnFilterManager.md +145 -0
  3. package/.docs/classes/DataUnit.md +334 -136
  4. package/.docs/classes/DataUnitInMemoryLoader.md +303 -0
  5. package/.docs/classes/DataUnitLoaderUtils.md +151 -0
  6. package/.docs/classes/FieldComparator.md +2 -2
  7. package/.docs/classes/IDBRepository.md +22 -0
  8. package/.docs/classes/LockManager.md +249 -0
  9. package/.docs/classes/MaskFormatter.md +66 -14
  10. package/.docs/classes/ObjectUtils.md +95 -2
  11. package/.docs/classes/SelectionInfo.md +25 -11
  12. package/.docs/classes/ServiceUtils.md +67 -0
  13. package/.docs/classes/SilentException.md +193 -0
  14. package/.docs/classes/UserAgentUtils.md +15 -1
  15. package/.docs/enumerations/Action.md +32 -22
  16. package/.docs/enumerations/ChangeOperation.md +4 -4
  17. package/.docs/enumerations/LockManagerOperation.md +33 -0
  18. package/.docs/enumerations/RECORD_DATE_FORMAT.md +27 -0
  19. package/.docs/enumerations/SelectionMode.md +2 -2
  20. package/.docs/enumerations/StorageType.md +37 -0
  21. package/.docs/globals.md +11 -0
  22. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  23. package/.docs/interfaces/DataUnitInMemoryLoaderConfig.md +37 -0
  24. package/.docs/interfaces/IRepository.md +18 -0
  25. package/.docs/interfaces/LoadDataRequest.md +1 -1
  26. package/.docs/interfaces/PageRequest.md +3 -3
  27. package/.docs/interfaces/PaginationInfo.md +25 -0
  28. package/.docs/interfaces/PaginationInfoBuilderParams.md +37 -0
  29. package/.docs/interfaces/QuickFilter.md +3 -3
  30. package/.docs/interfaces/Record.md +4 -4
  31. package/.docs/interfaces/SavedRecord.md +5 -5
  32. package/.docs/interfaces/WaitingChange.md +3 -3
  33. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +1 -1
  34. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +1 -1
  35. package/.docs/type-aliases/DataUnitEventOptions.md +1 -1
  36. package/.releaserc +1 -0
  37. package/bun.lockb +0 -0
  38. package/dist/dataunit/DataUnit.d.ts +60 -10
  39. package/dist/dataunit/DataUnit.js +151 -50
  40. package/dist/dataunit/DataUnit.js.map +1 -1
  41. package/dist/dataunit/formatting/PrettyFormatter.js +8 -2
  42. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  43. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.d.ts +9 -0
  44. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.js +6 -0
  45. package/dist/dataunit/loader/DataUnitInMemoryLoaderConfig.js.map +1 -0
  46. package/dist/dataunit/loader/dataUnitInMemoryLoader.d.ts +25 -0
  47. package/dist/dataunit/loader/dataUnitInMemoryLoader.js +131 -0
  48. package/dist/dataunit/loader/dataUnitInMemoryLoader.js.map +1 -0
  49. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.d.ts +20 -0
  50. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.js +62 -0
  51. package/dist/dataunit/loader/utils/dataUnitLoaderUtils.js.map +1 -0
  52. package/dist/dataunit/loading/LoadDataRequest.d.ts +1 -1
  53. package/dist/dataunit/loading/PaginationInfo.d.ts +8 -0
  54. package/dist/dataunit/sorting/FieldComparator.d.ts +2 -2
  55. package/dist/dataunit/sorting/FieldComparator.js +4 -9
  56. package/dist/dataunit/sorting/FieldComparator.js.map +1 -1
  57. package/dist/dataunit/state/action/DataUnitAction.d.ts +1 -0
  58. package/dist/dataunit/state/action/DataUnitAction.js +1 -0
  59. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  60. package/dist/dataunit/state/slice/LoadingControlSlice.js +16 -0
  61. package/dist/dataunit/state/slice/LoadingControlSlice.js.map +1 -1
  62. package/dist/dataunit/state/slice/RecordsSlice.js +1 -1
  63. package/dist/dataunit/state/slice/RecordsSlice.js.map +1 -1
  64. package/dist/dataunit/state/slice/SelectionSlice.js +2 -2
  65. package/dist/dataunit/state/slice/SelectionSlice.js.map +1 -1
  66. package/dist/exceptions/SilentException.d.ts +14 -0
  67. package/dist/exceptions/SilentException.js +13 -0
  68. package/dist/exceptions/SilentException.js.map +1 -0
  69. package/dist/index.d.ts +9 -1
  70. package/dist/index.js +9 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/repository/IRepository.d.ts +6 -0
  73. package/dist/repository/indexeddb/IDBRepository.d.ts +1 -0
  74. package/dist/repository/indexeddb/IDBRepository.js +3 -0
  75. package/dist/repository/indexeddb/IDBRepository.js.map +1 -1
  76. package/dist/utils/CacheManager/index.d.ts +52 -0
  77. package/dist/utils/CacheManager/index.js +101 -0
  78. package/dist/utils/CacheManager/index.js.map +1 -0
  79. package/dist/utils/CacheManager/interfaces/index.d.ts +5 -0
  80. package/dist/utils/CacheManager/interfaces/index.js +7 -0
  81. package/dist/utils/CacheManager/interfaces/index.js.map +1 -0
  82. package/dist/utils/ColumnFilterManager.d.ts +19 -0
  83. package/dist/utils/ColumnFilterManager.js +73 -0
  84. package/dist/utils/ColumnFilterManager.js.map +1 -0
  85. package/dist/utils/LockManager.d.ts +58 -0
  86. package/dist/utils/LockManager.js +191 -0
  87. package/dist/utils/LockManager.js.map +1 -0
  88. package/dist/utils/MaskFormatter.d.ts +16 -1
  89. package/dist/utils/MaskFormatter.js +82 -2
  90. package/dist/utils/MaskFormatter.js.map +1 -1
  91. package/dist/utils/ObjectUtils.d.ts +24 -0
  92. package/dist/utils/ObjectUtils.js +33 -0
  93. package/dist/utils/ObjectUtils.js.map +1 -1
  94. package/dist/utils/ServiceUtils.d.ts +24 -0
  95. package/dist/utils/ServiceUtils.js +40 -0
  96. package/dist/utils/ServiceUtils.js.map +1 -0
  97. package/dist/utils/SortingUtils.d.ts +9 -0
  98. package/dist/utils/SortingUtils.js +24 -0
  99. package/dist/utils/SortingUtils.js.map +1 -0
  100. package/dist/utils/UserAgentUtils/index.d.ts +1 -0
  101. package/dist/utils/UserAgentUtils/index.js +5 -0
  102. package/dist/utils/UserAgentUtils/index.js.map +1 -1
  103. package/package.json +1 -1
  104. package/reports/test-report.xml +148 -84
  105. package/src/dataunit/DataUnit.ts +191 -68
  106. package/src/dataunit/formatting/PrettyFormatter.ts +10 -2
  107. package/src/dataunit/loader/DataUnitInMemoryLoaderConfig.ts +10 -0
  108. package/src/dataunit/loader/dataUnitInMemoryLoader.ts +176 -0
  109. package/src/dataunit/loader/utils/dataUnitLoaderUtils.ts +86 -0
  110. package/src/dataunit/loading/LoadDataRequest.ts +1 -1
  111. package/src/dataunit/loading/PaginationInfo.ts +10 -0
  112. package/src/dataunit/sorting/FieldComparator.ts +18 -32
  113. package/src/dataunit/state/action/DataUnitAction.ts +1 -0
  114. package/src/dataunit/state/slice/LoadingControlSlice.ts +42 -0
  115. package/src/dataunit/state/slice/RecordsSlice.ts +1 -1
  116. package/src/dataunit/state/slice/SelectionSlice.ts +2 -2
  117. package/src/dataunit/state/slice/test/RecordsSlice.spec.ts +45 -0
  118. package/src/dataunit/test/DataUnit.spec.ts +44 -0
  119. package/src/exceptions/SilentException.ts +25 -0
  120. package/src/index.ts +20 -1
  121. package/src/repository/IRepository.ts +7 -0
  122. package/src/repository/indexeddb/IDBRepository.ts +4 -0
  123. package/src/utils/CacheManager/index.ts +103 -0
  124. package/src/utils/CacheManager/interfaces/index.ts +5 -0
  125. package/src/utils/ColumnFilterManager.ts +104 -0
  126. package/src/utils/LockManager.ts +213 -0
  127. package/src/utils/MaskFormatter.ts +93 -2
  128. package/src/utils/ObjectUtils.ts +36 -0
  129. package/src/utils/ServiceUtils.ts +36 -0
  130. package/src/utils/SortingUtils.ts +30 -0
  131. package/src/utils/UserAgentUtils/index.ts +6 -1
  132. package/src/utils/test/objectUtils.spec.ts +109 -0
  133. package/test/dataunit/loader/dataUnitInMemoryLoader.spec.ts +221 -0
  134. package/test/dataunit/loader/utils/dataUnitLoaderUtils.spec.ts +158 -0
  135. package/test/util/ColumnFilterManager.spec.ts +133 -0
@@ -0,0 +1,103 @@
1
+ import { StorageType } from './interfaces/index.js';
2
+
3
+ export * from "./interfaces/index.js";
4
+
5
+ export class CacheManager {
6
+ /**
7
+ * Nome da chave utilizada para armazenar o cache no armazenamento.
8
+ */
9
+ private static readonly storageKey = 'cacheManager';
10
+
11
+ /**
12
+ * Estrutura de armazenamento em memória.
13
+ */
14
+ private static inMemoryCache = new Map<string, any>();
15
+
16
+ /**
17
+ * Recupera ou define o valor no cache.
18
+ *
19
+ * @param key Identificador único para armazenar e recuperar o valor.
20
+ * @param fetchCallback Função que retorna uma `Promise` com o valor a ser armazenado no cache caso ele não exista ou tenha expirado.
21
+ * @param storageType Tipo de armazenamento: `'inMemoryCache'`, `'sessionStorage'` ou `'localStorage'`.
22
+ * @returns Uma `Promise` com o valor armazenado ou obtido via `fetchCallback`.
23
+ */
24
+ public static async getOrSet<T>(
25
+ key: string,
26
+ fetchCallback: () => Promise<T>,
27
+ storageType: StorageType = StorageType.IN_MEMORY_CACHE
28
+ ): Promise<T> {
29
+ const cache = this.getCache(storageType);
30
+
31
+ if (cache[key]) {
32
+ return cache[key].data;
33
+ }
34
+
35
+ const data = await fetchCallback();
36
+ cache[key] = { data };
37
+ this.saveCache(cache, storageType);
38
+ return data;
39
+ }
40
+
41
+ /**
42
+ * Remove uma entrada específica do cache.
43
+ *
44
+ * @param key Identificador único da entrada a ser removida.
45
+ * @param storageType Tipo de armazenamento: `'inMemoryCache'`, `'sessionStorage'` ou `'localStorage'`.
46
+ */
47
+ public static clear(key: string, storageType: StorageType = StorageType.IN_MEMORY_CACHE): void {
48
+ const cache = this.getCache(storageType);
49
+ delete cache[key];
50
+ this.saveCache(cache, storageType);
51
+ }
52
+
53
+ /**
54
+ * Remove todas as entradas do cache.
55
+ *
56
+ * @param storageType Tipo de armazenamento: `'inMemoryCache'`, `'sessionStorage'` ou `'localStorage'`.
57
+ */
58
+ public static clearAll(storageType: StorageType = StorageType.IN_MEMORY_CACHE): void {
59
+ if (storageType === StorageType.IN_MEMORY_CACHE) {
60
+ this.inMemoryCache.clear();
61
+ } else {
62
+ this.getStorage(storageType).removeItem(this.storageKey);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Obtém o cache armazenado no armazenamento especificado.
68
+ * @param storageType Tipo de armazenamento: `'inMemoryCache'`, `'sessionStorage'` ou `'localStorage'`.
69
+ * @returns Um objeto representando o cache armazenado.
70
+ */
71
+ private static getCache(storageType: StorageType): Record<string, any> {
72
+ if (storageType === StorageType.IN_MEMORY_CACHE) {
73
+ return Object.fromEntries(this.inMemoryCache);
74
+ }
75
+
76
+ const storage = this.getStorage(storageType);
77
+ const cache = storage.getItem(this.storageKey);
78
+ return cache ? JSON.parse(cache) : {};
79
+ }
80
+
81
+ /**
82
+ * Salva o cache no armazenamento especificado.
83
+ * @param cache O objeto representando o cache a ser salvo.
84
+ * @param storageType Tipo de armazenamento: `'inMemoryCache'`, `'sessionStorage'` ou `'localStorage'`.
85
+ */
86
+ private static saveCache(cache: Record<string, any>, storageType: StorageType): void {
87
+ if (storageType === StorageType.IN_MEMORY_CACHE) {
88
+ this.inMemoryCache = new Map(Object.entries(cache));
89
+ } else {
90
+ const storage = this.getStorage(storageType);
91
+ storage.setItem(this.storageKey, JSON.stringify(cache));
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Retorna o armazenamento correspondente ao tipo especificado.
97
+ * @param storageType Tipo de armazenamento: `'sessionStorage'` ou `'localStorage'`.
98
+ * @returns O objeto de armazenamento correspondente.
99
+ */
100
+ private static getStorage(storageType: StorageType): Storage {
101
+ return storageType === StorageType.LOCAL_STORAGE ? localStorage : sessionStorage;
102
+ }
103
+ }
@@ -0,0 +1,5 @@
1
+ export enum StorageType{
2
+ IN_MEMORY_CACHE = 'inMemoryCache',
3
+ SESSION_STORAGE = 'sessionStorage',
4
+ LOCAL_STORAGE = 'localStorage'
5
+ }
@@ -0,0 +1,104 @@
1
+ import { Filter } from '../dataunit/metadata/UnitMetadata.js';
2
+ import DataUnit, { Record } from '../dataunit/DataUnit.js';
3
+ import { FieldComparator } from '../dataunit/sorting/FieldComparator.js';
4
+ import { LoadDataRequest } from '../dataunit/loading/LoadDataRequest.js';
5
+
6
+ const COLUMN_FILTER_PATTERN = /FILTRO_COLUNA_(.+)/;
7
+
8
+ export class ColumnFilterManager {
9
+
10
+ public static getColumnFilters(filters: Array<Filter>, fieldName: string): Map<string, IColumnFilter> {
11
+ const columnFilters: Map<string, IColumnFilter> = new Map();
12
+
13
+ if (!filters?.length) {
14
+ return columnFilters;
15
+ }
16
+
17
+ filters.forEach(filter => {
18
+ const result = COLUMN_FILTER_PATTERN.exec(filter.name);
19
+ if (result) {
20
+ if (fieldName !== result[1]) {
21
+ columnFilters.set(filter.name, { columnName: result[1], ...filter });
22
+ }
23
+ }
24
+ });
25
+
26
+ return columnFilters;
27
+ }
28
+
29
+ public static getFilterFunction(dataUnit: DataUnit, filters?: Array<IColumnFilter>): ((record: Record) => boolean) | undefined {
30
+ if (!filters?.length) return undefined;
31
+
32
+ return record => {
33
+ for (const filter of filters) {
34
+ if (!ColumnFilterManager.recordMatchesFilter(dataUnit, record, filter)) {
35
+ return false;
36
+ }
37
+ }
38
+ return true;
39
+ };
40
+ }
41
+
42
+ private static recordMatchesFilter(dataUnit: DataUnit, record: Record, columnFilter: IColumnFilter): boolean {
43
+ const fieldDescriptor = dataUnit.getField(columnFilter.columnName);
44
+
45
+ if (!columnFilter.params || !fieldDescriptor) return false;
46
+
47
+ const fieldValue = record[columnFilter.columnName];
48
+
49
+ for (let param of columnFilter.params) {
50
+ const paramValue = dataUnit.valueFromString(columnFilter.columnName, param.value);
51
+ if (FieldComparator.compareValues(fieldDescriptor, fieldValue, paramValue) === 0) {
52
+ return true;
53
+ }
54
+
55
+ }
56
+ return false;
57
+ }
58
+
59
+ public static compileDistinct(fieldName: string, dataUnit: DataUnit): Array<IMultiSelectionOption> {
60
+ const request = dataUnit.getLastLoadRequest();
61
+ let list = dataUnit.records;
62
+ return this.doCompileDistinct(request, fieldName, dataUnit, list);
63
+ }
64
+
65
+ public static compileDistinctFromArray(fieldName: string, dataUnit: DataUnit, records: Array<Record>): Array<IMultiSelectionOption> {
66
+ const request = dataUnit.getLastLoadRequest();
67
+ return this.doCompileDistinct(request, fieldName, dataUnit, records);
68
+ }
69
+
70
+ private static doCompileDistinct(request: LoadDataRequest | undefined, fieldName: string, dataUnit: DataUnit, list: Array<Record>) {
71
+ if (request != undefined) {
72
+ const columnFilters: Array<IColumnFilter> = Array.from(ColumnFilterManager.getColumnFilters(request?.filters ?? [], fieldName).values());
73
+ const filterFunction = ColumnFilterManager.getFilterFunction(dataUnit, columnFilters);
74
+ if (filterFunction != undefined) {
75
+ list = list.filter(filterFunction);
76
+ }
77
+ }
78
+
79
+ const distinct: Map<string, IMultiSelectionOption> = new Map(
80
+ list.map(record => {
81
+ const fieldValue = record[fieldName];
82
+ if (fieldValue == undefined) {
83
+ return [null, { label: null, value: null, check: true }];
84
+ }
85
+ const item = { label: dataUnit.getFormattedValue(fieldName, fieldValue), value: fieldValue, check: true };
86
+ return [item.label, item];
87
+ }) as any,
88
+ );
89
+
90
+ return Array.from(distinct.values());
91
+ }
92
+
93
+ }
94
+
95
+
96
+ interface IMultiSelectionOption {
97
+ value: string;
98
+ label: string;
99
+ check: boolean;
100
+ }
101
+
102
+ export interface IColumnFilter extends Filter {
103
+ columnName: string;
104
+ }
@@ -0,0 +1,213 @@
1
+ import { StringUtils } from "./StringUtils.js";
2
+ /**
3
+ * Define os tipos de operação que o locker pode controlar
4
+ */
5
+ export enum LockManagerOperation {
6
+ /**
7
+ Operação de lock utilizada para controlar cliques nos botoes da taskbar.
8
+ */
9
+ TASKBAR_CLICK = "taskbar_click",
10
+ /**
11
+ Operação de lock utilizada para controlar carregamento da aplicação.
12
+ */
13
+ APP_LOADING = "app_loading"
14
+ }
15
+
16
+ type Lock = {
17
+ promise?: Promise<unknown>,
18
+ resolve?: () => void,
19
+ done: boolean
20
+ }
21
+
22
+ export class LockManager{
23
+ private static _locks = new Map<string, Array<Lock>>();
24
+
25
+ /**
26
+ * Nome do atributo que será utilizado para controlar contexto de locks nos elementos da DOM.
27
+ */
28
+ public static ATTRIBUTE_NAME = "data-locker-manger-context-id";
29
+
30
+ private static buildContextID(): string{
31
+ return StringUtils.generateUUID();
32
+ }
33
+
34
+ private static buildLockerID(ctxId:string|HTMLElement, operation: LockManagerOperation): string | undefined{
35
+ if(ctxId == undefined) return undefined;
36
+
37
+ let resolvedID:any = ctxId;
38
+
39
+ if(resolvedID instanceof HTMLElement){
40
+ resolvedID = (ctxId as HTMLElement).getAttribute(LockManager.ATTRIBUTE_NAME);
41
+ if(!resolvedID) return undefined;
42
+ }
43
+
44
+ return `${resolvedID}_${operation}`;
45
+ }
46
+
47
+ private static findExistingCtxId = (element: HTMLElement): string | null => {
48
+ let currentElement: HTMLElement | null = element;
49
+
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;
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ private static traverseAndAddAttr = (element: HTMLElement, ctxId: string): void => {
70
+ if (element.tagName.startsWith('EZ-') || element.tagName.startsWith('SNK-')) {
71
+ element.setAttribute(LockManager.ATTRIBUTE_NAME, ctxId);
72
+ }
73
+ Array.from(element.children).forEach((child) => {
74
+ if (child instanceof HTMLElement) {
75
+ LockManager.traverseAndAddAttr(child, ctxId);
76
+ }
77
+ });
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
+ */
87
+ public static addLockManagerCtxId(startElement: HTMLElement): string {
88
+ try{
89
+ if (!startElement) {
90
+ console.error("Elemento inicial não fornecido.");
91
+ return "";
92
+ }
93
+
94
+ const ctxId = LockManager.findExistingCtxId(startElement) ?? LockManager.buildContextID();
95
+
96
+ let currentElement: HTMLElement | null = startElement;
97
+
98
+ while (currentElement && currentElement.tagName != 'BODY') {
99
+ LockManager.traverseAndAddAttr(currentElement, ctxId);
100
+ currentElement = currentElement.parentElement;
101
+ }
102
+
103
+ return ctxId;
104
+ }catch(err){
105
+ console.warn(`Erro ao registrar locks para o elemento: ${startElement?.tagName}`, err);
106
+ return "";
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Reseta todos os locks existentes para um determinado contexto e operação de forma assíncrona
112
+ * @param id - ID do contexto ou elemento HTML contendo contexto
113
+ * @param operation - Operação específica para resetar os locks
114
+ * @returns Promise que será resolvida quando todos os locks forem resetados
115
+ */
116
+ public static async resetLocks(id: string | HTMLElement, operation: LockManagerOperation): Promise<void> {
117
+ const lockerId = this.buildLockerID(id, operation);
118
+ if (!lockerId) return;
119
+
120
+ const currentLocks = this._locks.get(lockerId);
121
+
122
+ if (currentLocks?.length) {
123
+ await Promise.all(currentLocks.map(lock => {
124
+ lock.done = true;
125
+ lock.resolve?.();
126
+ return lock.promise;
127
+ }));
128
+
129
+ this._locks.delete(lockerId);
130
+ }
131
+ }
132
+
133
+
134
+ /**
135
+ * Inicia um locker baseado em um contexto e uma operação.
136
+ *
137
+ * @param id - Pode ser um ID do contexto de locker, ou, o elemento contendo um contexto de locker.
138
+ * @param operation - Operação do contexto que o lock deve ser feito.
139
+ *
140
+ * @returns - Uma função que fara a liberação do lock.
141
+ */
142
+ public static lock(id:string|HTMLElement, operation:LockManagerOperation): () => void {
143
+ const lockerId = LockManager.buildLockerID(id, operation);
144
+
145
+ if(!lockerId) return () => {};
146
+
147
+ const lock:Lock = { done: false };
148
+ const promise = new Promise<void>(resolve => lock.resolve = resolve);
149
+ lock.promise = promise;
150
+
151
+ const currentLocks = LockManager._locks.get(lockerId) ?? [];
152
+ currentLocks.push(lock);
153
+ LockManager._locks.set(lockerId, currentLocks);
154
+
155
+ return () => {
156
+ lock.done = true;
157
+ lock.resolve?.();
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Aguarda todos os lockers de um contexto e operação serem resolvidos.
163
+ *
164
+ * @param id - Pode ser um ID do contexto de locker, ou, o elemento contendo um contexto de locker.
165
+ * @param operation - Operação do contexto que devera aguardar.
166
+ *
167
+ * @returns - Promise que será resolvida quando todos lockers forem finalizados.
168
+ */
169
+ public static async whenResolve(id:string|HTMLElement, operation:LockManagerOperation, debounce?: number, timeOut?: number): Promise<void> {
170
+ const lockerId = LockManager.buildLockerID(id, operation);
171
+
172
+ if(!lockerId) return;
173
+
174
+ if(debounce) await new Promise(resolve => setTimeout(resolve, debounce));
175
+
176
+ const startTime = Date.now();
177
+
178
+ while (LockManager._locks.get(lockerId)?.length) {
179
+
180
+ if (timeOut && Date.now() - startTime >= timeOut) {
181
+ await this.resetLocks(id, operation);
182
+ return;
183
+ }
184
+
185
+ const locks:Array<Lock> = LockManager._locks.get(lockerId) ?? [];
186
+ await Promise.all(locks.map(lock => lock.promise));
187
+
188
+ //Aguarda listeners da tela reagirem as mudancas de estado do dataunit
189
+ await new Promise(resolve => setTimeout(resolve, debounce || 200));
190
+
191
+ LockManager._locks.set(lockerId, locks.filter(lock => !lock.done));
192
+ }
193
+
194
+ }
195
+
196
+ public static async whenHasLock(id:string|HTMLElement, operation:LockManagerOperation, timeOut?: number): Promise<void> {
197
+ const lockerId = LockManager.buildLockerID(id, operation);
198
+
199
+ if(!lockerId) return;
200
+
201
+ const startTime = Date.now();
202
+
203
+ while (!LockManager._locks.get(lockerId)?.length) {
204
+
205
+ if (timeOut && Date.now() - startTime >= timeOut) {
206
+ await this.resetLocks(id, operation);
207
+ return;
208
+ }
209
+
210
+ await new Promise(resolve => setTimeout(resolve, 200));
211
+ }
212
+ }
213
+ }
@@ -58,7 +58,8 @@ export class MaskFormatter {
58
58
  "cnpj": "##.###.###/####-##",
59
59
  "cpf": "###.###.###-##",
60
60
  "phone": "(##) ####-####",
61
- "cep": "##.###-###"
61
+ "cep": "##.###-###",
62
+ "cor_rgb" : "'#AAAAAA"
62
63
  };
63
64
 
64
65
 
@@ -92,13 +93,103 @@ export class MaskFormatter {
92
93
  this.mask = mask;
93
94
  }
94
95
 
96
+
97
+ /**
98
+ * Aplica a máscara quando o input é alterado
99
+ *
100
+ * @param value Valor a ser aplicado com a máscara.
101
+ * @return O valor processado de acordo com o padrão.
102
+ */
103
+ public applyMask(value: string) {
104
+ if (this.mask === MaskFormatter.DEFAULT_MASKS.cor_rgb) {
105
+ value = value.replace("#", "")
106
+
107
+ const expectedFormattedValue = this.format(value.replace(/\s/g, ""));
108
+
109
+ if (value === expectedFormattedValue) {
110
+ return value;
111
+ }
112
+
113
+ if (value.length >= 6) {
114
+ const formattedValue = this.format(value);
115
+ return formattedValue;
116
+ } else {
117
+ return value;
118
+ }
119
+ }
120
+
121
+ const expectedFormattedValue = this.format(value.replace(/\s/g, ""));
122
+
123
+ if (value === expectedFormattedValue) {
124
+ return value;
125
+ }
126
+ const maskPlaceholders = this.mask.match(/[UAL#?*']/g) || [];
127
+ const placeholderCount = maskPlaceholders.length;
128
+
129
+ const validValue = value.split('').filter((char, index) => {
130
+ const placeholder = maskPlaceholders[index];
131
+ if (!placeholder) return false;
132
+
133
+ switch (placeholder) {
134
+ case MaskFormatter.DIGIT_KEY:
135
+ return /\d/.test(char);
136
+ case MaskFormatter.UPPERCASE_KEY:
137
+ return /[a-zA-Z]/.test(char);
138
+ case MaskFormatter.LOWERCASE_KEY:
139
+ return /[a-zA-Z]/.test(char);
140
+ case MaskFormatter.ALPHA_NUMERIC_KEY:
141
+ return /[a-zA-Z0-9]/.test(char);
142
+ case MaskFormatter.CHARACTER_KEY:
143
+ return /[a-zA-Z]/.test(char);
144
+ case MaskFormatter.ANYTHING_KEY:
145
+ return true;
146
+ case MaskFormatter.LITERAL_KEY:
147
+ const literalChar = this.mask.charAt(index);
148
+ return char === literalChar;
149
+ default:
150
+ return false;
151
+ }
152
+ });
153
+
154
+ if (validValue.length >= placeholderCount) {
155
+ const formattedValue = this.format(validValue.join(''));
156
+ return formattedValue;
157
+ } else {
158
+ const partialValue = validValue.join('');
159
+ return partialValue;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Remove a máscara formatando a string retornando sem máscara
165
+ *
166
+ * @param value Valor a ser formatado com máscara.
167
+ * @return O valor processado de acordo com o padrão.
168
+ */
169
+ public removeMask(value: string): string {
170
+ const maskString = this.mask.replace("#", "");
171
+ const maskElements = maskString.split("");
172
+
173
+ let valueMapped = value
174
+
175
+ maskElements.forEach(maskChar => {
176
+ valueMapped = valueMapped.replace(maskChar, "")
177
+ })
178
+
179
+ return valueMapped;
180
+ }
181
+
95
182
  /**
96
183
  * Formata a string passada baseada na máscara definda pelo atributo mask.
97
184
  *
98
185
  * @param value Valor a ser formatado.
186
+ * @param trimBefore Executa um trim para remover espaços em branco.
99
187
  * @return O valor processado de acordo com o padrão.
100
188
  */
101
- public format(value: string): string {
189
+ public format(value: string, trimBefore: boolean = false): string {
190
+ if(trimBefore){
191
+ value = value.trim();
192
+ }
102
193
  let result: string = '';
103
194
  const index: Array<number> = [0];
104
195
 
@@ -85,6 +85,31 @@ export default class ObjectUtils{
85
85
  return ObjectUtils.objectToString(obj1) === ObjectUtils.objectToString(obj2);
86
86
  }
87
87
 
88
+ /**
89
+ * Compara se o valor de dois items são equivalentes.
90
+ * Comparando tanto o valor do item em si, quanto sua propriedade "value"
91
+ *
92
+ * @param obj1 - Objeto a ser comparado.
93
+ * @param obj2 - Objeto a ser comparado.
94
+ * @param propToCompare - propriedade que deve ser comparada.
95
+ * @returns - Se o objeto 1 é equivalente ao objeto 2.
96
+ *
97
+ * * @example
98
+ * hasEquivalentProps('123', {value: '123', label: teste}, 'value') | Retorna: true
99
+ * @example
100
+ * hasEquivalentProps('xpto', {value: '123', label: teste}, 'propName') | Retorna: false
101
+ */
102
+ public static hasEquivalentProps(obj1: any, obj2: any, propToCompare:string = "value"): boolean{
103
+ return this.getComparableProp(obj1, propToCompare) == this.getComparableProp(obj2, propToCompare);
104
+ }
105
+
106
+ private static getComparableProp(value: any, propToCompare:string = "value"): boolean{
107
+ if (typeof value === 'object' && value !== null && propToCompare in value) {
108
+ return value[propToCompare];
109
+ }
110
+ return value;
111
+ }
112
+
88
113
  /**
89
114
  * Verifica se o objeto está vazio (sem atributos).
90
115
  *
@@ -104,4 +129,15 @@ export default class ObjectUtils{
104
129
  public static isNotEmpty(obj: object): boolean{
105
130
  return !this.isEmpty(obj);
106
131
  }
132
+
133
+ /**
134
+ * Busca a propriedade de um objeto baseado em seu caminho.
135
+ *
136
+ * @param obj - Objeto a ser verificado.
137
+ * @param keyPath - Caminho da propriedade a ser buscada.
138
+ * @returns - O valor da propriedade caso ela exista.
139
+ */
140
+ public static getProp(obj: Record<string, any>, keyPath: string): Record<string, any> | undefined {
141
+ return keyPath.split('.').reduce((previous, current) => previous?.[current], obj);
142
+ }
107
143
  }
@@ -0,0 +1,36 @@
1
+ import { CacheManager } from './CacheManager/index.js';
2
+ import { StorageType } from './CacheManager/interfaces/index.js';
3
+
4
+
5
+ export class ServiceUtils {
6
+
7
+
8
+ /**
9
+ * Auxilia no uso do CacheManager, gerando automaticamente uma chave de cache com base no identificador.
10
+ *
11
+ * @template T Tipo do dado a ser retornado.
12
+ * @param identifier Identificadores únicos usados para compor a chave de cache.
13
+ * @param fetchFunction Função que retorna uma `Promise` com o valor a ser armazenado no cache caso ele não exista ou tenha expirado.
14
+ * @param storageType Tipo de armazenamento: `'sessionStorage'` ou `'localStorage'`. O padrão é `'sessionStorage'`.
15
+ * @returns Uma `Promise` com o valor armazenado ou obtido via `fetchFunction`.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const actions = await useCacheWithService(
20
+ * `${this.entityName} - ${this.resourceID}`,
21
+ * async () => {
22
+ * return await fetchActionsFromAPI();
23
+ * }
24
+ * );
25
+ * console.log(actions);
26
+ * ```
27
+ */
28
+ public static async useCacheWithService<T>(
29
+ identifier: string,
30
+ fetchFunction: () => Promise<T>,
31
+ storageType: StorageType = StorageType.IN_MEMORY_CACHE
32
+ ): Promise<T> {
33
+ const cacheKey = `${identifier}`;
34
+ return CacheManager.getOrSet(cacheKey, fetchFunction, storageType);
35
+ }
36
+ }
@@ -0,0 +1,30 @@
1
+ import DataUnit from "../dataunit/DataUnit.js";
2
+ import { FieldDescriptor, Sort, SortMode } from "../dataunit/metadata/UnitMetadata.js";
3
+ import { FieldComparator } from "../dataunit/sorting/FieldComparator.js";
4
+ import { Record } from '../dataunit/DataUnit.js';
5
+
6
+ /**
7
+ * `SortingUtils`: Utilizado para auxiliar na ordenacao de registros.
8
+ */
9
+ export default class SortingUtils {
10
+
11
+ public static getSortingFunction(dataUnit: DataUnit, sorting?: Array<Sort>): ((recordA: Record, recordB: Record) => number) | undefined {
12
+
13
+ if (sorting == undefined || sorting.length == 0) {
14
+ return undefined;
15
+ }
16
+
17
+ return (recordA, recordB) => {
18
+ for (const sort of sorting) {
19
+ if (sort.field){
20
+ const result = FieldComparator.compare(dataUnit.getField(sort.field) as FieldDescriptor, recordA, recordB, sort.mode === SortMode.ASC);
21
+ if (result != 0) {
22
+ return result;
23
+ }
24
+ }
25
+ }
26
+ return 0;
27
+ };
28
+ }
29
+
30
+ }
@@ -15,6 +15,11 @@ export class UserAgentUtils {
15
15
  return !!browser.firefox;
16
16
  }
17
17
 
18
+ public static isElectron() {
19
+ const browser = this.getBrowserInfo();
20
+ return !!browser.electron;
21
+ }
22
+
18
23
  /**
19
24
  * Obtém nome e versão do navegador que está sendo utilizado.
20
25
  * @returns Objeto com o nome e versão do navegador.
@@ -40,6 +45,7 @@ export class UserAgentUtils {
40
45
  }
41
46
  } catch (e) {
42
47
  //ignored
48
+ console.warn(e);
43
49
  }
44
50
 
45
51
  browser = {
@@ -49,7 +55,6 @@ export class UserAgentUtils {
49
55
  simpleVersion,
50
56
  ...(Array.isArray(type) ? type.reduce((acc, val) => ({ ...acc, [val]: true}), {}) : { [type]: true })
51
57
  }
52
-
53
58
  return !hasFound;
54
59
  }
55
60
  })