@libs-ui/utils 0.2.28 → 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 +404 -90
- 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 -877
- 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 -9
- 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,379 +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(data()[key]));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return data;
|
|
306
|
-
};
|
|
307
|
-
const convertSignalToObject = (data) => {
|
|
308
|
-
if (data === null || typeof data !== 'object' || !(data instanceof signal)) {
|
|
309
|
-
return data;
|
|
310
|
-
}
|
|
311
|
-
if (Array.isArray(data)) {
|
|
312
|
-
if (typeof data[0] !== 'object') {
|
|
313
|
-
return signal(data);
|
|
314
|
-
}
|
|
315
|
-
return signal(data.map(item => convertObjectToSignal(cloneDeep(item))));
|
|
316
|
-
}
|
|
317
|
-
data = signal(data);
|
|
318
|
-
for (const key in data()) {
|
|
319
|
-
const value = data()[key];
|
|
320
|
-
if (value instanceof TemplateRef || value instanceof ElementRef || value instanceof Element || value instanceof Date || value instanceof RegExp || value instanceof Set || value instanceof Map) {
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
if (Object.prototype.hasOwnProperty.call(data(), key)) {
|
|
324
|
-
data()[key] = convertObjectToSignal(cloneDeep(data()[key]));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return data;
|
|
328
|
-
};
|
|
329
|
-
const keyBy = (data, key) => {
|
|
330
|
-
if (!data || !data.length || !key) {
|
|
331
|
-
return {};
|
|
332
|
-
}
|
|
333
|
-
return data.reduce((dir, nextItem) => {
|
|
334
|
-
const valueOfKey = nextItem[key];
|
|
335
|
-
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number') {
|
|
336
|
-
return dir;
|
|
337
|
-
}
|
|
338
|
-
dir[valueOfKey] = nextItem;
|
|
339
|
-
return dir;
|
|
340
|
-
}, {});
|
|
341
|
-
};
|
|
342
|
-
const groupBy = (data, key) => {
|
|
343
|
-
if (!data || !Object.keys(data).length || !key) {
|
|
344
|
-
return {};
|
|
345
|
-
}
|
|
346
|
-
return data.reduce((dir, nextItem) => {
|
|
347
|
-
const valueOfKey = nextItem[key];
|
|
348
|
-
if (typeof valueOfKey !== 'string' && typeof valueOfKey !== 'number') {
|
|
349
|
-
return dir;
|
|
350
|
-
}
|
|
351
|
-
if (!dir[valueOfKey]) {
|
|
352
|
-
dir[valueOfKey] = [];
|
|
353
|
-
}
|
|
354
|
-
dir[valueOfKey].push(nextItem);
|
|
355
|
-
return dir;
|
|
356
|
-
}, {});
|
|
357
|
-
};
|
|
358
|
-
const range = (start, end, step) => {
|
|
359
|
-
if (end === undefined || end === null) {
|
|
360
|
-
end = start;
|
|
361
|
-
start = 0;
|
|
362
|
-
}
|
|
363
|
-
if (!step) {
|
|
364
|
-
step = end < 0 ? -1 : 1;
|
|
365
|
-
}
|
|
366
|
-
const valueStartByStep = step + start;
|
|
367
|
-
const direction = start < end ? 'asc' : 'desc';
|
|
368
|
-
if ((direction === 'asc' && (valueStartByStep < start || valueStartByStep > end)) || (direction === 'desc' && (valueStartByStep > start || valueStartByStep < end))) {
|
|
369
|
-
return [start];
|
|
370
|
-
}
|
|
371
|
-
const arr = new Array();
|
|
372
|
-
for (let index = 0; index < Math.abs(end - start); index++) {
|
|
373
|
-
let value = start + index * step;
|
|
374
|
-
if (index === 0) {
|
|
375
|
-
value = start;
|
|
376
|
-
}
|
|
377
|
-
if ((direction === 'asc' && (value < start || value > end)) || (direction === 'desc' && (value > start || value < end))) {
|
|
378
|
-
return arr;
|
|
379
|
-
}
|
|
380
|
-
arr.push(value);
|
|
381
|
-
}
|
|
382
|
-
return arr;
|
|
383
|
-
};
|
|
384
|
-
const isEqual = (value1, value2) => {
|
|
385
|
-
if (value1 === value2 || (value1 === null && value2 === null) || (value1 === undefined && value2 === undefined)) {
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
if (typeof value1 !== 'object' || typeof value2 !== 'object' || (Array.isArray(value1) && !Array.isArray(value2))) {
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
if (Array.isArray(value1)) {
|
|
392
|
-
if (value1.length !== value2.length) {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
return !value1.some((item, index) => !isEqual(item, value2[index]));
|
|
396
|
-
}
|
|
397
|
-
if (Object.keys(value1).length !== Object.keys(value2).length) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
return !Object.keys(value1).some((key) => !isEqual(value1[key], value2[key]));
|
|
401
|
-
};
|
|
402
|
-
const uniqBy = (data, key) => {
|
|
403
|
-
if (!key || !data || !data.length || typeof data[0] !== 'object') {
|
|
404
|
-
console.log('vao dauy r', !key, !data, !data.length, typeof data[0] !== 'object');
|
|
405
|
-
return Array.from(new Set(data));
|
|
406
|
-
}
|
|
407
|
-
const dataUnique = keyBy(data, key);
|
|
408
|
-
return Object.keys(dataUnique).map(key => dataUnique[key]);
|
|
409
|
-
};
|
|
410
|
-
const generateInterface = (obj, interfaceName) => {
|
|
411
|
-
const generateType = (value) => {
|
|
412
|
-
if (value === null) {
|
|
413
|
-
return 'null';
|
|
414
|
-
}
|
|
415
|
-
const type = typeof value;
|
|
416
|
-
if (type === 'string') {
|
|
417
|
-
return 'string';
|
|
418
|
-
}
|
|
419
|
-
if (type === 'number') {
|
|
420
|
-
return 'number';
|
|
421
|
-
}
|
|
422
|
-
if (type === 'boolean') {
|
|
423
|
-
return 'boolean';
|
|
424
|
-
}
|
|
425
|
-
if (type === 'undefined') {
|
|
426
|
-
return 'any';
|
|
427
|
-
}
|
|
428
|
-
if (value instanceof Date) {
|
|
429
|
-
return 'Date';
|
|
430
|
-
}
|
|
431
|
-
if (value instanceof RegExp) {
|
|
432
|
-
return 'RegExp';
|
|
433
|
-
}
|
|
434
|
-
if (Array.isArray(value)) {
|
|
435
|
-
if (value.length === 0) {
|
|
436
|
-
return 'Array<any>';
|
|
437
|
-
}
|
|
438
|
-
return `Array<${generateType(value[0])}>`;
|
|
439
|
-
}
|
|
440
|
-
if (type === 'object') {
|
|
441
|
-
let interfaceStr = '{\n';
|
|
442
|
-
for (const key in value) {
|
|
443
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
444
|
-
const valueType = generateType(value[key]);
|
|
445
|
-
interfaceStr += ` ${key}: ${valueType};\n`;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
interfaceStr += '}';
|
|
449
|
-
return interfaceStr;
|
|
450
|
-
}
|
|
451
|
-
return 'any';
|
|
452
|
-
};
|
|
453
|
-
const interfaceStr = `interface ${interfaceName} ${generateType(obj)}`;
|
|
454
|
-
return interfaceStr;
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
;
|
|
458
|
-
const step = 20;
|
|
459
|
-
const percent = 0.05;
|
|
460
|
-
const colorStepContrastFromOrigin = (color, stepNumber) => {
|
|
461
|
-
return colorContrastFromOrigin(color).find(item => item.step === stepNumber);
|
|
462
|
-
};
|
|
463
|
-
const colorContrastFromOrigin = (color) => {
|
|
464
|
-
const parsedColorsArray = parseColorValues(color);
|
|
465
|
-
const colors = [];
|
|
466
|
-
let calculatedShades = [];
|
|
467
|
-
let calculatedTints = [];
|
|
468
|
-
if (parsedColorsArray !== null) {
|
|
469
|
-
for (let i = 0; i < parsedColorsArray.length; i++) {
|
|
470
|
-
calculatedShades = calculateShades(parsedColorsArray[i]);
|
|
471
|
-
calculatedTints = calculateTints(parsedColorsArray[i]);
|
|
472
|
-
}
|
|
473
|
-
for (let i = 0; i <= step; i++) {
|
|
474
|
-
colors.push({ step: i * 5, label: `${i * 5}%`, dark: `#${calculatedShades[i]}`, light: `#${calculatedTints[i]}` });
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return colors;
|
|
478
|
-
};
|
|
479
|
-
const parseColorValues = (colorValues) => {
|
|
480
|
-
let colorValuesArray = colorValues.match(/\b[0-9A-Fa-f]{3}\b|[0-9A-Fa-f]{6}\b/g);
|
|
481
|
-
if (colorValuesArray) {
|
|
482
|
-
colorValuesArray = colorValuesArray.map((item) => {
|
|
483
|
-
if (item.length === 3) {
|
|
484
|
-
let newItem = item.toString().split('');
|
|
485
|
-
newItem = newItem.reduce((acc, it) => {
|
|
486
|
-
return acc + it + it;
|
|
487
|
-
}, '');
|
|
488
|
-
return newItem;
|
|
489
|
-
}
|
|
490
|
-
return item;
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
return colorValuesArray;
|
|
494
|
-
};
|
|
495
|
-
const calculateShades = (colorValue) => {
|
|
496
|
-
return calculate(colorValue, rgbShade).concat("000000");
|
|
497
|
-
};
|
|
498
|
-
const calculateTints = (colorValue) => {
|
|
499
|
-
return calculate(colorValue, rgbTint).concat("ffffff");
|
|
500
|
-
};
|
|
501
|
-
const calculate = (colorValue, shadeOrTint) => {
|
|
502
|
-
const color = hexToRGB(colorValue);
|
|
503
|
-
const shadeValues = [];
|
|
504
|
-
for (let i = 0; i < step; i++) {
|
|
505
|
-
shadeValues[i] = rgbToHex(shadeOrTint(color, i));
|
|
506
|
-
}
|
|
507
|
-
return shadeValues;
|
|
508
|
-
};
|
|
509
|
-
const rgbShade = (rgb, i) => { return { red: rgb.red * (1 - percent * i), green: rgb.green * (1 - percent * i), blue: rgb.blue * (1 - percent * i) }; };
|
|
510
|
-
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 }; };
|
|
511
|
-
const rgbToHex = (rgb) => { return intToHex(rgb.red) + intToHex(rgb.green) + intToHex(rgb.blue); };
|
|
512
|
-
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) }; };
|
|
513
|
-
const intToHex = (rgbint) => { return pad(Math.min(Math.max(Math.round(rgbint), 0), 255).toString(16), 2); };
|
|
514
|
-
const pad = (number, length) => {
|
|
515
|
-
let str = '' + number;
|
|
516
|
-
while (str.length < length) {
|
|
517
|
-
str = '0' + str;
|
|
518
|
-
}
|
|
519
|
-
return str;
|
|
520
|
-
};
|
|
521
|
-
const listColorDefine$1 = ['#E62222', '#B81B1B', '#EB4E4E', '#F97316', '#C75C12', '#FA8F45', '#FFB700', '#CC9200', '#FFC533', '#84CC16', '#6AA312', '#9dd645', '#00BC62', '#00A757', '#33DA8A', '#06B6D4', '#1B59C4', '#4E8CF7', '#0EA5E9',
|
|
522
|
-
'#1B59C4', '#4E8CF7', '#226FF5', '#1B59C4', '#4E8CF7', '#6366F1', '#4F52C1', '#8285F4', '#5B04B3', '#49038F', '#7C36C2', '#D946EF', '#AE38BF', '#E16BF2', '#EC4899', '#BD3A7A', '#F06DAD', '#F43F5E', '#C3324B', '#F6657E', '#757380', '#5E5C66', '#918F99',
|
|
523
|
-
'#202020', '#1A1A1A', '#4D4D4D'
|
|
524
|
-
];
|
|
525
|
-
const GetColorById = (str) => {
|
|
526
|
-
let hashString = 0;
|
|
527
|
-
if (!str) {
|
|
528
|
-
return '';
|
|
529
|
-
}
|
|
530
|
-
for (let i = 0; i < str.length; i++) {
|
|
531
|
-
const char = str.charCodeAt(i);
|
|
532
|
-
hashString = ((hashString << 5) - hashString) + char;
|
|
533
|
-
hashString = hashString & hashString;
|
|
534
|
-
}
|
|
535
|
-
return listColorDefine$1[Math.abs(hashString) % listColorDefine$1.length];
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
let key$1 = '12~@#loqwsxacva(3rdhaq12';
|
|
539
|
-
const setKeyCrypto = (value) => {
|
|
540
|
-
if (value.length !== 24 && value.length !== 32) {
|
|
541
|
-
throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
|
|
542
|
-
}
|
|
543
|
-
key$1 = value;
|
|
544
|
-
};
|
|
545
|
-
const keyStore$1 = () => {
|
|
546
|
-
return key$1;
|
|
547
|
-
};
|
|
548
|
-
const encrypt = (plainData) => {
|
|
549
|
-
if (!keyStore$1()) {
|
|
550
|
-
throw Error("lỗi chưa setup key mã hóa");
|
|
551
|
-
}
|
|
552
|
-
const key = CryptoES.enc.Hex.parse(keyStore$1());
|
|
553
|
-
const iv = CryptoES.enc.Hex.parse(keyStore$1());
|
|
554
|
-
const mode = CryptoES.mode.CBC;
|
|
555
|
-
const padding = CryptoES.pad.Pkcs7;
|
|
556
|
-
const options = { iv: iv, mode: mode, padding: padding };
|
|
557
|
-
return CryptoES.AES.encrypt(plainData, key, options).toString();
|
|
558
|
-
};
|
|
559
|
-
const decrypt = (encryptedData) => {
|
|
560
|
-
if (!keyStore$1()) {
|
|
561
|
-
throw Error("lỗi chưa setup key mã hóa");
|
|
702
|
+
toString() {
|
|
703
|
+
return this.params.toString();
|
|
562
704
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
+
// };
|
|
573
718
|
|
|
574
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
|
+
*/
|
|
575
724
|
const setKeyCrypto3rd = (value) => {
|
|
576
725
|
if (value.length !== 24 && value.length !== 32) {
|
|
577
726
|
throw Error(`key.length = ${value.length}; Key phải là 1 chuỗi dài 24 hoặc 32 ký tự`);
|
|
@@ -604,219 +753,6 @@ const decrypt3rd = (encryptedData) => {
|
|
|
604
753
|
return CryptoES.AES.decrypt(encryptedData, key, options).toString(CryptoES.enc.Utf8);
|
|
605
754
|
};
|
|
606
755
|
|
|
607
|
-
class UtilsKeyCodeConstant {
|
|
608
|
-
static MAC_ENTER = 3;
|
|
609
|
-
static BACKSPACE = 8;
|
|
610
|
-
static TAB = 9;
|
|
611
|
-
static NUM_CENTER = 12;
|
|
612
|
-
static ENTER = 13;
|
|
613
|
-
static SHIFT = 16;
|
|
614
|
-
static CONTROL = 17;
|
|
615
|
-
static ALT = 18;
|
|
616
|
-
static PAUSE = 19;
|
|
617
|
-
static CAPS_LOCK = 20;
|
|
618
|
-
static ESCAPE = 27;
|
|
619
|
-
static SPACE = 32;
|
|
620
|
-
static PAGE_UP = 33;
|
|
621
|
-
static PAGE_DOWN = 34;
|
|
622
|
-
static END = 35;
|
|
623
|
-
static HOME = 36;
|
|
624
|
-
static LEFT_ARROW = 37;
|
|
625
|
-
static UP_ARROW = 38;
|
|
626
|
-
static RIGHT_ARROW = 39;
|
|
627
|
-
static DOWN_ARROW = 40;
|
|
628
|
-
static PLUS_SIGN = 43;
|
|
629
|
-
static PRINT_SCREEN = 44;
|
|
630
|
-
static INSERT = 45;
|
|
631
|
-
static DELETE = 46;
|
|
632
|
-
static ZERO = 48;
|
|
633
|
-
static ONE = 49;
|
|
634
|
-
static TWO = 50;
|
|
635
|
-
static THREE = 51;
|
|
636
|
-
static FOUR = 52;
|
|
637
|
-
static FIVE = 53;
|
|
638
|
-
static SIX = 54;
|
|
639
|
-
static SEVEN = 55;
|
|
640
|
-
static EIGHT = 56;
|
|
641
|
-
static NINE = 57;
|
|
642
|
-
static FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
643
|
-
static FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
644
|
-
static QUESTION_MARK = 63;
|
|
645
|
-
static AT_SIGN = 64;
|
|
646
|
-
static A = 65;
|
|
647
|
-
static B = 66;
|
|
648
|
-
static C = 67;
|
|
649
|
-
static D = 68;
|
|
650
|
-
static E = 69;
|
|
651
|
-
static F = 70;
|
|
652
|
-
static G = 71;
|
|
653
|
-
static H = 72;
|
|
654
|
-
static I = 73;
|
|
655
|
-
static J = 74;
|
|
656
|
-
static K = 75;
|
|
657
|
-
static L = 76;
|
|
658
|
-
static M = 77;
|
|
659
|
-
static N = 78;
|
|
660
|
-
static O = 79;
|
|
661
|
-
static P = 80;
|
|
662
|
-
static Q = 81;
|
|
663
|
-
static R = 82;
|
|
664
|
-
static S = 83;
|
|
665
|
-
static T = 84;
|
|
666
|
-
static U = 85;
|
|
667
|
-
static V = 86;
|
|
668
|
-
static W = 87;
|
|
669
|
-
static X = 88;
|
|
670
|
-
static Y = 89;
|
|
671
|
-
static Z = 90;
|
|
672
|
-
static META = 91; // WIN_KEY_LEFT
|
|
673
|
-
static MAC_WK_CMD_LEFT = 91;
|
|
674
|
-
static MAC_WK_CMD_RIGHT = 93;
|
|
675
|
-
static CONTEXT_MENU = 93;
|
|
676
|
-
static NUMPAD_ZERO = 96;
|
|
677
|
-
static NUMPAD_ONE = 97;
|
|
678
|
-
static NUMPAD_TWO = 98;
|
|
679
|
-
static NUMPAD_THREE = 99;
|
|
680
|
-
static NUMPAD_FOUR = 100;
|
|
681
|
-
static NUMPAD_FIVE = 101;
|
|
682
|
-
static NUMPAD_SIX = 102;
|
|
683
|
-
static NUMPAD_SEVEN = 103;
|
|
684
|
-
static NUMPAD_EIGHT = 104;
|
|
685
|
-
static NUMPAD_NINE = 105;
|
|
686
|
-
static NUMPAD_MULTIPLY = 106;
|
|
687
|
-
static NUMPAD_PLUS = 107;
|
|
688
|
-
static NUMPAD_MINUS = 109;
|
|
689
|
-
static NUMPAD_PERIOD = 110;
|
|
690
|
-
static NUMPAD_DIVIDE = 111;
|
|
691
|
-
static F1 = 112;
|
|
692
|
-
static F2 = 113;
|
|
693
|
-
static F3 = 114;
|
|
694
|
-
static F4 = 115;
|
|
695
|
-
static F5 = 116;
|
|
696
|
-
static F6 = 117;
|
|
697
|
-
static F7 = 118;
|
|
698
|
-
static F8 = 119;
|
|
699
|
-
static F9 = 120;
|
|
700
|
-
static F10 = 121;
|
|
701
|
-
static F11 = 122;
|
|
702
|
-
static F12 = 123;
|
|
703
|
-
static NUM_LOCK = 144;
|
|
704
|
-
static SCROLL_LOCK = 145;
|
|
705
|
-
static FIRST_MEDIA = 166;
|
|
706
|
-
static FF_MINUS = 173;
|
|
707
|
-
static MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
708
|
-
static VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
709
|
-
static VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
710
|
-
static FF_MUTE = 181;
|
|
711
|
-
static FF_VOLUME_DOWN = 182;
|
|
712
|
-
static LAST_MEDIA = 183;
|
|
713
|
-
static FF_VOLUME_UP = 183;
|
|
714
|
-
static SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
715
|
-
static EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
716
|
-
static COMMA = 188;
|
|
717
|
-
static DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
718
|
-
static PERIOD = 190;
|
|
719
|
-
static SLASH = 191;
|
|
720
|
-
static APOSTROPHE = 192;
|
|
721
|
-
static TILDE = 192;
|
|
722
|
-
static OPEN_SQUARE_BRACKET = 219;
|
|
723
|
-
static BACKSLASH = 220;
|
|
724
|
-
static CLOSE_SQUARE_BRACKET = 221;
|
|
725
|
-
static SINGLE_QUOTE = 222;
|
|
726
|
-
static MAC_META = 224;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
730
|
-
const HttpParamsRequestInstance = (options, instance) => {
|
|
731
|
-
return new UtilsHttpParamsRequest(options, instance);
|
|
732
|
-
};
|
|
733
|
-
class UtilsHttpParamsRequest extends HttpParams {
|
|
734
|
-
params;
|
|
735
|
-
constructor(options, instance) {
|
|
736
|
-
super(options);
|
|
737
|
-
if (!instance) {
|
|
738
|
-
this.params = new HttpParams(options);
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
if (instance instanceof UtilsHttpParamsRequest) {
|
|
742
|
-
this.params = instance.getInstance();
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
if (instance instanceof HttpParams) {
|
|
746
|
-
this.params = instance;
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
getInstance() {
|
|
751
|
-
return this.params;
|
|
752
|
-
}
|
|
753
|
-
setInstance(instance) {
|
|
754
|
-
if (instance instanceof UtilsHttpParamsRequest) {
|
|
755
|
-
this.params = instance.getInstance();
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
if (instance instanceof HttpParams) {
|
|
759
|
-
this.params = instance;
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
set(param, value) {
|
|
764
|
-
this.params = this.params.set(param, value);
|
|
765
|
-
return this;
|
|
766
|
-
}
|
|
767
|
-
has(param) {
|
|
768
|
-
return this.params.has(param);
|
|
769
|
-
}
|
|
770
|
-
get(param) {
|
|
771
|
-
return this.params.get(param);
|
|
772
|
-
}
|
|
773
|
-
getAll(param) {
|
|
774
|
-
return this.params.getAll(param);
|
|
775
|
-
}
|
|
776
|
-
keys() {
|
|
777
|
-
return this.params.keys();
|
|
778
|
-
}
|
|
779
|
-
append(param, value) {
|
|
780
|
-
this.params = this.params.append(param, value);
|
|
781
|
-
return this;
|
|
782
|
-
}
|
|
783
|
-
appendAll(params) {
|
|
784
|
-
this.params = this.params.appendAll(params);
|
|
785
|
-
return this;
|
|
786
|
-
}
|
|
787
|
-
delete(param, value) {
|
|
788
|
-
this.params = this.params.delete(param, value);
|
|
789
|
-
return this;
|
|
790
|
-
}
|
|
791
|
-
toString() {
|
|
792
|
-
return this.params.toString();
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
class UtilsLanguageConstants {
|
|
797
|
-
static VI = 'vi';
|
|
798
|
-
static EN = 'en';
|
|
799
|
-
static defaultLang = UtilsLanguageConstants.VI;
|
|
800
|
-
}
|
|
801
|
-
;
|
|
802
|
-
|
|
803
|
-
const getKeyCacheByArrayObject = (keyCache, argumentsValue) => {
|
|
804
|
-
if (!keyCache || !argumentsValue) {
|
|
805
|
-
return '';
|
|
806
|
-
}
|
|
807
|
-
let keyBuild = `${JSON.stringify(argumentsValue.slice(1))}-key-cache-plus`;
|
|
808
|
-
if (argumentsValue && argumentsValue[0] instanceof HttpParams) {
|
|
809
|
-
const httpParams = argumentsValue[0];
|
|
810
|
-
const keys = argumentsValue[0].keys().sort((str1, str2) => str1.localeCompare(str2));
|
|
811
|
-
keys.forEach(key => {
|
|
812
|
-
keyBuild = `${keyBuild}${JSON.stringify(httpParams.get(key))}`;
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
const keyCachePlus = md5(keyBuild);
|
|
816
|
-
const keyCacheMD5 = md5(keyCache);
|
|
817
|
-
return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
|
|
818
|
-
};
|
|
819
|
-
|
|
820
756
|
let functionCheck = () => {
|
|
821
757
|
return window.parent !== window.top;
|
|
822
758
|
};
|
|
@@ -827,22 +763,34 @@ const isEmbedFrame = () => {
|
|
|
827
763
|
return functionCheck();
|
|
828
764
|
};
|
|
829
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
|
+
|
|
830
777
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
778
|
+
class UtilsCommunicateMicroKeyGlobal {
|
|
779
|
+
static KEY_MESSAGE_MODAL = 'LIBS_UI_MODEL_EVENT';
|
|
780
|
+
}
|
|
831
781
|
class UtilsCommunicateMicro {
|
|
832
|
-
static KEY_GET_ALL_MESSAGE = 'MICRO_SITES_ALL_MESSAGE';
|
|
833
|
-
static PREFIX_TYPE = '3RD_INTEGRATE_MICRO_SITE_';
|
|
834
782
|
static initdEvent;
|
|
835
783
|
static subs = new Map();
|
|
836
784
|
static allMessageSub = new Subject();
|
|
837
|
-
static initEvent(currentWindow) {
|
|
785
|
+
static initEvent(currentWindow, onDestroy) {
|
|
838
786
|
if (this.initdEvent) {
|
|
839
787
|
return;
|
|
840
788
|
}
|
|
841
789
|
this.initdEvent = true;
|
|
842
|
-
if (!this.subs.get(
|
|
843
|
-
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);
|
|
844
792
|
}
|
|
845
|
-
fromEvent(currentWindow, 'message').subscribe(e => {
|
|
793
|
+
fromEvent(currentWindow, 'message').pipe(takeUntil(onDestroy)).subscribe(e => {
|
|
846
794
|
const event = { data: { ...e.data } };
|
|
847
795
|
const data = event.data;
|
|
848
796
|
const type = data.type;
|
|
@@ -854,7 +802,7 @@ class UtilsCommunicateMicro {
|
|
|
854
802
|
return;
|
|
855
803
|
}
|
|
856
804
|
try {
|
|
857
|
-
if (type.includes(
|
|
805
|
+
if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
|
|
858
806
|
data.response = JSON.parse(decrypt3rd(data.response));
|
|
859
807
|
sub.next(event);
|
|
860
808
|
this.allMessageSub.next(event);
|
|
@@ -870,6 +818,10 @@ class UtilsCommunicateMicro {
|
|
|
870
818
|
this.allMessageSub.next(event);
|
|
871
819
|
}
|
|
872
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
|
+
});
|
|
873
825
|
}
|
|
874
826
|
static PostMessageToParent(data) {
|
|
875
827
|
data = this.convertData(data);
|
|
@@ -904,14 +856,13 @@ class UtilsCommunicateMicro {
|
|
|
904
856
|
}
|
|
905
857
|
data = { ...data };
|
|
906
858
|
const type = data.type;
|
|
907
|
-
if (type.includes(
|
|
859
|
+
if (type.includes(COMMUNICATE_MICRO_PREFIX_TYPE)) {
|
|
908
860
|
try {
|
|
909
861
|
JSON.parse(decrypt3rd(data.response));
|
|
910
862
|
return data;
|
|
911
863
|
}
|
|
912
|
-
catch (
|
|
913
|
-
|
|
914
|
-
data.response = decrypt3rd(JSON.stringify(data.response));
|
|
864
|
+
catch (_) {
|
|
865
|
+
data.response = encrypt3rd(JSON.stringify(data.response));
|
|
915
866
|
return data;
|
|
916
867
|
}
|
|
917
868
|
}
|
|
@@ -919,9 +870,8 @@ class UtilsCommunicateMicro {
|
|
|
919
870
|
JSON.parse(decrypt(data.response));
|
|
920
871
|
return data;
|
|
921
872
|
}
|
|
922
|
-
catch (
|
|
923
|
-
|
|
924
|
-
data.response = decrypt(JSON.stringify(data.response));
|
|
873
|
+
catch (_) {
|
|
874
|
+
data.response = encrypt(JSON.stringify(data.response));
|
|
925
875
|
return data;
|
|
926
876
|
}
|
|
927
877
|
}
|
|
@@ -941,6 +891,9 @@ class UtilsCommunicateMicro {
|
|
|
941
891
|
if (!Array.isArray(messageType) || !messageType.length) {
|
|
942
892
|
throw new Error('message type empty');
|
|
943
893
|
}
|
|
894
|
+
if (messageType.length === 1) {
|
|
895
|
+
return this.GetMessage(messageType[0]);
|
|
896
|
+
}
|
|
944
897
|
const types = messageType.sort().join(';');
|
|
945
898
|
let sub = this.subs.get(types);
|
|
946
899
|
if (sub) {
|
|
@@ -958,90 +911,149 @@ class UtilsCommunicateMicro {
|
|
|
958
911
|
});
|
|
959
912
|
});
|
|
960
913
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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);
|
|
966
983
|
}
|
|
967
984
|
}
|
|
968
985
|
|
|
969
986
|
/* eslint-disable no-async-promise-executor */
|
|
970
987
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
971
988
|
class UtilsCache {
|
|
989
|
+
static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
|
|
972
990
|
static CACHE_EXPIRE_NONE = -1;
|
|
991
|
+
static idService = uuid();
|
|
973
992
|
static typeKeyClearLocalStorage = 'LIBS_UI_CLEAR_LOCAL_STORAGE_EVENT';
|
|
974
|
-
static listKeyKeepWhenClearALll = Array();
|
|
975
993
|
static languageKeyCache = 'SR3xQKxHgffiCevPQRralg';
|
|
994
|
+
static listKeyKeepWhenClearALll = Array();
|
|
976
995
|
static initdEvent;
|
|
977
|
-
static CACHE_EXPIRE_TIME_DEFAULT = 5 * 60;
|
|
978
|
-
static language = signal(UtilsLanguageConstants.defaultLang);
|
|
979
|
-
static idService = uuid();
|
|
980
996
|
static storage;
|
|
981
997
|
static dbName = 'libs-ui-cache';
|
|
982
998
|
static itemIndexByKey = 'key';
|
|
983
999
|
static dbVersion = 1;
|
|
984
1000
|
static db = null;
|
|
985
1001
|
static init(config) {
|
|
986
|
-
if (
|
|
1002
|
+
if (this.initdEvent) {
|
|
987
1003
|
return;
|
|
988
1004
|
}
|
|
989
|
-
|
|
1005
|
+
this.initdEvent = true;
|
|
990
1006
|
if (config.indexedDBName) {
|
|
991
|
-
|
|
1007
|
+
this.dbName = config.indexedDBName;
|
|
992
1008
|
}
|
|
993
1009
|
if (config.typeKeyClearLocalStorage) {
|
|
994
|
-
|
|
1010
|
+
this.typeKeyClearLocalStorage = config.typeKeyClearLocalStorage;
|
|
995
1011
|
}
|
|
996
|
-
if (config.
|
|
997
|
-
|
|
1012
|
+
if (config.listKeyKeepWhenClearAll) {
|
|
1013
|
+
this.listKeyKeepWhenClearALll = config.listKeyKeepWhenClearAll;
|
|
998
1014
|
}
|
|
999
1015
|
if (config.languageKeyCache) {
|
|
1000
|
-
|
|
1016
|
+
this.languageKeyCache = config.languageKeyCache;
|
|
1001
1017
|
}
|
|
1002
|
-
UtilsCommunicateMicro.GetMessage(UtilsCache.typeKeyClearLocalStorage).pipe(filter(e => e.data.response !== UtilsCache.idService)).subscribe(() => {
|
|
1003
|
-
UtilsCache.ClearAll();
|
|
1004
|
-
});
|
|
1005
1018
|
}
|
|
1006
1019
|
static setLang(lang) {
|
|
1007
|
-
if (
|
|
1008
|
-
throw Error(
|
|
1020
|
+
if (!UtilsLanguageConstants.isSupported(lang)) {
|
|
1021
|
+
throw Error(`Language not support ${lang}`);
|
|
1009
1022
|
}
|
|
1010
|
-
|
|
1011
|
-
UtilsCache.Set(UtilsCache.languageKeyCache, lang);
|
|
1023
|
+
this.Set(this.languageKeyCache, lang, this.CACHE_EXPIRE_NONE);
|
|
1012
1024
|
}
|
|
1013
1025
|
static getLang() {
|
|
1014
|
-
return
|
|
1026
|
+
return this.Get(this.languageKeyCache, UtilsLanguageConstants.defaultLang);
|
|
1015
1027
|
}
|
|
1016
1028
|
static openDB() {
|
|
1017
1029
|
return new Promise(resolve => {
|
|
1018
|
-
const request = indexedDB.open(
|
|
1030
|
+
const request = indexedDB.open(this.dbName, this.dbVersion);
|
|
1019
1031
|
request.onupgradeneeded = (event) => {
|
|
1020
1032
|
const db = event.target.result;
|
|
1021
|
-
if (!db.objectStoreNames.contains(
|
|
1022
|
-
const objectStore = db.createObjectStore(
|
|
1023
|
-
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 });
|
|
1024
1036
|
}
|
|
1025
1037
|
};
|
|
1026
1038
|
request.onsuccess = () => {
|
|
1027
|
-
|
|
1028
|
-
setTimeout(() => {
|
|
1029
|
-
UtilsCache.DeleteKeyStartWithAsync('');
|
|
1030
|
-
}, 2000);
|
|
1039
|
+
this.db = request.result;
|
|
1031
1040
|
resolve(true);
|
|
1032
1041
|
};
|
|
1033
1042
|
request.onerror = (event) => {
|
|
1034
|
-
console.
|
|
1043
|
+
console.trace('Error opening IndexedDB:', event);
|
|
1035
1044
|
resolve(false);
|
|
1036
1045
|
};
|
|
1037
1046
|
});
|
|
1038
1047
|
}
|
|
1039
1048
|
static async getObjectStore() {
|
|
1040
|
-
if (!
|
|
1041
|
-
await
|
|
1049
|
+
if (!this.db) {
|
|
1050
|
+
await this.openDB();
|
|
1051
|
+
}
|
|
1052
|
+
const transaction = this.db?.transaction([this.dbName], 'readwrite');
|
|
1053
|
+
if (!transaction) {
|
|
1054
|
+
return null;
|
|
1042
1055
|
}
|
|
1043
|
-
|
|
1044
|
-
return transaction?.objectStore(UtilsCache.dbName);
|
|
1056
|
+
return transaction.objectStore(this.dbName);
|
|
1045
1057
|
}
|
|
1046
1058
|
static get LocalStorage() {
|
|
1047
1059
|
try {
|
|
@@ -1055,12 +1067,12 @@ class UtilsCache {
|
|
|
1055
1067
|
return this.getLocalStorageFake();
|
|
1056
1068
|
}
|
|
1057
1069
|
catch (error) {
|
|
1058
|
-
console.
|
|
1070
|
+
console.trace(`LocalStorage `, error);
|
|
1059
1071
|
return this.getLocalStorageFake();
|
|
1060
1072
|
}
|
|
1061
1073
|
}
|
|
1062
1074
|
static getLocalStorageFakeOnSafari() {
|
|
1063
|
-
if (typeof window.localStorage !== 'undefined' && !
|
|
1075
|
+
if (typeof window.localStorage !== 'undefined' && !this.storage && Object.keys(localStorage).length) {
|
|
1064
1076
|
this.storage = { ...localStorage };
|
|
1065
1077
|
}
|
|
1066
1078
|
return {
|
|
@@ -1090,7 +1102,7 @@ class UtilsCache {
|
|
|
1090
1102
|
};
|
|
1091
1103
|
}
|
|
1092
1104
|
static getLocalStorageFake() {
|
|
1093
|
-
if (!
|
|
1105
|
+
if (!this.storage) {
|
|
1094
1106
|
this.storage = {};
|
|
1095
1107
|
}
|
|
1096
1108
|
return {
|
|
@@ -1110,19 +1122,18 @@ class UtilsCache {
|
|
|
1110
1122
|
}
|
|
1111
1123
|
static async GetAsync(key, default_value, isKeyMD5 = false) {
|
|
1112
1124
|
key = isKeyMD5 ? key : md5(key);
|
|
1113
|
-
return new Promise(async (resolve
|
|
1114
|
-
const objectStore = await
|
|
1125
|
+
return new Promise(async (resolve) => {
|
|
1126
|
+
const objectStore = await this.getObjectStore();
|
|
1115
1127
|
if (!objectStore) {
|
|
1116
1128
|
return resolve(default_value);
|
|
1117
1129
|
}
|
|
1118
|
-
const
|
|
1119
|
-
const request = index.get(key);
|
|
1130
|
+
const request = objectStore.get(key);
|
|
1120
1131
|
request.onsuccess = () => {
|
|
1121
1132
|
if (!request.result) {
|
|
1122
1133
|
return resolve(default_value);
|
|
1123
1134
|
}
|
|
1124
1135
|
const data = JSON.parse(decrypt(request.result.value));
|
|
1125
|
-
if (data.expire ===
|
|
1136
|
+
if (data.expire === this.CACHE_EXPIRE_NONE) {
|
|
1126
1137
|
return resolve(data.json);
|
|
1127
1138
|
}
|
|
1128
1139
|
const currentMillisecond = (new Date().valueOf() / 1000);
|
|
@@ -1132,7 +1143,8 @@ class UtilsCache {
|
|
|
1132
1143
|
return resolve(data.json);
|
|
1133
1144
|
};
|
|
1134
1145
|
request.onerror = () => {
|
|
1135
|
-
|
|
1146
|
+
console.trace(`Get key ${key} Error, return default value: ${default_value}`);
|
|
1147
|
+
return resolve(default_value);
|
|
1136
1148
|
};
|
|
1137
1149
|
});
|
|
1138
1150
|
}
|
|
@@ -1146,8 +1158,8 @@ class UtilsCache {
|
|
|
1146
1158
|
}
|
|
1147
1159
|
try {
|
|
1148
1160
|
const data = JSON.parse(decrypt(cachedData));
|
|
1149
|
-
if (data.expire ===
|
|
1150
|
-
return data.value;
|
|
1161
|
+
if (data.expire === this.CACHE_EXPIRE_NONE) {
|
|
1162
|
+
return data.value ?? default_value;
|
|
1151
1163
|
}
|
|
1152
1164
|
const currentMillisecond = (new Date().valueOf() / 1000);
|
|
1153
1165
|
if (data.expire < currentMillisecond) {
|
|
@@ -1156,97 +1168,71 @@ class UtilsCache {
|
|
|
1156
1168
|
return data.value;
|
|
1157
1169
|
}
|
|
1158
1170
|
catch (error) {
|
|
1159
|
-
console.
|
|
1171
|
+
console.trace(`Get key ${key} Error, return default value: ${default_value}`, error);
|
|
1160
1172
|
return this.GetDefaultValueBySpecificKey(key, default_value);
|
|
1161
1173
|
}
|
|
1162
1174
|
}
|
|
1163
1175
|
static GetDefaultValueBySpecificKey(key, default_value) {
|
|
1164
1176
|
return default_value;
|
|
1165
1177
|
}
|
|
1166
|
-
static async SetAsync(key, value, expireTimeBySecond =
|
|
1167
|
-
return new Promise(async (resolve
|
|
1168
|
-
|
|
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();
|
|
1169
1181
|
key = isKeyMD5 ? key : md5(key);
|
|
1170
1182
|
try {
|
|
1171
|
-
const currentMillisecond = expireTimeBySecond ===
|
|
1183
|
+
const currentMillisecond = expireTimeBySecond === this.CACHE_EXPIRE_NONE ? this.CACHE_EXPIRE_NONE : (new Date().valueOf() / 1000) + expireTimeBySecond;
|
|
1172
1184
|
const data = {
|
|
1173
|
-
key: key,
|
|
1174
1185
|
value: encrypt(JSON.stringify({ json: value, expire: currentMillisecond })),
|
|
1175
1186
|
};
|
|
1187
|
+
data[this.itemIndexByKey] = key;
|
|
1176
1188
|
if (!objectStore) {
|
|
1177
|
-
|
|
1189
|
+
console.trace(`Can not open object store`);
|
|
1190
|
+
return resolve({ key, messageError: 'Can not open object store' });
|
|
1178
1191
|
}
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
if (item) {
|
|
1184
|
-
await UtilsCache.ClearAsync(key, true);
|
|
1185
|
-
objectStore = await UtilsCache.getObjectStore();
|
|
1186
|
-
}
|
|
1187
|
-
if (!objectStore) {
|
|
1188
|
-
return resolve(-1);
|
|
1189
|
-
}
|
|
1190
|
-
const request = objectStore.add(data);
|
|
1191
|
-
request.onsuccess = () => {
|
|
1192
|
-
resolve(request.result);
|
|
1193
|
-
};
|
|
1194
|
-
request.onerror = () => {
|
|
1195
|
-
console.log(request.error);
|
|
1196
|
-
return resolve(-1);
|
|
1197
|
-
};
|
|
1192
|
+
const request = objectStore?.put(data);
|
|
1193
|
+
request.onsuccess = () => {
|
|
1194
|
+
console.log(`Set key ${key} Success`);
|
|
1195
|
+
resolve(request.result);
|
|
1198
1196
|
};
|
|
1199
|
-
|
|
1200
|
-
console.
|
|
1201
|
-
|
|
1197
|
+
request.onerror = (error) => {
|
|
1198
|
+
console.trace(`Set key ${key} Error`);
|
|
1199
|
+
resolve({ key, messageError: get(error, 'message') });
|
|
1202
1200
|
};
|
|
1203
1201
|
}
|
|
1204
1202
|
catch (error) {
|
|
1205
|
-
console.
|
|
1206
|
-
resolve({});
|
|
1203
|
+
console.trace(`Set key ${key} Error`);
|
|
1204
|
+
resolve({ key, messageError: get(error, 'message') });
|
|
1207
1205
|
}
|
|
1208
1206
|
});
|
|
1209
1207
|
}
|
|
1210
|
-
static Set(key, value, expireTimeBySecond =
|
|
1211
|
-
|
|
1212
|
-
return;
|
|
1213
|
-
}
|
|
1214
|
-
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;
|
|
1215
1210
|
const data = {
|
|
1216
1211
|
value: value,
|
|
1217
1212
|
expire: currentMillisecond
|
|
1218
1213
|
};
|
|
1219
1214
|
try {
|
|
1220
1215
|
this.LocalStorage.setItem(key, encrypt(JSON.stringify(data)));
|
|
1216
|
+
return true;
|
|
1221
1217
|
}
|
|
1222
1218
|
catch (error) {
|
|
1223
|
-
console.
|
|
1219
|
+
console.trace(`Set key ${key} Error`, error);
|
|
1220
|
+
return false;
|
|
1224
1221
|
}
|
|
1225
1222
|
}
|
|
1226
1223
|
static async ClearAsync(key, isMD5 = false) {
|
|
1227
1224
|
return new Promise(async (resolve) => {
|
|
1228
|
-
const objectStore = await
|
|
1225
|
+
const objectStore = await this.getObjectStore();
|
|
1229
1226
|
if (!objectStore) {
|
|
1230
|
-
return resolve(
|
|
1227
|
+
return resolve();
|
|
1231
1228
|
}
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
const result = getRequest.result;
|
|
1236
|
-
if (result) {
|
|
1237
|
-
const deleteRequest = objectStore.delete(result.id); // Xóa theo id của item tìm được
|
|
1238
|
-
deleteRequest.onsuccess = () => {
|
|
1239
|
-
resolve({});
|
|
1240
|
-
};
|
|
1241
|
-
deleteRequest.onerror = () => {
|
|
1242
|
-
resolve({});
|
|
1243
|
-
};
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
resolve({});
|
|
1229
|
+
const request = objectStore.delete(isMD5 ? key : md5(key));
|
|
1230
|
+
request.onsuccess = () => {
|
|
1231
|
+
resolve({ clearSuccess: true });
|
|
1247
1232
|
};
|
|
1248
|
-
|
|
1249
|
-
|
|
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 });
|
|
1250
1236
|
};
|
|
1251
1237
|
});
|
|
1252
1238
|
}
|
|
@@ -1257,38 +1243,41 @@ class UtilsCache {
|
|
|
1257
1243
|
this.LocalStorage.removeItem(key);
|
|
1258
1244
|
}
|
|
1259
1245
|
static ClearAllAsync() {
|
|
1260
|
-
return new Promise((resolve
|
|
1261
|
-
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();
|
|
1262
1252
|
request.onsuccess = () => {
|
|
1263
|
-
console.log('
|
|
1264
|
-
resolve(true);
|
|
1253
|
+
console.log('clear all successfully');
|
|
1254
|
+
resolve({ clearSuccess: true });
|
|
1265
1255
|
};
|
|
1266
1256
|
request.onerror = (event) => {
|
|
1267
|
-
console.
|
|
1268
|
-
|
|
1269
|
-
};
|
|
1270
|
-
request.onblocked = () => {
|
|
1271
|
-
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 });
|
|
1272
1259
|
};
|
|
1273
1260
|
});
|
|
1274
1261
|
}
|
|
1275
1262
|
static ClearAll() {
|
|
1276
|
-
|
|
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];
|
|
1277
1273
|
Object.keys(this.LocalStorage).forEach(key => {
|
|
1278
1274
|
if (key.includes('kc-callback-')) {
|
|
1279
1275
|
keys.push(key);
|
|
1280
1276
|
}
|
|
1281
1277
|
});
|
|
1282
|
-
const stores =
|
|
1278
|
+
const stores = this.GetDataByKeys(Array.from(keys));
|
|
1283
1279
|
this.LocalStorage.clear();
|
|
1284
|
-
|
|
1285
|
-
const data = {
|
|
1286
|
-
type: UtilsCache.typeKeyClearLocalStorage,
|
|
1287
|
-
response: {
|
|
1288
|
-
idEvent: this.idService
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
UtilsCommunicateMicro.PostMessageToParent(data);
|
|
1280
|
+
this.SetDataByKey(stores);
|
|
1292
1281
|
}
|
|
1293
1282
|
static GetDataByKeys(keys) {
|
|
1294
1283
|
const stores = new Map();
|
|
@@ -1297,62 +1286,1051 @@ class UtilsCache {
|
|
|
1297
1286
|
stores.set(key, this.LocalStorage.getItem(key));
|
|
1298
1287
|
return;
|
|
1299
1288
|
}
|
|
1300
|
-
stores.set(key,
|
|
1289
|
+
stores.set(key, this.Get(key));
|
|
1301
1290
|
});
|
|
1302
1291
|
return stores;
|
|
1303
1292
|
}
|
|
1304
|
-
static SetDataByKey(stores) {
|
|
1305
|
-
stores.forEach((value, key) => {
|
|
1306
|
-
if (key.includes('kc-callback-')) {
|
|
1307
|
-
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)}`;
|
|
1308
2317
|
return;
|
|
1309
2318
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
static DeleteKeyStartWithAsync(keyCacheStartWith, isKeyMD5 = false) {
|
|
1317
|
-
return new Promise(async (resolve) => {
|
|
1318
|
-
const objectStore = await UtilsCache.getObjectStore();
|
|
1319
|
-
if (!objectStore) {
|
|
1320
|
-
return resolve({});
|
|
1321
|
-
}
|
|
1322
|
-
const index = objectStore.index(UtilsCache.itemIndexByKey);
|
|
1323
|
-
// Lấy tất cả các keys từ index
|
|
1324
|
-
const request = index.getAll();
|
|
1325
|
-
keyCacheStartWith = isKeyMD5 ? keyCacheStartWith : md5(keyCacheStartWith);
|
|
1326
|
-
request.onsuccess = (e) => {
|
|
1327
|
-
const data = e.target.result;
|
|
1328
|
-
if (!Array.isArray(data)) {
|
|
1329
|
-
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;
|
|
1330
2323
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
UtilsCache.ClearAsync(obj.key, true);
|
|
1334
|
-
}
|
|
1335
|
-
});
|
|
1336
|
-
return resolve({});
|
|
1337
|
-
};
|
|
1338
|
-
request.onerror = () => {
|
|
1339
|
-
return resolve({});
|
|
1340
|
-
};
|
|
1341
|
-
});
|
|
1342
|
-
}
|
|
1343
|
-
static DeleteKeyStartWith(keyCache, isMD5 = false) {
|
|
1344
|
-
keyCache = isMD5 ? md5(keyCache) : keyCache;
|
|
1345
|
-
const keys = Object.keys(UtilsCache.LocalStorage);
|
|
1346
|
-
if (!keys || !keys.length) {
|
|
2324
|
+
keyBuild = `${keyBuild}${JSON.stringify(get(item, key))}`;
|
|
2325
|
+
});
|
|
1347
2326
|
return;
|
|
1348
2327
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
}
|
|
2328
|
+
keyBuild = `${keyBuild}${item}`;
|
|
2329
|
+
});
|
|
2330
|
+
const keyCachePlus = md5(keyBuild);
|
|
2331
|
+
const keyCacheMD5 = md5(keyCache);
|
|
2332
|
+
return `${keyCacheMD5}-${md5(`${keyCacheMD5}-${keyCachePlus}`)}`;
|
|
2333
|
+
};
|
|
1356
2334
|
|
|
1357
2335
|
const formatNumber = (value) => {
|
|
1358
2336
|
const lang = UtilsCache.getLang();
|
|
@@ -1414,138 +2392,521 @@ const viewDataNumberByLanguage = (value, acceptNegativeValue, parseFixed = 1, ig
|
|
|
1414
2392
|
}
|
|
1415
2393
|
};
|
|
1416
2394
|
|
|
1417
|
-
|
|
1418
|
-
return
|
|
2395
|
+
let functionXssFilter = async (value) => {
|
|
2396
|
+
return value;
|
|
1419
2397
|
};
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
return
|
|
2398
|
+
const updateFunctionXssFilter = (functionCustom) => {
|
|
2399
|
+
functionXssFilter = functionCustom;
|
|
2400
|
+
};
|
|
2401
|
+
const xssFilter = async (data) => {
|
|
2402
|
+
return await functionXssFilter(data);
|
|
1425
2403
|
};
|
|
1426
2404
|
|
|
1427
|
-
|
|
1428
|
-
|
|
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) {
|
|
1429
2445
|
return data;
|
|
1430
2446
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
.
|
|
1436
|
-
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
|
|
1444
|
-
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
|
|
1445
|
-
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
|
|
1446
|
-
str = str.replace(/đ/g, 'd');
|
|
1447
|
-
str = str.replace(/À|Á|Ạ|Ả|Ã|Â|Ầ|Ấ|Ậ|Ẩ|Ẫ|Ă|Ằ|Ắ|Ặ|Ẳ|Ẵ/g, 'A');
|
|
1448
|
-
str = str.replace(/È|É|Ẹ|Ẻ|Ẽ|Ê|Ề|Ế|Ệ|Ể|Ễ/g, 'E');
|
|
1449
|
-
str = str.replace(/Ì|Í|Ị|Ỉ|Ĩ/g, 'I');
|
|
1450
|
-
str = str.replace(/Ò|Ó|Ọ|Ỏ|Õ|Ô|Ồ|Ố|Ộ|Ổ|Ỗ|Ơ|Ờ|Ớ|Ợ|Ở|Ỡ/g, 'O');
|
|
1451
|
-
str = str.replace(/Ù|Ú|Ụ|Ủ|Ũ|Ư|Ừ|Ứ|Ự|Ử|Ữ/g, 'U');
|
|
1452
|
-
str = str.replace(/Ỳ|Ý|Ỵ|Ỷ|Ỹ/g, 'Y');
|
|
1453
|
-
str = str.replace(/Đ/g, 'D');
|
|
1454
|
-
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); // ̀ ́ ̃ ̉ ̣ huyền, sắc, ngã, hỏi, nặng
|
|
1455
|
-
str = str.replace(/\u02C6|\u0306|\u031B/g, ""); // ˆ ̆ ̛ Â, Ê, Ă, Ơ, Ư
|
|
1456
|
-
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;
|
|
1457
2459
|
};
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
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;
|
|
1467
2475
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
hashString = ((hashString << 5) - hashString) + char;
|
|
1471
|
-
hashString = hashString & hashString;
|
|
2476
|
+
if (data === null || typeof data === 'function' || typeof data !== 'object') {
|
|
2477
|
+
return data;
|
|
1472
2478
|
}
|
|
1473
|
-
|
|
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;
|
|
1474
2512
|
};
|
|
1475
2513
|
|
|
1476
|
-
const
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
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]$/;
|
|
1487
|
-
};
|
|
1488
|
-
const PATTERN_URL = () => {
|
|
1489
|
-
return /^(http|https|ftp):(\/){2}[^\s]+[.]{1}[^\s]+$/;
|
|
1490
|
-
};
|
|
1491
|
-
const PATTERN_HOST_URL = () => {
|
|
1492
|
-
return /^((https|http|ftp):[/]{2}[^/\s]+)/;
|
|
1493
|
-
};
|
|
1494
|
-
const PATTERN_DOMAIN = () => {
|
|
1495
|
-
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})+$/;
|
|
1496
|
-
};
|
|
1497
|
-
const PATTERN_MOBILE_PHONE = () => {
|
|
1498
|
-
return /^(\+?84|0|84)([0-9]{9})$/;
|
|
1499
|
-
};
|
|
1500
|
-
const PATTERN_PHONE = () => {
|
|
1501
|
-
return /^(\+?84|[0-9]|84)([0-9]{2,9})$/;
|
|
1502
|
-
};
|
|
1503
|
-
const PATTERN_PHONE_NORMAL = () => {
|
|
1504
|
-
return /^(\+?84|[0-9]|84)([0-9]{9,10})$/;
|
|
1505
|
-
};
|
|
1506
|
-
const PATTERN_NUMBER = () => {
|
|
1507
|
-
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
|
+
});
|
|
1508
2524
|
};
|
|
1509
|
-
const
|
|
1510
|
-
|
|
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)}`;
|
|
1511
2532
|
};
|
|
1512
|
-
|
|
1513
|
-
|
|
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
|
+
});
|
|
1514
2574
|
};
|
|
1515
|
-
|
|
1516
|
-
|
|
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();
|
|
1517
2610
|
};
|
|
1518
|
-
|
|
1519
|
-
|
|
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();
|
|
1520
2659
|
};
|
|
1521
|
-
const
|
|
1522
|
-
|
|
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`;
|
|
1523
2665
|
};
|
|
1524
|
-
const
|
|
1525
|
-
|
|
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() });
|
|
1526
2670
|
};
|
|
1527
|
-
const
|
|
1528
|
-
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
|
+
});
|
|
1529
2690
|
};
|
|
1530
|
-
|
|
1531
|
-
|
|
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
|
+
};
|
|
1532
2780
|
};
|
|
1533
|
-
|
|
1534
|
-
|
|
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;
|
|
1535
2824
|
};
|
|
1536
|
-
|
|
1537
|
-
|
|
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
|
+
}
|
|
1538
2837
|
};
|
|
1539
|
-
|
|
1540
|
-
|
|
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
|
+
}
|
|
1541
2863
|
};
|
|
1542
|
-
|
|
1543
|
-
|
|
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
|
+
};
|
|
1544
2905
|
};
|
|
1545
2906
|
|
|
1546
2907
|
/**
|
|
1547
2908
|
* Generated bundle index. Do not edit.
|
|
1548
2909
|
*/
|
|
1549
2910
|
|
|
1550
|
-
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 };
|
|
1551
2912
|
//# sourceMappingURL=libs-ui-utils.mjs.map
|