@sankhyalabs/core 5.20.0-dev.55 → 5.20.0-dev.57

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 (40) hide show
  1. package/.docs/classes/Change.md +11 -11
  2. package/.docs/classes/DataUnit.md +135 -135
  3. package/.docs/classes/LockManager.md +191 -0
  4. package/.docs/classes/MaskFormatter.md +61 -13
  5. package/.docs/classes/SelectionInfo.md +12 -12
  6. package/.docs/classes/SilentException.md +193 -0
  7. package/.docs/enumerations/ChangeOperation.md +4 -4
  8. package/.docs/enumerations/LockManagerOperation.md +21 -0
  9. package/.docs/enumerations/SelectionMode.md +2 -2
  10. package/.docs/globals.md +3 -0
  11. package/.docs/interfaces/DUActionInterceptor.md +1 -1
  12. package/.docs/interfaces/PageRequest.md +3 -3
  13. package/.docs/interfaces/QuickFilter.md +3 -3
  14. package/.docs/interfaces/Record.md +4 -4
  15. package/.docs/interfaces/SavedRecord.md +5 -5
  16. package/.docs/interfaces/WaitingChange.md +3 -3
  17. package/.docs/namespaces/MaskFormatter/type-aliases/MaskCharacter.md +1 -1
  18. package/.docs/namespaces/MaskFormatter/variables/MaskCharacter.md +1 -1
  19. package/.docs/type-aliases/DataUnitEventOptions.md +1 -1
  20. package/dist/dataunit/DataUnit.js +4 -2
  21. package/dist/dataunit/DataUnit.js.map +1 -1
  22. package/dist/exceptions/SilentException.d.ts +14 -0
  23. package/dist/exceptions/SilentException.js +13 -0
  24. package/dist/exceptions/SilentException.js.map +1 -0
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/utils/LockManager.d.ts +46 -0
  29. package/dist/utils/LockManager.js +141 -0
  30. package/dist/utils/LockManager.js.map +1 -0
  31. package/dist/utils/MaskFormatter.d.ts +14 -0
  32. package/dist/utils/MaskFormatter.js +77 -1
  33. package/dist/utils/MaskFormatter.js.map +1 -1
  34. package/package.json +1 -1
  35. package/reports/test-report.xml +107 -107
  36. package/src/dataunit/DataUnit.ts +7 -4
  37. package/src/exceptions/SilentException.ts +25 -0
  38. package/src/index.ts +6 -1
  39. package/src/utils/LockManager.ts +157 -0
  40. package/src/utils/MaskFormatter.ts +88 -1
@@ -28,6 +28,7 @@ import { getInvalidFieldMessage, InvalidFieldsReducer } from "./state/slice/Inva
28
28
  import { getLoadingProperties, LoadingPropertiesReducer } from "./state/slice/LoadingProperties.js";
29
29
  import SortingUtils from "../utils/SortingUtils.js";
30
30
  import ServiceCanceledException from "../exceptions/ServiceCanceledException.js";
31
+ import SilentException from "../exceptions/SilentException.js";
31
32
 
32
33
  /***
33
34
  * `DataUnit`: Atua como uma camada de abstração entre o back-end e a interface do usuário.
@@ -503,11 +504,13 @@ export default class DataUnit {
503
504
  this.dispatchAction(Action.SAVING_ERROR);
504
505
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
505
506
  if(cause instanceof ServiceCanceledException){
506
- console.debug("Service canceled: " + cause.message)
507
- resolve()
508
- } else{
509
- fail(new ErrorException("Erro ao salvar alterações", cause, errorCode))
507
+ console.debug("Service canceled: " + cause.message);
508
+ resolve();
509
+ }
510
+ if(cause instanceof SilentException){
511
+ fail();
510
512
  }
513
+ fail(new ErrorException("Erro ao salvar alterações", cause, errorCode));
511
514
  });
512
515
  } else {
513
516
  this.dispatchAction(Action.LOADING_PROPERTIES_CLEANED);
@@ -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
@@ -20,6 +20,7 @@ import WarningException from "./exceptions/WarningException.js";
20
20
  import WaitingChangeException from "./exceptions/WaitingChangeException.js";
21
21
  import ErrorException from "./exceptions/ErrorException.js";
22
22
  import ServiceCanceledException from "./exceptions/ServiceCanceledException.js";
23
+ import SilentException from "./exceptions/SilentException.js";
23
24
  import { ErrorTracking } from "./traking/ErrorTraking.js";
24
25
  import { PaginationInfo } from "./dataunit/loading/PaginationInfo.js";
25
26
  import { LoadDataRequest } from "./dataunit/loading/LoadDataRequest.js";
@@ -43,9 +44,12 @@ import { SearchUtils } from "./utils/SearchUtils.js";
43
44
  import { ServiceUtils } from "./utils/ServiceUtils.js";
44
45
  import { StorageType } from "./utils/CacheManager/index.js";
45
46
  import OverflowWatcher, { OnOverflowCallBack, OverflowDirection, OverFlowWatcherParams, OVERFLOWED_CLASS_NAME } from "./utils/OverflowWatcher/index.js";
47
+ import {LockManager, LockManagerOperation} from "./utils/LockManager.js";
46
48
 
47
49
  /*Classes públicas no pacote*/
48
50
  export {
51
+ LockManager,
52
+ LockManagerOperation,
49
53
  StringUtils,
50
54
  MaskFormatter,
51
55
  NumberUtils,
@@ -118,5 +122,6 @@ export {
118
122
  OverFlowWatcherParams,
119
123
  OVERFLOWED_CLASS_NAME,
120
124
  DataUnitEventOptions,
121
- ServiceCanceledException
125
+ ServiceCanceledException,
126
+ SilentException,
122
127
  };
@@ -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
+ }
@@ -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,6 +93,92 @@ 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
  *