@libs-ui/utils 0.2.29 → 0.2.30-6.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.
- package/base64.d.ts +5 -0
- package/cache.d.ts +16 -15
- package/color.d.ts +1 -1
- package/communicate-micro.d.ts +4 -5
- package/constants.d.ts +3 -0
- package/crypto-3rd.d.ts +4 -0
- package/crypto.d.ts +4 -0
- package/date.d.ts +49 -0
- package/dom.d.ts +36 -1
- package/download.d.ts +3 -0
- package/esm2022/base64.mjs +43 -0
- package/esm2022/cache.mjs +121 -132
- package/esm2022/color.mjs +2 -2
- package/esm2022/communicate-micro.mjs +26 -24
- package/esm2022/constants.mjs +4 -1
- package/esm2022/crypto-3rd.mjs +5 -1
- package/esm2022/crypto.mjs +5 -1
- package/esm2022/date.mjs +189 -0
- package/esm2022/dom.mjs +200 -22
- package/esm2022/download.mjs +37 -0
- package/esm2022/file.mjs +79 -0
- package/esm2022/format-text.mjs +150 -0
- package/esm2022/get-smart-axis-scale.mjs +174 -0
- package/esm2022/helpers.mjs +402 -91
- package/esm2022/http-params.mjs +15 -3
- package/esm2022/index.mjs +12 -5
- package/esm2022/inject-token.mjs +5 -0
- package/esm2022/key-cache.mjs +20 -8
- package/esm2022/language.mjs +67 -4
- package/esm2022/pattern.mjs +21 -21
- package/esm2022/random.mjs +42 -0
- package/esm2022/two-way-signal-object.mjs +113 -0
- package/esm2022/uri.mjs +22 -0
- package/esm2022/uuid.mjs +3 -2
- package/esm2022/xss-filter.mjs +10 -0
- package/fesm2022/libs-ui-utils.mjs +2238 -880
- package/fesm2022/libs-ui-utils.mjs.map +1 -1
- package/file.d.ts +18 -0
- package/format-text.d.ts +11 -0
- package/get-smart-axis-scale.d.ts +34 -0
- package/helpers.d.ts +218 -10
- package/http-params.d.ts +2 -2
- package/index.d.ts +11 -4
- package/inject-token.d.ts +4 -0
- package/language.d.ts +32 -0
- package/package.json +7 -4
- package/pattern.d.ts +20 -20
- package/random.d.ts +3 -0
- package/two-way-signal-object.d.ts +14 -0
- package/uri.d.ts +5 -0
- package/xss-filter.d.ts +3 -0
- package/delete-unicode.d.ts +0 -1
- package/escape-html.d.ts +0 -1
- package/esm2022/delete-unicode.mjs +0 -20
- package/esm2022/escape-html.mjs +0 -12
- package/esm2022/get-color-by-id.mjs +0 -17
- package/esm2022/remove-emoji.mjs +0 -10
- package/get-color-by-id.d.ts +0 -1
- package/remove-emoji.d.ts +0 -2
|
@@ -1,27 +1,249 @@
|
|
|
1
|
-
import
|
|
1
|
+
import DeviceDetector from 'device-detector-js';
|
|
2
|
+
import Quill from 'quill';
|
|
3
|
+
import { fromEvent, tap, takeUntil, mergeMap, startWith, finalize, lastValueFrom, timer, Subject, filter, Observable } from 'rxjs';
|
|
2
4
|
import CryptoES from 'crypto-es';
|
|
3
5
|
import { HttpParams } from '@angular/common/http';
|
|
4
|
-
import {
|
|
6
|
+
import { isSignal, TemplateRef, ElementRef, signal, InjectionToken } from '@angular/core';
|
|
7
|
+
import dayjs from 'dayjs';
|
|
8
|
+
import 'dayjs/locale/en';
|
|
9
|
+
import 'dayjs/locale/vi';
|
|
10
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
11
|
+
import localeData from 'dayjs/plugin/localeData';
|
|
12
|
+
import timezone from 'dayjs/plugin/timezone';
|
|
13
|
+
import updateLocale from 'dayjs/plugin/updateLocale';
|
|
14
|
+
import utc from 'dayjs/plugin/utc';
|
|
5
15
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
/* eslint-disable no-useless-escape */
|
|
17
|
+
const patternEmail = () => {
|
|
18
|
+
return /^[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]@[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]\.[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]$/;
|
|
19
|
+
};
|
|
20
|
+
const patternUrl = () => {
|
|
21
|
+
return /^(http|https|ftp):(\/){2}[^\s]+[.]{1}[^\s]+$/;
|
|
22
|
+
};
|
|
23
|
+
const patternHostUrl = () => {
|
|
24
|
+
return /^((https|http|ftp):[/]{2}[^/\s]+)/;
|
|
25
|
+
};
|
|
26
|
+
const patternDomain = () => {
|
|
27
|
+
return /^([a-zA-Z0-9])(([a-z0-9-]{1,61})?[a-z0-9]{1})?(\.[a-z0-9](([a-z0-9-]{1,61})?[a-z0-9]{1})?)?(\.[a-zA-Z]{2,4})+$/;
|
|
28
|
+
};
|
|
29
|
+
const patternMobilePhone = () => {
|
|
30
|
+
return /^(\+?84|0|84)([0-9]{9})$/;
|
|
31
|
+
};
|
|
32
|
+
const patternPhone = () => {
|
|
33
|
+
return /^(\+?84|[0-9]|84)([0-9]{2,9})$/;
|
|
34
|
+
};
|
|
35
|
+
const patternPhoneNormal = () => {
|
|
36
|
+
return /^(\+?84|[0-9]|84)([0-9]{9,10})$/;
|
|
37
|
+
};
|
|
38
|
+
const patternNumber = () => {
|
|
39
|
+
return /\d+/g;
|
|
40
|
+
};
|
|
41
|
+
const patternEncodeUri = () => {
|
|
42
|
+
return /%([0-9A-F]{2})/g;
|
|
43
|
+
};
|
|
44
|
+
const patternName = () => {
|
|
45
|
+
return /^\w+[A-Za-z\s\d]+$/;
|
|
46
|
+
};
|
|
47
|
+
const patternNameUtf8 = () => {
|
|
48
|
+
return /^[ àáảãạăắằẵặẳâầấậẫẩđèéẻẽẹêềếểễệìíỉĩịòóỏõọôồốổỗộơờớởỡợùúủũụưừứửữựỳýỷỹÀÁẢÃẠĂẮẰẴẶẲÂẦẤẬẪẨĐÈÉẺẼẸÊỀẾỂỄỆÌÍỈĨỊÒÓỎÕỌÔỒỐỔỖỘƠỜỚỞỠỢÙÚỦŨỤƯỪỨỬỮỰỲÝỶỸA-Za-z0-9]+$/;
|
|
49
|
+
};
|
|
50
|
+
const patternNameSpecial = () => {
|
|
51
|
+
return /[~!@#$%^&*()-+=<>,?\/\\:;"']/;
|
|
52
|
+
};
|
|
53
|
+
const patternNameProfile = () => {
|
|
54
|
+
return /([\w\W\d\s]+)+/;
|
|
55
|
+
};
|
|
56
|
+
const patternEmoji = () => {
|
|
57
|
+
return /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/g;
|
|
58
|
+
};
|
|
59
|
+
const patternRuleFieldReplace = () => {
|
|
60
|
+
return /[{]{2}[a-zA-Z_-]+[}]{2}/g;
|
|
61
|
+
};
|
|
62
|
+
const patternGetFieldByRuleFieldReplace = () => {
|
|
63
|
+
return /[a-zA-Z_-]+/g;
|
|
64
|
+
};
|
|
65
|
+
const patternPem = () => {
|
|
66
|
+
return /^(0|1):([0-9]{1,2}):(\{\{path-api\}\}):([a-zA-Z0-9\/]+)$/g;
|
|
67
|
+
};
|
|
68
|
+
const patternTax = () => {
|
|
69
|
+
return /^([0-9]){10}(-[0-9]{3})?$/;
|
|
70
|
+
};
|
|
71
|
+
const patternKey = () => {
|
|
72
|
+
return /^([0-9]){10}(-[0-9]{3})?$/;
|
|
73
|
+
};
|
|
74
|
+
const patternAccount = () => {
|
|
75
|
+
return /^(?=.*@)[a-z0-9@._-]{2,63}$/;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const highlightByKeyword = (value, search, ignoreHighlight, classHightLight) => {
|
|
79
|
+
if (!value) {
|
|
80
|
+
return '—';
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
if (!search || ignoreHighlight) {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
const keysReplace = getValueReplace(search, value);
|
|
87
|
+
if (!keysReplace || !keysReplace.length) {
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
if (!classHightLight) {
|
|
91
|
+
classHightLight = 'bg-[#19344a] text-white';
|
|
92
|
+
}
|
|
93
|
+
keysReplace.forEach(key => {
|
|
94
|
+
const regExp = new RegExp(key, 'gi');
|
|
95
|
+
value = value?.replace(regExp, `<span class="${classHightLight}">$&</span>`);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.log(error);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
// eslint-disable-next-line no-unsafe-finally
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const getValueReplace = (search, value) => {
|
|
107
|
+
const searchConvert = deleteUnicode(search).toLowerCase();
|
|
108
|
+
const valueConvert = deleteUnicode(value).toLowerCase();
|
|
109
|
+
const keys = new Set();
|
|
110
|
+
let i = 0;
|
|
111
|
+
while ((i = valueConvert.indexOf(searchConvert, i)) >= 0) {
|
|
112
|
+
const endIndex = i + search.length;
|
|
113
|
+
keys.add(value.substring(i, endIndex));
|
|
114
|
+
i += +valueConvert.length;
|
|
115
|
+
}
|
|
116
|
+
return Array.from(keys);
|
|
117
|
+
};
|
|
118
|
+
const formatTextCompare = (text, options) => {
|
|
119
|
+
if (!text) {
|
|
120
|
+
return text;
|
|
121
|
+
}
|
|
122
|
+
text = text.normalize("NFC");
|
|
123
|
+
return formatByOptions(text, options);
|
|
124
|
+
};
|
|
125
|
+
const fullNameFormat = (value) => {
|
|
126
|
+
if (!value) {
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
return capitalize(value, { lowercaseOtherCharacter: true, trim: true, removeMultipleSpace: true, removeEmoji: true });
|
|
130
|
+
};
|
|
131
|
+
const capitalize = (text, options) => {
|
|
132
|
+
if (!text) {
|
|
133
|
+
return text;
|
|
134
|
+
}
|
|
135
|
+
text = formatByOptions(text, options);
|
|
136
|
+
return text.split(' ').map(word => firstLetterToUpperCase(word)).join(' ');
|
|
137
|
+
};
|
|
138
|
+
const firstLetterToUpperCase = (text, options) => {
|
|
139
|
+
if (!text) {
|
|
140
|
+
return text;
|
|
141
|
+
}
|
|
142
|
+
return uppercaseByPosition(text, 0, options);
|
|
143
|
+
};
|
|
144
|
+
const uppercaseByPosition = (text, position, options) => {
|
|
145
|
+
if (!text) {
|
|
146
|
+
return text;
|
|
147
|
+
}
|
|
148
|
+
text = formatByOptions(text, options);
|
|
149
|
+
return `${text.substring(0, position)}${text.charAt(position).toUpperCase()}${text.substring(position + 1)}`;
|
|
150
|
+
};
|
|
151
|
+
const formatByOptions = (text, options) => {
|
|
152
|
+
if (!text || !options) {
|
|
153
|
+
return text;
|
|
154
|
+
}
|
|
155
|
+
if (options?.uppercaseOtherCharacter) {
|
|
156
|
+
text = text.toUpperCase();
|
|
13
157
|
}
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
158
|
+
if (options?.lowercaseOtherCharacter) {
|
|
159
|
+
text = text.toLowerCase();
|
|
160
|
+
}
|
|
161
|
+
if (options?.removeMultipleSpace) {
|
|
162
|
+
text = text.replace(/\u200B|\u00A0/g, "");
|
|
163
|
+
text = text.replace(/\s+/g, ' ');
|
|
164
|
+
}
|
|
165
|
+
if (options?.removeEmoji) {
|
|
166
|
+
text = removeEmoji(text);
|
|
167
|
+
}
|
|
168
|
+
if (options?.removeUnicode) {
|
|
169
|
+
text = deleteUnicode(text);
|
|
170
|
+
}
|
|
171
|
+
if (options?.trim) {
|
|
172
|
+
text = text.trim();
|
|
173
|
+
}
|
|
174
|
+
return text;
|
|
175
|
+
};
|
|
176
|
+
const escapeHtml = (str) => {
|
|
177
|
+
if (!str || typeof str !== 'string') {
|
|
178
|
+
return str;
|
|
179
|
+
}
|
|
180
|
+
return str
|
|
181
|
+
.replace(/&/g, "&")
|
|
182
|
+
.replace(/</g, "<")
|
|
183
|
+
.replace(/>/g, ">")
|
|
184
|
+
.replace(/"/g, """)
|
|
185
|
+
.replace(/'/g, "'");
|
|
186
|
+
};
|
|
187
|
+
const decodeEscapeHtml = (str) => {
|
|
188
|
+
const htmlTag = document.createElement('textarea');
|
|
189
|
+
htmlTag.innerHTML = str;
|
|
190
|
+
while (str) {
|
|
191
|
+
if (str === htmlTag.value) {
|
|
192
|
+
str = htmlTag.value;
|
|
193
|
+
htmlTag.remove();
|
|
194
|
+
break;
|
|
18
195
|
}
|
|
196
|
+
str = htmlTag.value;
|
|
197
|
+
htmlTag.innerHTML = str;
|
|
198
|
+
}
|
|
199
|
+
return str;
|
|
200
|
+
};
|
|
201
|
+
const deleteUnicode = (str) => {
|
|
202
|
+
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
|
|
203
|
+
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ|ễ/g, 'e');
|
|
204
|
+
str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
|
|
205
|
+
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
|
|
206
|
+
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
|
|
207
|
+
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
|
|
208
|
+
str = str.replace(/đ/g, 'd');
|
|
209
|
+
str = str.replace(/À|Á|Ạ|Ả|Ã|Â|Ầ|Ấ|Ậ|Ẩ|Ẫ|Ă|Ằ|Ắ|Ặ|Ẳ|Ẵ/g, 'A');
|
|
210
|
+
str = str.replace(/È|É|Ẹ|Ẻ|Ẽ|Ê|Ề|Ế|Ệ|Ể|Ễ/g, 'E');
|
|
211
|
+
str = str.replace(/Ì|Í|Ị|Ỉ|Ĩ/g, 'I');
|
|
212
|
+
str = str.replace(/Ò|Ó|Ọ|Ỏ|Õ|Ô|Ồ|Ố|Ộ|Ổ|Ỗ|Ơ|Ờ|Ớ|Ợ|Ở|Ỡ/g, 'O');
|
|
213
|
+
str = str.replace(/Ù|Ú|Ụ|Ủ|Ũ|Ư|Ừ|Ứ|Ự|Ử|Ữ/g, 'U');
|
|
214
|
+
str = str.replace(/Ỳ|Ý|Ỵ|Ỷ|Ỹ/g, 'Y');
|
|
215
|
+
str = str.replace(/Đ/g, 'D');
|
|
216
|
+
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); // ̀ ́ ̃ ̉ ̣ huyền, sắc, ngã, hỏi, nặng
|
|
217
|
+
str = str.replace(/\u02C6|\u0306|\u031B/g, ""); // ˆ ̆ ̛ Â, Ê, Ă, Ơ, Ư
|
|
218
|
+
return str.normalize("NFC");
|
|
219
|
+
};
|
|
220
|
+
const removeEmoji = (text) => {
|
|
221
|
+
if (!text || !text.trim()) {
|
|
222
|
+
return text;
|
|
19
223
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
224
|
+
return text.replace(patternEmoji(), '');
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const deviceDetector = new DeviceDetector();
|
|
228
|
+
const parser = new DOMParser();
|
|
229
|
+
const quill = new Quill(document.createElement('div'));
|
|
230
|
+
const getDeviceInfo = () => {
|
|
231
|
+
return deviceDetector.parse(window.navigator.userAgent);
|
|
232
|
+
};
|
|
233
|
+
const getPlatFromBrowser = () => {
|
|
234
|
+
const info = getDeviceInfo();
|
|
235
|
+
return `${info.client?.name} ${info.client?.version}`;
|
|
236
|
+
};
|
|
237
|
+
const getEventNameHandleClick = (() => {
|
|
238
|
+
const deviceInfo = getDeviceInfo();
|
|
239
|
+
let nameEventHandleClick = deviceInfo.device?.type === 'desktop' ? 'click' : 'touchstart';
|
|
240
|
+
if ('onpointerdown' in window) {
|
|
241
|
+
nameEventHandleClick = 'pointerdown';
|
|
23
242
|
}
|
|
24
|
-
return
|
|
243
|
+
return nameEventHandleClick;
|
|
244
|
+
})();
|
|
245
|
+
const getDocumentByString = (htmlStr) => {
|
|
246
|
+
return parser.parseFromString(htmlStr, 'text/html');
|
|
25
247
|
};
|
|
26
248
|
const cloneIBoundingClientRect = (rect) => {
|
|
27
249
|
return {
|
|
@@ -29,10 +251,10 @@ const cloneIBoundingClientRect = (rect) => {
|
|
|
29
251
|
left: rect['left'],
|
|
30
252
|
width: rect['width'],
|
|
31
253
|
height: rect['height'],
|
|
32
|
-
bottom: rect['bottom']
|
|
254
|
+
bottom: rect['bottom'],
|
|
33
255
|
};
|
|
34
256
|
};
|
|
35
|
-
const
|
|
257
|
+
const setStylesElement = (el, styles, render2) => {
|
|
36
258
|
Object.keys(styles).forEach((key) => render2.setStyle(el, key, styles[key]));
|
|
37
259
|
};
|
|
38
260
|
const getViewport = (win = window) => {
|
|
@@ -68,7 +290,10 @@ const checkViewInScreen = (container, element, elementScroll, maxTopLeft) => {
|
|
|
68
290
|
const limitLeft = leftContainer + scrollLeftContainer + widthContainer;
|
|
69
291
|
const maxTopItem = top + (maxTopLeft?.top || 0);
|
|
70
292
|
const maxLeftItem = left + (maxTopLeft?.left || 0);
|
|
71
|
-
if (topContainer <= maxTopItem &&
|
|
293
|
+
if (topContainer <= maxTopItem &&
|
|
294
|
+
maxTopItem <= limitTop &&
|
|
295
|
+
leftContainer <= maxLeftItem &&
|
|
296
|
+
maxLeftItem <= limitLeft) {
|
|
72
297
|
return true;
|
|
73
298
|
}
|
|
74
299
|
return false;
|
|
@@ -82,11 +307,181 @@ const checkMouseOverInContainer = (mousePosition, element, rect) => {
|
|
|
82
307
|
const limitLeft = left + width;
|
|
83
308
|
const limitTop = top + height;
|
|
84
309
|
const { clientX, clientY } = mousePosition;
|
|
85
|
-
if (left <= clientX &&
|
|
310
|
+
if (left <= clientX &&
|
|
311
|
+
clientX <= limitLeft &&
|
|
312
|
+
top <= clientY &&
|
|
313
|
+
clientY <= limitTop) {
|
|
86
314
|
return true;
|
|
87
315
|
}
|
|
88
316
|
return false;
|
|
89
317
|
};
|
|
318
|
+
const getDragEventByElement = (config) => {
|
|
319
|
+
let timer;
|
|
320
|
+
const addClass = () => {
|
|
321
|
+
timer = setTimeout(() => {
|
|
322
|
+
document.body.classList.add('!select-none');
|
|
323
|
+
document.body.classList.add('!cursor-grabbing');
|
|
324
|
+
}, 250);
|
|
325
|
+
};
|
|
326
|
+
const removeClass = () => {
|
|
327
|
+
document.body.classList.remove('!select-none');
|
|
328
|
+
document.body.classList.remove('!cursor-grabbing');
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
};
|
|
331
|
+
fromEvent(config.elementMouseDown, 'mouseleave')
|
|
332
|
+
.pipe(tap(() => document.body.classList.remove('cursor-pointer')), takeUntil(config.onDestroy))
|
|
333
|
+
.subscribe();
|
|
334
|
+
fromEvent(config.elementMouseDown, 'mouseenter')
|
|
335
|
+
.pipe(tap(() => document.body.classList.add('cursor-pointer')), takeUntil(config.onDestroy))
|
|
336
|
+
.subscribe();
|
|
337
|
+
const mouseDown = fromEvent(config.elementMouseDown, 'mousedown').pipe(tap((e) => {
|
|
338
|
+
e.stopPropagation();
|
|
339
|
+
addClass();
|
|
340
|
+
config.functionMouseDown?.(e);
|
|
341
|
+
}));
|
|
342
|
+
const mouseup = fromEvent(config.elementMouseUp || document, 'mouseup').pipe(tap((e) => {
|
|
343
|
+
e.stopPropagation();
|
|
344
|
+
removeClass();
|
|
345
|
+
config.functionMouseUp?.(e);
|
|
346
|
+
}));
|
|
347
|
+
const mousemove = fromEvent(config.elementMouseMove || document, 'mousemove').pipe(tap((e) => {
|
|
348
|
+
e.stopPropagation();
|
|
349
|
+
config.functionMouseMove?.(e);
|
|
350
|
+
}), takeUntil(mouseup));
|
|
351
|
+
return mouseDown.pipe(mergeMap((e) => config.isStartWithMouseDownEvent
|
|
352
|
+
? mousemove.pipe(startWith(e))
|
|
353
|
+
: mousemove), takeUntil(config.onDestroy), finalize(removeClass));
|
|
354
|
+
};
|
|
355
|
+
const getHTMLFromQuill = async (data, options) => {
|
|
356
|
+
const { replaceNewLineTo = '<br>', replaceTagBRTo, replaceTags, replaceBrToDiv } = options || {};
|
|
357
|
+
const delta = (typeof data === 'string') ? await getDeltaFromHTML(data) : data;
|
|
358
|
+
if (options?.functionReplaceDelta) {
|
|
359
|
+
options.functionReplaceDelta(delta);
|
|
360
|
+
}
|
|
361
|
+
delta.ops.forEach((op) => {
|
|
362
|
+
if (op.insert) {
|
|
363
|
+
if (typeof op.insert === 'string') {
|
|
364
|
+
if (replaceNewLineTo) {
|
|
365
|
+
op.insert = op.insert.replace(/\n/g, replaceNewLineTo);
|
|
366
|
+
}
|
|
367
|
+
if (replaceTagBRTo) {
|
|
368
|
+
op.insert = op.insert.replace(/<br>/g, replaceTagBRTo);
|
|
369
|
+
}
|
|
370
|
+
if (replaceTags?.length) {
|
|
371
|
+
for (const tag of replaceTags) {
|
|
372
|
+
op.insert = op.insert.replace(new RegExp(`<${tag.tag}>`, 'g'), `<${tag.replaceTo}>`);
|
|
373
|
+
op.insert = op.insert.replace(new RegExp(`</${tag.tag}>`, 'g'), `</${tag.replaceTo}>`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
quill.setContents(delta);
|
|
380
|
+
let htmlText = options?.getRootHtml ? quill.root.innerHTML : quill.root.firstElementChild?.innerHTML;
|
|
381
|
+
if (replaceBrToDiv) {
|
|
382
|
+
htmlText = convertHtmlToDivBlocks(htmlText || '');
|
|
383
|
+
}
|
|
384
|
+
return decodeEscapeHtml(htmlText || '');
|
|
385
|
+
};
|
|
386
|
+
const convertHtmlToDivBlocks = (html) => {
|
|
387
|
+
const BREAK_TOKEN = '<<<BREAK>>>';
|
|
388
|
+
// Bước 1: thay <br> thành token tạm
|
|
389
|
+
const normalizedHtml = html.replace(/<br\s*\/?>/gi, BREAK_TOKEN);
|
|
390
|
+
// Bước 2: tách theo token
|
|
391
|
+
const parts = normalizedHtml.split(BREAK_TOKEN);
|
|
392
|
+
const parser = new DOMParser();
|
|
393
|
+
const divs = [];
|
|
394
|
+
for (const raw of parts) {
|
|
395
|
+
const trimmed = raw.trim();
|
|
396
|
+
if (!trimmed)
|
|
397
|
+
continue;
|
|
398
|
+
// parse mỗi phần nhỏ như một document riêng
|
|
399
|
+
const doc = parser.parseFromString(trimmed, 'text/html');
|
|
400
|
+
const body = doc.body;
|
|
401
|
+
// Lấy lại nội dung bên trong body
|
|
402
|
+
divs.push(`<div>${body.innerHTML}</div>`);
|
|
403
|
+
}
|
|
404
|
+
return divs.join('');
|
|
405
|
+
};
|
|
406
|
+
const getDeltaFromHTML = async (html) => {
|
|
407
|
+
quill.root.innerHTML = html;
|
|
408
|
+
setTimeout(() => {
|
|
409
|
+
console.log(quill.getContents());
|
|
410
|
+
}, 1000);
|
|
411
|
+
await lastValueFrom(timer(1000));
|
|
412
|
+
return quill.getContents();
|
|
413
|
+
};
|
|
414
|
+
const processPasteData = async (e, config) => {
|
|
415
|
+
const element = config.element;
|
|
416
|
+
const files = e.clipboardData?.files;
|
|
417
|
+
if (files?.length) {
|
|
418
|
+
e.preventDefault();
|
|
419
|
+
config.handlerPasteFile?.(files);
|
|
420
|
+
config.callBack?.('file');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Lưu selection TRƯỚC khi prevent default
|
|
424
|
+
const selection = window.getSelection();
|
|
425
|
+
let savedRange = null;
|
|
426
|
+
if (selection && selection.rangeCount > 0) {
|
|
427
|
+
const range = selection.getRangeAt(0);
|
|
428
|
+
// Chỉ lưu nếu range nằm trong contentText element
|
|
429
|
+
const container = range.commonAncestorContainer;
|
|
430
|
+
const isInContentElement = element.contains(container.nodeType === Node.TEXT_NODE ? container.parentNode : container);
|
|
431
|
+
if (isInContentElement) {
|
|
432
|
+
savedRange = range.cloneRange();
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Prevent default để tự xử lý paste
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
// Sử dụng Quill để clean HTML content
|
|
438
|
+
const htmlContent = e.clipboardData?.getData('text/html') || '';
|
|
439
|
+
const plainText = e.clipboardData?.getData('text/plain') || '';
|
|
440
|
+
let contentToInsert = (plainText || '').replace(/\n/g, '<br>');
|
|
441
|
+
if (htmlContent) {
|
|
442
|
+
contentToInsert = await getHTMLFromQuill(htmlContent);
|
|
443
|
+
}
|
|
444
|
+
if (!contentToInsert) {
|
|
445
|
+
config.callBack?.('no-content');
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (savedRange) {
|
|
449
|
+
insertContentWithRange(contentToInsert, savedRange, element);
|
|
450
|
+
config.callBack?.('range');
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
element.innerHTML += contentToInsert;
|
|
454
|
+
config.callBack?.('content');
|
|
455
|
+
};
|
|
456
|
+
const insertContentWithRange = (content, savedRange, element) => {
|
|
457
|
+
const selection = window.getSelection();
|
|
458
|
+
if (!selection) {
|
|
459
|
+
// Fallback: append vào cuối
|
|
460
|
+
element.innerHTML += content;
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
// Restore selection
|
|
464
|
+
selection.removeAllRanges();
|
|
465
|
+
selection.addRange(savedRange);
|
|
466
|
+
// Xóa nội dung đã select (nếu có)
|
|
467
|
+
savedRange.deleteContents();
|
|
468
|
+
// Tạo document fragment từ HTML content
|
|
469
|
+
const tempDiv = document.createElement('div');
|
|
470
|
+
tempDiv.innerHTML = content;
|
|
471
|
+
const fragment = document.createDocumentFragment();
|
|
472
|
+
while (tempDiv.firstChild) {
|
|
473
|
+
fragment.appendChild(tempDiv.firstChild);
|
|
474
|
+
}
|
|
475
|
+
// Insert fragment tại vị trí range
|
|
476
|
+
savedRange.insertNode(fragment);
|
|
477
|
+
// Di chuyển cursor đến cuối nội dung vừa insert
|
|
478
|
+
if (fragment.lastChild) {
|
|
479
|
+
savedRange.setStartAfter(fragment.lastChild);
|
|
480
|
+
}
|
|
481
|
+
savedRange.collapse(true);
|
|
482
|
+
selection.removeAllRanges();
|
|
483
|
+
selection.addRange(savedRange);
|
|
484
|
+
};
|
|
90
485
|
|
|
91
486
|
class UtilsUrlSearchParams {
|
|
92
487
|
static instance;
|
|
@@ -185,6 +580,46 @@ class UtilsUrlSearchParams {
|
|
|
185
580
|
}
|
|
186
581
|
}
|
|
187
582
|
|
|
583
|
+
let key$1 = '12~@#loqwsxacva(3rdhaq12';
|
|
584
|
+
/**
|
|
585
|
+
* @description Thiết lập key mã hóa
|
|
586
|
+
* @param value key mã hóa, độ dài bằng 24 hoặc 32 ký tự.
|
|
587
|
+
*/
|
|
588
|
+
const setKeyCrypto = (value) => {
|
|
589
|
+
if (value.length !== 24 && value.length !== 32) {
|
|
590
|
+
throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
|
|
591
|
+
}
|
|
592
|
+
key$1 = value;
|
|
593
|
+
};
|
|
594
|
+
const keyStore$1 = () => {
|
|
595
|
+
return key$1;
|
|
596
|
+
};
|
|
597
|
+
const encrypt = (plainData) => {
|
|
598
|
+
if (!keyStore$1()) {
|
|
599
|
+
throw Error("lỗi chưa setup key mã hóa");
|
|
600
|
+
}
|
|
601
|
+
const key = CryptoES.enc.Hex.parse(keyStore$1());
|
|
602
|
+
const iv = CryptoES.enc.Hex.parse(keyStore$1());
|
|
603
|
+
const mode = CryptoES.mode.CBC;
|
|
604
|
+
const padding = CryptoES.pad.Pkcs7;
|
|
605
|
+
const options = { iv: iv, mode: mode, padding: padding };
|
|
606
|
+
return CryptoES.AES.encrypt(plainData, key, options).toString();
|
|
607
|
+
};
|
|
608
|
+
const decrypt = (encryptedData) => {
|
|
609
|
+
if (!keyStore$1()) {
|
|
610
|
+
throw Error("lỗi chưa setup key mã hóa");
|
|
611
|
+
}
|
|
612
|
+
const key = CryptoES.enc.Hex.parse(keyStore$1());
|
|
613
|
+
const iv = CryptoES.enc.Hex.parse(keyStore$1());
|
|
614
|
+
const mode = CryptoES.mode.CBC;
|
|
615
|
+
const padding = CryptoES.pad.Pkcs7;
|
|
616
|
+
const options = { iv: iv, mode: mode, padding: padding };
|
|
617
|
+
return CryptoES.AES.decrypt(encryptedData, key, options).toString(CryptoES.enc.Utf8);
|
|
618
|
+
};
|
|
619
|
+
const md5 = (plainData) => {
|
|
620
|
+
return CryptoES.MD5(plainData).toString();
|
|
621
|
+
};
|
|
622
|
+
|
|
188
623
|
const uuid = () => {
|
|
189
624
|
const timestamp = performance.now() * 1000;
|
|
190
625
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?/~';
|
|
@@ -199,382 +634,93 @@ const uuid = () => {
|
|
|
199
634
|
[charArray[i], charArray[j]] = [charArray[j], charArray[i]];
|
|
200
635
|
}
|
|
201
636
|
const shuffledString = charArray.join('');
|
|
202
|
-
return shuffledString;
|
|
637
|
+
return md5(shuffledString);
|
|
203
638
|
};
|
|
204
639
|
|
|
205
640
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
206
|
-
const
|
|
207
|
-
return
|
|
208
|
-
};
|
|
209
|
-
const isEmpty = (value) => {
|
|
210
|
-
return value === null || value === '' || value === undefined || (typeof value === 'object' && (JSON.stringify(value) === '{}' || JSON.stringify(value) === '[]'));
|
|
641
|
+
const UtilsHttpParamsRequestInstance = (options, instance) => {
|
|
642
|
+
return new UtilsHttpParamsRequest(options, instance);
|
|
211
643
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
644
|
+
class UtilsHttpParamsRequest extends HttpParams {
|
|
645
|
+
params = new HttpParams();
|
|
646
|
+
constructor(options, instance) {
|
|
647
|
+
super(options);
|
|
648
|
+
if (!instance) {
|
|
649
|
+
this.params = new HttpParams(options);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (instance instanceof UtilsHttpParamsRequest) {
|
|
653
|
+
this.params = instance.getInstance();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (instance instanceof HttpParams) {
|
|
657
|
+
this.params = instance;
|
|
658
|
+
return;
|
|
220
659
|
}
|
|
221
|
-
obj = obj[paths[i]];
|
|
222
660
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const set = (obj, path, value) => {
|
|
226
|
-
if (!obj || typeof obj !== "object") {
|
|
227
|
-
throw new Error("The first argument must be an object");
|
|
661
|
+
getInstance() {
|
|
662
|
+
return this.params;
|
|
228
663
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
current[key] = /\[(\d+)]/g.test(nextKey) ? [] : {};
|
|
237
|
-
}
|
|
238
|
-
current = current[key];
|
|
664
|
+
setInstance(instance) {
|
|
665
|
+
if (instance instanceof UtilsHttpParamsRequest) {
|
|
666
|
+
this.params = instance.getInstance();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (instance instanceof HttpParams) {
|
|
670
|
+
this.params = instance;
|
|
239
671
|
return;
|
|
240
672
|
}
|
|
241
|
-
// Gán giá trị ở cuối đường dẫn
|
|
242
|
-
current[key] = value;
|
|
243
|
-
});
|
|
244
|
-
return obj;
|
|
245
|
-
};
|
|
246
|
-
const cloneDeep = (value) => {
|
|
247
|
-
if (value === null || typeof value !== 'object') {
|
|
248
|
-
return value;
|
|
249
673
|
}
|
|
250
|
-
|
|
251
|
-
|
|
674
|
+
set(param, value) {
|
|
675
|
+
this.params = this.params.set(param, value);
|
|
676
|
+
return this;
|
|
252
677
|
}
|
|
253
|
-
|
|
254
|
-
return
|
|
678
|
+
has(param) {
|
|
679
|
+
return this.params.has(param);
|
|
255
680
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
value.forEach((val, key) => {
|
|
259
|
-
mapCopy.set(cloneDeep(key), cloneDeep(val));
|
|
260
|
-
});
|
|
261
|
-
return mapCopy;
|
|
681
|
+
get(param) {
|
|
682
|
+
return this.params.get(param);
|
|
262
683
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
value.forEach(val => {
|
|
266
|
-
setCopy.add(cloneDeep(val));
|
|
267
|
-
});
|
|
268
|
-
return setCopy;
|
|
684
|
+
getAll(param) {
|
|
685
|
+
return this.params.getAll(param);
|
|
269
686
|
}
|
|
270
|
-
|
|
271
|
-
return
|
|
687
|
+
keys() {
|
|
688
|
+
return this.params.keys();
|
|
272
689
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
result[key] = value[key];
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
280
|
-
result[key] = cloneDeep(value[key]);
|
|
281
|
-
}
|
|
690
|
+
append(param, value) {
|
|
691
|
+
this.params = this.params.append(param, value);
|
|
692
|
+
return this;
|
|
282
693
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (data === null || typeof data !== 'object') {
|
|
287
|
-
return data;
|
|
694
|
+
appendAll(params) {
|
|
695
|
+
this.params = this.params.appendAll(params);
|
|
696
|
+
return this;
|
|
288
697
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
return signal(data.map(item => convertObjectToSignal(cloneDeep(item))));
|
|
698
|
+
delete(param, value) {
|
|
699
|
+
this.params = this.params.delete(param, value);
|
|
700
|
+
return this;
|
|
294
701
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const value = data()[key];
|
|
298
|
-
if (value instanceof TemplateRef || value instanceof ElementRef || value instanceof Element || value instanceof Date || value instanceof RegExp || value instanceof Set || value instanceof Map) {
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
if (Object.prototype.hasOwnProperty.call(data(), key)) {
|
|
302
|
-
data()[key] = convertObjectToSignal(cloneDeep(value));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return data;
|
|
306
|
-
};
|
|
307
|
-
const isSignal = (data) => {
|
|
308
|
-
return typeof data === 'function' && data.toString().includes('Signal:');
|
|
309
|
-
};
|
|
310
|
-
const convertSignalToObject = (data) => {
|
|
311
|
-
if (data === null || !isSignal(data)) {
|
|
312
|
-
return data;
|
|
313
|
-
}
|
|
314
|
-
data = data();
|
|
315
|
-
if (Array.isArray(data)) {
|
|
316
|
-
if (!isSignal(data[0])) {
|
|
317
|
-
return data;
|
|
318
|
-
}
|
|
319
|
-
return data.map(item => convertSignalToObject(cloneDeep(item)));
|
|
320
|
-
}
|
|
321
|
-
for (const key in data) {
|
|
322
|
-
const value = data[key];
|
|
323
|
-
if (!isSignal(value)) {
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
327
|
-
data[key] = convertSignalToObject(cloneDeep(value));
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return data;
|
|
331
|
-
};
|
|
332
|
-
const keyBy = (data, key) => {
|
|
333
|
-
if (!data || !data.length || !key) {
|
|
334
|
-
return {};
|
|
335
|
-
}
|
|
336
|
-
return data.reduce((dir, nextItem) => {
|
|
337
|
-
const valueOfKey = nextItem[key];
|
|
338
|
-
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number') {
|
|
339
|
-
return dir;
|
|
340
|
-
}
|
|
341
|
-
dir[valueOfKey] = nextItem;
|
|
342
|
-
return dir;
|
|
343
|
-
}, {});
|
|
344
|
-
};
|
|
345
|
-
const groupBy = (data, key) => {
|
|
346
|
-
if (!data || !Object.keys(data).length || !key) {
|
|
347
|
-
return {};
|
|
348
|
-
}
|
|
349
|
-
return data.reduce((dir, nextItem) => {
|
|
350
|
-
const valueOfKey = nextItem[key];
|
|
351
|
-
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number') {
|
|
352
|
-
return dir;
|
|
353
|
-
}
|
|
354
|
-
if (!dir[valueOfKey]) {
|
|
355
|
-
dir[valueOfKey] = [];
|
|
356
|
-
}
|
|
357
|
-
dir[valueOfKey].push(nextItem);
|
|
358
|
-
return dir;
|
|
359
|
-
}, {});
|
|
360
|
-
};
|
|
361
|
-
const range = (start, end, step) => {
|
|
362
|
-
if (end === undefined || end === null) {
|
|
363
|
-
end = start;
|
|
364
|
-
start = 0;
|
|
365
|
-
}
|
|
366
|
-
if (!step) {
|
|
367
|
-
step = end < 0 ? -1 : 1;
|
|
368
|
-
}
|
|
369
|
-
const valueStartByStep = step + start;
|
|
370
|
-
const direction = start < end ? 'asc' : 'desc';
|
|
371
|
-
if ((direction === 'asc' && (valueStartByStep < start || valueStartByStep > end)) || (direction === 'desc' && (valueStartByStep > start || valueStartByStep < end))) {
|
|
372
|
-
return [start];
|
|
373
|
-
}
|
|
374
|
-
const arr = new Array();
|
|
375
|
-
for (let index = 0; index < Math.abs(end - start); index++) {
|
|
376
|
-
let value = start + index * step;
|
|
377
|
-
if (index === 0) {
|
|
378
|
-
value = start;
|
|
379
|
-
}
|
|
380
|
-
if ((direction === 'asc' && (value < start || value > end)) || (direction === 'desc' && (value > start || value < end))) {
|
|
381
|
-
return arr;
|
|
382
|
-
}
|
|
383
|
-
arr.push(value);
|
|
384
|
-
}
|
|
385
|
-
return arr;
|
|
386
|
-
};
|
|
387
|
-
const isEqual = (value1, value2) => {
|
|
388
|
-
if (value1 === value2 || (value1 === null && value2 === null) || (value1 === undefined && value2 === undefined)) {
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
if (typeof value1 !== 'object' || typeof value2 !== 'object' || (Array.isArray(value1) && !Array.isArray(value2))) {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
if (Array.isArray(value1)) {
|
|
395
|
-
if (value1.length !== value2.length) {
|
|
396
|
-
return false;
|
|
397
|
-
}
|
|
398
|
-
return !value1.some((item, index) => !isEqual(item, value2[index]));
|
|
399
|
-
}
|
|
400
|
-
if (Object.keys(value1).length !== Object.keys(value2).length) {
|
|
401
|
-
return false;
|
|
402
|
-
}
|
|
403
|
-
return !Object.keys(value1).some((key) => !isEqual(value1[key], value2[key]));
|
|
404
|
-
};
|
|
405
|
-
const uniqBy = (data, key) => {
|
|
406
|
-
if (!key || !data || !data.length || typeof data[0] !== 'object') {
|
|
407
|
-
console.log('vao dauy r', !key, !data, !data.length, typeof data[0] !== 'object');
|
|
408
|
-
return Array.from(new Set(data));
|
|
409
|
-
}
|
|
410
|
-
const dataUnique = keyBy(data, key);
|
|
411
|
-
return Object.keys(dataUnique).map(key => dataUnique[key]);
|
|
412
|
-
};
|
|
413
|
-
const generateInterface = (obj, interfaceName) => {
|
|
414
|
-
const generateType = (value) => {
|
|
415
|
-
if (value === null) {
|
|
416
|
-
return 'null';
|
|
417
|
-
}
|
|
418
|
-
const type = typeof value;
|
|
419
|
-
if (type === 'string') {
|
|
420
|
-
return 'string';
|
|
421
|
-
}
|
|
422
|
-
if (type === 'number') {
|
|
423
|
-
return 'number';
|
|
424
|
-
}
|
|
425
|
-
if (type === 'boolean') {
|
|
426
|
-
return 'boolean';
|
|
427
|
-
}
|
|
428
|
-
if (type === 'undefined') {
|
|
429
|
-
return 'any';
|
|
430
|
-
}
|
|
431
|
-
if (value instanceof Date) {
|
|
432
|
-
return 'Date';
|
|
433
|
-
}
|
|
434
|
-
if (value instanceof RegExp) {
|
|
435
|
-
return 'RegExp';
|
|
436
|
-
}
|
|
437
|
-
if (Array.isArray(value)) {
|
|
438
|
-
if (value.length === 0) {
|
|
439
|
-
return 'Array<any>';
|
|
440
|
-
}
|
|
441
|
-
return `Array<${generateType(value[0])}>`;
|
|
442
|
-
}
|
|
443
|
-
if (type === 'object') {
|
|
444
|
-
let interfaceStr = '{\n';
|
|
445
|
-
for (const key in value) {
|
|
446
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
447
|
-
const valueType = generateType(value[key]);
|
|
448
|
-
interfaceStr += ` ${key}: ${valueType};\n`;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
interfaceStr += '}';
|
|
452
|
-
return interfaceStr;
|
|
453
|
-
}
|
|
454
|
-
return 'any';
|
|
455
|
-
};
|
|
456
|
-
const interfaceStr = `interface ${interfaceName} ${generateType(obj)}`;
|
|
457
|
-
return interfaceStr;
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
;
|
|
461
|
-
const step = 20;
|
|
462
|
-
const percent = 0.05;
|
|
463
|
-
const colorStepContrastFromOrigin = (color, stepNumber) => {
|
|
464
|
-
return colorContrastFromOrigin(color).find(item => item.step === stepNumber);
|
|
465
|
-
};
|
|
466
|
-
const colorContrastFromOrigin = (color) => {
|
|
467
|
-
const parsedColorsArray = parseColorValues(color);
|
|
468
|
-
const colors = [];
|
|
469
|
-
let calculatedShades = [];
|
|
470
|
-
let calculatedTints = [];
|
|
471
|
-
if (parsedColorsArray !== null) {
|
|
472
|
-
for (let i = 0; i < parsedColorsArray.length; i++) {
|
|
473
|
-
calculatedShades = calculateShades(parsedColorsArray[i]);
|
|
474
|
-
calculatedTints = calculateTints(parsedColorsArray[i]);
|
|
475
|
-
}
|
|
476
|
-
for (let i = 0; i <= step; i++) {
|
|
477
|
-
colors.push({ step: i * 5, label: `${i * 5}%`, dark: `#${calculatedShades[i]}`, light: `#${calculatedTints[i]}` });
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return colors;
|
|
481
|
-
};
|
|
482
|
-
const parseColorValues = (colorValues) => {
|
|
483
|
-
let colorValuesArray = colorValues.match(/\b[0-9A-Fa-f]{3}\b|[0-9A-Fa-f]{6}\b/g);
|
|
484
|
-
if (colorValuesArray) {
|
|
485
|
-
colorValuesArray = colorValuesArray.map((item) => {
|
|
486
|
-
if (item.length === 3) {
|
|
487
|
-
let newItem = item.toString().split('');
|
|
488
|
-
newItem = newItem.reduce((acc, it) => {
|
|
489
|
-
return acc + it + it;
|
|
490
|
-
}, '');
|
|
491
|
-
return newItem;
|
|
492
|
-
}
|
|
493
|
-
return item;
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
return colorValuesArray;
|
|
497
|
-
};
|
|
498
|
-
const calculateShades = (colorValue) => {
|
|
499
|
-
return calculate(colorValue, rgbShade).concat("000000");
|
|
500
|
-
};
|
|
501
|
-
const calculateTints = (colorValue) => {
|
|
502
|
-
return calculate(colorValue, rgbTint).concat("ffffff");
|
|
503
|
-
};
|
|
504
|
-
const calculate = (colorValue, shadeOrTint) => {
|
|
505
|
-
const color = hexToRGB(colorValue);
|
|
506
|
-
const shadeValues = [];
|
|
507
|
-
for (let i = 0; i < step; i++) {
|
|
508
|
-
shadeValues[i] = rgbToHex(shadeOrTint(color, i));
|
|
509
|
-
}
|
|
510
|
-
return shadeValues;
|
|
511
|
-
};
|
|
512
|
-
const rgbShade = (rgb, i) => { return { red: rgb.red * (1 - percent * i), green: rgb.green * (1 - percent * i), blue: rgb.blue * (1 - percent * i) }; };
|
|
513
|
-
const rgbTint = (rgb, i) => { return { red: rgb.red + (255 - rgb.red) * i * percent, green: rgb.green + (255 - rgb.green) * i * percent, blue: rgb.blue + (255 - rgb.blue) * i * percent }; };
|
|
514
|
-
const rgbToHex = (rgb) => { return intToHex(rgb.red) + intToHex(rgb.green) + intToHex(rgb.blue); };
|
|
515
|
-
const hexToRGB = (colorValue) => { return { red: parseInt(colorValue.substr(0, 2), 16), green: parseInt(colorValue.substr(2, 2), 16), blue: parseInt(colorValue.substr(4, 2), 16) }; };
|
|
516
|
-
const intToHex = (rgbint) => { return pad(Math.min(Math.max(Math.round(rgbint), 0), 255).toString(16), 2); };
|
|
517
|
-
const pad = (number, length) => {
|
|
518
|
-
let str = '' + number;
|
|
519
|
-
while (str.length < length) {
|
|
520
|
-
str = '0' + str;
|
|
521
|
-
}
|
|
522
|
-
return str;
|
|
523
|
-
};
|
|
524
|
-
const listColorDefine$1 = ['#E62222', '#B81B1B', '#EB4E4E', '#F97316', '#C75C12', '#FA8F45', '#FFB700', '#CC9200', '#FFC533', '#84CC16', '#6AA312', '#9dd645', '#00BC62', '#00A757', '#33DA8A', '#06B6D4', '#1B59C4', '#4E8CF7', '#0EA5E9',
|
|
525
|
-
'#1B59C4', '#4E8CF7', '#226FF5', '#1B59C4', '#4E8CF7', '#6366F1', '#4F52C1', '#8285F4', '#5B04B3', '#49038F', '#7C36C2', '#D946EF', '#AE38BF', '#E16BF2', '#EC4899', '#BD3A7A', '#F06DAD', '#F43F5E', '#C3324B', '#F6657E', '#757380', '#5E5C66', '#918F99',
|
|
526
|
-
'#202020', '#1A1A1A', '#4D4D4D'
|
|
527
|
-
];
|
|
528
|
-
const GetColorById = (str) => {
|
|
529
|
-
let hashString = 0;
|
|
530
|
-
if (!str) {
|
|
531
|
-
return '';
|
|
532
|
-
}
|
|
533
|
-
for (let i = 0; i < str.length; i++) {
|
|
534
|
-
const char = str.charCodeAt(i);
|
|
535
|
-
hashString = ((hashString << 5) - hashString) + char;
|
|
536
|
-
hashString = hashString & hashString;
|
|
537
|
-
}
|
|
538
|
-
return listColorDefine$1[Math.abs(hashString) % listColorDefine$1.length];
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
let key$1 = '12~@#loqwsxacva(3rdhaq12';
|
|
542
|
-
const setKeyCrypto = (value) => {
|
|
543
|
-
if (value.length !== 24 && value.length !== 32) {
|
|
544
|
-
throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
|
|
545
|
-
}
|
|
546
|
-
key$1 = value;
|
|
547
|
-
};
|
|
548
|
-
const keyStore$1 = () => {
|
|
549
|
-
return key$1;
|
|
550
|
-
};
|
|
551
|
-
const encrypt = (plainData) => {
|
|
552
|
-
if (!keyStore$1()) {
|
|
553
|
-
throw Error("lỗi chưa setup key mã hóa");
|
|
554
|
-
}
|
|
555
|
-
const key = CryptoES.enc.Hex.parse(keyStore$1());
|
|
556
|
-
const iv = CryptoES.enc.Hex.parse(keyStore$1());
|
|
557
|
-
const mode = CryptoES.mode.CBC;
|
|
558
|
-
const padding = CryptoES.pad.Pkcs7;
|
|
559
|
-
const options = { iv: iv, mode: mode, padding: padding };
|
|
560
|
-
return CryptoES.AES.encrypt(plainData, key, options).toString();
|
|
561
|
-
};
|
|
562
|
-
const decrypt = (encryptedData) => {
|
|
563
|
-
if (!keyStore$1()) {
|
|
564
|
-
throw Error("lỗi chưa setup key mã hóa");
|
|
702
|
+
toString() {
|
|
703
|
+
return this.params.toString();
|
|
565
704
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
705
|
+
}
|
|
706
|
+
// Demo su dung GET_PATH_VARIABLE
|
|
707
|
+
// interface Person {
|
|
708
|
+
// name: string;
|
|
709
|
+
// age: number;
|
|
710
|
+
// location: string;
|
|
711
|
+
// }
|
|
712
|
+
// type c = GET_PATH_VARIABLE<Person, unknown>;
|
|
713
|
+
// const a: c = {
|
|
714
|
+
// "pathVariable-age": 1,
|
|
715
|
+
// "pathVariable-location": '12',
|
|
716
|
+
// "pathVariable-name": '124',
|
|
717
|
+
// };
|
|
576
718
|
|
|
577
719
|
let key = '12~@#loqwsxacva(3rdhaq12';
|
|
720
|
+
/**
|
|
721
|
+
* @description Thiết lập key mã hóa
|
|
722
|
+
* @param value key mã hóa, độ dài bằng 24 hoặc 32 ký tự.
|
|
723
|
+
*/
|
|
578
724
|
const setKeyCrypto3rd = (value) => {
|
|
579
725
|
if (value.length !== 24 && value.length !== 32) {
|
|
580
726
|
throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
|
|
@@ -607,219 +753,6 @@ const decrypt3rd = (encryptedData) => {
|
|
|
607
753
|
return CryptoES.AES.decrypt(encryptedData, key, options).toString(CryptoES.enc.Utf8);
|
|
608
754
|
};
|
|
609
755
|
|
|
610
|
-
class UtilsKeyCodeConstant {
|
|
611
|
-
static MAC_ENTER = 3;
|
|
612
|
-
static BACKSPACE = 8;
|
|
613
|
-
static TAB = 9;
|
|
614
|
-
static NUM_CENTER = 12;
|
|
615
|
-
static ENTER = 13;
|
|
616
|
-
static SHIFT = 16;
|
|
617
|
-
static CONTROL = 17;
|
|
618
|
-
static ALT = 18;
|
|
619
|
-
static PAUSE = 19;
|
|
620
|
-
static CAPS_LOCK = 20;
|
|
621
|
-
static ESCAPE = 27;
|
|
622
|
-
static SPACE = 32;
|
|
623
|
-
static PAGE_UP = 33;
|
|
624
|
-
static PAGE_DOWN = 34;
|
|
625
|
-
static END = 35;
|
|
626
|
-
static HOME = 36;
|
|
627
|
-
static LEFT_ARROW = 37;
|
|
628
|
-
static UP_ARROW = 38;
|
|
629
|
-
static RIGHT_ARROW = 39;
|
|
630
|
-
static DOWN_ARROW = 40;
|
|
631
|
-
static PLUS_SIGN = 43;
|
|
632
|
-
static PRINT_SCREEN = 44;
|
|
633
|
-
static INSERT = 45;
|
|
634
|
-
static DELETE = 46;
|
|
635
|
-
static ZERO = 48;
|
|
636
|
-
static ONE = 49;
|
|
637
|
-
static TWO = 50;
|
|
638
|
-
static THREE = 51;
|
|
639
|
-
static FOUR = 52;
|
|
640
|
-
static FIVE = 53;
|
|
641
|
-
static SIX = 54;
|
|
642
|
-
static SEVEN = 55;
|
|
643
|
-
static EIGHT = 56;
|
|
644
|
-
static NINE = 57;
|
|
645
|
-
static FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
646
|
-
static FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
647
|
-
static QUESTION_MARK = 63;
|
|
648
|
-
static AT_SIGN = 64;
|
|
649
|
-
static A = 65;
|
|
650
|
-
static B = 66;
|
|
651
|
-
static C = 67;
|
|
652
|
-
static D = 68;
|
|
653
|
-
static E = 69;
|
|
654
|
-
static F = 70;
|
|
655
|
-
static G = 71;
|
|
656
|
-
static H = 72;
|
|
657
|
-
static I = 73;
|
|
658
|
-
static J = 74;
|
|
659
|
-
static K = 75;
|
|
660
|
-
static L = 76;
|
|
661
|
-
static M = 77;
|
|
662
|
-
static N = 78;
|
|
663
|
-
static O = 79;
|
|
664
|
-
static P = 80;
|
|
665
|
-
static Q = 81;
|
|
666
|
-
static R = 82;
|
|
667
|
-
static S = 83;
|
|
668
|
-
static T = 84;
|
|
669
|
-
static U = 85;
|
|
670
|
-
static V = 86;
|
|
671
|
-
static W = 87;
|
|
672
|
-
static X = 88;
|
|
673
|
-
static Y = 89;
|
|
674
|
-
static Z = 90;
|
|
675
|
-
static META = 91; // WIN_KEY_LEFT
|
|
676
|
-
static MAC_WK_CMD_LEFT = 91;
|
|
677
|
-
static MAC_WK_CMD_RIGHT = 93;
|
|
678
|
-
static CONTEXT_MENU = 93;
|
|
679
|
-
static NUMPAD_ZERO = 96;
|
|
680
|
-
static NUMPAD_ONE = 97;
|
|
681
|
-
static NUMPAD_TWO = 98;
|
|
682
|
-
static NUMPAD_THREE = 99;
|
|
683
|
-
static NUMPAD_FOUR = 100;
|
|
684
|
-
static NUMPAD_FIVE = 101;
|
|
685
|
-
static NUMPAD_SIX = 102;
|
|
686
|
-
static NUMPAD_SEVEN = 103;
|
|
687
|
-
static NUMPAD_EIGHT = 104;
|
|
688
|
-
static NUMPAD_NINE = 105;
|
|
689
|
-
static NUMPAD_MULTIPLY = 106;
|
|
690
|
-
static NUMPAD_PLUS = 107;
|
|
691
|
-
static NUMPAD_MINUS = 109;
|
|
692
|
-
static NUMPAD_PERIOD = 110;
|
|
693
|
-
static NUMPAD_DIVIDE = 111;
|
|
694
|
-
static F1 = 112;
|
|
695
|
-
static F2 = 113;
|
|
696
|
-
static F3 = 114;
|
|
697
|
-
static F4 = 115;
|
|
698
|
-
static F5 = 116;
|
|
699
|
-
static F6 = 117;
|
|
700
|
-
static F7 = 118;
|
|
701
|
-
static F8 = 119;
|
|
702
|
-
static F9 = 120;
|
|
703
|
-
static F10 = 121;
|
|
704
|
-
static F11 = 122;
|
|
705
|
-
static F12 = 123;
|
|
706
|
-
static NUM_LOCK = 144;
|
|
707
|
-
static SCROLL_LOCK = 145;
|
|
708
|
-
static FIRST_MEDIA = 166;
|
|
709
|
-
static FF_MINUS = 173;
|
|
710
|
-
static MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
711
|
-
static VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
712
|
-
static VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
713
|
-
static FF_MUTE = 181;
|
|
714
|
-
static FF_VOLUME_DOWN = 182;
|
|
715
|
-
static LAST_MEDIA = 183;
|
|
716
|
-
static FF_VOLUME_UP = 183;
|
|
717
|
-
static SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
718
|
-
static EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
719
|
-
static COMMA = 188;
|
|
720
|
-
static DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
721
|
-
static PERIOD = 190;
|
|
722
|
-
static SLASH = 191;
|
|
723
|
-
static APOSTROPHE = 192;
|
|
724
|
-
static TILDE = 192;
|
|
725
|
-
static OPEN_SQUARE_BRACKET = 219;
|
|
726
|
-
static BACKSLASH = 220;
|
|
727
|
-
static CLOSE_SQUARE_BRACKET = 221;
|
|
728
|
-
static SINGLE_QUOTE = 222;
|
|
729
|
-
static MAC_META = 224;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
733
|
-
const HttpParamsRequestInstance = (options, instance) => {
|
|
734
|
-
return new UtilsHttpParamsRequest(options, instance);
|
|
735
|
-
};
|
|
736
|
-
class UtilsHttpParamsRequest extends HttpParams {
|
|
737
|
-
params;
|
|
738
|
-
constructor(options, instance) {
|
|
739
|
-
super(options);
|
|
740
|
-
if (!instance) {
|
|
741
|
-
this.params = new HttpParams(options);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
if (instance instanceof UtilsHttpParamsRequest) {
|
|
745
|
-
this.params = instance.getInstance();
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
if (instance instanceof HttpParams) {
|
|
749
|
-
this.params = instance;
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
getInstance() {
|
|
754
|
-
return this.params;
|
|
755
|
-
}
|
|
756
|
-
setInstance(instance) {
|
|
757
|
-
if (instance instanceof UtilsHttpParamsRequest) {
|
|
758
|
-
this.params = instance.getInstance();
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
if (instance instanceof HttpParams) {
|
|
762
|
-
this.params = instance;
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
set(param, value) {
|
|
767
|
-
this.params = this.params.set(param, value);
|
|
768
|
-
return this;
|
|
769
|
-
}
|
|
770
|
-
has(param) {
|
|
771
|
-
return this.params.has(param);
|
|
772
|
-
}
|
|
773
|
-
get(param) {
|
|
774
|
-
return this.params.get(param);
|
|
775
|
-
}
|
|
776
|
-
getAll(param) {
|
|
777
|
-
return this.params.getAll(param);
|
|
778
|
-
}
|
|
779
|
-
keys() {
|
|
780
|
-
return this.params.keys();
|
|
781
|
-
}
|
|
782
|
-
append(param, value) {
|
|
783
|
-
this.params = this.params.append(param, value);
|
|
784
|
-
return this;
|
|
785
|
-
}
|
|
786
|
-
appendAll(params) {
|
|
787
|
-
this.params = this.params.appendAll(params);
|
|
788
|
-
return this;
|
|
789
|
-
}
|
|
790
|
-
delete(param, value) {
|
|
791
|
-
this.params = this.params.delete(param, value);
|
|
792
|
-
return this;
|
|
793
|
-
}
|
|
794
|
-
toString() {
|
|
795
|
-
return this.params.toString();
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
class UtilsLanguageConstants {
|
|
800
|
-
static VI = 'vi';
|
|
801
|
-
static EN = 'en';
|
|
802
|
-
static defaultLang = UtilsLanguageConstants.VI;
|
|
803
|
-
}
|
|
804
|
-
;
|
|
805
|
-
|
|
806
|
-
const getKeyCacheByArrayObject = (keyCache, argumentsValue) => {
|
|
807
|
-
if (!keyCache || !argumentsValue) {
|
|
808
|
-
return '';
|
|
809
|
-
}
|
|
810
|
-
let keyBuild = `${JSON.stringify(argumentsValue.slice(1))}-key-cache-plus`;
|
|
811
|
-
if (argumentsValue && argumentsValue[0] instanceof HttpParams) {
|
|
812
|
-
const httpParams = argumentsValue[0];
|
|
813
|
-
const keys = argumentsValue[0].keys().sort((str1, str2) => str1.localeCompare(str2));
|
|
814
|
-
keys.forEach(key => {
|
|
815
|
-
keyBuild = `${keyBuild}${JSON.stringify(httpParams.get(key))}`;
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
|
-
const keyCachePlus = md5(keyBuild);
|
|
819
|
-
const keyCacheMD5 = md5(keyCache);
|
|
820
|
-
return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
|
|
821
|
-
};
|
|
822
|
-
|
|
823
756
|
let functionCheck = () => {
|
|
824
757
|
return window.parent !== window.top;
|
|
825
758
|
};
|
|
@@ -830,22 +763,34 @@ const isEmbedFrame = () => {
|
|
|
830
763
|
return functionCheck();
|
|
831
764
|
};
|
|
832
765
|
|
|
766
|
+
const ERROR_MESSAGE_EMPTY_VALID = 'i18n_valid_empty_message';
|
|
767
|
+
const ERROR_MESSAGE_PATTEN_VALID = 'i18n_valid_pattern_message';
|
|
768
|
+
const ERROR_MESSAGE_MIN_VALID = 'i18n_message_error_input_min_value';
|
|
769
|
+
const ERROR_MESSAGE_MAX_VALID = 'i18n_message_error_input_max_value';
|
|
770
|
+
const ERROR_MESSAGE_MIN_LENGTH = 'i18n_message_error_input_min_length';
|
|
771
|
+
const ERROR_MESSAGE_MAX_LENGTH = 'i18n_message_error_input_max_length';
|
|
772
|
+
const CHARACTER_DATA_EMPTY = '__';
|
|
773
|
+
const DEFAULT_START_PAGE_0 = 'defaultStartPage0';
|
|
774
|
+
const COMMUNICATE_MICRO_PREFIX_TYPE = '3RD_INTEGRATE_MICRO_SITE_';
|
|
775
|
+
const COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE = 'MICRO_SITES_ALL_MESSAGE';
|
|
776
|
+
|
|
833
777
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
778
|
+
class UtilsCommunicateMicroKeyGlobal {
|
|
779
|
+
static KEY_MESSAGE_MODAL = 'LIBS_UI_MODEL_EVENT';
|
|
780
|
+
}
|
|
834
781
|
class UtilsCommunicateMicro {
|
|
835
|
-
static KEY_GET_ALL_MESSAGE = 'MICRO_SITES_ALL_MESSAGE';
|
|
836
|
-
static PREFIX_TYPE = '3RD_INTEGRATE_MICRO_SITE_';
|
|
837
782
|
static initdEvent;
|
|
838
783
|
static subs = new Map();
|
|
839
784
|
static allMessageSub = new Subject();
|
|
840
|
-
static initEvent(currentWindow) {
|
|
785
|
+
static initEvent(currentWindow, onDestroy) {
|
|
841
786
|
if (this.initdEvent) {
|
|
842
787
|
return;
|
|
843
788
|
}
|
|
844
789
|
this.initdEvent = true;
|
|
845
|
-
if (!this.subs.get(
|
|
846
|
-
this.subs.set(
|
|
790
|
+
if (!this.subs.get(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE)) {
|
|
791
|
+
this.subs.set(COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, UtilsCommunicateMicro.allMessageSub);
|
|
847
792
|
}
|
|
848
|
-
fromEvent(currentWindow, 'message').subscribe(e => {
|
|
793
|
+
fromEvent(currentWindow, 'message').pipe(takeUntil(onDestroy)).subscribe(e => {
|
|
849
794
|
const event = { data: { ...e.data } };
|
|
850
795
|
const data = event.data;
|
|
851
796
|
const type = data.type;
|
|
@@ -857,7 +802,7 @@ class UtilsCommunicateMicro {
|
|
|
857
802
|
return;
|
|
858
803
|
}
|
|
859
804
|
try {
|
|
860
|
-
if (type.includes(
|
|
805
|
+
if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
|
|
861
806
|
data.response = JSON.parse(decrypt3rd(data.response));
|
|
862
807
|
sub.next(event);
|
|
863
808
|
this.allMessageSub.next(event);
|
|
@@ -873,6 +818,10 @@ class UtilsCommunicateMicro {
|
|
|
873
818
|
this.allMessageSub.next(event);
|
|
874
819
|
}
|
|
875
820
|
});
|
|
821
|
+
UtilsCommunicateMicro.GetMessage(UtilsCache.typeKeyClearLocalStorage).pipe(filter(e => e.data.response !== UtilsCache.idService), takeUntil(onDestroy)).subscribe(() => {
|
|
822
|
+
console.log('clear all cache by event');
|
|
823
|
+
UtilsCache.ClearAll();
|
|
824
|
+
});
|
|
876
825
|
}
|
|
877
826
|
static PostMessageToParent(data) {
|
|
878
827
|
data = this.convertData(data);
|
|
@@ -907,14 +856,13 @@ class UtilsCommunicateMicro {
|
|
|
907
856
|
}
|
|
908
857
|
data = { ...data };
|
|
909
858
|
const type = data.type;
|
|
910
|
-
if (type.includes(
|
|
859
|
+
if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
|
|
911
860
|
try {
|
|
912
861
|
JSON.parse(decrypt3rd(data.response));
|
|
913
862
|
return data;
|
|
914
863
|
}
|
|
915
|
-
catch (
|
|
916
|
-
|
|
917
|
-
data.response = decrypt3rd(JSON.stringify(data.response));
|
|
864
|
+
catch (_) {
|
|
865
|
+
data.response = encrypt3rd(JSON.stringify(data.response));
|
|
918
866
|
return data;
|
|
919
867
|
}
|
|
920
868
|
}
|
|
@@ -922,9 +870,8 @@ class UtilsCommunicateMicro {
|
|
|
922
870
|
JSON.parse(decrypt(data.response));
|
|
923
871
|
return data;
|
|
924
872
|
}
|
|
925
|
-
catch (
|
|
926
|
-
|
|
927
|
-
data.response = decrypt(JSON.stringify(data.response));
|
|
873
|
+
catch (_) {
|
|
874
|
+
data.response = encrypt(JSON.stringify(data.response));
|
|
928
875
|
return data;
|
|
929
876
|
}
|
|
930
877
|
}
|
|
@@ -944,6 +891,9 @@ class UtilsCommunicateMicro {
|
|
|
944
891
|
if (!Array.isArray(messageType) || !messageType.length) {
|
|
945
892
|
throw new Error('message type empty');
|
|
946
893
|
}
|
|
894
|
+
if (messageType.length === 1) {
|
|
895
|
+
return this.GetMessage(messageType[0]);
|
|
896
|
+
}
|
|
947
897
|
const types = messageType.sort().join(';');
|
|
948
898
|
let sub = this.subs.get(types);
|
|
949
899
|
if (sub) {
|
|
@@ -961,90 +911,149 @@ class UtilsCommunicateMicro {
|
|
|
961
911
|
});
|
|
962
912
|
});
|
|
963
913
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
class UtilsLanguageConstants {
|
|
917
|
+
static VI = "vi"; // Tiếng Việt
|
|
918
|
+
static EN = "en"; // Tiếng Anh
|
|
919
|
+
static FR = "fr"; // Tiếng Pháp
|
|
920
|
+
static DE = "de"; // Tiếng Đức
|
|
921
|
+
static ES = "es"; // Tiếng Tây Ban Nha
|
|
922
|
+
static ZH = "zh"; // Tiếng Trung (Giản thể)
|
|
923
|
+
static ZH_TW = "zh-TW"; // Tiếng Trung (Phồn thể)
|
|
924
|
+
static JA = "ja"; // Tiếng Nhật
|
|
925
|
+
static KO = "ko"; // Tiếng Hàn
|
|
926
|
+
static RU = "ru"; // Tiếng Nga
|
|
927
|
+
static IT = "it"; // Tiếng Ý
|
|
928
|
+
static PT = "pt"; // Tiếng Bồ Đào Nha
|
|
929
|
+
static TH = "th"; // Tiếng Thái
|
|
930
|
+
static ID = "id"; // Tiếng Indonesia
|
|
931
|
+
static MS = "ms"; // Tiếng Malaysia
|
|
932
|
+
static AR = "ar"; // Tiếng Ả Rập
|
|
933
|
+
static HI = "hi"; // Tiếng Hindi
|
|
934
|
+
static BN = "bn"; // Tiếng Bengal
|
|
935
|
+
static TR = "tr"; // Tiếng Thổ Nhĩ Kỳ
|
|
936
|
+
static NL = "nl"; // Tiếng Hà Lan
|
|
937
|
+
static KM = "km"; // Tiếng Khmer (Campuchia)
|
|
938
|
+
static LO = "lo"; // Tiếng Lào
|
|
939
|
+
static MY = "my"; // Tiếng Miến Điện (Myanmar)
|
|
940
|
+
static TL = "tl"; // Tiếng Tagalog (Philippines)
|
|
941
|
+
static CEB = "ceb"; // Tiếng Cebuano (Philippines)
|
|
942
|
+
static JV = "jv"; // Tiếng Java (Indonesia)
|
|
943
|
+
static SU = "su"; // Tiếng Sundanese (Indonesia)
|
|
944
|
+
// Ngôn ngữ mặc định
|
|
945
|
+
static defaultLang = UtilsLanguageConstants.VI;
|
|
946
|
+
// Danh sách các ngôn ngữ được hỗ trợ
|
|
947
|
+
static supportedLanguages = new Set([
|
|
948
|
+
UtilsLanguageConstants.VI,
|
|
949
|
+
UtilsLanguageConstants.EN,
|
|
950
|
+
UtilsLanguageConstants.FR,
|
|
951
|
+
UtilsLanguageConstants.DE,
|
|
952
|
+
UtilsLanguageConstants.ES,
|
|
953
|
+
UtilsLanguageConstants.ZH,
|
|
954
|
+
UtilsLanguageConstants.ZH_TW,
|
|
955
|
+
UtilsLanguageConstants.JA,
|
|
956
|
+
UtilsLanguageConstants.KO,
|
|
957
|
+
UtilsLanguageConstants.RU,
|
|
958
|
+
UtilsLanguageConstants.IT,
|
|
959
|
+
UtilsLanguageConstants.PT,
|
|
960
|
+
UtilsLanguageConstants.TH,
|
|
961
|
+
UtilsLanguageConstants.ID,
|
|
962
|
+
UtilsLanguageConstants.MS,
|
|
963
|
+
UtilsLanguageConstants.AR,
|
|
964
|
+
UtilsLanguageConstants.HI,
|
|
965
|
+
UtilsLanguageConstants.BN,
|
|
966
|
+
UtilsLanguageConstants.TR,
|
|
967
|
+
UtilsLanguageConstants.NL,
|
|
968
|
+
UtilsLanguageConstants.KM,
|
|
969
|
+
UtilsLanguageConstants.LO,
|
|
970
|
+
UtilsLanguageConstants.MY,
|
|
971
|
+
UtilsLanguageConstants.TL,
|
|
972
|
+
UtilsLanguageConstants.CEB,
|
|
973
|
+
UtilsLanguageConstants.JV,
|
|
974
|
+
UtilsLanguageConstants.SU,
|
|
975
|
+
]);
|
|
976
|
+
/**
|
|
977
|
+
* Kiểm tra xem ngôn ngữ đầu vào có được hỗ trợ không
|
|
978
|
+
* @param lang Mã ngôn ngữ cần kiểm tra
|
|
979
|
+
* @returns True nếu được hỗ trợ, False nếu không
|
|
980
|
+
*/
|
|
981
|
+
static isSupported(lang) {
|
|
982
|
+
return UtilsLanguageConstants.supportedLanguages.has(lang);
|
|
969
983
|
}
|
|
970
984
|
}
|
|
971
985
|
|
|
972
986
|
/* eslint-disable no-async-promise-executor */
|
|
973
987
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
974
988
|
class UtilsCache {
|
|
989
|
+
static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
|
|
975
990
|
static CACHE_EXPIRE_NONE = -1;
|
|
991
|
+
static idService = uuid();
|
|
976
992
|
static typeKeyClearLocalStorage = 'LIBS_UI_CLEAR_LOCAL_STORAGE_EVENT';
|
|
977
|
-
static listKeyKeepWhenClearALll = Array();
|
|
978
993
|
static languageKeyCache = 'SR3xQKxHgffiCevPQRralg';
|
|
994
|
+
static listKeyKeepWhenClearALll = Array();
|
|
979
995
|
static initdEvent;
|
|
980
|
-
static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
|
|
981
|
-
static language = signal(UtilsLanguageConstants.defaultLang);
|
|
982
|
-
static idService = uuid();
|
|
983
996
|
static storage;
|
|
984
997
|
static dbName = 'libs-ui-cache';
|
|
985
998
|
static itemIndexByKey = 'key';
|
|
986
999
|
static dbVersion = 1;
|
|
987
1000
|
static db = null;
|
|
988
1001
|
static init(config) {
|
|
989
|
-
if (
|
|
1002
|
+
if (this.initdEvent) {
|
|
990
1003
|
return;
|
|
991
1004
|
}
|
|
992
|
-
|
|
1005
|
+
this.initdEvent = true;
|
|
993
1006
|
if (config.indexedDBName) {
|
|
994
|
-
|
|
1007
|
+
this.dbName = config.indexedDBName;
|
|
995
1008
|
}
|
|
996
1009
|
if (config.typeKeyClearLocalStorage) {
|
|
997
|
-
|
|
1010
|
+
this.typeKeyClearLocalStorage = config.typeKeyClearLocalStorage;
|
|
998
1011
|
}
|
|
999
|
-
if (config.
|
|
1000
|
-
|
|
1012
|
+
if (config.listKeyKeepWhenClearAll) {
|
|
1013
|
+
this.listKeyKeepWhenClearALll = config.listKeyKeepWhenClearAll;
|
|
1001
1014
|
}
|
|
1002
1015
|
if (config.languageKeyCache) {
|
|
1003
|
-
|
|
1016
|
+
this.languageKeyCache = config.languageKeyCache;
|
|
1004
1017
|
}
|
|
1005
|
-
UtilsCommunicateMicro.GetMessage(UtilsCache.typeKeyClearLocalStorage).pipe(filter(e => e.data.response !== UtilsCache.idService)).subscribe(() => {
|
|
1006
|
-
UtilsCache.ClearAll();
|
|
1007
|
-
});
|
|
1008
1018
|
}
|
|
1009
1019
|
static setLang(lang) {
|
|
1010
|
-
if (
|
|
1011
|
-
throw Error(
|
|
1020
|
+
if (!UtilsLanguageConstants.isSupported(lang)) {
|
|
1021
|
+
throw Error(`Language not support ${lang}`);
|
|
1012
1022
|
}
|
|
1013
|
-
|
|
1014
|
-
UtilsCache.Set(UtilsCache.languageKeyCache, lang);
|
|
1023
|
+
this.Set(this.languageKeyCache, lang, this.CACHE_EXPIRE_NONE);
|
|
1015
1024
|
}
|
|
1016
1025
|
static getLang() {
|
|
1017
|
-
return
|
|
1026
|
+
return this.Get(this.languageKeyCache, UtilsLanguageConstants.defaultLang);
|
|
1018
1027
|
}
|
|
1019
1028
|
static openDB() {
|
|
1020
1029
|
return new Promise(resolve => {
|
|
1021
|
-
const request = indexedDB.open(
|
|
1030
|
+
const request = indexedDB.open(this.dbName, this.dbVersion);
|
|
1022
1031
|
request.onupgradeneeded = (event) => {
|
|
1023
1032
|
const db = event.target.result;
|
|
1024
|
-
if (!db.objectStoreNames.contains(
|
|
1025
|
-
const objectStore = db.createObjectStore(
|
|
1026
|
-
objectStore.createIndex(
|
|
1033
|
+
if (!db.objectStoreNames.contains(this.dbName)) {
|
|
1034
|
+
const objectStore = db.createObjectStore(this.dbName, { keyPath: this.itemIndexByKey });
|
|
1035
|
+
objectStore.createIndex(this.itemIndexByKey, this.itemIndexByKey, { unique: true });
|
|
1027
1036
|
}
|
|
1028
1037
|
};
|
|
1029
1038
|
request.onsuccess = () => {
|
|
1030
|
-
|
|
1031
|
-
setTimeout(() => {
|
|
1032
|
-
UtilsCache.DeleteKeyStartWithAsync('');
|
|
1033
|
-
}, 2000);
|
|
1039
|
+
this.db = request.result;
|
|
1034
1040
|
resolve(true);
|
|
1035
1041
|
};
|
|
1036
1042
|
request.onerror = (event) => {
|
|
1037
|
-
console.
|
|
1043
|
+
console.trace('Error opening IndexedDB:', event);
|
|
1038
1044
|
resolve(false);
|
|
1039
1045
|
};
|
|
1040
1046
|
});
|
|
1041
1047
|
}
|
|
1042
1048
|
static async getObjectStore() {
|
|
1043
|
-
if (!
|
|
1044
|
-
await
|
|
1049
|
+
if (!this.db) {
|
|
1050
|
+
await this.openDB();
|
|
1045
1051
|
}
|
|
1046
|
-
const transaction =
|
|
1047
|
-
|
|
1052
|
+
const transaction = this.db?.transaction([this.dbName], 'readwrite');
|
|
1053
|
+
if (!transaction) {
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
return transaction.objectStore(this.dbName);
|
|
1048
1057
|
}
|
|
1049
1058
|
static get LocalStorage() {
|
|
1050
1059
|
try {
|
|
@@ -1058,12 +1067,12 @@ class UtilsCache {
|
|
|
1058
1067
|
return this.getLocalStorageFake();
|
|
1059
1068
|
}
|
|
1060
1069
|
catch (error) {
|
|
1061
|
-
console.
|
|
1070
|
+
console.trace(`LocalStorage `, error);
|
|
1062
1071
|
return this.getLocalStorageFake();
|
|
1063
1072
|
}
|
|
1064
1073
|
}
|
|
1065
1074
|
static getLocalStorageFakeOnSafari() {
|
|
1066
|
-
if (typeof window.localStorage !== 'undefined' && !
|
|
1075
|
+
if (typeof window.localStorage !== 'undefined' && !this.storage && Object.keys(localStorage).length) {
|
|
1067
1076
|
this.storage = { ...localStorage };
|
|
1068
1077
|
}
|
|
1069
1078
|
return {
|
|
@@ -1093,7 +1102,7 @@ class UtilsCache {
|
|
|
1093
1102
|
};
|
|
1094
1103
|
}
|
|
1095
1104
|
static getLocalStorageFake() {
|
|
1096
|
-
if (!
|
|
1105
|
+
if (!this.storage) {
|
|
1097
1106
|
this.storage = {};
|
|
1098
1107
|
}
|
|
1099
1108
|
return {
|
|
@@ -1113,19 +1122,18 @@ class UtilsCache {
|
|
|
1113
1122
|
}
|
|
1114
1123
|
static async GetAsync(key, default_value, isKeyMD5 = false) {
|
|
1115
1124
|
key = isKeyMD5 ? key : md5(key);
|
|
1116
|
-
return new Promise(async (resolve
|
|
1117
|
-
const objectStore = await
|
|
1125
|
+
return new Promise(async (resolve) => {
|
|
1126
|
+
const objectStore = await this.getObjectStore();
|
|
1118
1127
|
if (!objectStore) {
|
|
1119
1128
|
return resolve(default_value);
|
|
1120
1129
|
}
|
|
1121
|
-
const
|
|
1122
|
-
const request = index.get(key);
|
|
1130
|
+
const request = objectStore.get(key);
|
|
1123
1131
|
request.onsuccess = () => {
|
|
1124
1132
|
if (!request.result) {
|
|
1125
1133
|
return resolve(default_value);
|
|
1126
1134
|
}
|
|
1127
1135
|
const data = JSON.parse(decrypt(request.result.value));
|
|
1128
|
-
if (data.expire ===
|
|
1136
|
+
if (data.expire === this.CACHE_EXPIRE_NONE) {
|
|
1129
1137
|
return resolve(data.json);
|
|
1130
1138
|
}
|
|
1131
1139
|
const currentMillisecond = (new Date().valueOf() / 1000);
|
|
@@ -1135,7 +1143,8 @@ class UtilsCache {
|
|
|
1135
1143
|
return resolve(data.json);
|
|
1136
1144
|
};
|
|
1137
1145
|
request.onerror = () => {
|
|
1138
|
-
|
|
1146
|
+
console.trace(`Get key ${key} Error, return default value: ${default_value}`);
|
|
1147
|
+
return resolve(default_value);
|
|
1139
1148
|
};
|
|
1140
1149
|
});
|
|
1141
1150
|
}
|
|
@@ -1149,8 +1158,8 @@ class UtilsCache {
|
|
|
1149
1158
|
}
|
|
1150
1159
|
try {
|
|
1151
1160
|
const data = JSON.parse(decrypt(cachedData));
|
|
1152
|
-
if (data.expire ===
|
|
1153
|
-
return data.value;
|
|
1161
|
+
if (data.expire === this.CACHE_EXPIRE_NONE) {
|
|
1162
|
+
return data.value ?? default_value;
|
|
1154
1163
|
}
|
|
1155
1164
|
const currentMillisecond = (new Date().valueOf() / 1000);
|
|
1156
1165
|
if (data.expire < currentMillisecond) {
|
|
@@ -1159,97 +1168,71 @@ class UtilsCache {
|
|
|
1159
1168
|
return data.value;
|
|
1160
1169
|
}
|
|
1161
1170
|
catch (error) {
|
|
1162
|
-
console.
|
|
1171
|
+
console.trace(`Get key ${key} Error, return default value: ${default_value}`, error);
|
|
1163
1172
|
return this.GetDefaultValueBySpecificKey(key, default_value);
|
|
1164
1173
|
}
|
|
1165
1174
|
}
|
|
1166
1175
|
static GetDefaultValueBySpecificKey(key, default_value) {
|
|
1167
1176
|
return default_value;
|
|
1168
1177
|
}
|
|
1169
|
-
static async SetAsync(key, value, expireTimeBySecond =
|
|
1170
|
-
return new Promise(async (resolve
|
|
1171
|
-
|
|
1178
|
+
static async SetAsync(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT, isKeyMD5 = false) {
|
|
1179
|
+
return new Promise(async (resolve) => {
|
|
1180
|
+
const objectStore = await this.getObjectStore();
|
|
1172
1181
|
key = isKeyMD5 ? key : md5(key);
|
|
1173
1182
|
try {
|
|
1174
|
-
const currentMillisecond = expireTimeBySecond ===
|
|
1183
|
+
const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
|
|
1175
1184
|
const data = {
|
|
1176
|
-
key: key,
|
|
1177
1185
|
value: encrypt(JSON.stringify({ json: value, expire: currentMillisecond })),
|
|
1178
1186
|
};
|
|
1187
|
+
data[this.itemIndexByKey] = key;
|
|
1179
1188
|
if (!objectStore) {
|
|
1180
|
-
|
|
1189
|
+
console.trace(`Can not open object store`);
|
|
1190
|
+
return resolve({ key, messageError: 'Can not open object store' });
|
|
1181
1191
|
}
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
if (item) {
|
|
1187
|
-
await UtilsCache.ClearAsync(key, true);
|
|
1188
|
-
objectStore = await UtilsCache.getObjectStore();
|
|
1189
|
-
}
|
|
1190
|
-
if (!objectStore) {
|
|
1191
|
-
return resolve(-1);
|
|
1192
|
-
}
|
|
1193
|
-
const request = objectStore.add(data);
|
|
1194
|
-
request.onsuccess = () => {
|
|
1195
|
-
resolve(request.result);
|
|
1196
|
-
};
|
|
1197
|
-
request.onerror = () => {
|
|
1198
|
-
console.log(request.error);
|
|
1199
|
-
return resolve(-1);
|
|
1200
|
-
};
|
|
1192
|
+
const request = objectStore?.put(data);
|
|
1193
|
+
request.onsuccess = () => {
|
|
1194
|
+
console.log(`Set key ${key} Success`);
|
|
1195
|
+
resolve(request.result);
|
|
1201
1196
|
};
|
|
1202
|
-
|
|
1203
|
-
console.
|
|
1204
|
-
|
|
1197
|
+
request.onerror = (error) => {
|
|
1198
|
+
console.trace(`Set key ${key} Error`);
|
|
1199
|
+
resolve({ key, messageError: get(error, 'message') });
|
|
1205
1200
|
};
|
|
1206
1201
|
}
|
|
1207
1202
|
catch (error) {
|
|
1208
|
-
console.
|
|
1209
|
-
resolve({});
|
|
1203
|
+
console.trace(`Set key ${key} Error`);
|
|
1204
|
+
resolve({ key, messageError: get(error, 'message') });
|
|
1210
1205
|
}
|
|
1211
1206
|
});
|
|
1212
1207
|
}
|
|
1213
|
-
static Set(key, value, expireTimeBySecond =
|
|
1214
|
-
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
const currentMillisecond = expireTimeBySecond === UtilsCache.CACHE_EXPIRE_NONE ? UtilsCache.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
|
|
1208
|
+
static Set(key, value, expireTimeBySecond = this.CACHE_EXPIRE_TIME_DEFAULT) {
|
|
1209
|
+
const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
|
|
1218
1210
|
const data = {
|
|
1219
1211
|
value: value,
|
|
1220
1212
|
expire: currentMillisecond
|
|
1221
1213
|
};
|
|
1222
1214
|
try {
|
|
1223
1215
|
this.LocalStorage.setItem(key, encrypt(JSON.stringify(data)));
|
|
1216
|
+
return true;
|
|
1224
1217
|
}
|
|
1225
1218
|
catch (error) {
|
|
1226
|
-
console.
|
|
1219
|
+
console.trace(`Set key ${key} Error`, error);
|
|
1220
|
+
return false;
|
|
1227
1221
|
}
|
|
1228
1222
|
}
|
|
1229
1223
|
static async ClearAsync(key, isMD5 = false) {
|
|
1230
1224
|
return new Promise(async (resolve) => {
|
|
1231
|
-
const objectStore = await
|
|
1225
|
+
const objectStore = await this.getObjectStore();
|
|
1232
1226
|
if (!objectStore) {
|
|
1233
|
-
return resolve(
|
|
1227
|
+
return resolve();
|
|
1234
1228
|
}
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
const result = getRequest.result;
|
|
1239
|
-
if (result) {
|
|
1240
|
-
const deleteRequest = objectStore.delete(result.id); // Xóa theo id của item tìm được
|
|
1241
|
-
deleteRequest.onsuccess = () => {
|
|
1242
|
-
resolve({});
|
|
1243
|
-
};
|
|
1244
|
-
deleteRequest.onerror = () => {
|
|
1245
|
-
resolve({});
|
|
1246
|
-
};
|
|
1247
|
-
return;
|
|
1248
|
-
}
|
|
1249
|
-
resolve({});
|
|
1229
|
+
const request = objectStore.delete(isMD5 ? key : md5(key));
|
|
1230
|
+
request.onsuccess = () => {
|
|
1231
|
+
resolve({ clearSuccess: true });
|
|
1250
1232
|
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1233
|
+
request.onerror = (event) => {
|
|
1234
|
+
console.trace('Error deleting Key:', get(event.target.error, 'message'));
|
|
1235
|
+
resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
|
|
1253
1236
|
};
|
|
1254
1237
|
});
|
|
1255
1238
|
}
|
|
@@ -1260,38 +1243,41 @@ class UtilsCache {
|
|
|
1260
1243
|
this.LocalStorage.removeItem(key);
|
|
1261
1244
|
}
|
|
1262
1245
|
static ClearAllAsync() {
|
|
1263
|
-
return new Promise((resolve
|
|
1264
|
-
const
|
|
1246
|
+
return new Promise(async (resolve) => {
|
|
1247
|
+
const objectStore = await this.getObjectStore();
|
|
1248
|
+
if (!objectStore) {
|
|
1249
|
+
return resolve();
|
|
1250
|
+
}
|
|
1251
|
+
const request = objectStore.clear();
|
|
1265
1252
|
request.onsuccess = () => {
|
|
1266
|
-
console.log('
|
|
1267
|
-
resolve(true);
|
|
1253
|
+
console.log('clear all successfully');
|
|
1254
|
+
resolve({ clearSuccess: true });
|
|
1268
1255
|
};
|
|
1269
1256
|
request.onerror = (event) => {
|
|
1270
|
-
console.
|
|
1271
|
-
|
|
1272
|
-
};
|
|
1273
|
-
request.onblocked = () => {
|
|
1274
|
-
console.warn('Delete request is blocked');
|
|
1257
|
+
console.trace('Error deleting key:', get(event.target.error, 'message'));
|
|
1258
|
+
resolve({ messageError: get(event.target.error, 'message'), clearSuccess: false });
|
|
1275
1259
|
};
|
|
1276
1260
|
});
|
|
1277
1261
|
}
|
|
1278
1262
|
static ClearAll() {
|
|
1279
|
-
|
|
1263
|
+
if (isEmbedFrame()) {
|
|
1264
|
+
const data = {
|
|
1265
|
+
type: this.typeKeyClearLocalStorage,
|
|
1266
|
+
response: {
|
|
1267
|
+
idEvent: this.idService
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
UtilsCommunicateMicro.PostMessageToParent(data);
|
|
1271
|
+
}
|
|
1272
|
+
const keys = [...this.listKeyKeepWhenClearALll];
|
|
1280
1273
|
Object.keys(this.LocalStorage).forEach(key => {
|
|
1281
1274
|
if (key.includes('kc-callback-')) {
|
|
1282
1275
|
keys.push(key);
|
|
1283
1276
|
}
|
|
1284
1277
|
});
|
|
1285
|
-
const stores =
|
|
1278
|
+
const stores = this.GetDataByKeys(Array.from(keys));
|
|
1286
1279
|
this.LocalStorage.clear();
|
|
1287
|
-
|
|
1288
|
-
const data = {
|
|
1289
|
-
type: UtilsCache.typeKeyClearLocalStorage,
|
|
1290
|
-
response: {
|
|
1291
|
-
idEvent: this.idService
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
1294
|
-
UtilsCommunicateMicro.PostMessageToParent(data);
|
|
1280
|
+
this.SetDataByKey(stores);
|
|
1295
1281
|
}
|
|
1296
1282
|
static GetDataByKeys(keys) {
|
|
1297
1283
|
const stores = new Map();
|
|
@@ -1300,62 +1286,1051 @@ class UtilsCache {
|
|
|
1300
1286
|
stores.set(key, this.LocalStorage.getItem(key));
|
|
1301
1287
|
return;
|
|
1302
1288
|
}
|
|
1303
|
-
stores.set(key,
|
|
1289
|
+
stores.set(key, this.Get(key));
|
|
1304
1290
|
});
|
|
1305
1291
|
return stores;
|
|
1306
1292
|
}
|
|
1307
|
-
static SetDataByKey(stores) {
|
|
1308
|
-
stores.forEach((value, key) => {
|
|
1309
|
-
if (key.includes('kc-callback-')) {
|
|
1310
|
-
this.LocalStorage.setItem(key, value);
|
|
1293
|
+
static SetDataByKey(stores) {
|
|
1294
|
+
stores.forEach((value, key) => {
|
|
1295
|
+
if (key.includes('kc-callback-')) {
|
|
1296
|
+
this.LocalStorage.setItem(key, value);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (key === this.languageKeyCache) {
|
|
1300
|
+
return this.setLang(value);
|
|
1301
|
+
}
|
|
1302
|
+
this.Set(key, value, this.CACHE_EXPIRE_NONE);
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
static DeleteKeyStartWithAsync(keyCacheStartWith, isKeyMD5 = false) {
|
|
1306
|
+
return new Promise(async (resolve) => {
|
|
1307
|
+
const objectStore = await this.getObjectStore();
|
|
1308
|
+
if (!objectStore) {
|
|
1309
|
+
return resolve({});
|
|
1310
|
+
}
|
|
1311
|
+
// Lấy tất cả các keys từ index
|
|
1312
|
+
const request = objectStore.getAll();
|
|
1313
|
+
keyCacheStartWith = isKeyMD5 ? keyCacheStartWith : md5(keyCacheStartWith);
|
|
1314
|
+
request.onsuccess = (e) => {
|
|
1315
|
+
const data = e.target.result;
|
|
1316
|
+
if (!Array.isArray(data)) {
|
|
1317
|
+
return resolve({});
|
|
1318
|
+
}
|
|
1319
|
+
data.forEach(obj => {
|
|
1320
|
+
if (obj[this.itemIndexByKey].startsWith(keyCacheStartWith)) {
|
|
1321
|
+
this.ClearAsync(obj[this.itemIndexByKey], true);
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
return resolve({});
|
|
1325
|
+
};
|
|
1326
|
+
request.onerror = () => {
|
|
1327
|
+
return resolve({});
|
|
1328
|
+
};
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
static DeleteKeyStartWith(keyCache, isMD5 = false) {
|
|
1332
|
+
keyCache = isMD5 ? md5(keyCache) : keyCache;
|
|
1333
|
+
const keys = Object.keys(this.LocalStorage);
|
|
1334
|
+
if (!keys || !keys.length) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
keys.forEach(key => {
|
|
1338
|
+
if (key.startsWith(keyCache)) {
|
|
1339
|
+
this.Clear(key);
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
static DeleteDatabaseIndexDB(dbName) {
|
|
1344
|
+
return new Promise((resolve) => {
|
|
1345
|
+
dbName = (dbName || this.dbName);
|
|
1346
|
+
const request = indexedDB.deleteDatabase(dbName);
|
|
1347
|
+
request.onsuccess = () => {
|
|
1348
|
+
console.trace('Database deleted successfully');
|
|
1349
|
+
resolve({ deleteSuccess: true });
|
|
1350
|
+
};
|
|
1351
|
+
request.onerror = (event) => {
|
|
1352
|
+
const error = event.target.error;
|
|
1353
|
+
console.trace('Error deleting database:', error);
|
|
1354
|
+
resolve({ messageError: get(error || {}, 'message'), deleteSuccess: false });
|
|
1355
|
+
};
|
|
1356
|
+
request.onblocked = () => {
|
|
1357
|
+
console.trace('Delete request is blocked');
|
|
1358
|
+
resolve({ messageError: 'Delete request is blocked', deleteSuccess: false });
|
|
1359
|
+
};
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
dayjs.extend(localeData);
|
|
1365
|
+
dayjs.extend(updateLocale);
|
|
1366
|
+
dayjs.extend(utc);
|
|
1367
|
+
dayjs.extend(timezone);
|
|
1368
|
+
dayjs.extend(customParseFormat);
|
|
1369
|
+
let timeZoneSetup = "Asia/Ho_Chi_Minh";
|
|
1370
|
+
const setDefaultTimeZone = (localeZone = timeZoneSetup) => {
|
|
1371
|
+
timeZoneSetup = localeZone;
|
|
1372
|
+
dayjs.tz.setDefault(localeZone);
|
|
1373
|
+
};
|
|
1374
|
+
let functionFormatDate = undefined;
|
|
1375
|
+
const updateFunctionFormatDate = (functionCustom) => {
|
|
1376
|
+
functionFormatDate = functionCustom;
|
|
1377
|
+
};
|
|
1378
|
+
/**
|
|
1379
|
+
* @description Lấy đối tượng dayjs theo config
|
|
1380
|
+
* @param config nếu không có config sẽ trả về đối tượng dayjs là thời gian hiện tại
|
|
1381
|
+
* @param config.date thời gian cần lấy
|
|
1382
|
+
* @param config.returnDayjsIfConfigDateNotExist true nếu muốn trả về đối tượng dayjs nếu config.date không có
|
|
1383
|
+
* @param config.utc true nếu muốn lấy thời gian UTC
|
|
1384
|
+
* @param config.formatOfDate định dạng thời gian đang được truyền vào
|
|
1385
|
+
*/
|
|
1386
|
+
const getDayjs = (config) => {
|
|
1387
|
+
if (!config) {
|
|
1388
|
+
return dayjs().tz();
|
|
1389
|
+
}
|
|
1390
|
+
config.date = !config.date && config.returnDayjsIfConfigDateNotExist ? dayjs().tz() : config.date;
|
|
1391
|
+
if (typeof config.date === 'number') {
|
|
1392
|
+
config.date = dayjs.unix(config.date);
|
|
1393
|
+
}
|
|
1394
|
+
if (!config.date) {
|
|
1395
|
+
return undefined;
|
|
1396
|
+
}
|
|
1397
|
+
let { date, utc, formatOfDate } = config;
|
|
1398
|
+
while (isSignal(date)) {
|
|
1399
|
+
date = date();
|
|
1400
|
+
}
|
|
1401
|
+
while (isSignal(utc)) {
|
|
1402
|
+
utc = utc();
|
|
1403
|
+
}
|
|
1404
|
+
while (isSignal(formatOfDate)) {
|
|
1405
|
+
formatOfDate = formatOfDate();
|
|
1406
|
+
}
|
|
1407
|
+
if (utc) {
|
|
1408
|
+
if (formatOfDate) {
|
|
1409
|
+
return dayjs(date, formatOfDate).utc();
|
|
1410
|
+
}
|
|
1411
|
+
const dateInputIsUTC = (dayjs.isDayjs(date) && date.isUTC()) || (typeof date === 'string' && date.includes('Z'));
|
|
1412
|
+
if (dateInputIsUTC) {
|
|
1413
|
+
return dayjs(date);
|
|
1414
|
+
}
|
|
1415
|
+
return dayjs(date).utc();
|
|
1416
|
+
}
|
|
1417
|
+
if (typeof date === 'string' && !date.includes('Z') && !date.includes('+')) {
|
|
1418
|
+
return (formatOfDate ? dayjs.tz(date, formatOfDate, config.localeZone || timeZoneSetup) : dayjs.tz(date, config.localeZone || timeZoneSetup));
|
|
1419
|
+
}
|
|
1420
|
+
return (formatOfDate ? dayjs(date, formatOfDate) : dayjs(date)).tz();
|
|
1421
|
+
};
|
|
1422
|
+
/**
|
|
1423
|
+
* @description Kiểm tra xem hai ngày có khác nhau không (khác ngày trong tuần)
|
|
1424
|
+
* @param date1 ngày thứ nhất cần so sánh
|
|
1425
|
+
* @param date2 ngày thứ hai cần so sánh
|
|
1426
|
+
* @returns true nếu hai ngày khác nhau, false nếu giống nhau hoặc không thể so sánh
|
|
1427
|
+
*/
|
|
1428
|
+
const isDifferenceDay = (date1, date2) => {
|
|
1429
|
+
if (isDifferenceMonth(date1, date2)) {
|
|
1430
|
+
return true;
|
|
1431
|
+
}
|
|
1432
|
+
const day1 = getDayjs({ date: date1 })?.day();
|
|
1433
|
+
const day2 = getDayjs({ date: date2 })?.day();
|
|
1434
|
+
return day1 !== day2;
|
|
1435
|
+
};
|
|
1436
|
+
/**
|
|
1437
|
+
* @description Kiểm tra xem hai ngày có khác tháng không
|
|
1438
|
+
* @param date1 ngày thứ nhất cần so sánh
|
|
1439
|
+
* @param date2 ngày thứ hai cần so sánh
|
|
1440
|
+
* @returns true nếu hai ngày khác tháng, false nếu cùng tháng hoặc không thể so sánh
|
|
1441
|
+
*/
|
|
1442
|
+
const isDifferenceMonth = (date1, date2) => {
|
|
1443
|
+
if (isDifferenceYear(date1, date2)) {
|
|
1444
|
+
return true;
|
|
1445
|
+
}
|
|
1446
|
+
const month1 = getDayjs({ date: date1 })?.month();
|
|
1447
|
+
const month2 = getDayjs({ date: date2 })?.month();
|
|
1448
|
+
return month1 !== month2;
|
|
1449
|
+
};
|
|
1450
|
+
/**
|
|
1451
|
+
* @description Kiểm tra xem hai ngày có khác năm không
|
|
1452
|
+
* @param date1 ngày thứ nhất cần so sánh
|
|
1453
|
+
* @param date2 ngày thứ hai cần so sánh
|
|
1454
|
+
* @returns true nếu hai ngày khác năm, false nếu cùng năm hoặc không thể so sánh
|
|
1455
|
+
*/
|
|
1456
|
+
const isDifferenceYear = (date1, date2) => {
|
|
1457
|
+
const year1 = getDayjs({ date: date1 })?.year();
|
|
1458
|
+
const year2 = getDayjs({ date: date2 })?.year();
|
|
1459
|
+
return year1 !== year2;
|
|
1460
|
+
};
|
|
1461
|
+
/**
|
|
1462
|
+
* @description Lấy ra chuỗi thời gian hiển thị theo định dạng và ngôn ngữ
|
|
1463
|
+
* @param date thời gian cần định dạng
|
|
1464
|
+
* @param format định dạng thời gian muốn lấy ra
|
|
1465
|
+
* @param lang lấy theo ngôn ngữ
|
|
1466
|
+
*/
|
|
1467
|
+
const formatDate = (date, formatOutput = 'YYYY/MM/DD HH:mm', lang, formatInput) => {
|
|
1468
|
+
if (!date) {
|
|
1469
|
+
return '';
|
|
1470
|
+
}
|
|
1471
|
+
lang = lang || UtilsCache.getLang();
|
|
1472
|
+
if (functionFormatDate) {
|
|
1473
|
+
return functionFormatDate(date, formatOutput, lang);
|
|
1474
|
+
}
|
|
1475
|
+
date = getDayjs({ date, formatOfDate: formatInput }).locale(lang);
|
|
1476
|
+
if (lang === 'vi') {
|
|
1477
|
+
dayjs.updateLocale('vi', {
|
|
1478
|
+
monthsShort: 'Thg 1_Thg 2_Thg 3_Thg 4_Thg 5_Thg 6_Thg 7_Thg 8_Thg 9_Thg 10_Thg 11_Thg 12'.split('_')
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
return date.format(getFormatData(getTypeByFormat(formatOutput), lang)) || '';
|
|
1482
|
+
};
|
|
1483
|
+
const getTypeByFormat = (format) => {
|
|
1484
|
+
if (format === 'dm' || format === 'dmy' || format === 'dmy hm' || format === 'my') {
|
|
1485
|
+
return format;
|
|
1486
|
+
}
|
|
1487
|
+
if (format === 'DD/MM/YYYY' || format === 'YYYY-MM-DD' || format === 'dd/MM/yyyy' || format === 'dd-MM-yyyy' || format === 'dd/mm/yyyy') {
|
|
1488
|
+
return 'dmy';
|
|
1489
|
+
}
|
|
1490
|
+
if (format === 'MM-DD' || format === 'dd/MM' || format === 'dd/mm') {
|
|
1491
|
+
return 'dm';
|
|
1492
|
+
}
|
|
1493
|
+
if (format === 'M/YYYY' || format === 'YYYY-MM' || format === 'MM/yyyy') {
|
|
1494
|
+
return 'my';
|
|
1495
|
+
}
|
|
1496
|
+
if (format === 'YYYY/MM/DD hh:mm:ss' || format === 'dmy hms' || format === 'dd/mm/yyyy hh:mm:ss') {
|
|
1497
|
+
return 'dmy hms';
|
|
1498
|
+
}
|
|
1499
|
+
if (format === 'hh:mm' || format === 'HH:mm') {
|
|
1500
|
+
return 'hh:mm';
|
|
1501
|
+
}
|
|
1502
|
+
if (format === 'hh:mm A' || format === 'HH:mm A') {
|
|
1503
|
+
return 'hh:mm A';
|
|
1504
|
+
}
|
|
1505
|
+
return 'dmy hm';
|
|
1506
|
+
};
|
|
1507
|
+
const getFormatData = (type, lang) => {
|
|
1508
|
+
switch (type) {
|
|
1509
|
+
case 'dm':
|
|
1510
|
+
if (lang === 'vi') {
|
|
1511
|
+
return 'D MMM';
|
|
1512
|
+
}
|
|
1513
|
+
return 'MMM D';
|
|
1514
|
+
case 'dmy':
|
|
1515
|
+
if (lang === 'vi') {
|
|
1516
|
+
return 'D MMM, YYYY';
|
|
1517
|
+
}
|
|
1518
|
+
return 'MMM D, YYYY';
|
|
1519
|
+
case 'dmy hm':
|
|
1520
|
+
if (lang === 'vi') {
|
|
1521
|
+
return 'D MMM, YYYY HH:mm';
|
|
1522
|
+
}
|
|
1523
|
+
return 'MMM D, YYYY HH:mm';
|
|
1524
|
+
case 'my':
|
|
1525
|
+
if (lang === 'vi') {
|
|
1526
|
+
return 'MMM, YYYY';
|
|
1527
|
+
}
|
|
1528
|
+
return 'MMM YYYY ';
|
|
1529
|
+
case 'dmy hms':
|
|
1530
|
+
if (lang === 'vi') {
|
|
1531
|
+
return 'D MMM, YYYY HH:mm:ss';
|
|
1532
|
+
}
|
|
1533
|
+
return 'MMM D, YYYY HH:mm:ss';
|
|
1534
|
+
case 'hh:mm':
|
|
1535
|
+
case 'HH:mm':
|
|
1536
|
+
return 'HH:mm';
|
|
1537
|
+
case 'hh:mm A':
|
|
1538
|
+
case 'HH:mm A':
|
|
1539
|
+
return 'HH:mm A';
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
|
|
1543
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1544
|
+
/**Các hàm tương tự thư viện lodash */
|
|
1545
|
+
/**
|
|
1546
|
+
* Kiểm tra xem một giá trị có phải là null hoặc undefined hay không
|
|
1547
|
+
* @param value Giá trị cần kiểm tra
|
|
1548
|
+
* @returns true nếu giá trị là null hoặc undefined, false nếu không
|
|
1549
|
+
* @example
|
|
1550
|
+
* isNil(null); // true
|
|
1551
|
+
* isNil(undefined); // true
|
|
1552
|
+
* isNil(0); // false
|
|
1553
|
+
* isNil('hello'); // false
|
|
1554
|
+
*/
|
|
1555
|
+
const isNil = (value) => {
|
|
1556
|
+
return value === null || value === undefined;
|
|
1557
|
+
};
|
|
1558
|
+
/**
|
|
1559
|
+
* Kiểm tra xem một giá trị có phải là rỗng hay không
|
|
1560
|
+
* @param value Giá trị cần kiểm tra
|
|
1561
|
+
* @returns true nếu giá trị là null, rỗng hoặc undefined, false nếu không
|
|
1562
|
+
* @example
|
|
1563
|
+
* isEmpty(null); // true
|
|
1564
|
+
* isEmpty(''); // true
|
|
1565
|
+
* isEmpty(undefined); // true
|
|
1566
|
+
* isEmpty({}); // true
|
|
1567
|
+
* isEmpty([]); // true
|
|
1568
|
+
* isEmpty([1, 2, 3]); // false
|
|
1569
|
+
* isEmpty({ a: 1 }); // false
|
|
1570
|
+
*/
|
|
1571
|
+
const isEmpty = (value) => {
|
|
1572
|
+
while (isSignal(value)) {
|
|
1573
|
+
value = value();
|
|
1574
|
+
}
|
|
1575
|
+
return value === null || value === '' || value === undefined || (typeof value === 'object' && (JSON.stringify(value) === '{}' || JSON.stringify(value) === '[]'));
|
|
1576
|
+
};
|
|
1577
|
+
/**
|
|
1578
|
+
* Loại bỏ các thuộc tính của đối tượng dựa trên một hàm điều kiện
|
|
1579
|
+
* @param objData Đối tượng cần xử lý
|
|
1580
|
+
* @param predicate Hàm điều kiện để kiểm tra giá trị của thuộc tính. Nếu hàm trả về true thì thuộc tính đó sẽ bị loại bỏ
|
|
1581
|
+
* @returns Đối tượng mới sau khi đã loại bỏ các thuộc tính thỏa mãn điều kiện
|
|
1582
|
+
* @example
|
|
1583
|
+
* const obj = { a: 1, b: null, c: 3, d: undefined };
|
|
1584
|
+
* omitBy(obj, isNil); // { a: 1, c: 3 }
|
|
1585
|
+
*/
|
|
1586
|
+
const omitBy = (objData, predicate) => {
|
|
1587
|
+
if (!objData || typeof objData !== 'object' || Array.isArray(objData)) {
|
|
1588
|
+
return objData;
|
|
1589
|
+
}
|
|
1590
|
+
const newObj = {};
|
|
1591
|
+
Object.keys(objData).forEach((key) => {
|
|
1592
|
+
const valueOfKey = get(objData, key);
|
|
1593
|
+
if (!predicate(valueOfKey)) {
|
|
1594
|
+
set(newObj, key, valueOfKey);
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
return newObj;
|
|
1598
|
+
};
|
|
1599
|
+
/**
|
|
1600
|
+
* Lấy giá trị từ đối tượng theo đường dẫn chỉ định
|
|
1601
|
+
*
|
|
1602
|
+
* Hàm này giúp bạn truy cập vào các thuộc tính sâu bên trong một đối tượng một cách an toàn,
|
|
1603
|
+
* tránh lỗi khi thuộc tính không tồn tại.
|
|
1604
|
+
*
|
|
1605
|
+
* @param obj Đối tượng nguồn cần lấy giá trị
|
|
1606
|
+
* @param path Đường dẫn đến thuộc tính cần lấy. Có thể là:
|
|
1607
|
+
* - Chuỗi: 'user.profile.name' hoặc 'items[0].title'
|
|
1608
|
+
* - Mảng: ['user', 'profile', 'name'] hoặc ['items', '0', 'title']
|
|
1609
|
+
* - Chuỗi rỗng '': trả về chính đối tượng gốc
|
|
1610
|
+
* @param defaultValue Giá trị mặc định trả về khi không tìm thấy thuộc tính (mặc định: undefined)
|
|
1611
|
+
* @param keepLastValueIfSignal Có giữ nguyên signal cuối cùng hay không (mặc định: false - sẽ gọi signal())
|
|
1612
|
+
* @returns Giá trị tìm được hoặc giá trị mặc định
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* // Ví dụ cơ bản
|
|
1616
|
+
* const user = { name: 'John', age: 30 };
|
|
1617
|
+
* get(user, 'name'); // 'John'
|
|
1618
|
+
* get(user, 'email'); // undefined
|
|
1619
|
+
* get(user, 'email', 'no-email'); // 'no-email'
|
|
1620
|
+
*
|
|
1621
|
+
* @example
|
|
1622
|
+
* // Truyền path rỗng - trả về chính đối tượng gốc
|
|
1623
|
+
* const data = { name: 'Alice', age: 25 };
|
|
1624
|
+
* get(data, ''); // { name: 'Alice', age: 25 } (chính đối tượng data)
|
|
1625
|
+
* get(data, '', 'default'); // { name: 'Alice', age: 25 } (bỏ qua defaultValue)
|
|
1626
|
+
*
|
|
1627
|
+
* @example
|
|
1628
|
+
* // Truy cập thuộc tính sâu
|
|
1629
|
+
* const data = {
|
|
1630
|
+
* user: {
|
|
1631
|
+
* profile: {
|
|
1632
|
+
* name: 'Alice',
|
|
1633
|
+
* settings: { theme: 'dark' }
|
|
1634
|
+
* }
|
|
1635
|
+
* }
|
|
1636
|
+
* };
|
|
1637
|
+
* get(data, 'user.profile.name'); // 'Alice'
|
|
1638
|
+
* get(data, 'user.profile.settings.theme'); // 'dark'
|
|
1639
|
+
* get(data, 'user.profile.avatar', 'default.jpg'); // 'default.jpg'
|
|
1640
|
+
*
|
|
1641
|
+
* @example
|
|
1642
|
+
* // Truy cập mảng
|
|
1643
|
+
* const items = [
|
|
1644
|
+
* { name: 'Item 1', price: 100 },
|
|
1645
|
+
* { name: 'Item 2', price: 200 }
|
|
1646
|
+
* ];
|
|
1647
|
+
* get(items, '[0].name'); // 'Item 1'
|
|
1648
|
+
* get(items, '[1].name'); // 'Item 2'
|
|
1649
|
+
* get(items, '[2].name', 'Not found'); // 'Not found'
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* // Sử dụng với mảng path
|
|
1653
|
+
* const nested = { a: { b: { c: 'deep value' } } };
|
|
1654
|
+
* get(nested, ['a', 'b', 'c']); // 'deep value'
|
|
1655
|
+
* get(nested, ['a', 'b', 'd'], 'default'); // 'default'
|
|
1656
|
+
*
|
|
1657
|
+
* @example
|
|
1658
|
+
* // Trường hợp đặc biệt
|
|
1659
|
+
* get(null, 'any.path'); // undefined
|
|
1660
|
+
* get(undefined, 'any.path', 'fallback'); // 'fallback'
|
|
1661
|
+
*/
|
|
1662
|
+
const get = (obj, path, defaultValue, keepLastValueIfSignal) => {
|
|
1663
|
+
if (isNil(obj)) {
|
|
1664
|
+
return defaultValue;
|
|
1665
|
+
}
|
|
1666
|
+
while (isSignal(obj)) {
|
|
1667
|
+
obj = obj();
|
|
1668
|
+
}
|
|
1669
|
+
if (path === '') {
|
|
1670
|
+
return obj;
|
|
1671
|
+
}
|
|
1672
|
+
if (obj instanceof HttpParams) {
|
|
1673
|
+
return (obj.get(`${path}`) ?? defaultValue);
|
|
1674
|
+
}
|
|
1675
|
+
if (obj instanceof DOMRect) {
|
|
1676
|
+
return obj[path];
|
|
1677
|
+
}
|
|
1678
|
+
const paths = Array.isArray(path)
|
|
1679
|
+
? path
|
|
1680
|
+
: path
|
|
1681
|
+
.replace(/\[(\d+)]/g, '.$1')
|
|
1682
|
+
.split('.')
|
|
1683
|
+
.filter((key) => key);
|
|
1684
|
+
for (const index in paths) {
|
|
1685
|
+
const key = paths[index];
|
|
1686
|
+
if (obj instanceof CSSStyleDeclaration) {
|
|
1687
|
+
obj = obj[key];
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
if (obj instanceof HTMLElement) {
|
|
1691
|
+
obj = obj[key];
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1694
|
+
if (isNil(obj) || !Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1695
|
+
return defaultValue;
|
|
1696
|
+
}
|
|
1697
|
+
const val = isSignal(obj[key]) && !keepLastValueIfSignal ? obj[key]() : obj[key];
|
|
1698
|
+
obj = val;
|
|
1699
|
+
}
|
|
1700
|
+
return obj;
|
|
1701
|
+
};
|
|
1702
|
+
/**
|
|
1703
|
+
* Thiết lập giá trị cho một thuộc tính trong đối tượng theo đường dẫn chỉ định
|
|
1704
|
+
* @param obj Đối tượng cần thiết lập giá trị
|
|
1705
|
+
* @param path Đường dẫn đến thuộc tính, có thể là chuỗi (vd: 'a.b.c') hoặc mảng (vd: ['a', 'b', 'c'])
|
|
1706
|
+
* @param value Giá trị cần thiết lập
|
|
1707
|
+
* @returns Đối tượng sau khi đã thiết lập giá trị
|
|
1708
|
+
* @throws Error nếu tham số đầu tiên không phải là đối tượng
|
|
1709
|
+
* @example
|
|
1710
|
+
* const obj = { a: { b: 1 } };
|
|
1711
|
+
* set(obj, 'a.b', 2); // { a: { b: 2 } }
|
|
1712
|
+
*/
|
|
1713
|
+
const set = (obj, path, value) => {
|
|
1714
|
+
if (!obj || (typeof obj !== 'object' && !isSignal(obj)) || (isSignal(obj) && typeof obj() !== 'object')) {
|
|
1715
|
+
throw new Error('The first argument must be an object');
|
|
1716
|
+
}
|
|
1717
|
+
if (obj instanceof HttpParams) {
|
|
1718
|
+
return obj.set(`${path}`, value);
|
|
1719
|
+
}
|
|
1720
|
+
const pathArray = Array.isArray(path)
|
|
1721
|
+
? path
|
|
1722
|
+
: path
|
|
1723
|
+
.replace(/\[(\d+)]/g, '.[$1]')
|
|
1724
|
+
.split('.')
|
|
1725
|
+
.filter((key) => key);
|
|
1726
|
+
let currentObjectByKey = isSignal(obj) ? obj() : obj;
|
|
1727
|
+
let preObjectByKey = obj;
|
|
1728
|
+
pathArray.forEach((key, index) => {
|
|
1729
|
+
if (index < pathArray.length - 1) {
|
|
1730
|
+
if (!(key in currentObjectByKey) || (typeof currentObjectByKey[key] !== 'object' && !isSignal(currentObjectByKey[key]))) {
|
|
1731
|
+
const nextKey = pathArray[index + 1];
|
|
1732
|
+
key = key.replace(/\[(\d+)]/g, '$1');
|
|
1733
|
+
currentObjectByKey[key] = /\[(\d+)]/g.test(nextKey) ? [] : {};
|
|
1734
|
+
}
|
|
1735
|
+
currentObjectByKey = key ? currentObjectByKey[key] : currentObjectByKey;
|
|
1736
|
+
preObjectByKey = currentObjectByKey;
|
|
1737
|
+
currentObjectByKey = isSignal(currentObjectByKey) ? currentObjectByKey() : currentObjectByKey;
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
if (typeof currentObjectByKey !== 'object') {
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
// Gán giá trị ở cuối đường dẫn
|
|
1744
|
+
key = key.replace(/\[(\d+)]/g, '$1');
|
|
1745
|
+
const valueOfKey = currentObjectByKey[key];
|
|
1746
|
+
const valueOfKeyIsSignal = isSignal(valueOfKey);
|
|
1747
|
+
if (valueOfKeyIsSignal && (typeof valueOfKey() !== 'object' || valueOfKey() === null)) {
|
|
1748
|
+
valueOfKey.set(isSignal(value) ? value() : value);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (isSignal(preObjectByKey)) {
|
|
1752
|
+
preObjectByKey.update((data) => {
|
|
1753
|
+
const dataOfKey = data[key];
|
|
1754
|
+
if (isNil(dataOfKey) || !valueOfKeyIsSignal) {
|
|
1755
|
+
data[key] = value;
|
|
1756
|
+
}
|
|
1757
|
+
if (valueOfKeyIsSignal) {
|
|
1758
|
+
valueOfKey.set(isSignal(value) ? value() : value);
|
|
1759
|
+
}
|
|
1760
|
+
if (Array.isArray(data)) {
|
|
1761
|
+
return [...data];
|
|
1762
|
+
}
|
|
1763
|
+
return { ...data };
|
|
1764
|
+
});
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
if (valueOfKeyIsSignal) {
|
|
1768
|
+
valueOfKey.set(isSignal(value) ? value() : value);
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
currentObjectByKey[key] = value;
|
|
1772
|
+
});
|
|
1773
|
+
return obj;
|
|
1774
|
+
};
|
|
1775
|
+
/**
|
|
1776
|
+
* Tạo một bản sao sâu của một đối tượng hoặc giá trị bất kỳ
|
|
1777
|
+
* @param data Dữ liệu cần sao chép
|
|
1778
|
+
* @param options Tùy chọn cấu hình
|
|
1779
|
+
* @param options.ignoreSignal Nếu true, sẽ không sao chép các signal mà trả về signal gốc
|
|
1780
|
+
* @param seen WeakMap để theo dõi các tham chiếu đã được sao chép, tránh lặp vô hạn với các tham chiếu vòng
|
|
1781
|
+
* @returns Bản sao sâu của dữ liệu đầu vào
|
|
1782
|
+
* @example
|
|
1783
|
+
* const obj = {
|
|
1784
|
+
* a: 1,
|
|
1785
|
+
* b: { c: 2 },
|
|
1786
|
+
* d: [1, 2, 3]
|
|
1787
|
+
* };
|
|
1788
|
+
* const clone = cloneDeep(obj);
|
|
1789
|
+
* // clone là một bản sao hoàn toàn độc lập của obj
|
|
1790
|
+
*/
|
|
1791
|
+
const cloneDeep = (data, options = { ignoreSignal: false }, seen = new WeakMap()) => {
|
|
1792
|
+
if (data === null || (typeof data !== 'object' && !isSignal(data))) {
|
|
1793
|
+
return data;
|
|
1794
|
+
}
|
|
1795
|
+
if (seen.has(data)) {
|
|
1796
|
+
return seen.get(data);
|
|
1797
|
+
}
|
|
1798
|
+
if (data instanceof HttpParams) {
|
|
1799
|
+
return new UtilsHttpParamsRequest(undefined, data);
|
|
1800
|
+
}
|
|
1801
|
+
if (data instanceof Date) {
|
|
1802
|
+
return new Date(data.getTime());
|
|
1803
|
+
}
|
|
1804
|
+
if (dayjs.isDayjs(data)) {
|
|
1805
|
+
return getDayjs({ date: data.valueOf() });
|
|
1806
|
+
}
|
|
1807
|
+
if (data instanceof RegExp) {
|
|
1808
|
+
return new RegExp(data.source, data.flags);
|
|
1809
|
+
}
|
|
1810
|
+
if (data instanceof Map) {
|
|
1811
|
+
const mapCopy = new Map();
|
|
1812
|
+
seen.set(data, mapCopy);
|
|
1813
|
+
data.forEach((val, key) => {
|
|
1814
|
+
mapCopy.set(cloneDeep(key, options, seen), cloneDeep(val, options, seen));
|
|
1815
|
+
});
|
|
1816
|
+
return mapCopy;
|
|
1817
|
+
}
|
|
1818
|
+
if (data instanceof Set) {
|
|
1819
|
+
const setCopy = new Set();
|
|
1820
|
+
seen.set(data, setCopy);
|
|
1821
|
+
data.forEach((val) => {
|
|
1822
|
+
setCopy.add(cloneDeep(val, options, seen));
|
|
1823
|
+
});
|
|
1824
|
+
return setCopy;
|
|
1825
|
+
}
|
|
1826
|
+
if (Array.isArray(data)) {
|
|
1827
|
+
seen.set(data, data.map((item) => cloneDeep(item, options, seen)));
|
|
1828
|
+
return seen.get(data);
|
|
1829
|
+
}
|
|
1830
|
+
if (data instanceof File || data instanceof Blob || Object.prototype.toString.call(data) === '[object File]') {
|
|
1831
|
+
return data;
|
|
1832
|
+
}
|
|
1833
|
+
if (data instanceof TemplateRef || data instanceof ElementRef || data instanceof Element || data instanceof Promise || data instanceof Observable) {
|
|
1834
|
+
return data;
|
|
1835
|
+
}
|
|
1836
|
+
if (isSignal(data)) {
|
|
1837
|
+
if (options?.ignoreSignal) {
|
|
1838
|
+
return data;
|
|
1839
|
+
}
|
|
1840
|
+
seen.set(data, signal(cloneDeep(data(), options, seen)));
|
|
1841
|
+
return seen.get(data);
|
|
1842
|
+
}
|
|
1843
|
+
const result = {};
|
|
1844
|
+
seen.set(data, result);
|
|
1845
|
+
for (const key in data) {
|
|
1846
|
+
const value = data[key];
|
|
1847
|
+
if (value instanceof HttpParams) {
|
|
1848
|
+
result[key] = new UtilsHttpParamsRequest(undefined, value);
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1851
|
+
if (dayjs.isDayjs(value)) {
|
|
1852
|
+
result[key] = getDayjs({ date: value.valueOf() });
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
if (value instanceof TemplateRef || value instanceof ElementRef || value instanceof Element || value instanceof Promise || value instanceof Observable) {
|
|
1856
|
+
result[key] = value;
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
1860
|
+
result[key] = cloneDeep(value, options, seen);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return result;
|
|
1864
|
+
};
|
|
1865
|
+
/**
|
|
1866
|
+
* Chuyển đổi một mảng các đối tượng thành một đối tượng với khóa được chỉ định
|
|
1867
|
+
* @param data Mảng các đối tượng cần chuyển đổi
|
|
1868
|
+
* @param key Tên thuộc tính được sử dụng làm khóa trong đối tượng kết quả
|
|
1869
|
+
* @returns Đối tượng với các giá trị từ mảng được đánh key theo thuộc tính đã chỉ định
|
|
1870
|
+
* @example
|
|
1871
|
+
* const data = [
|
|
1872
|
+
* { id: 1, name: 'John' },
|
|
1873
|
+
* { id: 2, name: 'Jane' }
|
|
1874
|
+
* ];
|
|
1875
|
+
* keyBy(data, 'id');
|
|
1876
|
+
* // Kết quả: {
|
|
1877
|
+
* // '1': { id: 1, name: 'John' },
|
|
1878
|
+
* // '2': { id: 2, name: 'Jane' }
|
|
1879
|
+
* // }
|
|
1880
|
+
*/
|
|
1881
|
+
const keyBy = (data, key) => {
|
|
1882
|
+
if (!data || !data.length || !key) {
|
|
1883
|
+
return {};
|
|
1884
|
+
}
|
|
1885
|
+
return data.reduce((dir, nextItem) => {
|
|
1886
|
+
const valueOfKey = get(nextItem, key);
|
|
1887
|
+
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
|
|
1888
|
+
return dir;
|
|
1889
|
+
}
|
|
1890
|
+
if (!Object.keys(dir).includes(`${valueOfKey}`)) {
|
|
1891
|
+
dir[`${valueOfKey}`] = nextItem;
|
|
1892
|
+
}
|
|
1893
|
+
return dir;
|
|
1894
|
+
}, {});
|
|
1895
|
+
};
|
|
1896
|
+
/**
|
|
1897
|
+
* Nhóm các đối tượng trong một mảng thành các nhóm dựa trên một thuộc tính cụ thể
|
|
1898
|
+
* @param data Mảng các đối tượng cần nhóm
|
|
1899
|
+
* @param key Tên thuộc tính được sử dụng làm khóa nhóm
|
|
1900
|
+
* @returns Đối tượng với các giá trị từ mảng được nhóm theo thuộc tính đã chỉ định
|
|
1901
|
+
* @example
|
|
1902
|
+
* const data = [
|
|
1903
|
+
* { id: 1, name: 'John' },
|
|
1904
|
+
* { id: 2, name: 'Jane' },
|
|
1905
|
+
* { id: 1, name: 'John' }
|
|
1906
|
+
* ];
|
|
1907
|
+
* groupBy(data, 'id');
|
|
1908
|
+
* // Kết quả: {
|
|
1909
|
+
* // '1': [
|
|
1910
|
+
* // { id: 1, name: 'John' },
|
|
1911
|
+
* // { id: 1, name: 'John' }
|
|
1912
|
+
* // ],
|
|
1913
|
+
* // '2': [
|
|
1914
|
+
* // { id: 2, name: 'Jane' }
|
|
1915
|
+
* // }
|
|
1916
|
+
*/
|
|
1917
|
+
const groupBy = (data, key) => {
|
|
1918
|
+
if (!data || !data.length || !Object.keys(get(data, '0')).length || !key) {
|
|
1919
|
+
return {};
|
|
1920
|
+
}
|
|
1921
|
+
return data.reduce((dir, nextItem) => {
|
|
1922
|
+
const valueOfKey = get(nextItem, key);
|
|
1923
|
+
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number' && typeof valueOfKey !== 'boolean') {
|
|
1924
|
+
return dir;
|
|
1925
|
+
}
|
|
1926
|
+
if (!Object.keys(dir).includes(`${valueOfKey}`)) {
|
|
1927
|
+
dir[`${valueOfKey}`] = [];
|
|
1928
|
+
}
|
|
1929
|
+
dir[`${valueOfKey}`].push(nextItem);
|
|
1930
|
+
return dir;
|
|
1931
|
+
}, {});
|
|
1932
|
+
};
|
|
1933
|
+
/**
|
|
1934
|
+
* Tạo một mảng các số từ giá trị bắt đầu đến giá trị kết thúc với bước nhảy tùy chọn
|
|
1935
|
+
* @param start Giá trị bắt đầu của dãy số. Nếu chỉ có một tham số, đây sẽ là giá trị kết thúc và giá trị bắt đầu sẽ là 0
|
|
1936
|
+
* @param end Giá trị kết thúc của dãy số (tùy chọn)
|
|
1937
|
+
* @param step Bước nhảy giữa các số trong dãy (tùy chọn). Mặc định là 1 nếu end > start, -1 nếu end < start
|
|
1938
|
+
* @returns Mảng các số từ start đến end với bước nhảy step
|
|
1939
|
+
* @example
|
|
1940
|
+
* range(4); // [0, 1, 2, 3]
|
|
1941
|
+
* range(1, 5); // [1, 2, 3, 4]
|
|
1942
|
+
* range(0, 20, 5); // [0, 5, 10, 15]
|
|
1943
|
+
* range(5, 2); // [5, 4, 3]
|
|
1944
|
+
*/
|
|
1945
|
+
const range = (start, end, step) => {
|
|
1946
|
+
if (end === undefined || end === null) {
|
|
1947
|
+
end = start;
|
|
1948
|
+
start = 0;
|
|
1949
|
+
}
|
|
1950
|
+
if (!step) {
|
|
1951
|
+
step = end < 0 ? -1 : 1;
|
|
1952
|
+
}
|
|
1953
|
+
if (end < start && step > 0) {
|
|
1954
|
+
step *= -1;
|
|
1955
|
+
}
|
|
1956
|
+
const valueStartByStep = step + start;
|
|
1957
|
+
const direction = start < end ? 'asc' : 'desc';
|
|
1958
|
+
if ((direction === 'asc' && (valueStartByStep < start || valueStartByStep > end)) || (direction === 'desc' && (valueStartByStep > start || valueStartByStep < end))) {
|
|
1959
|
+
return [start];
|
|
1960
|
+
}
|
|
1961
|
+
const arr = new Array();
|
|
1962
|
+
for (let index = 0; index < Math.abs(end - start); index++) {
|
|
1963
|
+
let value = start + index * step;
|
|
1964
|
+
if (index === 0) {
|
|
1965
|
+
value = start;
|
|
1966
|
+
}
|
|
1967
|
+
if ((direction === 'asc' && (value < start || value > end)) || (direction === 'desc' && (value > start || value < end))) {
|
|
1968
|
+
return arr;
|
|
1969
|
+
}
|
|
1970
|
+
arr.push(value);
|
|
1971
|
+
}
|
|
1972
|
+
return arr;
|
|
1973
|
+
};
|
|
1974
|
+
/**
|
|
1975
|
+
* So sánh hai giá trị bất kỳ có bằng nhau hay không
|
|
1976
|
+
* @param value1 Giá trị thứ nhất cần so sánh
|
|
1977
|
+
* @param value2 Giá trị thứ hai cần so sánh
|
|
1978
|
+
* @param exactlyPosition Có so sánh chính xác vị trí các phần tử trong mảng hay không
|
|
1979
|
+
* @returns true nếu hai giá trị bằng nhau, false nếu không bằng nhau
|
|
1980
|
+
* @example
|
|
1981
|
+
* isEqual([1,2,3], [1,2,3]); // true
|
|
1982
|
+
* isEqual([1,2,3], [3,2,1]); // true khi exactlyPosition = false
|
|
1983
|
+
* isEqual([1,2,3], [3,2,1]); // false khi exactlyPosition = true
|
|
1984
|
+
* isEqual({a:1}, {a:1}); // true
|
|
1985
|
+
*/
|
|
1986
|
+
const isEqual = (value1, value2, options) => {
|
|
1987
|
+
const { exactlyPosition = false, ignoreExactlyDataType = false } = options || {};
|
|
1988
|
+
if (value1 === value2 || (value1 === null && value2 === null) || (value1 === undefined && value2 === undefined)) {
|
|
1989
|
+
return true;
|
|
1990
|
+
}
|
|
1991
|
+
if (ignoreExactlyDataType) {
|
|
1992
|
+
return isEqual(isNil(value1) ? undefined : `${value1}`, isNil(value2) ? undefined : `${value2}`);
|
|
1993
|
+
}
|
|
1994
|
+
// Handle signals
|
|
1995
|
+
while (isSignal(value1)) {
|
|
1996
|
+
value1 = value1();
|
|
1997
|
+
}
|
|
1998
|
+
while (isSignal(value2)) {
|
|
1999
|
+
value2 = value2();
|
|
2000
|
+
}
|
|
2001
|
+
if (typeof value1 !== 'object' || typeof value2 !== 'object' || (Array.isArray(value1) && !Array.isArray(value2)) || (!Array.isArray(value1) && Array.isArray(value2))) {
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
if (Array.isArray(value1)) {
|
|
2005
|
+
if (value1.length !== value2.length) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
if (!exactlyPosition) {
|
|
2009
|
+
return !value1.some((item) => !value2.includes(item));
|
|
2010
|
+
}
|
|
2011
|
+
return !value1.some((item, index) => !isEqual(item, value2[index], options));
|
|
2012
|
+
}
|
|
2013
|
+
if (Object.keys(value1).length !== Object.keys(value2).length) {
|
|
2014
|
+
return false;
|
|
2015
|
+
}
|
|
2016
|
+
return !Object.keys(value1).some((key) => !isEqual(value1[key], value2[key], options));
|
|
2017
|
+
};
|
|
2018
|
+
/**
|
|
2019
|
+
* Loại bỏ các phần tử trùng lặp trong mảng dựa trên một thuộc tính chỉ định
|
|
2020
|
+
* @param data Mảng dữ liệu cần xử lý
|
|
2021
|
+
* @param key Tên thuộc tính dùng để so sánh trùng lặp. Nếu không có key thì so sánh trực tiếp giá trị
|
|
2022
|
+
* @returns Mảng mới chứa các phần tử không trùng lặp
|
|
2023
|
+
* @example
|
|
2024
|
+
* const arr = [
|
|
2025
|
+
* { id: 1, name: 'A' },
|
|
2026
|
+
* { id: 2, name: 'B' },
|
|
2027
|
+
* { id: 1, name: 'C' }
|
|
2028
|
+
* ];
|
|
2029
|
+
* uniqBy(arr, 'id'); // [{ id: 1, name: 'A' }, { id: 2, name: 'B' }]
|
|
2030
|
+
*
|
|
2031
|
+
* const numbers = [1, 2, 2, 3, 3];
|
|
2032
|
+
* uniqBy(numbers); // [1, 2, 3]
|
|
2033
|
+
*
|
|
2034
|
+
* const numbersSignal = [signal(1), signal(2), signal(3), signal(2), signal(5), signal(4), signal(1), signal(6), signal(7), signal(6)];
|
|
2035
|
+
* uniqBy(numbersSignal); // [signal(1), signal(2), signal(3), signal(5), signal(4), signal(6), signal(7)]
|
|
2036
|
+
*/
|
|
2037
|
+
const uniqBy = (data, key) => {
|
|
2038
|
+
if (!key || !data?.length || typeof get(data, '0') !== 'object') {
|
|
2039
|
+
// Xử lý mảng chứa signal values
|
|
2040
|
+
if (data[0] && isSignal(data[0])) {
|
|
2041
|
+
const seen = new Set();
|
|
2042
|
+
return data.filter((item) => {
|
|
2043
|
+
const value = `${get(item, '')}`;
|
|
2044
|
+
if (seen.has(value)) {
|
|
2045
|
+
return false;
|
|
2046
|
+
}
|
|
2047
|
+
seen.add(value);
|
|
2048
|
+
return true;
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
// Xử lý mảng primitive values
|
|
2052
|
+
return Array.from(new Set(data));
|
|
2053
|
+
}
|
|
2054
|
+
const dataUnique = keyBy(data, key);
|
|
2055
|
+
return Object.keys(dataUnique).map((key) => dataUnique[key]);
|
|
2056
|
+
};
|
|
2057
|
+
const generateInterface = (obj, interfaceName) => {
|
|
2058
|
+
const generateType = (value) => {
|
|
2059
|
+
if (value === null) {
|
|
2060
|
+
return 'null';
|
|
2061
|
+
}
|
|
2062
|
+
const type = typeof value;
|
|
2063
|
+
if (type === 'string') {
|
|
2064
|
+
return 'string';
|
|
2065
|
+
}
|
|
2066
|
+
if (type === 'number') {
|
|
2067
|
+
return 'number';
|
|
2068
|
+
}
|
|
2069
|
+
if (type === 'boolean') {
|
|
2070
|
+
return 'boolean';
|
|
2071
|
+
}
|
|
2072
|
+
if (type === 'undefined') {
|
|
2073
|
+
return 'any';
|
|
2074
|
+
}
|
|
2075
|
+
if (value instanceof Date) {
|
|
2076
|
+
return 'Date';
|
|
2077
|
+
}
|
|
2078
|
+
if (value instanceof RegExp) {
|
|
2079
|
+
return 'RegExp';
|
|
2080
|
+
}
|
|
2081
|
+
if (Array.isArray(value)) {
|
|
2082
|
+
if (value.length === 0) {
|
|
2083
|
+
return 'Array<any>';
|
|
2084
|
+
}
|
|
2085
|
+
return `Array<${generateType(value[0])}>`;
|
|
2086
|
+
}
|
|
2087
|
+
if (type === 'object') {
|
|
2088
|
+
let interfaceStr = '{\n';
|
|
2089
|
+
for (const key in value) {
|
|
2090
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
2091
|
+
const valueType = generateType(value[key]);
|
|
2092
|
+
interfaceStr += ` ${key}: ${valueType};\n`;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
interfaceStr += '}';
|
|
2096
|
+
return interfaceStr;
|
|
2097
|
+
}
|
|
2098
|
+
return 'any';
|
|
2099
|
+
};
|
|
2100
|
+
const interfaceStr = `interface ${interfaceName} ${generateType(obj)}`;
|
|
2101
|
+
return interfaceStr;
|
|
2102
|
+
};
|
|
2103
|
+
|
|
2104
|
+
;
|
|
2105
|
+
const step = 20;
|
|
2106
|
+
const percent = 0.05;
|
|
2107
|
+
const colorStepContrastFromOrigin = (color, stepNumber) => {
|
|
2108
|
+
return colorContrastFromOrigin(color).find(item => item.step === stepNumber);
|
|
2109
|
+
};
|
|
2110
|
+
const colorContrastFromOrigin = (color) => {
|
|
2111
|
+
const parsedColorsArray = parseColorValues(color);
|
|
2112
|
+
const colors = [];
|
|
2113
|
+
let calculatedShades = [];
|
|
2114
|
+
let calculatedTints = [];
|
|
2115
|
+
if (parsedColorsArray !== null) {
|
|
2116
|
+
for (let i = 0; i < parsedColorsArray.length; i++) {
|
|
2117
|
+
calculatedShades = calculateShades(parsedColorsArray[i]);
|
|
2118
|
+
calculatedTints = calculateTints(parsedColorsArray[i]);
|
|
2119
|
+
}
|
|
2120
|
+
for (let i = 0; i <= step; i++) {
|
|
2121
|
+
colors.push({ step: i * 5, label: `${i * 5}%`, dark: `#${calculatedShades[i]}`, light: `#${calculatedTints[i]}` });
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
return colors;
|
|
2125
|
+
};
|
|
2126
|
+
const parseColorValues = (colorValues) => {
|
|
2127
|
+
let colorValuesArray = colorValues.match(/\b[0-9A-Fa-f]{3}\b|[0-9A-Fa-f]{6}\b/g);
|
|
2128
|
+
if (colorValuesArray) {
|
|
2129
|
+
colorValuesArray = colorValuesArray.map((item) => {
|
|
2130
|
+
if (item.length === 3) {
|
|
2131
|
+
let newItem = item.toString().split('');
|
|
2132
|
+
newItem = newItem.reduce((acc, it) => {
|
|
2133
|
+
return acc + it + it;
|
|
2134
|
+
}, '');
|
|
2135
|
+
return newItem;
|
|
2136
|
+
}
|
|
2137
|
+
return item;
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
return colorValuesArray;
|
|
2141
|
+
};
|
|
2142
|
+
const calculateShades = (colorValue) => {
|
|
2143
|
+
return calculate(colorValue, rgbShade).concat("000000");
|
|
2144
|
+
};
|
|
2145
|
+
const calculateTints = (colorValue) => {
|
|
2146
|
+
return calculate(colorValue, rgbTint).concat("ffffff");
|
|
2147
|
+
};
|
|
2148
|
+
const calculate = (colorValue, shadeOrTint) => {
|
|
2149
|
+
const color = hexToRGB(colorValue);
|
|
2150
|
+
const shadeValues = [];
|
|
2151
|
+
for (let i = 0; i < step; i++) {
|
|
2152
|
+
shadeValues[i] = rgbToHex(shadeOrTint(color, i));
|
|
2153
|
+
}
|
|
2154
|
+
return shadeValues;
|
|
2155
|
+
};
|
|
2156
|
+
const rgbShade = (rgb, i) => { return { red: rgb.red * (1 - percent * i), green: rgb.green * (1 - percent * i), blue: rgb.blue * (1 - percent * i) }; };
|
|
2157
|
+
const rgbTint = (rgb, i) => { return { red: rgb.red + (255 - rgb.red) * i * percent, green: rgb.green + (255 - rgb.green) * i * percent, blue: rgb.blue + (255 - rgb.blue) * i * percent }; };
|
|
2158
|
+
const rgbToHex = (rgb) => { return intToHex(rgb.red) + intToHex(rgb.green) + intToHex(rgb.blue); };
|
|
2159
|
+
const hexToRGB = (colorValue) => { return { red: parseInt(colorValue.substr(0, 2), 16), green: parseInt(colorValue.substr(2, 2), 16), blue: parseInt(colorValue.substr(4, 2), 16) }; };
|
|
2160
|
+
const intToHex = (rgbint) => { return pad(Math.min(Math.max(Math.round(rgbint), 0), 255).toString(16), 2); };
|
|
2161
|
+
const pad = (number, length) => {
|
|
2162
|
+
let str = '' + number;
|
|
2163
|
+
while (str.length < length) {
|
|
2164
|
+
str = '0' + str;
|
|
2165
|
+
}
|
|
2166
|
+
return str;
|
|
2167
|
+
};
|
|
2168
|
+
const listColorDefine = ['#E62222', '#B81B1B', '#EB4E4E', '#F97316', '#C75C12', '#FA8F45', '#FFB700', '#CC9200', '#FFC533', '#84CC16', '#6AA312', '#9dd645', '#00BC62', '#00A757', '#33DA8A', '#06B6D4', '#1B59C4', '#4E8CF7', '#0EA5E9',
|
|
2169
|
+
'#1B59C4', '#4E8CF7', '#226FF5', '#1B59C4', '#4E8CF7', '#6366F1', '#4F52C1', '#8285F4', '#5B04B3', '#49038F', '#7C36C2', '#D946EF', '#AE38BF', '#E16BF2', '#EC4899', '#BD3A7A', '#F06DAD', '#F43F5E', '#C3324B', '#F6657E', '#757380', '#5E5C66', '#918F99',
|
|
2170
|
+
'#202020', '#1A1A1A', '#4D4D4D'
|
|
2171
|
+
];
|
|
2172
|
+
const getColorById = (str) => {
|
|
2173
|
+
let hashString = 0;
|
|
2174
|
+
if (!str) {
|
|
2175
|
+
return '';
|
|
2176
|
+
}
|
|
2177
|
+
for (let i = 0; i < str.length; i++) {
|
|
2178
|
+
const char = str.charCodeAt(i);
|
|
2179
|
+
hashString = ((hashString << 5) - hashString) + char;
|
|
2180
|
+
hashString = hashString & hashString;
|
|
2181
|
+
}
|
|
2182
|
+
return listColorDefine[Math.abs(hashString) % listColorDefine.length];
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
class UtilsKeyCodeConstant {
|
|
2186
|
+
static MAC_ENTER = 3;
|
|
2187
|
+
static BACKSPACE = 8;
|
|
2188
|
+
static TAB = 9;
|
|
2189
|
+
static NUM_CENTER = 12;
|
|
2190
|
+
static ENTER = 13;
|
|
2191
|
+
static SHIFT = 16;
|
|
2192
|
+
static CONTROL = 17;
|
|
2193
|
+
static ALT = 18;
|
|
2194
|
+
static PAUSE = 19;
|
|
2195
|
+
static CAPS_LOCK = 20;
|
|
2196
|
+
static ESCAPE = 27;
|
|
2197
|
+
static SPACE = 32;
|
|
2198
|
+
static PAGE_UP = 33;
|
|
2199
|
+
static PAGE_DOWN = 34;
|
|
2200
|
+
static END = 35;
|
|
2201
|
+
static HOME = 36;
|
|
2202
|
+
static LEFT_ARROW = 37;
|
|
2203
|
+
static UP_ARROW = 38;
|
|
2204
|
+
static RIGHT_ARROW = 39;
|
|
2205
|
+
static DOWN_ARROW = 40;
|
|
2206
|
+
static PLUS_SIGN = 43;
|
|
2207
|
+
static PRINT_SCREEN = 44;
|
|
2208
|
+
static INSERT = 45;
|
|
2209
|
+
static DELETE = 46;
|
|
2210
|
+
static ZERO = 48;
|
|
2211
|
+
static ONE = 49;
|
|
2212
|
+
static TWO = 50;
|
|
2213
|
+
static THREE = 51;
|
|
2214
|
+
static FOUR = 52;
|
|
2215
|
+
static FIVE = 53;
|
|
2216
|
+
static SIX = 54;
|
|
2217
|
+
static SEVEN = 55;
|
|
2218
|
+
static EIGHT = 56;
|
|
2219
|
+
static NINE = 57;
|
|
2220
|
+
static FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
2221
|
+
static FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
2222
|
+
static QUESTION_MARK = 63;
|
|
2223
|
+
static AT_SIGN = 64;
|
|
2224
|
+
static A = 65;
|
|
2225
|
+
static B = 66;
|
|
2226
|
+
static C = 67;
|
|
2227
|
+
static D = 68;
|
|
2228
|
+
static E = 69;
|
|
2229
|
+
static F = 70;
|
|
2230
|
+
static G = 71;
|
|
2231
|
+
static H = 72;
|
|
2232
|
+
static I = 73;
|
|
2233
|
+
static J = 74;
|
|
2234
|
+
static K = 75;
|
|
2235
|
+
static L = 76;
|
|
2236
|
+
static M = 77;
|
|
2237
|
+
static N = 78;
|
|
2238
|
+
static O = 79;
|
|
2239
|
+
static P = 80;
|
|
2240
|
+
static Q = 81;
|
|
2241
|
+
static R = 82;
|
|
2242
|
+
static S = 83;
|
|
2243
|
+
static T = 84;
|
|
2244
|
+
static U = 85;
|
|
2245
|
+
static V = 86;
|
|
2246
|
+
static W = 87;
|
|
2247
|
+
static X = 88;
|
|
2248
|
+
static Y = 89;
|
|
2249
|
+
static Z = 90;
|
|
2250
|
+
static META = 91; // WIN_KEY_LEFT
|
|
2251
|
+
static MAC_WK_CMD_LEFT = 91;
|
|
2252
|
+
static MAC_WK_CMD_RIGHT = 93;
|
|
2253
|
+
static CONTEXT_MENU = 93;
|
|
2254
|
+
static NUMPAD_ZERO = 96;
|
|
2255
|
+
static NUMPAD_ONE = 97;
|
|
2256
|
+
static NUMPAD_TWO = 98;
|
|
2257
|
+
static NUMPAD_THREE = 99;
|
|
2258
|
+
static NUMPAD_FOUR = 100;
|
|
2259
|
+
static NUMPAD_FIVE = 101;
|
|
2260
|
+
static NUMPAD_SIX = 102;
|
|
2261
|
+
static NUMPAD_SEVEN = 103;
|
|
2262
|
+
static NUMPAD_EIGHT = 104;
|
|
2263
|
+
static NUMPAD_NINE = 105;
|
|
2264
|
+
static NUMPAD_MULTIPLY = 106;
|
|
2265
|
+
static NUMPAD_PLUS = 107;
|
|
2266
|
+
static NUMPAD_MINUS = 109;
|
|
2267
|
+
static NUMPAD_PERIOD = 110;
|
|
2268
|
+
static NUMPAD_DIVIDE = 111;
|
|
2269
|
+
static F1 = 112;
|
|
2270
|
+
static F2 = 113;
|
|
2271
|
+
static F3 = 114;
|
|
2272
|
+
static F4 = 115;
|
|
2273
|
+
static F5 = 116;
|
|
2274
|
+
static F6 = 117;
|
|
2275
|
+
static F7 = 118;
|
|
2276
|
+
static F8 = 119;
|
|
2277
|
+
static F9 = 120;
|
|
2278
|
+
static F10 = 121;
|
|
2279
|
+
static F11 = 122;
|
|
2280
|
+
static F12 = 123;
|
|
2281
|
+
static NUM_LOCK = 144;
|
|
2282
|
+
static SCROLL_LOCK = 145;
|
|
2283
|
+
static FIRST_MEDIA = 166;
|
|
2284
|
+
static FF_MINUS = 173;
|
|
2285
|
+
static MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
2286
|
+
static VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
2287
|
+
static VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
2288
|
+
static FF_MUTE = 181;
|
|
2289
|
+
static FF_VOLUME_DOWN = 182;
|
|
2290
|
+
static LAST_MEDIA = 183;
|
|
2291
|
+
static FF_VOLUME_UP = 183;
|
|
2292
|
+
static SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
2293
|
+
static EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
2294
|
+
static COMMA = 188;
|
|
2295
|
+
static DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
2296
|
+
static PERIOD = 190;
|
|
2297
|
+
static SLASH = 191;
|
|
2298
|
+
static APOSTROPHE = 192;
|
|
2299
|
+
static TILDE = 192;
|
|
2300
|
+
static OPEN_SQUARE_BRACKET = 219;
|
|
2301
|
+
static BACKSLASH = 220;
|
|
2302
|
+
static CLOSE_SQUARE_BRACKET = 221;
|
|
2303
|
+
static SINGLE_QUOTE = 222;
|
|
2304
|
+
static MAC_META = 224;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2308
|
+
const getKeyCacheByArrayObject = (keyCache, argumentsValue) => {
|
|
2309
|
+
if (!keyCache || !argumentsValue) {
|
|
2310
|
+
return '';
|
|
2311
|
+
}
|
|
2312
|
+
let keyBuild = `${JSON.stringify(argumentsValue.slice(1))}-key-cache-plus`;
|
|
2313
|
+
argumentsValue.forEach((item) => {
|
|
2314
|
+
if (typeof item === 'object') {
|
|
2315
|
+
if (Array.isArray(item)) {
|
|
2316
|
+
keyBuild = `${keyBuild}${JSON.stringify(item)}`;
|
|
1311
2317
|
return;
|
|
1312
2318
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
static DeleteKeyStartWithAsync(keyCacheStartWith, isKeyMD5 = false) {
|
|
1320
|
-
return new Promise(async (resolve) => {
|
|
1321
|
-
const objectStore = await UtilsCache.getObjectStore();
|
|
1322
|
-
if (!objectStore) {
|
|
1323
|
-
return resolve({});
|
|
1324
|
-
}
|
|
1325
|
-
const index = objectStore.index(UtilsCache.itemIndexByKey);
|
|
1326
|
-
// Lấy tất cả các keys từ index
|
|
1327
|
-
const request = index.getAll();
|
|
1328
|
-
keyCacheStartWith = isKeyMD5 ? keyCacheStartWith : md5(keyCacheStartWith);
|
|
1329
|
-
request.onsuccess = (e) => {
|
|
1330
|
-
const data = e.target.result;
|
|
1331
|
-
if (!Array.isArray(data)) {
|
|
1332
|
-
return resolve({});
|
|
2319
|
+
const keys = (item instanceof HttpParams ? item.keys() : Object.keys(item)).sort(((str1, str2) => str1.localeCompare(str2)));
|
|
2320
|
+
keys.forEach(key => {
|
|
2321
|
+
if (key.toLocaleLowerCase() === 'pem') {
|
|
2322
|
+
return;
|
|
1333
2323
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
UtilsCache.ClearAsync(obj.key, true);
|
|
1337
|
-
}
|
|
1338
|
-
});
|
|
1339
|
-
return resolve({});
|
|
1340
|
-
};
|
|
1341
|
-
request.onerror = () => {
|
|
1342
|
-
return resolve({});
|
|
1343
|
-
};
|
|
1344
|
-
});
|
|
1345
|
-
}
|
|
1346
|
-
static DeleteKeyStartWith(keyCache, isMD5 = false) {
|
|
1347
|
-
keyCache = isMD5 ? md5(keyCache) : keyCache;
|
|
1348
|
-
const keys = Object.keys(UtilsCache.LocalStorage);
|
|
1349
|
-
if (!keys || !keys.length) {
|
|
2324
|
+
keyBuild = `${keyBuild}${JSON.stringify(get(item, key))}`;
|
|
2325
|
+
});
|
|
1350
2326
|
return;
|
|
1351
2327
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
}
|
|
2328
|
+
keyBuild = `${keyBuild}${item}`;
|
|
2329
|
+
});
|
|
2330
|
+
const keyCachePlus = md5(keyBuild);
|
|
2331
|
+
const keyCacheMD5 = md5(keyCache);
|
|
2332
|
+
return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
|
|
2333
|
+
};
|
|
1359
2334
|
|
|
1360
2335
|
const formatNumber = (value) => {
|
|
1361
2336
|
const lang = UtilsCache.getLang();
|
|
@@ -1417,138 +2392,521 @@ const viewDataNumberByLanguage = (value, acceptNegativeValue, parseFixed = 1, ig
|
|
|
1417
2392
|
}
|
|
1418
2393
|
};
|
|
1419
2394
|
|
|
1420
|
-
|
|
1421
|
-
return
|
|
2395
|
+
let functionXssFilter = async (value) => {
|
|
2396
|
+
return value;
|
|
1422
2397
|
};
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
return
|
|
2398
|
+
const updateFunctionXssFilter = (functionCustom) => {
|
|
2399
|
+
functionXssFilter = functionCustom;
|
|
2400
|
+
};
|
|
2401
|
+
const xssFilter = async (data) => {
|
|
2402
|
+
return await functionXssFilter(data);
|
|
1428
2403
|
};
|
|
1429
2404
|
|
|
1430
|
-
|
|
1431
|
-
|
|
2405
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2406
|
+
/**
|
|
2407
|
+
* Chuyển đổi một đối tượng hoặc giá trị thành signal
|
|
2408
|
+
* @param data Dữ liệu cần chuyển đổi thành signal
|
|
2409
|
+
* @param cloneDeepIfNotSignal Có thực hiện sao chép sâu dữ liệu trước khi chuyển đổi hay không nếu data không phải signal. Mặc định là true.
|
|
2410
|
+
* Đặt false nếu muốn giữ nguyên tham chiếu đến dữ liệu gốc.
|
|
2411
|
+
* Nếu muốn sao chép sâu đối tượng signal thì đặt cloneDeepIfNotSignal là true và acceptConvertObjectInnerWritableSignal là true.
|
|
2412
|
+
* @param isSignalPrimitiveType Có chuyển đổi các giá trị nguyên thủy (string, number, boolean) thành signal hay không. Mặc định là false.
|
|
2413
|
+
* Đặt true nếu muốn bọc các giá trị nguyên thủy trong signal.
|
|
2414
|
+
* @param acceptConvertObjectInnerWritableSignal Có tiếp tục tìm kiếm và chuyển đổi các đối tượng bên trong signal hay không. Mặc định là false.
|
|
2415
|
+
* Đặt true nếu muốn tìm và chuyển đổi các đối tượng bên trong signal hoặc chính nó thành signal mới.
|
|
2416
|
+
* @returns Dữ liệu đã được chuyển đổi theo kiểu T
|
|
2417
|
+
*/
|
|
2418
|
+
const convertObjectToSignal = (data, cloneDeepIfNotSignal = true, isSignalPrimitiveType = false, acceptConvertObjectInnerWritableSignal = false, seen = new WeakMap()) => {
|
|
2419
|
+
if ((data === null || typeof data !== 'object') && !isSignal(data)) {
|
|
2420
|
+
return (isSignalPrimitiveType ? signal(data) : data);
|
|
2421
|
+
}
|
|
2422
|
+
if (seen.has(data)) {
|
|
2423
|
+
return seen.get(data);
|
|
2424
|
+
}
|
|
2425
|
+
if (isSignal(data)) {
|
|
2426
|
+
if (!acceptConvertObjectInnerWritableSignal) {
|
|
2427
|
+
return data;
|
|
2428
|
+
}
|
|
2429
|
+
seen.set(data, convertObjectToSignal(data(), cloneDeepIfNotSignal, isSignalPrimitiveType, acceptConvertObjectInnerWritableSignal, seen));
|
|
2430
|
+
return seen.get(data);
|
|
2431
|
+
}
|
|
2432
|
+
if (Array.isArray(data)) {
|
|
2433
|
+
seen.set(data, signal(data.map(item => convertObjectToSignal(item, cloneDeepIfNotSignal, isSignalPrimitiveType, acceptConvertObjectInnerWritableSignal, seen))));
|
|
2434
|
+
return seen.get(data);
|
|
2435
|
+
}
|
|
2436
|
+
if (data instanceof Map) {
|
|
2437
|
+
const mapCopy = new Map();
|
|
2438
|
+
Array.from(data.keys()).forEach(key => {
|
|
2439
|
+
mapCopy.set(key, convertObjectToSignal(data.get(key), cloneDeepIfNotSignal, isSignalPrimitiveType, acceptConvertObjectInnerWritableSignal));
|
|
2440
|
+
});
|
|
2441
|
+
seen.set(data, signal(mapCopy));
|
|
2442
|
+
return seen.get(data);
|
|
2443
|
+
}
|
|
2444
|
+
if (data instanceof Promise || data instanceof Observable) {
|
|
1432
2445
|
return data;
|
|
1433
2446
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
.
|
|
1439
|
-
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
|
|
1447
|
-
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
|
|
1448
|
-
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
|
|
1449
|
-
str = str.replace(/đ/g, 'd');
|
|
1450
|
-
str = str.replace(/À|Á|Ạ|Ả|Ã|Â|Ầ|Ấ|Ậ|Ẩ|Ẫ|Ă|Ằ|Ắ|Ặ|Ẳ|Ẵ/g, 'A');
|
|
1451
|
-
str = str.replace(/È|É|Ẹ|Ẻ|Ẽ|Ê|Ề|Ế|Ệ|Ể|Ễ/g, 'E');
|
|
1452
|
-
str = str.replace(/Ì|Í|Ị|Ỉ|Ĩ/g, 'I');
|
|
1453
|
-
str = str.replace(/Ò|Ó|Ọ|Ỏ|Õ|Ô|Ồ|Ố|Ộ|Ổ|Ỗ|Ơ|Ờ|Ớ|Ợ|Ở|Ỡ/g, 'O');
|
|
1454
|
-
str = str.replace(/Ù|Ú|Ụ|Ủ|Ũ|Ư|Ừ|Ứ|Ự|Ử|Ữ/g, 'U');
|
|
1455
|
-
str = str.replace(/Ỳ|Ý|Ỵ|Ỷ|Ỹ/g, 'Y');
|
|
1456
|
-
str = str.replace(/Đ/g, 'D');
|
|
1457
|
-
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); // ̀ ́ ̃ ̉ ̣ huyền, sắc, ngã, hỏi, nặng
|
|
1458
|
-
str = str.replace(/\u02C6|\u0306|\u031B/g, ""); // ˆ ̆ ̛ Â, Ê, Ă, Ơ, Ư
|
|
1459
|
-
return str.normalize();
|
|
2447
|
+
data = signal(cloneDeepIfNotSignal ? { ...data } : data);
|
|
2448
|
+
seen.set(data, data);
|
|
2449
|
+
for (const key in data()) {
|
|
2450
|
+
const value = data()[key];
|
|
2451
|
+
if (value instanceof TemplateRef || value instanceof File || value instanceof Blob || Object.prototype.toString.call(value) === '[object File]' || value instanceof ElementRef || value instanceof Element || value instanceof Date || value instanceof RegExp || value instanceof Set || value instanceof Map || value instanceof Promise || value instanceof Observable || value instanceof HttpParams) {
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
if (Object.prototype.hasOwnProperty.call(data(), key)) {
|
|
2455
|
+
data()[key] = convertObjectToSignal(value, cloneDeepIfNotSignal, isSignalPrimitiveType, acceptConvertObjectInnerWritableSignal);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
return data;
|
|
1460
2459
|
};
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
2460
|
+
const convertSignalToObject = (data, isCloneDeep = true, seen = new WeakMap()) => {
|
|
2461
|
+
let ignoreCheckSeenHasDataAfterWhile = false;
|
|
2462
|
+
while (isSignal(data) && !seen.has(data)) {
|
|
2463
|
+
const dataConvert = data();
|
|
2464
|
+
if (typeof dataConvert === 'object') {
|
|
2465
|
+
if (dataConvert === null) {
|
|
2466
|
+
return dataConvert;
|
|
2467
|
+
}
|
|
2468
|
+
seen.set(dataConvert, dataConvert);
|
|
2469
|
+
data = dataConvert;
|
|
2470
|
+
ignoreCheckSeenHasDataAfterWhile = true;
|
|
2471
|
+
break;
|
|
2472
|
+
}
|
|
2473
|
+
seen.set(data, dataConvert);
|
|
2474
|
+
data = dataConvert;
|
|
1470
2475
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
hashString = ((hashString << 5) - hashString) + char;
|
|
1474
|
-
hashString = hashString & hashString;
|
|
2476
|
+
if (data === null || typeof data === 'function' || typeof data !== 'object') {
|
|
2477
|
+
return data;
|
|
1475
2478
|
}
|
|
1476
|
-
|
|
2479
|
+
if (!ignoreCheckSeenHasDataAfterWhile && seen.has(data)) {
|
|
2480
|
+
return seen.get(data);
|
|
2481
|
+
}
|
|
2482
|
+
if (Array.isArray(data)) {
|
|
2483
|
+
if (!isSignal(data[0])) {
|
|
2484
|
+
return data;
|
|
2485
|
+
}
|
|
2486
|
+
seen.set(data, data.map(item => convertSignalToObject((isCloneDeep ? cloneDeep(item) : item), isCloneDeep, seen)));
|
|
2487
|
+
return seen.get(data);
|
|
2488
|
+
}
|
|
2489
|
+
if (data instanceof Map) {
|
|
2490
|
+
const mapCopy = new Map();
|
|
2491
|
+
Array.from(data.keys()).forEach(key => {
|
|
2492
|
+
mapCopy.set(key, convertSignalToObject(isCloneDeep ? cloneDeep(data.get(key)) : data.get(key), isCloneDeep));
|
|
2493
|
+
});
|
|
2494
|
+
seen.set(data, mapCopy);
|
|
2495
|
+
return seen.get(data);
|
|
2496
|
+
}
|
|
2497
|
+
if (data instanceof File || data instanceof Blob || Object.prototype.toString.call(data) === '[object File]') {
|
|
2498
|
+
return data;
|
|
2499
|
+
}
|
|
2500
|
+
if (data instanceof TemplateRef || data instanceof ElementRef || data instanceof Element || data instanceof Promise || data instanceof Observable || data instanceof HttpParams) {
|
|
2501
|
+
return data;
|
|
2502
|
+
}
|
|
2503
|
+
const result = isCloneDeep ? {} : data;
|
|
2504
|
+
seen.set(data, result);
|
|
2505
|
+
for (const key in data) {
|
|
2506
|
+
const value = data[key];
|
|
2507
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
2508
|
+
result[key] = convertSignalToObject(isCloneDeep ? cloneDeep(value) : value, isCloneDeep);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
return result;
|
|
1477
2512
|
};
|
|
1478
2513
|
|
|
1479
|
-
const
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
return /^[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]@[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]\.[A-z0-9]+[A-z0-9\_\.\+\-]*[A-z0-9]$/;
|
|
1490
|
-
};
|
|
1491
|
-
const PATTERN_URL = () => {
|
|
1492
|
-
return /^(http|https|ftp):(\/){2}[^\s]+[.]{1}[^\s]+$/;
|
|
1493
|
-
};
|
|
1494
|
-
const PATTERN_HOST_URL = () => {
|
|
1495
|
-
return /^((https|http|ftp):[/]{2}[^/\s]+)/;
|
|
1496
|
-
};
|
|
1497
|
-
const PATTERN_DOMAIN = () => {
|
|
1498
|
-
return /^([a-zA-Z0-9])(([a-z0-9-]{1,61})?[a-z0-9]{1})?(\.[a-z0-9](([a-z0-9-]{1,61})?[a-z0-9]{1})?)?(\.[a-zA-Z]{2,4})+$/;
|
|
1499
|
-
};
|
|
1500
|
-
const PATTERN_MOBILE_PHONE = () => {
|
|
1501
|
-
return /^(\+?84|0|84)([0-9]{9})$/;
|
|
1502
|
-
};
|
|
1503
|
-
const PATTERN_PHONE = () => {
|
|
1504
|
-
return /^(\+?84|[0-9]|84)([0-9]{2,9})$/;
|
|
1505
|
-
};
|
|
1506
|
-
const PATTERN_PHONE_NORMAL = () => {
|
|
1507
|
-
return /^(\+?84|[0-9]|84)([0-9]{9,10})$/;
|
|
1508
|
-
};
|
|
1509
|
-
const PATTERN_NUMBER = () => {
|
|
1510
|
-
return /\d+/g;
|
|
2514
|
+
const ENCODE_URI_PATTERN = /%([0-9A-F]{2})/g;
|
|
2515
|
+
const decodeURI = (value) => {
|
|
2516
|
+
return decodeURIComponent(value.split('').map((c) => {
|
|
2517
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
2518
|
+
}).join(''));
|
|
2519
|
+
};
|
|
2520
|
+
const encodeURI = (value) => {
|
|
2521
|
+
return encodeURIComponent(value).replace(ENCODE_URI_PATTERN, (match, p1) => {
|
|
2522
|
+
return String.fromCharCode(parseInt(p1, 16));
|
|
2523
|
+
});
|
|
1511
2524
|
};
|
|
1512
|
-
const
|
|
1513
|
-
|
|
2525
|
+
const endCodeUrl = (params, isBody) => {
|
|
2526
|
+
params = omitBy(params, param => param === '' || isNil(param));
|
|
2527
|
+
let res = '';
|
|
2528
|
+
for (const p in params) {
|
|
2529
|
+
res += `&${p}=${encodeURIComponent(params[p])}`;
|
|
2530
|
+
}
|
|
2531
|
+
return res === '' ? '' : `${isBody ? '' : '?'}${res.substring(1)}`;
|
|
1514
2532
|
};
|
|
1515
|
-
|
|
1516
|
-
|
|
2533
|
+
|
|
2534
|
+
const base64Encode = (value) => {
|
|
2535
|
+
return btoa(encodeURI(value));
|
|
2536
|
+
};
|
|
2537
|
+
const base64Decode = (value) => {
|
|
2538
|
+
return decodeURI(atob(value));
|
|
2539
|
+
};
|
|
2540
|
+
const convertBase64ToBlob = (dataBase64) => {
|
|
2541
|
+
const BASE64_MARKER = ';base64,';
|
|
2542
|
+
if (dataBase64.indexOf(BASE64_MARKER) === -1) {
|
|
2543
|
+
const parts = dataBase64.split(',');
|
|
2544
|
+
const contentType = parts[0].split(':')[1];
|
|
2545
|
+
const raw = parts[1];
|
|
2546
|
+
const blob = new Blob([raw], { type: contentType });
|
|
2547
|
+
return blob;
|
|
2548
|
+
}
|
|
2549
|
+
const parts = dataBase64.split(BASE64_MARKER);
|
|
2550
|
+
const contentType = parts[0].split(':')[1];
|
|
2551
|
+
const raw = window.atob(parts[1]);
|
|
2552
|
+
const rawLength = raw.length;
|
|
2553
|
+
const uInt8Array = new Uint8Array(rawLength);
|
|
2554
|
+
for (let i = 0; i < rawLength; ++i) {
|
|
2555
|
+
uInt8Array[i] = raw.charCodeAt(i);
|
|
2556
|
+
}
|
|
2557
|
+
const blob = new Blob([uInt8Array], { type: contentType });
|
|
2558
|
+
return blob;
|
|
2559
|
+
};
|
|
2560
|
+
const convertFileToBase64_ObjectUrl = async (file) => {
|
|
2561
|
+
if (!file.type.match(/image.*/)) {
|
|
2562
|
+
return URL.createObjectURL(file);
|
|
2563
|
+
}
|
|
2564
|
+
return await convertFileToBase64(file);
|
|
2565
|
+
};
|
|
2566
|
+
const convertFileToBase64 = (file) => {
|
|
2567
|
+
return new Promise((resolve) => {
|
|
2568
|
+
const reader = new FileReader();
|
|
2569
|
+
reader.readAsDataURL(file);
|
|
2570
|
+
reader.onload = (event) => {
|
|
2571
|
+
resolve(event.target?.result ?? '');
|
|
2572
|
+
};
|
|
2573
|
+
});
|
|
1517
2574
|
};
|
|
1518
|
-
|
|
1519
|
-
|
|
2575
|
+
|
|
2576
|
+
const downloadFileByUrlUseXmlRequest = (fileUrl, filename) => {
|
|
2577
|
+
const xmlRequest = new XMLHttpRequest();
|
|
2578
|
+
xmlRequest.open('GET', fileUrl, true);
|
|
2579
|
+
xmlRequest.responseType = 'blob';
|
|
2580
|
+
xmlRequest.onload = () => {
|
|
2581
|
+
const url = window.URL.createObjectURL(xmlRequest.response);
|
|
2582
|
+
downloadFileByUrl(url, filename);
|
|
2583
|
+
};
|
|
2584
|
+
xmlRequest.send();
|
|
2585
|
+
};
|
|
2586
|
+
const downloadFileByUrl = async (fileUrl, filename, onlyOpen) => {
|
|
2587
|
+
const downloadFileElement = document.createElement('a');
|
|
2588
|
+
if (!onlyOpen) {
|
|
2589
|
+
const response = await fetch(fileUrl);
|
|
2590
|
+
if (response.ok) {
|
|
2591
|
+
const blob = await response.blob();
|
|
2592
|
+
fileUrl = URL.createObjectURL(blob);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
downloadFileElement.href = fileUrl;
|
|
2596
|
+
downloadFileElement.download = filename;
|
|
2597
|
+
downloadFileElement.target = '_blank';
|
|
2598
|
+
downloadFileElement.click();
|
|
2599
|
+
downloadFileElement.remove();
|
|
2600
|
+
};
|
|
2601
|
+
const downloadImageFromELement = (imageElement, typeFileDownload, nameFile) => {
|
|
2602
|
+
const parentElement = imageElement?.src;
|
|
2603
|
+
const blobData = convertBase64ToBlob(parentElement);
|
|
2604
|
+
const blob = new Blob([blobData], { type: typeFileDownload || "image/png" });
|
|
2605
|
+
const url = window.URL.createObjectURL(blob);
|
|
2606
|
+
const link = document.createElement('a');
|
|
2607
|
+
link.href = url;
|
|
2608
|
+
link.download = nameFile || 'barcode';
|
|
2609
|
+
link.click();
|
|
1520
2610
|
};
|
|
1521
|
-
|
|
1522
|
-
|
|
2611
|
+
|
|
2612
|
+
const LINK_IMAGE_ERROR_TOKEN_INJECT = new InjectionToken('LINK_IMAGE_ERROR_TOKEN_INJECT');
|
|
2613
|
+
const PROCESS_BAR_STANDARD_CONFIG_DEFAULT_TOKEN_INJECT = new InjectionToken('PROCESS_BAR_STANDARD_CONFIG_DEFAULT_TOKEN_INJECT');
|
|
2614
|
+
const PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT = new InjectionToken('PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT');
|
|
2615
|
+
|
|
2616
|
+
const isTypeImage = (file) => file.type.match(/image.*/) ? true : false;
|
|
2617
|
+
const isTypeVideo = (file) => file.type.match(/video.*/) ? true : false;
|
|
2618
|
+
const isTypeAudio = (file) => file.type.match(/audio.*/) ? true : false;
|
|
2619
|
+
const isTypeFile = (file) => file instanceof File || Object.prototype.toString.call(file) === '[object File]' ? true : false;
|
|
2620
|
+
const ExcelExtList = ['xls', 'xlsx', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
|
|
2621
|
+
const DocumentExtList = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'json', 'xml', 'application/msword',
|
|
2622
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
2623
|
+
'application/vnd.ms-excel',
|
|
2624
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
2625
|
+
'application/vnd.ms-powerpoint',
|
|
2626
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
2627
|
+
'application/pdf',
|
|
2628
|
+
'application/json',
|
|
2629
|
+
'application/xml'];
|
|
2630
|
+
const isIncludeDocumentExtList = (ext, listExt = DocumentExtList) => listExt.includes(ext) || listExt.includes(`application/${ext}`);
|
|
2631
|
+
const ImageExtList = ['gif', 'jpg', 'jpeg', 'png', 'image/gif', 'image/jpeg', 'image/jpeg', 'image/png'];
|
|
2632
|
+
const isIncludeImageExtList = (ext, listExt = ImageExtList) => listExt.includes(ext);
|
|
2633
|
+
const VideoExtList = ['mp4', 'mov', 'mpg', 'avi', 'wmv', 'video/mp4', 'video/quicktime', 'video/mpeg', 'video/x-msvideo', 'video/x-ms-wmv'];
|
|
2634
|
+
const isIncludeVideoExtList = (ext, listExt = VideoExtList) => listExt.includes(ext);
|
|
2635
|
+
const AudioExtList = ['mp3', 'wav', 'ogg', 'aac', 'm4a', 'flac', 'wma', 'audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/aac', 'audio/mp4', 'audio/flac', 'audio/x-ms-wma'];
|
|
2636
|
+
const isIncludeAudioExtList = (ext, listExt = AudioExtList) => listExt.includes(ext);
|
|
2637
|
+
const getFileExtension = (file) => {
|
|
2638
|
+
if (file instanceof File && file.type) {
|
|
2639
|
+
return (file.type.split('/').pop() || '').toLowerCase();
|
|
2640
|
+
}
|
|
2641
|
+
file = file;
|
|
2642
|
+
if (file.mimetype || file.file?.type) {
|
|
2643
|
+
return file.mimetype || file.file?.type;
|
|
2644
|
+
}
|
|
2645
|
+
let fileName = file.name;
|
|
2646
|
+
const url = get(file, 'url') || get(file, 'origin_url');
|
|
2647
|
+
if (!fileName && !url) {
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (!fileName && url) {
|
|
2651
|
+
fileName = url.split('/').pop() || '';
|
|
2652
|
+
set(file, 'name', fileName);
|
|
2653
|
+
}
|
|
2654
|
+
const dots = fileName.split(".");
|
|
2655
|
+
if (!dots) {
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
return (dots.pop() || '').toLowerCase();
|
|
1523
2659
|
};
|
|
1524
|
-
const
|
|
1525
|
-
|
|
2660
|
+
const getLabelBySizeFile = (size, toFixed = 2) => {
|
|
2661
|
+
if (size < 1024 * 1024) {
|
|
2662
|
+
return ` ${(size / 1024).toFixed(toFixed)} KB`;
|
|
2663
|
+
}
|
|
2664
|
+
return ` ${(size / (1024 * 1024)).toFixed(toFixed)} MB`;
|
|
1526
2665
|
};
|
|
1527
|
-
const
|
|
1528
|
-
|
|
2666
|
+
const convertBlobToFile = (blob, fileName) => {
|
|
2667
|
+
const type = blob.type.split('/')[1];
|
|
2668
|
+
const name = fileName ? `${fileName.split('.')[0]}.${type}` : `${uuid()}.${type}`;
|
|
2669
|
+
return new File([blob], name, { type: blob.type, lastModified: Date.now() });
|
|
1529
2670
|
};
|
|
1530
|
-
const
|
|
1531
|
-
return
|
|
2671
|
+
const convertUrlToFile = (url, fileName) => {
|
|
2672
|
+
return new Promise((resolve, reject) => {
|
|
2673
|
+
const xhr = new XMLHttpRequest();
|
|
2674
|
+
xhr.onload = function () {
|
|
2675
|
+
const reader = new FileReader();
|
|
2676
|
+
reader.onloadend = () => {
|
|
2677
|
+
const file = convertBase64ToBlob(reader.result);
|
|
2678
|
+
resolve(convertBlobToFile(file, fileName || Date.now().toLocaleString()));
|
|
2679
|
+
};
|
|
2680
|
+
reader.onerror = () => resolve(undefined);
|
|
2681
|
+
reader.readAsDataURL(xhr.response);
|
|
2682
|
+
};
|
|
2683
|
+
xhr.onerror = (err) => {
|
|
2684
|
+
reject(err);
|
|
2685
|
+
};
|
|
2686
|
+
xhr.open('GET', url);
|
|
2687
|
+
xhr.responseType = 'blob';
|
|
2688
|
+
xhr.send();
|
|
2689
|
+
});
|
|
1532
2690
|
};
|
|
1533
|
-
|
|
1534
|
-
|
|
2691
|
+
|
|
2692
|
+
const DEFAULT_MIN_TICK_COUNT = 5;
|
|
2693
|
+
const DEFAULT_MAX_TICK_COUNT = 10;
|
|
2694
|
+
const MIN_DISTANCE_TO_ZERO = 3;
|
|
2695
|
+
const NEGATIVE_THRESHOLD = -5;
|
|
2696
|
+
const FALLBACK_NEGATIVE_VALUE = -4;
|
|
2697
|
+
const MAX_POW_NUMBER = 14;
|
|
2698
|
+
const MAX_TEMPLATE_NUMBER = 10;
|
|
2699
|
+
/**
|
|
2700
|
+
* Tính toán smart axis scale cho biểu đồ
|
|
2701
|
+
*
|
|
2702
|
+
* @param maxData - Giá trị tối đa của dữ liệu
|
|
2703
|
+
* @param options - Các tùy chọn cấu hình axis scale
|
|
2704
|
+
* @returns Cấu hình axis scale bao gồm stepSize, max, min, tickAmount
|
|
2705
|
+
*
|
|
2706
|
+
* @throws {Error} INVALID_NEGATIVE_DATA - khi maxData < 0 mà acceptNegative = false
|
|
2707
|
+
* @throws {Error} MISSING_MIN_NEGATIVE - khi acceptNegative = true nhưng thiếu minNegative
|
|
2708
|
+
* @throws {Error} INVALID_RANGE - khi maxData < minNegative
|
|
2709
|
+
* @throws {Error} INVALID_MIN_NEGATIVE - khi minNegative >= 0
|
|
2710
|
+
*
|
|
2711
|
+
* @example
|
|
2712
|
+
* ```typescript
|
|
2713
|
+
* const result = getSmartAxisScale(100, { minTickCount: 5, maxTickCount: 10 });
|
|
2714
|
+
* // returns { stepSize: 20, max: 120, min: 0, tickAmount: 6 }
|
|
2715
|
+
* ```
|
|
2716
|
+
*/
|
|
2717
|
+
const getSmartAxisScale = (originalMaxData, options) => {
|
|
2718
|
+
let maxData = originalMaxData;
|
|
2719
|
+
validateInputs(maxData, options);
|
|
2720
|
+
const minTickCount = options?.minTickCount || DEFAULT_MIN_TICK_COUNT;
|
|
2721
|
+
const maxTickCount = options?.maxTickCount || DEFAULT_MAX_TICK_COUNT;
|
|
2722
|
+
let scaleDirection = 1;
|
|
2723
|
+
let rangeDistance = maxData;
|
|
2724
|
+
if (options?.acceptNegative && !isNil(options.minNegative)) {
|
|
2725
|
+
if (maxData === 0) {
|
|
2726
|
+
maxData = -1;
|
|
2727
|
+
}
|
|
2728
|
+
if (maxData <= 0) {
|
|
2729
|
+
rangeDistance = options.minNegative;
|
|
2730
|
+
}
|
|
2731
|
+
if (rangeDistance > NEGATIVE_THRESHOLD) {
|
|
2732
|
+
rangeDistance = FALLBACK_NEGATIVE_VALUE;
|
|
2733
|
+
}
|
|
2734
|
+
if (maxData > 0) {
|
|
2735
|
+
rangeDistance = maxData + Math.abs(options.minNegative);
|
|
2736
|
+
scaleDirection = -1;
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
if (Math.abs(rangeDistance) < MIN_DISTANCE_TO_ZERO) {
|
|
2740
|
+
rangeDistance = MIN_DISTANCE_TO_ZERO;
|
|
2741
|
+
}
|
|
2742
|
+
const stepCandidates = getStepCandidates(maxData, minTickCount, options?.minNegative || 0, options?.acceptStepIsTypeFloat, options?.stepCandidates, options?.acceptNegative);
|
|
2743
|
+
for (const step of stepCandidates) {
|
|
2744
|
+
let tickCount = Math.abs(Math.ceil(rangeDistance / step)) + (scaleDirection === -1 ? 2 : 1);
|
|
2745
|
+
let maxValue = maxData <= 0 ? 0 : step * tickCount * scaleDirection;
|
|
2746
|
+
let minValue = 0;
|
|
2747
|
+
if (tickCount >= minTickCount && tickCount <= maxTickCount) {
|
|
2748
|
+
if (maxData < maxValue) {
|
|
2749
|
+
if (options?.acceptNegative) {
|
|
2750
|
+
let tick = 1;
|
|
2751
|
+
while (!isNil(options.minNegative) && tick <= tickCount && minValue >= options.minNegative) {
|
|
2752
|
+
minValue = tick * step;
|
|
2753
|
+
tick++;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
const maxValuePositive = maxValue - Math.abs(minValue);
|
|
2757
|
+
tickCount = maxValuePositive - originalMaxData > Math.abs(step) && tickCount - 1 >= minTickCount ? tickCount - 1 : tickCount;
|
|
2758
|
+
maxValue = (step * tickCount * scaleDirection) - (minValue * scaleDirection);
|
|
2759
|
+
return {
|
|
2760
|
+
stepSize: Math.abs(step),
|
|
2761
|
+
max: maxValue,
|
|
2762
|
+
min: minValue,
|
|
2763
|
+
tickAmount: tickCount
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
let step = Math.ceil(rangeDistance / minTickCount) || 1;
|
|
2769
|
+
let maxValue = step * minTickCount;
|
|
2770
|
+
if (rangeDistance === maxValue) {
|
|
2771
|
+
step = step + Math.ceil(step / 10);
|
|
2772
|
+
maxValue = step * minTickCount;
|
|
2773
|
+
}
|
|
2774
|
+
return {
|
|
2775
|
+
stepSize: Math.abs(step),
|
|
2776
|
+
max: maxValue,
|
|
2777
|
+
min: 0,
|
|
2778
|
+
tickAmount: minTickCount
|
|
2779
|
+
};
|
|
1535
2780
|
};
|
|
1536
|
-
|
|
1537
|
-
|
|
2781
|
+
// Cache cho các giá trị lũy thừa 10 để tối ưu hiệu suất
|
|
2782
|
+
const POWER_CACHE = new Map();
|
|
2783
|
+
/**
|
|
2784
|
+
* Tạo danh sách các step candidates cho việc tính toán scale
|
|
2785
|
+
* @param maxData - Giá trị dữ liệu tối đa
|
|
2786
|
+
* @param minStep - Số bước tối thiểu
|
|
2787
|
+
* @param minNegative - Giá trị âm tối thiểu
|
|
2788
|
+
* @param acceptStepIsTypeFloat - Có chấp nhận step thập phân không
|
|
2789
|
+
* @param stepCandidatesByOptions - Step candidates do người dùng cung cấp
|
|
2790
|
+
* @param includeNegativeSteps - Có bao gồm các step âm không
|
|
2791
|
+
* @returns Danh sách các step candidates
|
|
2792
|
+
*/
|
|
2793
|
+
const getStepCandidates = (maxData, minStep, minNegative, acceptStepIsTypeFloat = false, stepCandidatesByOptions, includeNegativeSteps = false) => {
|
|
2794
|
+
// Nếu có step candidates tùy chỉnh, sử dụng chúng
|
|
2795
|
+
if (stepCandidatesByOptions && stepCandidatesByOptions.length > 0) {
|
|
2796
|
+
return stepCandidatesByOptions;
|
|
2797
|
+
}
|
|
2798
|
+
const stepCandidates = new Array();
|
|
2799
|
+
const maxValueStep = Math.abs(Math.max(maxData, Math.abs(minNegative || 0))) / minStep;
|
|
2800
|
+
// Tạo step candidates dựa trên lũy thừa của 10
|
|
2801
|
+
for (let powNumber = 0; powNumber <= MAX_POW_NUMBER; powNumber++) {
|
|
2802
|
+
// Sử dụng cache để tối ưu hiệu suất
|
|
2803
|
+
if (!POWER_CACHE.has(powNumber)) {
|
|
2804
|
+
POWER_CACHE.set(powNumber, Math.pow(10, powNumber));
|
|
2805
|
+
}
|
|
2806
|
+
const powValue = POWER_CACHE.get(powNumber);
|
|
2807
|
+
for (let templateNumber = 1; templateNumber < MAX_TEMPLATE_NUMBER; templateNumber++) {
|
|
2808
|
+
// Thêm step thập phân nếu được chấp nhận
|
|
2809
|
+
if (acceptStepIsTypeFloat) {
|
|
2810
|
+
const step = powValue * (((templateNumber - 1) * 2 + 1) / 2);
|
|
2811
|
+
stepCandidates.push(step);
|
|
2812
|
+
}
|
|
2813
|
+
const step = powValue * templateNumber;
|
|
2814
|
+
stepCandidates.push(step);
|
|
2815
|
+
// Dừng khi step đã đủ lớn
|
|
2816
|
+
if (step >= maxValueStep) {
|
|
2817
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2818
|
+
return stepCandidates;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
checkAndSetNegativeSteps(stepCandidates, includeNegativeSteps, minNegative);
|
|
2823
|
+
return stepCandidates;
|
|
1538
2824
|
};
|
|
1539
|
-
|
|
1540
|
-
|
|
2825
|
+
/**
|
|
2826
|
+
* Kiểm tra và thêm các step âm vào danh sách candidates nếu cần
|
|
2827
|
+
* @param stepCandidates - Danh sách step candidates hiện tại
|
|
2828
|
+
* @param acceptNegative - Có chấp nhận giá trị âm không
|
|
2829
|
+
* @param minNegative - Giá trị âm tối thiểu
|
|
2830
|
+
*/
|
|
2831
|
+
const checkAndSetNegativeSteps = (stepCandidates, acceptNegative, minNegative) => {
|
|
2832
|
+
if (acceptNegative && minNegative < 0) {
|
|
2833
|
+
// Tạo các step âm và thêm vào đầu danh sách
|
|
2834
|
+
const negativeSteps = [...stepCandidates].map(step => -step);
|
|
2835
|
+
stepCandidates.unshift(...negativeSteps);
|
|
2836
|
+
}
|
|
1541
2837
|
};
|
|
1542
|
-
|
|
1543
|
-
|
|
2838
|
+
/**
|
|
2839
|
+
* Kiểm tra tính hợp lệ của các tham số đầu vào
|
|
2840
|
+
* @param maxData - Giá trị dữ liệu tối đa
|
|
2841
|
+
* @param options - Các tùy chọn cấu hình
|
|
2842
|
+
* @throws {Error} Khi các tham số không hợp lệ
|
|
2843
|
+
*/
|
|
2844
|
+
const validateInputs = (maxData, options) => {
|
|
2845
|
+
// Kiểm tra maxData âm khi không chấp nhận giá trị âm
|
|
2846
|
+
if (maxData < 0 && !options?.acceptNegative) {
|
|
2847
|
+
throw new Error("maxData is less than 0 and acceptNegative is false");
|
|
2848
|
+
}
|
|
2849
|
+
if (options?.acceptNegative) {
|
|
2850
|
+
// Kiểm tra minNegative có được cung cấp không
|
|
2851
|
+
if (isNil(options.minNegative)) {
|
|
2852
|
+
throw new Error("minNegative is required when acceptNegative is true");
|
|
2853
|
+
}
|
|
2854
|
+
// Kiểm tra maxData phải >= minNegative
|
|
2855
|
+
if (maxData < options.minNegative) {
|
|
2856
|
+
throw new Error("maxData is less than minNegative");
|
|
2857
|
+
}
|
|
2858
|
+
// Kiểm tra minNegative phải là số âm
|
|
2859
|
+
if (options.minNegative >= 0) {
|
|
2860
|
+
throw new Error("minNegative must be negative");
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
1544
2863
|
};
|
|
1545
|
-
|
|
1546
|
-
|
|
2864
|
+
|
|
2865
|
+
const protectString = (input) => {
|
|
2866
|
+
const xorKey = 77;
|
|
2867
|
+
const encoded = input
|
|
2868
|
+
.split('')
|
|
2869
|
+
.map((char) => String.fromCharCode(char.charCodeAt(0) ^ xorKey))
|
|
2870
|
+
.reverse()
|
|
2871
|
+
.join('');
|
|
2872
|
+
return btoa(encoded);
|
|
2873
|
+
};
|
|
2874
|
+
const revealString = (encoded) => {
|
|
2875
|
+
const xorKey = 77;
|
|
2876
|
+
const base = atob(encoded);
|
|
2877
|
+
const decoded = base
|
|
2878
|
+
.split('')
|
|
2879
|
+
.reverse()
|
|
2880
|
+
.map(c => String.fromCharCode(c.charCodeAt(0) ^ xorKey))
|
|
2881
|
+
.join('');
|
|
2882
|
+
return decoded;
|
|
2883
|
+
};
|
|
2884
|
+
const createUniqueRandomIntGenerator = (min, max) => {
|
|
2885
|
+
if (max - min + 1 < min + 10) {
|
|
2886
|
+
throw new Error(`Range too small: need at least min + 10 unique values`);
|
|
2887
|
+
}
|
|
2888
|
+
const history = [];
|
|
2889
|
+
return () => {
|
|
2890
|
+
let num;
|
|
2891
|
+
let loop = 0;
|
|
2892
|
+
do {
|
|
2893
|
+
num = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
2894
|
+
loop++;
|
|
2895
|
+
if (loop > 1000) {
|
|
2896
|
+
break;
|
|
2897
|
+
}
|
|
2898
|
+
} while (history.includes(num));
|
|
2899
|
+
history.push(num);
|
|
2900
|
+
if (history.length > 10) {
|
|
2901
|
+
history.shift();
|
|
2902
|
+
}
|
|
2903
|
+
return num;
|
|
2904
|
+
};
|
|
1547
2905
|
};
|
|
1548
2906
|
|
|
1549
2907
|
/**
|
|
1550
2908
|
* Generated bundle index. Do not edit.
|
|
1551
2909
|
*/
|
|
1552
2910
|
|
|
1553
|
-
export { CHARACTER_DATA_EMPTY, ERROR_MESSAGE_EMPTY_VALID, ERROR_MESSAGE_MAX_LENGTH, ERROR_MESSAGE_MAX_VALID, ERROR_MESSAGE_MIN_LENGTH, ERROR_MESSAGE_MIN_VALID, ERROR_MESSAGE_PATTEN_VALID,
|
|
2911
|
+
export { AudioExtList, CHARACTER_DATA_EMPTY, COMMUNICATE_MICRO_KEY_GET_ALL_MESSAGE, COMMUNICATE_MICRO_PREFIX_TYPE, DEFAULT_START_PAGE_0, DocumentExtList, ENCODE_URI_PATTERN, ERROR_MESSAGE_EMPTY_VALID, ERROR_MESSAGE_MAX_LENGTH, ERROR_MESSAGE_MAX_VALID, ERROR_MESSAGE_MIN_LENGTH, ERROR_MESSAGE_MIN_VALID, ERROR_MESSAGE_PATTEN_VALID, ExcelExtList, ImageExtList, LINK_IMAGE_ERROR_TOKEN_INJECT, PROCESS_BAR_STANDARD_CONFIG_DEFAULT_TOKEN_INJECT, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT, UtilsCache, UtilsCommunicateMicro, UtilsCommunicateMicroKeyGlobal, UtilsHttpParamsRequest, UtilsHttpParamsRequestInstance, UtilsKeyCodeConstant, UtilsLanguageConstants, UtilsUrlSearchParams, VideoExtList, base64Decode, base64Encode, capitalize, checkMouseOverInContainer, checkViewInScreen, cloneDeep, cloneIBoundingClientRect, colorContrastFromOrigin, colorStepContrastFromOrigin, convertBase64ToBlob, convertBlobToFile, convertFileToBase64, convertFileToBase64_ObjectUrl, convertHtmlToDivBlocks, convertObjectToSignal, convertSignalToObject, convertUrlToFile, createUniqueRandomIntGenerator, decodeEscapeHtml, decodeURI, decrypt, decrypt3rd, deleteUnicode, downloadFileByUrl, downloadFileByUrlUseXmlRequest, downloadImageFromELement, encodeURI, encrypt, encrypt3rd, endCodeUrl, escapeHtml, firstLetterToUpperCase, formatDate, formatNumber, formatTextCompare, fullNameFormat, generateInterface, get, getColorById, getDayjs, getDeltaFromHTML, getDeviceInfo, getDocumentByString, getDragEventByElement, getEventNameHandleClick, getFileExtension, getHTMLFromQuill, getKeyCacheByArrayObject, getLabelBySizeFile, getPlatFromBrowser, getSmartAxisScale, getViewport, groupBy, highlightByKeyword, insertContentWithRange, isDifferenceDay, isDifferenceMonth, isDifferenceYear, isEmbedFrame, isEmpty, isEqual, isIncludeAudioExtList, isIncludeDocumentExtList, isIncludeImageExtList, isIncludeVideoExtList, isNil, isTypeAudio, isTypeFile, isTypeImage, isTypeVideo, keyBy, listColorDefine, md5, omitBy, patternAccount, patternDomain, patternEmail, patternEmoji, patternEncodeUri, patternGetFieldByRuleFieldReplace, patternHostUrl, patternKey, patternMobilePhone, patternName, patternNameProfile, patternNameSpecial, patternNameUtf8, patternNumber, patternPem, patternPhone, patternPhoneNormal, patternRuleFieldReplace, patternTax, patternUrl, processPasteData, protectString, range, removeEmoji, revealString, rgbToHex, set, setCaretPosition, setDefaultTimeZone, setKeyCrypto, setKeyCrypto3rd, setStylesElement, uniqBy, updateFunctionCheckEmbedFrame, updateFunctionFormatDate, updateFunctionXssFilter, uppercaseByPosition, uuid, viewDataNumberByLanguage, xssFilter };
|
|
1554
2912
|
//# sourceMappingURL=libs-ui-utils.mjs.map
|