@sankhyalabs/core 5.20.0-dev.6 → 5.20.0-dev.61

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 (144) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/DataUnit.md +317 -137
  3. package/.docs/classes/DateUtils.md +8 -8
  4. package/.docs/classes/IDBRepository.md +22 -0
  5. package/.docs/classes/LockManager.md +191 -0
  6. package/.docs/classes/MaskFormatter.md +66 -14
  7. package/.docs/classes/ObjectUtils.md +141 -0
  8. package/.docs/classes/OverflowWatcher.md +533 -0
  9. package/.docs/classes/SelectionInfo.md +25 -11
  10. package/.docs/classes/ServiceCanceledException.md +193 -0
  11. package/.docs/classes/ServiceUtils.md +67 -0
  12. package/.docs/classes/SilentException.md +193 -0
  13. package/.docs/enumerations/Action.md +41 -21
  14. package/.docs/enumerations/ChangeOperation.md +4 -4
  15. package/.docs/enumerations/LockManagerOperation.md +21 -0
  16. package/.docs/enumerations/OverflowDirection.md +29 -0
  17. package/.docs/enumerations/SelectionMode.md +2 -2
  18. package/.docs/enumerations/StorageType.md +37 -0
  19. package/.docs/globals.md +18 -0
  20. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  21. package/.docs/interfaces/IRepository.md +18 -0
  22. package/.docs/interfaces/LoadDataRequest.md +1 -1
  23. package/.docs/interfaces/OverFlowWatcherParams.md +67 -0
  24. package/.docs/interfaces/PageRequest.md +3 -3
  25. package/.docs/interfaces/PaginationInfo.md +13 -0
  26. package/.docs/interfaces/QuickFilter.md +3 -3
  27. package/.docs/interfaces/Record.md +4 -4
  28. package/.docs/interfaces/SavedRecord.md +5 -5
  29. package/.docs/interfaces/WaitingChange.md +3 -3
  30. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +1 -1
  31. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +1 -1
  32. package/.docs/type-aliases/DataUnitEventOptions.md +17 -0
  33. package/.docs/type-aliases/OnOverflowCallBack.md +25 -0
  34. package/.docs/variables/OVERFLOWED_CLASS_NAME.md +13 -0
  35. package/bun.lockb +0 -0
  36. package/dist/dataunit/DataUnit.d.ts +60 -11
  37. package/dist/dataunit/DataUnit.js +158 -46
  38. package/dist/dataunit/DataUnit.js.map +1 -1
  39. package/dist/dataunit/DataUnitHelper.js +6 -5
  40. package/dist/dataunit/DataUnitHelper.js.map +1 -1
  41. package/dist/dataunit/formatting/PrettyFormatter.js +14 -6
  42. package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -1
  43. package/dist/dataunit/loading/LoadDataRequest.d.ts +1 -1
  44. package/dist/dataunit/loading/PaginationInfo.d.ts +4 -0
  45. package/dist/dataunit/metadata/DataType.js +3 -0
  46. package/dist/dataunit/metadata/DataType.js.map +1 -1
  47. package/dist/dataunit/state/action/DataUnitAction.d.ts +2 -0
  48. package/dist/dataunit/state/action/DataUnitAction.js +2 -0
  49. package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
  50. package/dist/dataunit/state/slice/InvalidFieldsSlice.js +2 -0
  51. package/dist/dataunit/state/slice/InvalidFieldsSlice.js.map +1 -1
  52. package/dist/dataunit/state/slice/RecordsSlice.js +1 -1
  53. package/dist/dataunit/state/slice/RecordsSlice.js.map +1 -1
  54. package/dist/dataunit/state/slice/SelectionSlice.js +4 -4
  55. package/dist/dataunit/state/slice/SelectionSlice.js.map +1 -1
  56. package/dist/exceptions/ServiceCanceledException.d.ts +14 -0
  57. package/dist/exceptions/ServiceCanceledException.js +13 -0
  58. package/dist/exceptions/ServiceCanceledException.js.map +1 -0
  59. package/dist/exceptions/SilentException.d.ts +14 -0
  60. package/dist/exceptions/SilentException.js +13 -0
  61. package/dist/exceptions/SilentException.js.map +1 -0
  62. package/dist/index.d.ts +8 -2
  63. package/dist/index.js +7 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/repository/IRepository.d.ts +6 -0
  66. package/dist/repository/indexeddb/IDBRepository.d.ts +1 -0
  67. package/dist/repository/indexeddb/IDBRepository.js +3 -0
  68. package/dist/repository/indexeddb/IDBRepository.js.map +1 -1
  69. package/dist/utils/CacheManager/index.d.ts +52 -0
  70. package/dist/utils/CacheManager/index.js +101 -0
  71. package/dist/utils/CacheManager/index.js.map +1 -0
  72. package/dist/utils/CacheManager/interfaces/index.d.ts +5 -0
  73. package/dist/utils/CacheManager/interfaces/index.js +7 -0
  74. package/dist/utils/CacheManager/interfaces/index.js.map +1 -0
  75. package/dist/utils/DateUtils.js +3 -0
  76. package/dist/utils/DateUtils.js.map +1 -1
  77. package/dist/utils/ElementUtils.d.ts +2 -0
  78. package/dist/utils/ElementUtils.js +9 -0
  79. package/dist/utils/ElementUtils.js.map +1 -0
  80. package/dist/utils/LockManager.d.ts +46 -0
  81. package/dist/utils/LockManager.js +141 -0
  82. package/dist/utils/LockManager.js.map +1 -0
  83. package/dist/utils/MaskFormatter.d.ts +16 -1
  84. package/dist/utils/MaskFormatter.js +82 -2
  85. package/dist/utils/MaskFormatter.js.map +1 -1
  86. package/dist/utils/ObjectUtils.d.ts +38 -0
  87. package/dist/utils/ObjectUtils.js +51 -0
  88. package/dist/utils/ObjectUtils.js.map +1 -1
  89. package/dist/utils/OnboardingUtils.js +1 -1
  90. package/dist/utils/OnboardingUtils.js.map +1 -1
  91. package/dist/utils/OverflowWatcher/index.d.ts +59 -0
  92. package/dist/utils/OverflowWatcher/index.js +188 -0
  93. package/dist/utils/OverflowWatcher/index.js.map +1 -0
  94. package/dist/utils/OverflowWatcher/types/overflow-callback.d.ts +6 -0
  95. package/dist/utils/OverflowWatcher/types/overflow-callback.js +2 -0
  96. package/dist/utils/OverflowWatcher/types/overflow-callback.js.map +1 -0
  97. package/dist/utils/OverflowWatcher/types/overflow-direction.d.ts +7 -0
  98. package/dist/utils/OverflowWatcher/types/overflow-direction.js +9 -0
  99. package/dist/utils/OverflowWatcher/types/overflow-direction.js.map +1 -0
  100. package/dist/utils/ServiceUtils.d.ts +24 -0
  101. package/dist/utils/ServiceUtils.js +40 -0
  102. package/dist/utils/ServiceUtils.js.map +1 -0
  103. package/dist/utils/SortingUtils.d.ts +9 -0
  104. package/dist/utils/SortingUtils.js +24 -0
  105. package/dist/utils/SortingUtils.js.map +1 -0
  106. package/jest.config.ts +2 -0
  107. package/package.json +2 -1
  108. package/reports/test-report.xml +175 -0
  109. package/setupTests.js +7 -0
  110. package/sonar-project.properties +6 -3
  111. package/src/dataunit/DataUnit.ts +189 -59
  112. package/src/dataunit/DataUnitHelper.ts +6 -5
  113. package/src/dataunit/formatting/PrettyFormatter.ts +15 -6
  114. package/src/dataunit/loading/LoadDataRequest.ts +1 -1
  115. package/src/dataunit/loading/PaginationInfo.ts +5 -0
  116. package/src/dataunit/metadata/DataType.ts +3 -0
  117. package/src/dataunit/state/action/DataUnitAction.ts +2 -0
  118. package/src/dataunit/state/slice/InvalidFieldsSlice.ts +2 -0
  119. package/src/dataunit/state/slice/RecordsSlice.ts +1 -1
  120. package/src/dataunit/state/slice/SelectionSlice.ts +4 -4
  121. package/src/dataunit/state/slice/test/RecordsSlice.spec.ts +45 -0
  122. package/src/dataunit/test/DataUnit.spec.ts +44 -0
  123. package/src/exceptions/ServiceCanceledException.ts +25 -0
  124. package/src/exceptions/SilentException.ts +25 -0
  125. package/src/index.ts +19 -1
  126. package/src/repository/IRepository.ts +7 -0
  127. package/src/repository/indexeddb/IDBRepository.ts +4 -0
  128. package/src/utils/CacheManager/index.ts +103 -0
  129. package/src/utils/CacheManager/interfaces/index.ts +5 -0
  130. package/src/utils/DateUtils.ts +3 -0
  131. package/src/utils/ElementUtils.ts +10 -0
  132. package/src/utils/LockManager.ts +157 -0
  133. package/src/utils/MaskFormatter.ts +93 -2
  134. package/src/utils/ObjectUtils.ts +56 -0
  135. package/src/utils/OnboardingUtils.ts +1 -1
  136. package/src/utils/OverflowWatcher/index.ts +243 -0
  137. package/src/utils/OverflowWatcher/types/overflow-callback.ts +6 -0
  138. package/src/utils/OverflowWatcher/types/overflow-direction.ts +7 -0
  139. package/src/utils/ServiceUtils.ts +36 -0
  140. package/src/utils/SortingUtils.ts +30 -0
  141. package/src/utils/test/objectUtils.spec.ts +109 -0
  142. package/test/dataunit/formatting/PrettyFormatter.spec.ts +177 -0
  143. package/test/util/ElementUtils.spec.ts +34 -0
  144. package/test/util/OverflowWatcher.spec.ts +152 -0
@@ -18,4 +18,9 @@ export interface PaginationInfo {
18
18
 
19
19
  /** Se ainda existem mais registros */
20
20
  hasMore: boolean;
21
+
22
+ /** Informa se o carregamento de dados em background está sendo executado
23
+ * Caso o dataunit não tenha carga paralela o valor será indefinido
24
+ */
25
+ loadingInProgress?: boolean;
21
26
  }
@@ -119,6 +119,9 @@ export const toString = ( dataType: DataType|undefined, value: any): string => {
119
119
  } else if (dataType === DataType.DATE) {
120
120
  if (typeof value === "string") {
121
121
  value = DateUtils.strToDate(value);
122
+ if (value == undefined) {
123
+ return value;
124
+ }
122
125
  }
123
126
  return DateUtils.formatRfc3339(value as Date);
124
127
  } else {
@@ -35,6 +35,8 @@ export enum Action{
35
35
 
36
36
  SAVING_DATA = "savingData",
37
37
  DATA_SAVED = "dataSaved",
38
+ SAVING_ERROR = "savingError",
39
+ SAVING_CANCELED = "savingCanceled",
38
40
 
39
41
  REMOVING_RECORDS = "removingRecords",
40
42
  RECORDS_REMOVED = "recordsRemoved",
@@ -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;
@@ -47,7 +47,7 @@ class RecordsReducerImpl implements ActionReducer {
47
47
  newRecords.push(newRecord);
48
48
  }
49
49
  });
50
- return newRecords.concat(Array.from(recordsMap.values()));
50
+ return Array.from(recordsMap.values()).concat(newRecords);
51
51
 
52
52
  case Action.RECORD_LOADED:
53
53
  action.payload.forEach((record: any) => {
@@ -31,7 +31,7 @@ class SelectionReducerImpl implements ActionReducer {
31
31
  if (currentRecords && currentRecords.size > 0) {
32
32
  let index: number;
33
33
  if (!currentState || currentSelection.length === 0) {
34
- index = action.type === Action.PREVIOUS_SELECTED ? 0 : Math.min(1, currentRecords.size);
34
+ index = action.type === Action.PREVIOUS_SELECTED ? 0 : Math.min(0, currentRecords.size);
35
35
  } else {
36
36
  index = getItemIndex(currentSelection[0], currentRecords) + (action.type === Action.PREVIOUS_SELECTED ? -1 : 1);
37
37
  }
@@ -68,7 +68,7 @@ class SelectionReducerImpl implements ActionReducer {
68
68
  if(currentState?.lastSelection){
69
69
  return { currentSelection: currentState.lastSelection };
70
70
  }
71
- return { currentSelection: [] }
71
+ return { currentSelection: currentState?.currentSelection || [] }
72
72
  }
73
73
 
74
74
  return currentState;
@@ -94,8 +94,8 @@ export const getSelection = (stateManager: StateManager): Array<string> => {
94
94
  export const getSelectionInfo = (stateManager: StateManager): SelectionInfo => {
95
95
 
96
96
  const selection = getSelectionState(stateManager);
97
+ const currentRequest = getCurrentRequest(stateManager);
97
98
  if(selection && selection.mode === SelectionMode.ALL_RECORDS){
98
- const currentRequest = getCurrentRequest(stateManager);
99
99
  const paginationInfo: PaginationInfo | undefined = getPaginationInfo(stateManager);
100
100
  return new SelectionInfo(
101
101
  [],
@@ -106,7 +106,7 @@ export const getSelectionInfo = (stateManager: StateManager): SelectionInfo => {
106
106
  );
107
107
  }
108
108
 
109
- return new SelectionInfo(getSelectionRecords(stateManager) || [], SelectionMode.SOME_RECORDS, undefined);
109
+ return new SelectionInfo(getSelectionRecords(stateManager) || [], SelectionMode.SOME_RECORDS, undefined, undefined, currentRequest?.sort);
110
110
  }
111
111
 
112
112
  export const hasNext = (stateManager: StateManager): boolean => {
@@ -0,0 +1,45 @@
1
+
2
+ import * as RecordsReducerModule from '../RecordsSlice';
3
+ import StateManager from '../../StateManager';
4
+ import { Record } from '../../../DataUnit';
5
+ import { Action } from '../../action/DataUnitAction';
6
+
7
+ jest.spyOn(RecordsReducerModule, "getRecords").mockImplementation(() => [
8
+ {
9
+ __record__id__: '0',
10
+ }, {
11
+ __record__id__: '1',
12
+ }]);
13
+
14
+ describe('RecordsSlice', () => {
15
+ let stateManager: StateManager;
16
+ let currentState: Array<Record>;
17
+
18
+ beforeEach(() => {
19
+ stateManager = new StateManager([RecordsReducerModule.RecordsReducer]);
20
+ currentState = [{
21
+ __record__id__: '0',
22
+ }, {
23
+ __record__id__: '1',
24
+ }]
25
+ });
26
+
27
+ it('should return newlines at the end', () => {
28
+ let records = RecordsReducerModule.RecordsReducer.reduce(stateManager, currentState, {
29
+ type: Action.DATA_SAVED,
30
+ payload: {
31
+ records: [
32
+ {__record__id__: '0'},
33
+ {__record__id__: '2'},
34
+ {__record__id__: '1'},
35
+ ]
36
+ }
37
+ })
38
+ expect(records).toEqual([
39
+ {__record__id__: '0'},
40
+ {__record__id__: '1'},
41
+ {__record__id__: '2'}
42
+ ]);
43
+ });
44
+
45
+ });
@@ -0,0 +1,44 @@
1
+ import DataUnit from '../DataUnit';
2
+ import { Action } from '../state/action/DataUnitAction';
3
+
4
+ describe('DataUnit', () => {
5
+
6
+ let dataUnit: DataUnit;
7
+
8
+ beforeEach(() => {
9
+ dataUnit = new DataUnit();
10
+
11
+ dataUnit['dispatchAction'] = jest.fn();
12
+ });
13
+
14
+ it('should return false when not waiting to reload', () => {
15
+ expect(dataUnit.isWaitingToReload()).toBe(false);
16
+ });
17
+
18
+ it('should set waitingToReload to true', () => {
19
+ dataUnit.setWaitingToReload(true);
20
+ expect(dataUnit.isWaitingToReload()).toBe(true);
21
+ });
22
+
23
+ it('should set waitingToReload to false', () => {
24
+ dataUnit.setWaitingToReload(true);
25
+ dataUnit.setWaitingToReload(false);
26
+ expect(dataUnit.isWaitingToReload()).toBe(false);
27
+ });
28
+
29
+ it('should dispatch SAVING_CANCELED action with correct fields and recordId', () => {
30
+ const fields = [
31
+ { name: 'field1', message: 'error1' },
32
+ { name: 'field2', message: 'error2' }
33
+ ];
34
+ const recordId = '12345';
35
+
36
+ dataUnit.savingCanceled(fields, recordId);
37
+
38
+ expect(dataUnit['dispatchAction']).toHaveBeenCalledWith(
39
+ Action.SAVING_CANCELED,
40
+ { fields, recordId },
41
+ undefined
42
+ );
43
+ });
44
+ });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `ServiceCanceledException`: Exceção lançada quando ocorre o cancelamento de um serviço.
3
+ */
4
+ export default class ServiceCanceledException extends Error {
5
+
6
+ /** Nome da exceção. */
7
+ public name: string;
8
+
9
+ /** Titulo do erro. */
10
+ public title: string;
11
+
12
+ /** Descrição do erro. */
13
+ public message: string;
14
+
15
+ /** Código do erro, indica o erro disparado pelo backend. */
16
+ public errorCode: string;
17
+
18
+ constructor(title: string, message: string, errorCode: string = "") {
19
+ super(message);
20
+ this.name = "Service Canceled";
21
+ this.title = title;
22
+ this.message = message;
23
+ this.errorCode = errorCode;
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `SilentException`: Exceção lançada quando uma exception silenciosa é.
3
+ */
4
+ export default class SilentException extends Error {
5
+
6
+ /** Nome da exceção. */
7
+ public name: string;
8
+
9
+ /** Titulo do erro. */
10
+ public title: string;
11
+
12
+ /** Descrição do erro. */
13
+ public message: string;
14
+
15
+ /** Código do erro, indica o erro disparado pelo backend. */
16
+ public errorCode: string;
17
+
18
+ constructor(title: string = "", message: string = "", errorCode: string = "") {
19
+ super(message);
20
+ this.name = "SilentException";
21
+ this.title = title;
22
+ this.message = message;
23
+ this.errorCode = errorCode;
24
+ }
25
+ }
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@ import { HttpProvider } from "./http/HttpProvider.js";
9
9
  import { SkwHttpProvider } from "./http/SkwHttpProvider.js";
10
10
  import { RequestMetadata } from "./http/RequestMetadata.js";
11
11
  import { AuthorizedServiceCaller } from "./http/AuthorizedServiceCaller.js";
12
- import DataUnit, {SavedRecord, Record, Change, ChangeOperation, DUActionInterceptor, WaitingChange, PageRequest, QuickFilter, SelectionMode, SelectionInfo} from "./dataunit/DataUnit.js";
12
+ import DataUnit, {SavedRecord, Record, Change, ChangeOperation, DUActionInterceptor, WaitingChange, PageRequest, QuickFilter, SelectionMode, SelectionInfo, DataUnitEventOptions} from "./dataunit/DataUnit.js";
13
13
  import { DataType } from "./dataunit/metadata/DataType.js";
14
14
  import { UnitMetadata, FieldDescriptor, UserInterface, Sort, SortMode, SortingProvider, Filter, DependencyType, ChildDescriptor, ChildLink } from "./dataunit/metadata/UnitMetadata.js";
15
15
  import { DataUnitAction, Action, ExecutionContext } from "./dataunit/state/action/DataUnitAction.js";
@@ -19,6 +19,8 @@ import ObjectUtils from "./utils/ObjectUtils.js";
19
19
  import WarningException from "./exceptions/WarningException.js";
20
20
  import WaitingChangeException from "./exceptions/WaitingChangeException.js";
21
21
  import ErrorException from "./exceptions/ErrorException.js";
22
+ import ServiceCanceledException from "./exceptions/ServiceCanceledException.js";
23
+ import SilentException from "./exceptions/SilentException.js";
22
24
  import { ErrorTracking } from "./traking/ErrorTraking.js";
23
25
  import { PaginationInfo } from "./dataunit/loading/PaginationInfo.js";
24
26
  import { LoadDataRequest } from "./dataunit/loading/LoadDataRequest.js";
@@ -39,9 +41,15 @@ import { IRepositoryIndex } from "./repository/indexeddb/IRepositoryIndex.js"
39
41
  import { FieldComparator } from "./dataunit/sorting/FieldComparator.js";
40
42
  import { KeyboardManager } from "./utils/KeyboardManager/index.js";
41
43
  import { SearchUtils } from "./utils/SearchUtils.js";
44
+ import { ServiceUtils } from "./utils/ServiceUtils.js";
45
+ import { StorageType } from "./utils/CacheManager/index.js";
46
+ import OverflowWatcher, { OnOverflowCallBack, OverflowDirection, OverFlowWatcherParams, OVERFLOWED_CLASS_NAME } from "./utils/OverflowWatcher/index.js";
47
+ import {LockManager, LockManagerOperation} from "./utils/LockManager.js";
42
48
 
43
49
  /*Classes públicas no pacote*/
44
50
  export {
51
+ LockManager,
52
+ LockManagerOperation,
45
53
  StringUtils,
46
54
  MaskFormatter,
47
55
  NumberUtils,
@@ -106,4 +114,14 @@ export {
106
114
  defaultDataLoader,
107
115
  KeyboardManager,
108
116
  SearchUtils,
117
+ ServiceUtils,
118
+ StorageType,
119
+ OverflowWatcher,
120
+ OnOverflowCallBack,
121
+ OverflowDirection,
122
+ OverFlowWatcherParams,
123
+ OVERFLOWED_CLASS_NAME,
124
+ DataUnitEventOptions,
125
+ ServiceCanceledException,
126
+ SilentException,
109
127
  };
@@ -79,4 +79,11 @@ export interface IRepository<T>{
79
79
  * @returns Promessa de quantidade
80
80
  */
81
81
  count(): Promise<number>;
82
+
83
+ /**
84
+ * Retorna todos os registros que estão em cache no momento
85
+ *
86
+ * @returns Todos registros que estão em cache no momento
87
+ */
88
+ getFromCache(): Array<T> | undefined
82
89
  }
@@ -320,6 +320,10 @@ export class IDBRepository<T> implements IRepository<T>{
320
320
  const tx = db.transaction(this._addedStoreName);
321
321
  return tx.objectStore(this._addedStoreName).getAll();
322
322
  }
323
+
324
+ public getFromCache(): Array<T> | undefined{
325
+ return undefined;
326
+ }
323
327
  }
324
328
 
325
329
  /**
@@ -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
+ }
@@ -39,6 +39,9 @@ export default class DateUtils{
39
39
  * @returns - Uma string com as horas no formato HH:MM ou HH:MM:SS.
40
40
  */
41
41
  public static formatTime(date: Date, showSeconds: boolean = false): string{
42
+ if (date == null){
43
+ return '';
44
+ }
42
45
  const timeOptions:Intl.DateTimeFormatOptions = {hour: "2-digit", minute: "2-digit", second: showSeconds ? "2-digit" : undefined};
43
46
  return new Intl.DateTimeFormat("pt-BR", timeOptions).format(date);
44
47
  }
@@ -0,0 +1,10 @@
1
+ import { OverflowDirection } from './OverflowWatcher/types/overflow-direction.js';
2
+
3
+ export function calcMarginSize(el:Element, scrollDirection: OverflowDirection){
4
+ const computedStyle = getComputedStyle(el);
5
+ if(OverflowDirection.HORIZONTAL === scrollDirection){
6
+ return (parseInt(computedStyle.marginLeft || '0') + parseInt(computedStyle.marginRight || '0'));
7
+ }
8
+
9
+ return (parseInt(computedStyle.marginTop || '0') + parseInt(computedStyle.marginBottom || '0'));
10
+ }
@@ -0,0 +1,157 @@
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
+
12
+ type Lock = {
13
+ promise?: Promise<unknown>,
14
+ resolve?: () => void,
15
+ done: boolean
16
+ }
17
+
18
+ export class LockManager{
19
+ private static _locks = new Map<string, Array<Lock>>();
20
+
21
+ /**
22
+ * Nome do atributo que será utilizado para controlar contexto de locks nos elementos da DOM.
23
+ */
24
+ public static ATTRIBUTE_NAME = "data-locker-manger-context-id";
25
+
26
+ private static buildContextID(): string{
27
+ return StringUtils.generateUUID();
28
+ }
29
+
30
+ private static buildLockerID(ctxId:string|HTMLElement, operation: LockManagerOperation): string | undefined{
31
+ if(ctxId == undefined) return undefined;
32
+
33
+ let resolvedID:any = ctxId;
34
+
35
+ if(resolvedID instanceof HTMLElement){
36
+ resolvedID = (ctxId as HTMLElement).getAttribute(LockManager.ATTRIBUTE_NAME);
37
+ if(!resolvedID) return undefined;
38
+ }
39
+
40
+ return `${resolvedID}_${operation}`;
41
+ }
42
+
43
+ private static findExistingCtxId = (element: HTMLElement): string | null => {
44
+ let currentElement: HTMLElement | null = element;
45
+
46
+ while (currentElement) {
47
+ if (currentElement.hasAttribute(LockManager.ATTRIBUTE_NAME)) {
48
+ return currentElement.getAttribute(LockManager.ATTRIBUTE_NAME);
49
+ }
50
+
51
+ const childWithCtxId = Array.from(currentElement.children).find(child =>
52
+ (child instanceof HTMLElement) && child.hasAttribute(LockManager.ATTRIBUTE_NAME)
53
+ ) as HTMLElement | undefined;
54
+
55
+ if (childWithCtxId) {
56
+ return childWithCtxId.getAttribute(LockManager.ATTRIBUTE_NAME);
57
+ }
58
+
59
+ currentElement = currentElement.parentElement;
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ private static traverseAndAddAttr = (element: HTMLElement, ctxId: string): void => {
66
+ if (element.tagName.startsWith('EZ-') || element.tagName.startsWith('SNK-')) {
67
+ element.setAttribute(LockManager.ATTRIBUTE_NAME, ctxId);
68
+ }
69
+ Array.from(element.children).forEach((child) => {
70
+ if (child instanceof HTMLElement) {
71
+ LockManager.traverseAndAddAttr(child, ctxId);
72
+ }
73
+ });
74
+ };
75
+
76
+ /**
77
+ * Cria um contexto de locker, caso nao exista, para todos elementos pais iniciados com ez- ou snk-.
78
+ *
79
+ * @param startElement - Elemento de de onde o lock deve começar.
80
+ *
81
+ * @returns - O id do locker, que pode ser usado para iniciar ou aguardar um lock do contexto.
82
+ */
83
+ public static addLockManagerCtxId(startElement: HTMLElement): string {
84
+ try{
85
+ if (!startElement) {
86
+ console.error("Elemento inicial não fornecido.");
87
+ return "";
88
+ }
89
+
90
+ const ctxId = LockManager.findExistingCtxId(startElement) ?? LockManager.buildContextID();
91
+
92
+ let currentElement: HTMLElement | null = startElement;
93
+
94
+ while (currentElement && currentElement.tagName != 'BODY') {
95
+ LockManager.traverseAndAddAttr(currentElement, ctxId);
96
+ currentElement = currentElement.parentElement;
97
+ }
98
+
99
+ return ctxId;
100
+ }catch(err){
101
+ console.warn(`Erro ao registrar locks para o elemento: ${startElement?.tagName}`, err);
102
+ return "";
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Inicia um locker baseado em um contexto e uma operação.
108
+ *
109
+ * @param id - Pode ser um ID do contexto de locker, ou, o elemento contendo um contexto de locker.
110
+ * @param operation - Operação do contexto que o lock deve ser feito.
111
+ *
112
+ * @returns - Uma função que fara a liberação do lock.
113
+ */
114
+ public static lock(id:string|HTMLElement, operation:LockManagerOperation): any{
115
+ const lockerId = LockManager.buildLockerID(id, operation);
116
+
117
+ if(!lockerId) return () => {};
118
+
119
+ const lock:Lock = { done: false };
120
+ const promise = new Promise<void>(resolve => lock.resolve = resolve);
121
+ lock.promise = promise;
122
+
123
+ const currentLocks = LockManager._locks.get(lockerId) ?? [];
124
+ currentLocks.push(lock);
125
+
126
+ LockManager._locks.set(lockerId, currentLocks);
127
+
128
+ return () => {
129
+ lock.done = true;
130
+ lock.resolve?.();
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Aguarda todos os lockers de um contexto e operação serem resolvidos.
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 devera aguardar.
139
+ *
140
+ * @returns - Promise que será resolvida quando todos lockers forem finalizados.
141
+ */
142
+ public static async whenResolve(id:string|HTMLElement, operation:LockManagerOperation): Promise<void> {
143
+ const lockerId = LockManager.buildLockerID(id, operation);
144
+
145
+ if(!lockerId) return;
146
+
147
+ while (LockManager._locks.get(lockerId)?.length) {
148
+ const locks:Array<Lock> = LockManager._locks.get(lockerId) ?? [];
149
+ await Promise.all(locks.map(lock => lock.promise));
150
+
151
+ //Aguarda listeners da tela reagirem as mudancas de estado do dataunit
152
+ await new Promise(resolve => setTimeout(resolve, 200));
153
+
154
+ LockManager._locks.set(lockerId, locks.filter(lock => !lock.done));
155
+ }
156
+ }
157
+ }