@sd-angular/core 19.0.0-beta.95 → 19.0.0-beta.96
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/README.md +8 -9
- package/components/badge/src/badge.component.d.ts +3 -4
- package/components/button/src/button.component.d.ts +2 -2
- package/components/index.d.ts +0 -2
- package/components/modal/src/modal.component.d.ts +1 -2
- package/components/section/src/section.component.d.ts +1 -2
- package/components/table/src/components/column-filter/column-filter.component.d.ts +3 -3
- package/fesm2022/sd-angular-core-components-form-generic.mjs +4 -4
- package/fesm2022/sd-angular-core-components-form-generic.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-tab-router.mjs +29 -24
- package/fesm2022/sd-angular-core-components-tab-router.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components.mjs +0 -2
- package/fesm2022/sd-angular-core-components.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-input.mjs +20 -5
- package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
- package/fesm2022/sd-angular-core-i18n.mjs +35 -0
- package/fesm2022/sd-angular-core-i18n.mjs.map +1 -1
- package/fesm2022/sd-angular-core-models.mjs +1 -4
- package/fesm2022/sd-angular-core-models.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services-notify.mjs +21 -21
- package/fesm2022/sd-angular-core-services-notify.mjs.map +1 -1
- package/fesm2022/sd-angular-core-utilities-extensions.mjs +29 -819
- package/fesm2022/sd-angular-core-utilities-extensions.mjs.map +1 -1
- package/fesm2022/sd-angular-core-utilities-models.mjs +36 -136
- package/fesm2022/sd-angular-core-utilities-models.mjs.map +1 -1
- package/forms/autocomplete/src/autocomplete.component.d.ts +1 -2
- package/forms/chip/src/chip.component.d.ts +1 -2
- package/forms/chip-calendar/src/chip-calendar.component.d.ts +1 -2
- package/forms/date/src/date.component.d.ts +1 -2
- package/forms/date-range/src/date-range.component.d.ts +1 -2
- package/forms/datetime/src/datetime.component.d.ts +1 -2
- package/forms/input/src/input.component.d.ts +1 -2
- package/forms/input-number/src/input-number.component.d.ts +1 -2
- package/forms/select/src/select.component.d.ts +5 -5
- package/forms/textarea/src/textarea.component.d.ts +1 -2
- package/i18n/src/en.d.ts +6 -0
- package/models/src/language.model.d.ts +2 -4
- package/package.json +75 -86
- package/sd-angular-core-19.0.0-beta.96.tgz +0 -0
- package/services/notify/index.d.ts +1 -1
- package/services/notify/src/components/toast/toast.component.d.ts +3 -3
- package/services/notify/src/components/toast-container.component.d.ts +3 -4
- package/services/notify/src/notify.model.d.ts +1 -1
- package/services/notify/src/notify.service.d.ts +5 -5
- package/utilities/extensions/src/array.extension.d.ts +1 -7
- package/utilities/extensions/src/color.extension.d.ts +5 -20
- package/utilities/extensions/src/date.extension.d.ts +1 -18
- package/utilities/extensions/src/number.extension.d.ts +1 -10
- package/utilities/extensions/src/string.extension.d.ts +30 -4
- package/utilities/extensions/src/utility.extension.d.ts +6 -0
- package/utilities/models/src/color.model.d.ts +4 -1
- package/utilities/models/src/empty.model.d.ts +3 -0
- package/utilities/models/src/filter.model.d.ts +12 -24
- package/utilities/models/src/maybe-async.model.d.ts +10 -4
- package/utilities/models/src/nested-key-of.model.d.ts +4 -5
- package/utilities/models/src/operator.model.d.ts +9 -4
- package/utilities/models/src/order.model.d.ts +4 -5
- package/utilities/models/src/paging.model.d.ts +8 -16
- package/utilities/models/src/pattern.model.d.ts +13 -0
- package/utilities/models/src/size.model.d.ts +4 -1
- package/components/anchor-v2/index.d.ts +0 -1
- package/components/anchor-v2/src/components/anchor-item-v2/anchor-item-v2.component.d.ts +0 -12
- package/components/anchor-v2/src/components/anchor-v2/anchor-v2.component.d.ts +0 -19
- package/components/anchor-v2/src/components/anchor-vertical-v2/anchor-vertical-list-v2.component.d.ts +0 -16
- package/components/anchor-v2/src/components/index.d.ts +0 -2
- package/components/anchor-v2/src/index.d.ts +0 -2
- package/components/anchor-v2/src/models/index.d.ts +0 -1
- package/components/anchor-v2/src/models/sd-anchor-v2.model.d.ts +0 -7
- package/components/workflow/index.d.ts +0 -2
- package/components/workflow/src/configurations/index.d.ts +0 -1
- package/components/workflow/src/configurations/workflow.configuration.d.ts +0 -10
- package/fesm2022/sd-angular-core-components-anchor-v2.mjs +0 -151
- package/fesm2022/sd-angular-core-components-anchor-v2.mjs.map +0 -1
- package/fesm2022/sd-angular-core-components-workflow.mjs +0 -17
- package/fesm2022/sd-angular-core-components-workflow.mjs.map +0 -1
- package/fesm2022/sd-angular-core-services-firebase.mjs +0 -38
- package/fesm2022/sd-angular-core-services-firebase.mjs.map +0 -1
- package/sd-angular-core-19.0.0-beta.95.tgz +0 -0
- package/services/firebase/index.d.ts +0 -2
- package/services/firebase/src/firebase.model.d.ts +0 -8
- package/services/firebase/src/firebase.service.d.ts +0 -14
|
@@ -1,762 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
import { ValidationUtilities, StringUtilities as StringUtilities$1, ColorUtilities } from '@sdcorejs/utils/fns';
|
|
2
|
+
export { ArrayUtilities, BrowserUtilities, ColorUtilities, DateUtilities, NumberUtilities, Utilities, ValidationUtilities } from '@sdcorejs/utils/fns';
|
|
3
|
+
|
|
2
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// isValidCode(value: any): boolean;
|
|
8
|
-
// changeAliasLowerCase(alias: any): string;
|
|
9
|
-
// aliasIncludes(alias: any, searchText: any): boolean;
|
|
10
|
-
// format(template: string, ...arr: any[]): string;
|
|
11
|
-
// templateToDisplay(template: string, entity: any): string; // Convert theo format ${key}
|
|
12
|
-
// encrypt(obj: any): string;
|
|
13
|
-
// decrypt(encripted: string): any;
|
|
14
|
-
// isNullOrEmpty(value: any): boolean;
|
|
15
|
-
// isNullOrWhiteSpace(value: any): boolean;
|
|
16
|
-
// }
|
|
17
|
-
// }
|
|
18
|
-
const REGEX_EMAIL = '^(([^<>()[\\].,;:\\s@"]+(\\.[^<>()[\\].,;:\\s@"]+)*)|(".+"))@(([^<>()[\\].,;:\\s@"]+\\.)+[^<>()[\\].,;:\\s@"]{2,})$';
|
|
19
|
-
const REGEX_PHONE = '^[+]*[(]{0,1}[+]?[0-9]{1,4}[)]{0,1}[-\\s./0-9]*$';
|
|
20
|
-
const REGEX_PHONE_VN = '^(?:\\+84|0|84)(3[2-9]|5[2689]|7[06-9]|8[1-689]|9[0-9])\\d{7}$';
|
|
21
|
-
const REGEX_IDVN = '^\\d{12}$';
|
|
22
|
-
const REGEX_PASSPORT = '^[A-Z]\\d{7}$';
|
|
23
|
-
const REGEX_IDVN_OR_PASSPORT = '^(\\d{12}|[A-Z]\\d{7})$';
|
|
24
|
-
const REGEX_TIME = '^(?:[01]\\d|2[0-3]):[0-5]\\d$';
|
|
25
|
-
const isValidEmail = (value) => {
|
|
26
|
-
if (!value) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
const re = new RegExp(REGEX_EMAIL);
|
|
30
|
-
return re.test(value);
|
|
31
|
-
};
|
|
32
|
-
const isValidPhone = (value) => {
|
|
33
|
-
if (!value) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const re = new RegExp(REGEX_PHONE);
|
|
37
|
-
return re.test(value);
|
|
38
|
-
};
|
|
39
|
-
const isValidCode = (value) => {
|
|
40
|
-
if (!value) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
const re = /^[a-zA-Z0-9\@\_\-]{2,20}$/;
|
|
44
|
-
return re.test(value);
|
|
45
|
-
};
|
|
46
|
-
const isNullOrEmpty = (value) => {
|
|
47
|
-
return value === undefined || value === null || value === '';
|
|
48
|
-
};
|
|
49
|
-
const isNullOrWhiteSpace = (value) => {
|
|
50
|
-
return value === undefined || value === null || typeof value !== 'string' || value.match(/^\s*$/) !== null;
|
|
51
|
-
};
|
|
52
|
-
const changeAliasLowerCase$1 = (alias) => {
|
|
53
|
-
let str = alias?.toString() ?? '';
|
|
54
|
-
str = str.toString().toLowerCase();
|
|
55
|
-
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
|
|
56
|
-
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
|
|
57
|
-
str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
|
|
58
|
-
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
|
|
59
|
-
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
|
|
60
|
-
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
|
|
61
|
-
str = str.replace(/đ/g, 'd');
|
|
62
|
-
str = str.replace(/!|@|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\;|\'|\"|\&|\#|\[|\]|~|\$|_|`|-|{|}|\||\\/g, ' ');
|
|
63
|
-
str = str.replace(/ + /g, ' ');
|
|
64
|
-
str = str.trim();
|
|
65
|
-
return str;
|
|
66
|
-
};
|
|
67
|
-
const aliasIncludes = (alias, searchText) => {
|
|
68
|
-
return changeAliasLowerCase$1(alias).includes(changeAliasLowerCase$1(searchText));
|
|
69
|
-
};
|
|
70
|
-
const format = (template, ...arr) => {
|
|
71
|
-
for (let i = 0; i < arr.length; i++) {
|
|
72
|
-
const regexp = new RegExp(`\\{${i}\\}`, 'gi');
|
|
73
|
-
template = template.replace(regexp, arr[i]);
|
|
74
|
-
}
|
|
75
|
-
return template;
|
|
76
|
-
};
|
|
77
|
-
const getValueByPath = (entity, key) => {
|
|
78
|
-
let value = entity;
|
|
79
|
-
for (const part of key.split('.')) {
|
|
80
|
-
value = value?.[part];
|
|
81
|
-
}
|
|
82
|
-
return value;
|
|
83
|
-
};
|
|
84
|
-
// Convert: `[${code}] ${name}` => `[Mã] Tên`;
|
|
85
|
-
const templateToDisplay = (template, entity) => {
|
|
86
|
-
if (!template) {
|
|
87
|
-
return template;
|
|
88
|
-
}
|
|
89
|
-
const regex = /\$\{([A-Za-z0-9._-]*)\}/g;
|
|
90
|
-
const matches = template.match(regex) || [];
|
|
91
|
-
for (const match of matches) {
|
|
92
|
-
const key = match.slice(2, match.length - 1);
|
|
93
|
-
if (key) {
|
|
94
|
-
template = template.replace(match, getValueByPath(entity, key) ?? '');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return template;
|
|
98
|
-
};
|
|
99
|
-
const EXACT_TEMPLATE_REGEX = /^\$\{([A-Za-z0-9._-]*)\}$/;
|
|
100
|
-
const NUMBER_LITERAL_REGEX = /^-?\d+(\.\d+)?$/;
|
|
101
|
-
// Convert placeholder/literal an toàn, không thực thi JavaScript động.
|
|
102
|
-
const parseExpression = (template, entity) => {
|
|
103
|
-
if (!template) {
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
const trimmedTemplate = template.trim();
|
|
107
|
-
const exactTemplateMatch = trimmedTemplate.match(EXACT_TEMPLATE_REGEX);
|
|
108
|
-
if (exactTemplateMatch?.[1]) {
|
|
109
|
-
return getValueByPath(entity, exactTemplateMatch[1]);
|
|
110
|
-
}
|
|
111
|
-
if (trimmedTemplate === 'true') {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
if (trimmedTemplate === 'false') {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (trimmedTemplate === 'null') {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
if (trimmedTemplate === 'undefined') {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
if (NUMBER_LITERAL_REGEX.test(trimmedTemplate)) {
|
|
124
|
-
return Number(trimmedTemplate);
|
|
125
|
-
}
|
|
126
|
-
return templateToDisplay(template, entity);
|
|
127
|
-
};
|
|
128
|
-
const SALT = 'cb9f4b2a-d26c-4787-a66e-e7130ee00f95';
|
|
129
|
-
const encrypt = (obj) => {
|
|
130
|
-
obj = JSON.stringify(obj).split('');
|
|
131
|
-
for (let i = 0, l = obj.length; i < l; i++)
|
|
132
|
-
if (obj[i] == '{')
|
|
133
|
-
obj[i] = '}';
|
|
134
|
-
else if (obj[i] == '}')
|
|
135
|
-
obj[i] = '{';
|
|
136
|
-
return encodeURI(SALT + obj.join(''));
|
|
137
|
-
};
|
|
138
|
-
const decrypt = (encripted) => {
|
|
139
|
-
encripted = decodeURI(encripted);
|
|
140
|
-
if (SALT && encripted.indexOf(SALT) != 0)
|
|
141
|
-
throw new Error('object cannot be decrypted');
|
|
142
|
-
const strs = encripted.substring(SALT.length).split('');
|
|
143
|
-
for (let i = 0, l = strs.length; i < l; i++)
|
|
144
|
-
if (strs[i] == '{')
|
|
145
|
-
strs[i] = '}';
|
|
146
|
-
else if (strs[i] == '}')
|
|
147
|
-
strs[i] = '{';
|
|
148
|
-
return JSON.parse(strs.join(''));
|
|
149
|
-
};
|
|
150
|
-
// Conver "Đội Kỹ Thuật" thành "doi_ky_thuat"
|
|
151
|
-
const convertToSnakeCaseCode = (name) => {
|
|
152
|
-
if (typeof name !== 'string') {
|
|
153
|
-
throw new Error('Invalid name');
|
|
154
|
-
}
|
|
155
|
-
return name
|
|
156
|
-
.normalize('NFD') // Tách dấu
|
|
157
|
-
.replace(/[\u0300-\u036f]/g, '') // Xóa các dấu vừa tách
|
|
158
|
-
.replace(/[đĐ]/g, 'd') // Quan trọng: Xử lý chữ đ/Đ thành d
|
|
159
|
-
.trim()
|
|
160
|
-
.replace(/[^a-zA-Z0-9]+/g, '_') // Thay ký tự đặc biệt bằng _
|
|
161
|
-
.replace(/^_+|_+$/g, '') // Xóa _ ở đầu/cuối
|
|
162
|
-
.toLowerCase(); // Chuyển thành chữ thường: doi_ky_thuat
|
|
163
|
-
};
|
|
164
|
-
/**
|
|
165
|
-
* Tạo code duy nhất, tự động thêm suffix _n nếu bị trùng
|
|
166
|
-
* @param name Tên cần convert
|
|
167
|
-
* @param existingCodes Danh sách các code đã tồn tại
|
|
168
|
-
*/
|
|
169
|
-
const generateUniqueCode = (name, existingCodes) => {
|
|
170
|
-
// 1. Tạo base code trước
|
|
171
|
-
const baseCode = convertToSnakeCaseCode(name);
|
|
172
|
-
// 2. Nếu code chưa tồn tại thì trả về luôn
|
|
173
|
-
if (!existingCodes.includes(baseCode)) {
|
|
174
|
-
return baseCode;
|
|
175
|
-
}
|
|
176
|
-
// 3. Nếu trùng, bắt đầu loop để tìm index phù hợp
|
|
177
|
-
let index = 1;
|
|
178
|
-
let newCode = `${baseCode}_${index}`;
|
|
179
|
-
while (existingCodes.includes(newCode)) {
|
|
180
|
-
index++;
|
|
181
|
-
newCode = `${baseCode}_${index}`;
|
|
182
|
-
}
|
|
183
|
-
return newCode;
|
|
184
|
-
};
|
|
185
|
-
const sha256 = async (input) => {
|
|
186
|
-
const buffer = new TextEncoder().encode(input);
|
|
187
|
-
const hash = await crypto.subtle.digest('SHA-256', buffer);
|
|
188
|
-
const bytes = new Uint8Array(hash);
|
|
189
|
-
let binary = '';
|
|
190
|
-
for (let i = 0; i < bytes.length; i++)
|
|
191
|
-
binary += String.fromCharCode(bytes[i]);
|
|
192
|
-
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
193
|
-
};
|
|
5
|
+
// Deprecated wrappers — moved to ValidationUtilities
|
|
6
|
+
const isValidEmail = (value) => ValidationUtilities.isEmail(value);
|
|
7
|
+
const isValidPhone = (value) => ValidationUtilities.isPhone(value);
|
|
8
|
+
const isValidCode = (value) => ValidationUtilities.isCode(value);
|
|
194
9
|
const StringUtilities = {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
10
|
+
...StringUtilities$1,
|
|
11
|
+
// Deprecated aliases for renamed regex constants (renamed in @sdcorejs/utils v1.0.2)
|
|
12
|
+
/** @deprecated Use REGEX_VN_PHONE instead */
|
|
13
|
+
REGEX_PHONE_VN: StringUtilities$1.REGEX_VN_PHONE,
|
|
14
|
+
/** @deprecated Use REGEX_VN_ID instead */
|
|
15
|
+
REGEX_IDVN: StringUtilities$1.REGEX_VN_ID,
|
|
16
|
+
/** @deprecated Use REGEX_VN_ID_OR_PASSPORT instead */
|
|
17
|
+
REGEX_IDVN_OR_PASSPORT: StringUtilities$1.REGEX_VN_ID_OR_PASSPORT,
|
|
18
|
+
// Deprecated validators — moved to ValidationUtilities
|
|
19
|
+
/** @deprecated Use {@link ValidationUtilities.isEmail} instead */
|
|
202
20
|
isValidEmail,
|
|
21
|
+
/** @deprecated Use {@link ValidationUtilities.isPhone} instead */
|
|
203
22
|
isValidPhone,
|
|
23
|
+
/** @deprecated Use {@link ValidationUtilities.isCode} instead */
|
|
204
24
|
isValidCode,
|
|
205
|
-
changeAliasLowerCase: changeAliasLowerCase$1,
|
|
206
|
-
aliasIncludes,
|
|
207
|
-
format,
|
|
208
|
-
templateToDisplay,
|
|
209
|
-
parseExpression,
|
|
210
|
-
encrypt,
|
|
211
|
-
decrypt,
|
|
212
|
-
isNullOrEmpty,
|
|
213
|
-
isNullOrWhiteSpace,
|
|
214
|
-
convertToSnakeCaseCode,
|
|
215
|
-
generateUniqueCode,
|
|
216
|
-
sha256,
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
220
|
-
// export {};
|
|
221
|
-
// declare global {
|
|
222
|
-
// interface Array<T = any> {
|
|
223
|
-
// search(searchText: string | number, fields?: (string | undefined) | (string | undefined)[], children?: string): T[];
|
|
224
|
-
// paging(pageSize: number, page?: number): T[];
|
|
225
|
-
// toObject(key: string): Record<string, T>;
|
|
226
|
-
// union(key: string, ...args: T[][]): T[];
|
|
227
|
-
// distinct(): T[];
|
|
228
|
-
// }
|
|
229
|
-
// interface ArrayConstructor {
|
|
230
|
-
// search<T = any>(items: T[], searchText: string | number, fields?: (string | undefined) | (string | undefined)[]): T[];
|
|
231
|
-
// union<T = any>(key: string, ...args: T[][]): T[];
|
|
232
|
-
// toObject<T = any>(key: string, items: T[]): Record<string, T>;
|
|
233
|
-
// }
|
|
234
|
-
// }
|
|
235
|
-
const search = (items, searchText, fields, children) => {
|
|
236
|
-
if (!searchText?.toString()) {
|
|
237
|
-
return items;
|
|
238
|
-
}
|
|
239
|
-
if (!items?.length) {
|
|
240
|
-
return items;
|
|
241
|
-
}
|
|
242
|
-
if (Array.isArray(fields)) {
|
|
243
|
-
const fs = fields.filter(e => e !== undefined && e !== null && e !== '');
|
|
244
|
-
if (!fs.length) {
|
|
245
|
-
return items.filter(item => item !== undefined && item !== null && StringUtilities.aliasIncludes(item, searchText));
|
|
246
|
-
}
|
|
247
|
-
return items.filter(item => (item !== undefined && item !== null && fs.some(field => StringUtilities.aliasIncludes(item[field], searchText))) ||
|
|
248
|
-
(children && search(item[children], searchText, fields, children)?.length));
|
|
249
|
-
}
|
|
250
|
-
if (!fields) {
|
|
251
|
-
return items.filter(item => item !== undefined && item !== null && StringUtilities.aliasIncludes(item, searchText));
|
|
252
|
-
}
|
|
253
|
-
return items.filter(item => (item !== undefined && item !== null && StringUtilities.aliasIncludes(item[fields], searchText)) ||
|
|
254
|
-
(children && search(item[children], searchText, fields, children)?.length));
|
|
255
|
-
};
|
|
256
|
-
// Array.prototype.search = function <T = any>(
|
|
257
|
-
// searchText: any,
|
|
258
|
-
// fields?: (string | undefined) | (string | undefined)[],
|
|
259
|
-
// children?: string
|
|
260
|
-
// ): T[] {
|
|
261
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
262
|
-
// const items: T[] = this;
|
|
263
|
-
// return search(items, searchText, fields, children);
|
|
264
|
-
// };
|
|
265
|
-
// Array.prototype.paging = function <T extends Record<string, any> = any>(pageSize: number, page = 0): T[] {
|
|
266
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
267
|
-
// const items: T[] = this;
|
|
268
|
-
// return items.filter((_, index) => {
|
|
269
|
-
// return index >= page * pageSize && index < (page + 1) * pageSize;
|
|
270
|
-
// });
|
|
271
|
-
// };
|
|
272
|
-
// Array.prototype.toObject = function <T = any>(key: string): Record<string, T> {
|
|
273
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
274
|
-
// const items: T[] = this;
|
|
275
|
-
// return items
|
|
276
|
-
// .filter(item => item !== undefined && item !== null)
|
|
277
|
-
// .reduce<Record<string, any>>((obj, item: Record<string, any>) => {
|
|
278
|
-
// const objKey = item?.[key]?.toString();
|
|
279
|
-
// if (typeof objKey === 'string') {
|
|
280
|
-
// obj[objKey] = item;
|
|
281
|
-
// }
|
|
282
|
-
// return obj;
|
|
283
|
-
// }, {});
|
|
284
|
-
// };
|
|
285
|
-
// Array.prototype.union = function <T = Record<string, any>>(key: string, ...args: T[][]) {
|
|
286
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
287
|
-
// let results: T[] = this;
|
|
288
|
-
// const filterUnion = <T = any>(val: T, index: number, self: T[]) =>
|
|
289
|
-
// val !== undefined && val !== null && self.findIndex(e => (e as any)[key] === (val as any)[key]) === index;
|
|
290
|
-
// if (!args?.length) {
|
|
291
|
-
// return results.filter(filterUnion);
|
|
292
|
-
// }
|
|
293
|
-
// for (const arg of args) {
|
|
294
|
-
// if (Array.isArray(arg)) {
|
|
295
|
-
// results = [...results, ...arg].filter(filterUnion);
|
|
296
|
-
// }
|
|
297
|
-
// }
|
|
298
|
-
// return results;
|
|
299
|
-
// };
|
|
300
|
-
// Array.prototype.distinct = function () {
|
|
301
|
-
// if (Array.isArray(this)) {
|
|
302
|
-
// return [...new Set(this)];
|
|
303
|
-
// }
|
|
304
|
-
// return this;
|
|
305
|
-
// };
|
|
306
|
-
const union = (key, ...args) => {
|
|
307
|
-
let results = [];
|
|
308
|
-
const filterUnion = (val, index, self) => val !== undefined && val !== null && self.findIndex(e => e[key] === val[key]) === index;
|
|
309
|
-
if (!args?.length) {
|
|
310
|
-
return [];
|
|
311
|
-
}
|
|
312
|
-
for (const arg of args) {
|
|
313
|
-
if (Array.isArray(arg)) {
|
|
314
|
-
results = [...results, ...arg].filter(filterUnion);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return results;
|
|
318
|
-
};
|
|
319
|
-
const toObject = (key, items) => {
|
|
320
|
-
if (!Array.isArray(items)) {
|
|
321
|
-
return {};
|
|
322
|
-
}
|
|
323
|
-
return items
|
|
324
|
-
.filter(item => item !== undefined && item !== null)
|
|
325
|
-
.reduce((obj, item) => {
|
|
326
|
-
const objKey = item?.[key]?.toString();
|
|
327
|
-
if (typeof objKey === 'string') {
|
|
328
|
-
obj[objKey] = item;
|
|
329
|
-
}
|
|
330
|
-
return obj;
|
|
331
|
-
}, {});
|
|
332
|
-
};
|
|
333
|
-
const distinct = (items) => {
|
|
334
|
-
if (Array.isArray(items)) {
|
|
335
|
-
return [...new Set(items)];
|
|
336
|
-
}
|
|
337
|
-
return [];
|
|
338
|
-
};
|
|
339
|
-
const paging = (items, pageSize, page = 0) => {
|
|
340
|
-
if (!Array.isArray(items)) {
|
|
341
|
-
return [];
|
|
342
|
-
}
|
|
343
|
-
return items.filter((_, index) => {
|
|
344
|
-
return index >= page * pageSize && index < (page + 1) * pageSize;
|
|
345
|
-
});
|
|
346
|
-
};
|
|
347
|
-
const ArrayUtilities = {
|
|
348
|
-
search,
|
|
349
|
-
union,
|
|
350
|
-
toObject,
|
|
351
|
-
distinct,
|
|
352
|
-
paging,
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
356
|
-
// export {};
|
|
357
|
-
// declare global {
|
|
358
|
-
// interface NumberConstructor {
|
|
359
|
-
// toVNCurrency(value: any): string | null;
|
|
360
|
-
// toISO(value: any): string | null;
|
|
361
|
-
// isNumber(value: any): boolean;
|
|
362
|
-
// isPositiveInteger(value: any): boolean;
|
|
363
|
-
// isPositiveNumber(value: any): boolean;
|
|
364
|
-
// round(value: any, digits?: number): number | null;
|
|
365
|
-
// }
|
|
366
|
-
// }
|
|
367
|
-
const toVNCurrency = (value) => {
|
|
368
|
-
value = (value ?? '').toString().replace(/,/g, '');
|
|
369
|
-
if (!value) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
const val = +value;
|
|
373
|
-
if (!Number.isNaN(val)) {
|
|
374
|
-
return val.toLocaleString('vi-VN', { maximumFractionDigits: 10 });
|
|
375
|
-
}
|
|
376
|
-
return null;
|
|
377
|
-
};
|
|
378
|
-
const toVN = (value) => {
|
|
379
|
-
value = (value ?? '').toString().replace(/,/g, '');
|
|
380
|
-
if (!value) {
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
383
|
-
const val = +value;
|
|
384
|
-
if (!Number.isNaN(val)) {
|
|
385
|
-
return val.toLocaleString('vi-VN', { maximumFractionDigits: 10 });
|
|
386
|
-
}
|
|
387
|
-
return null;
|
|
388
|
-
};
|
|
389
|
-
const toISO = (value) => {
|
|
390
|
-
value = (value ?? '').toString().replace(/,/g, '');
|
|
391
|
-
if (!value) {
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
const val = +value;
|
|
395
|
-
if (!Number.isNaN(val)) {
|
|
396
|
-
return val.toLocaleString('en-US', { maximumFractionDigits: 10 });
|
|
397
|
-
}
|
|
398
|
-
return null;
|
|
399
|
-
};
|
|
400
|
-
const isPositiveInteger = (value) => {
|
|
401
|
-
if (!value) {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
404
|
-
const regex = /^([0-9]*)$/;
|
|
405
|
-
return regex.test(value) && +value > 0;
|
|
406
|
-
};
|
|
407
|
-
const isPositiveNumber = (value) => {
|
|
408
|
-
if (!value) {
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
const regex = /^([0-9]*)(\.[0-9]+$){0,1}$/;
|
|
412
|
-
return regex.test(value) && +value > 0;
|
|
413
|
-
};
|
|
414
|
-
const isNumber = (value) => {
|
|
415
|
-
if (value === undefined || value === null || value === '') {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
return !Number.isNaN(+value);
|
|
419
|
-
};
|
|
420
|
-
const round = (value, digits) => {
|
|
421
|
-
digits = digits ?? 2;
|
|
422
|
-
if (!NumberUtilities.isNumber(value)) {
|
|
423
|
-
return null;
|
|
424
|
-
}
|
|
425
|
-
const val = Math.pow(10, digits);
|
|
426
|
-
return Math.round(value * val) / val;
|
|
427
|
-
};
|
|
428
|
-
const NumberUtilities = {
|
|
429
|
-
toVNCurrency,
|
|
430
|
-
toVN,
|
|
431
|
-
toISO,
|
|
432
|
-
isPositiveInteger,
|
|
433
|
-
isPositiveNumber,
|
|
434
|
-
isNumber,
|
|
435
|
-
round,
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
439
|
-
// export {};
|
|
440
|
-
// declare global {
|
|
441
|
-
// interface Date {
|
|
442
|
-
// begin(): Date;
|
|
443
|
-
// end(): Date;
|
|
444
|
-
// addMiliseconds(miliseconds: number): Date;
|
|
445
|
-
// addHours(hours: number): Date;
|
|
446
|
-
// addDays(days: number): Date;
|
|
447
|
-
// addMonths(months: number): Date;
|
|
448
|
-
// toFormat(format: string): string;
|
|
449
|
-
// }
|
|
450
|
-
// interface DateConstructor {
|
|
451
|
-
// equal(date1: any, date2: any): boolean;
|
|
452
|
-
// dayDiff(date1: any, date2: any): number | null;
|
|
453
|
-
// monthDiff(date1: any, date2: any): number | null;
|
|
454
|
-
// yearDiff(date1: any, date2: any): number | null;
|
|
455
|
-
// age(date1: any, date2: any): number | null;
|
|
456
|
-
// parseFrom(value: any, format: string): Date | null;
|
|
457
|
-
// isDate(value: any): boolean;
|
|
458
|
-
// toFormat(value: any, format: string): string;
|
|
459
|
-
// addMiliseconds(value: any, miliseconds: number): Date | null;
|
|
460
|
-
// addHours(value: any, hours: number): Date | null;
|
|
461
|
-
// addDays(value: any, days: number): Date | null;
|
|
462
|
-
// addMonths(value: any, months: number): Date | null;
|
|
463
|
-
// begin(value: any): Date | null;
|
|
464
|
-
// end(value: any): Date | null;
|
|
465
|
-
// timeDifference(previous: any, current?: any): string;
|
|
466
|
-
// }
|
|
467
|
-
// }
|
|
468
|
-
const isDate = (value) => {
|
|
469
|
-
if (value === undefined || value === null || value === '') {
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
if (typeof value === 'string') {
|
|
473
|
-
// Kiểm tra regex
|
|
474
|
-
if (value.length < 8) {
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
const date1 = value.substring(0, 10); // yyyy/MM/dd
|
|
479
|
-
const date2 = value.substring(0, 9); // yyyy/M/dd, yyyy/MM/d
|
|
480
|
-
const date3 = value.substring(0, 8); // yyyy/M/d
|
|
481
|
-
const regex1 = /^(0[1-9]|[1-9]|1[0-2])(-|\/)(0[1-9]|[1-9]|[12][0-9]|3[01])(-|\/)(\d{4})$/; // MM/dd/yyyy, MM-dd-yyyy
|
|
482
|
-
const regex2 = /^(\d{4})(-|\/)(0[1-9]|[1-9]|1[0-2])(-|\/)(0[1-9]|[1-9]|[12][0-9]|3[01])$/; // yyyy/MM/dd, yyyy-MM-dd
|
|
483
|
-
const result = regex1.test(date1) || regex2.test(date1) || regex1.test(date2) || regex2.test(date2) || regex1.test(date3) || regex2.test(date3);
|
|
484
|
-
if (result) {
|
|
485
|
-
const date = new Date(value);
|
|
486
|
-
return !isNaN(date.getTime());
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
return false;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
const date = new Date(value);
|
|
495
|
-
return !isNaN(date.getTime());
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
// const toFormatBackup = (value: any, format: string): string => {
|
|
499
|
-
// if (!isDate(value)) {
|
|
500
|
-
// return '';
|
|
501
|
-
// }
|
|
502
|
-
// const date: Date = new Date(value);
|
|
503
|
-
// let result = format;
|
|
504
|
-
// result = result.replace('yyyy', date.getFullYear().toString());
|
|
505
|
-
// result = result.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'));
|
|
506
|
-
// result = result.replace('dd', date.getDate().toString().padStart(2, '0'));
|
|
507
|
-
// result = result.replace('HH', date.getHours().toString().padStart(2, '0'));
|
|
508
|
-
// result = result.replace('mm', date.getMinutes().toString().padStart(2, '0'));
|
|
509
|
-
// result = result.replace('ss', date.getSeconds().toString().padStart(2, '0'));
|
|
510
|
-
// return result;
|
|
511
|
-
// };
|
|
512
|
-
const toFormat = (value, format) => {
|
|
513
|
-
if (!isDate(value)) {
|
|
514
|
-
return '';
|
|
515
|
-
}
|
|
516
|
-
const isDatePart = (type) => {
|
|
517
|
-
return type === 'year' || type === 'month' || type === 'day' || type === 'hour' || type === 'minute' || type === 'second';
|
|
518
|
-
};
|
|
519
|
-
const date = new Date(value);
|
|
520
|
-
const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
521
|
-
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
522
|
-
timeZone,
|
|
523
|
-
year: 'numeric',
|
|
524
|
-
month: '2-digit',
|
|
525
|
-
day: '2-digit',
|
|
526
|
-
hour: '2-digit',
|
|
527
|
-
minute: '2-digit',
|
|
528
|
-
second: '2-digit',
|
|
529
|
-
hour12: false,
|
|
530
|
-
}).formatToParts(date);
|
|
531
|
-
const map = {};
|
|
532
|
-
parts.forEach(part => {
|
|
533
|
-
if (isDatePart(part.type)) {
|
|
534
|
-
map[part.type] = part.value;
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
return format
|
|
538
|
-
.replace('yyyy', map.year ?? '')
|
|
539
|
-
.replace('MM', map.month ?? '')
|
|
540
|
-
.replace('dd', map.day ?? '')
|
|
541
|
-
.replace('HH', map.hour ?? '')
|
|
542
|
-
.replace('mm', map.minute ?? '')
|
|
543
|
-
.replace('ss', map.second ?? '');
|
|
544
|
-
};
|
|
545
|
-
const addDays = (value, days) => {
|
|
546
|
-
if (!isDate(value)) {
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
549
|
-
const date = new Date(value);
|
|
550
|
-
date.setDate(date.getDate() + days);
|
|
551
|
-
return date;
|
|
552
|
-
};
|
|
553
|
-
const addMiliseconds = (value, miliseconds) => {
|
|
554
|
-
if (!isDate(value)) {
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
const date = new Date(value);
|
|
558
|
-
date.setMilliseconds(date.getMilliseconds() + miliseconds);
|
|
559
|
-
return date;
|
|
560
|
-
};
|
|
561
|
-
const begin = (value) => {
|
|
562
|
-
if (!isDate(value)) {
|
|
563
|
-
return null;
|
|
564
|
-
}
|
|
565
|
-
return new Date(toFormat(value, 'MM/dd/yyyy'));
|
|
566
|
-
};
|
|
567
|
-
const end = (value) => {
|
|
568
|
-
if (!isDate(value)) {
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
return addMiliseconds(begin(addDays(value, 1)), -1);
|
|
572
|
-
};
|
|
573
|
-
const equal = (date1, date2) => {
|
|
574
|
-
if (!isDate(date1) && !isDate(date2)) {
|
|
575
|
-
return true;
|
|
576
|
-
}
|
|
577
|
-
if (!isDate(date1) || !isDate(date2)) {
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
return new Date(date1).getTime() === new Date(date2).getTime();
|
|
581
|
-
};
|
|
582
|
-
const dayDiff = (date1, date2) => {
|
|
583
|
-
if (!isDate(date1) || !isDate(date2)) {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
const diff = new Date(date2).getTime() - new Date(date1).getTime();
|
|
587
|
-
return Math.floor(diff / (24 * 3600 * 1000));
|
|
588
|
-
};
|
|
589
|
-
const monthDiff = (date1, date2) => {
|
|
590
|
-
if (!isDate(date1) || !isDate(date2)) {
|
|
591
|
-
return null;
|
|
592
|
-
}
|
|
593
|
-
const d1Y = date1.getFullYear();
|
|
594
|
-
const d2Y = date2.getFullYear();
|
|
595
|
-
const d1M = date2.getMonth();
|
|
596
|
-
const d2M = date2.getMonth();
|
|
597
|
-
return d2M + 12 * d2Y - (d1M + 12 * d1Y);
|
|
598
|
-
};
|
|
599
|
-
const yearDiff = (date1, date2) => {
|
|
600
|
-
if (!isDate(date1) || !isDate(date2)) {
|
|
601
|
-
return null;
|
|
602
|
-
}
|
|
603
|
-
return new Date(date2).getFullYear() - new Date(date1).getFullYear();
|
|
604
|
-
};
|
|
605
|
-
const age = (date1, date2) => {
|
|
606
|
-
const diff = monthDiff(date1, date2);
|
|
607
|
-
if (!diff) {
|
|
608
|
-
return null;
|
|
609
|
-
}
|
|
610
|
-
return NumberUtilities.round(diff / 12);
|
|
611
|
-
};
|
|
612
|
-
const parseFrom = (value, format) => {
|
|
613
|
-
if (!value) {
|
|
614
|
-
return null;
|
|
615
|
-
}
|
|
616
|
-
if (!format) {
|
|
617
|
-
return null;
|
|
618
|
-
}
|
|
619
|
-
value = value.toString();
|
|
620
|
-
const dmy = format.indexOf('dd') > -1 && format.indexOf('MM') > -1 && format.indexOf('yyyy') > -1;
|
|
621
|
-
const hms = format.indexOf('HH') > -1 || format.indexOf('mm') > -1 || format.indexOf('ss') > -1;
|
|
622
|
-
let strDate = '';
|
|
623
|
-
if (dmy) {
|
|
624
|
-
const dd = value.substr(format.indexOf('dd'), 'dd'.length);
|
|
625
|
-
const MM = value.substr(format.indexOf('MM'), 'MM'.length);
|
|
626
|
-
const yyyy = value.substr(format.indexOf('yyyy'), 'yyyy'.length);
|
|
627
|
-
if (+yyyy > 0 && +MM > 0 && +dd > 0) {
|
|
628
|
-
strDate += `${MM}/${dd}/${yyyy}`;
|
|
629
|
-
}
|
|
630
|
-
else {
|
|
631
|
-
return null;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
const today = new Date();
|
|
636
|
-
const yyyy = today.getFullYear().toString();
|
|
637
|
-
const MM = (today.getMonth() + 1).toString().padStart(2, '0');
|
|
638
|
-
const dd = today.getDate().toString().padStart(2, '0');
|
|
639
|
-
strDate += `${MM}/${dd}/${yyyy}`;
|
|
640
|
-
}
|
|
641
|
-
if (hms) {
|
|
642
|
-
const HH = format.indexOf('HH') > -1 ? value.substr(format.indexOf('HH'), 'HH'.length) : '00';
|
|
643
|
-
const mm = format.indexOf('mm') > -1 ? value.substr(format.indexOf('mm'), 'mm'.length) : '00';
|
|
644
|
-
const ss = format.indexOf('ss') > -1 ? value.substr(format.indexOf('ss'), 'ss'.length) : '00';
|
|
645
|
-
strDate += ` ${HH || '00'}:${mm || '00'}:${ss || '00'}`;
|
|
646
|
-
}
|
|
647
|
-
if (!isDate(strDate)) {
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
return new Date(strDate);
|
|
651
|
-
};
|
|
652
|
-
const addHours = (value, hours) => {
|
|
653
|
-
if (!isDate(value)) {
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
const date = new Date(value);
|
|
657
|
-
date.setHours(date.getHours() + hours);
|
|
658
|
-
return date;
|
|
659
|
-
};
|
|
660
|
-
const addMonths = (value, months) => {
|
|
661
|
-
if (!isDate(value)) {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
const date = new Date(value);
|
|
665
|
-
date.setMonth(date.getMonth() + months);
|
|
666
|
-
return date;
|
|
667
|
-
};
|
|
668
|
-
const timeDifference = function (previous, current = new Date()) {
|
|
669
|
-
if (!isDate(previous)) {
|
|
670
|
-
return '';
|
|
671
|
-
}
|
|
672
|
-
if (!isDate(current)) {
|
|
673
|
-
return '';
|
|
674
|
-
}
|
|
675
|
-
const from = new Date(previous);
|
|
676
|
-
const to = new Date(current);
|
|
677
|
-
const msPerMinute = 60 * 1000;
|
|
678
|
-
const msPerHour = msPerMinute * 60;
|
|
679
|
-
const msPerDay = msPerHour * 24;
|
|
680
|
-
const msPerMonth = msPerDay * 30;
|
|
681
|
-
const msPerYear = msPerDay * 365;
|
|
682
|
-
const elapsed = to.getTime() - from.getTime();
|
|
683
|
-
if (elapsed < msPerMinute) {
|
|
684
|
-
return `${Math.round(elapsed / 1000)} seconds ago`;
|
|
685
|
-
}
|
|
686
|
-
if (elapsed < msPerHour) {
|
|
687
|
-
return `${Math.round(elapsed / msPerMinute)} minutes ago`;
|
|
688
|
-
}
|
|
689
|
-
if (elapsed < msPerDay) {
|
|
690
|
-
return `${Math.round(elapsed / msPerHour)} hours ago`;
|
|
691
|
-
}
|
|
692
|
-
if (elapsed < msPerMonth) {
|
|
693
|
-
return `${Math.round(elapsed / msPerDay)} days ago`;
|
|
694
|
-
}
|
|
695
|
-
if (elapsed < msPerYear) {
|
|
696
|
-
return `${Math.round(elapsed / msPerMonth)} months ago`;
|
|
697
|
-
}
|
|
698
|
-
return `${Math.round(elapsed / msPerYear)} years ago`;
|
|
699
|
-
};
|
|
700
|
-
// Date.prototype.begin = function () {
|
|
701
|
-
// return new Date(this.toFormat('MM/dd/yyyy'));
|
|
702
|
-
// };
|
|
703
|
-
// Date.prototype.end = function () {
|
|
704
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
705
|
-
// const date: Date = this;
|
|
706
|
-
// return date.addDays(1).begin().addMiliseconds(-1);
|
|
707
|
-
// };
|
|
708
|
-
// Date.prototype.addMiliseconds = function (miliseconds: number) {
|
|
709
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
710
|
-
// const date: Date = this;
|
|
711
|
-
// date.setMilliseconds(date.getMilliseconds() + miliseconds);
|
|
712
|
-
// return date;
|
|
713
|
-
// };
|
|
714
|
-
// Date.prototype.addHours = function (hours: number) {
|
|
715
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
716
|
-
// const date: Date = this;
|
|
717
|
-
// date.setHours(date.getHours() + hours);
|
|
718
|
-
// return date;
|
|
719
|
-
// };
|
|
720
|
-
// Date.prototype.addDays = function (days: number) {
|
|
721
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
722
|
-
// const date: Date = this;
|
|
723
|
-
// date.setDate(date.getDate() + days);
|
|
724
|
-
// return date;
|
|
725
|
-
// };
|
|
726
|
-
// Date.prototype.addMonths = function (months: number) {
|
|
727
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
728
|
-
// const date: Date = this;
|
|
729
|
-
// date.setMonth(date.getMonth() + months);
|
|
730
|
-
// return date;
|
|
731
|
-
// };
|
|
732
|
-
// Date.prototype.toFormat = function (format: string) {
|
|
733
|
-
// // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
734
|
-
// const date: Date = this;
|
|
735
|
-
// let result = format;
|
|
736
|
-
// result = result.replace('yyyy', date.getFullYear().toString());
|
|
737
|
-
// result = result.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'));
|
|
738
|
-
// result = result.replace('dd', date.getDate().toString().padStart(2, '0'));
|
|
739
|
-
// result = result.replace('HH', date.getHours().toString().padStart(2, '0'));
|
|
740
|
-
// result = result.replace('mm', date.getMinutes().toString().padStart(2, '0'));
|
|
741
|
-
// result = result.replace('ss', date.getSeconds().toString().padStart(2, '0'));
|
|
742
|
-
// return result;
|
|
743
|
-
// };
|
|
744
|
-
const DateUtilities = {
|
|
745
|
-
equal,
|
|
746
|
-
dayDiff,
|
|
747
|
-
monthDiff,
|
|
748
|
-
yearDiff,
|
|
749
|
-
age,
|
|
750
|
-
parseFrom,
|
|
751
|
-
isDate,
|
|
752
|
-
toFormat,
|
|
753
|
-
addMiliseconds,
|
|
754
|
-
addHours,
|
|
755
|
-
addDays,
|
|
756
|
-
addMonths,
|
|
757
|
-
begin,
|
|
758
|
-
end,
|
|
759
|
-
timeDifference,
|
|
760
25
|
};
|
|
761
26
|
|
|
762
27
|
// Hardcoded i18n cho upload utility — pure function nên không inject() được I18nService.
|
|
@@ -1091,6 +356,11 @@ const getNestedValue = (obj, path) => {
|
|
|
1091
356
|
// Tách chuỗi theo dấu chấm và duyệt qua từng cấp của object
|
|
1092
357
|
return path.split('.').reduce((acc, part) => acc?.[part], obj);
|
|
1093
358
|
};
|
|
359
|
+
/**
|
|
360
|
+
* @deprecated Use {@link Utilities} and {@link BrowserUtilities} from `@sdcorejs/utils/fns` instead.
|
|
361
|
+
* - upload/download/clipboard/isMobile → BrowserUtilities
|
|
362
|
+
* - allWithPaging (→ fetchAllByPaging) / randomId / hash / parseQueryParams / generateUuid / getNestedValue → Utilities
|
|
363
|
+
*/
|
|
1094
364
|
const SdUtilities = {
|
|
1095
365
|
upload,
|
|
1096
366
|
download,
|
|
@@ -1108,74 +378,14 @@ const SdUtilities = {
|
|
|
1108
378
|
getNestedValue
|
|
1109
379
|
};
|
|
1110
380
|
|
|
1111
|
-
/**
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
/**
|
|
1116
|
-
* Chuyển đổi màu HSL sang định dạng hex
|
|
1117
|
-
* @param h - Hue (0-360)
|
|
1118
|
-
* @param s - Saturation (0-100)
|
|
1119
|
-
* @param l - Lightness (0-100)
|
|
1120
|
-
* @returns Mã màu hex
|
|
1121
|
-
*/
|
|
1122
|
-
function hslToHex(h, s, l) {
|
|
1123
|
-
// Chuyển đổi sang định dạng 0-1
|
|
1124
|
-
const hNormalized = h / 360;
|
|
1125
|
-
const sNormalized = s / 100;
|
|
1126
|
-
const lNormalized = l / 100;
|
|
1127
|
-
let r, g, b;
|
|
1128
|
-
if (sNormalized === 0) {
|
|
1129
|
-
// Màu xám (grayscale)
|
|
1130
|
-
r = g = b = lNormalized;
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
const hue2rgb = (p, q, t) => {
|
|
1134
|
-
if (t < 0)
|
|
1135
|
-
t += 1;
|
|
1136
|
-
if (t > 1)
|
|
1137
|
-
t -= 1;
|
|
1138
|
-
if (t < 1 / 6)
|
|
1139
|
-
return p + (q - p) * 6 * t;
|
|
1140
|
-
if (t < 1 / 2)
|
|
1141
|
-
return q;
|
|
1142
|
-
if (t < 2 / 3)
|
|
1143
|
-
return p + (q - p) * (2 / 3 - t) * 6;
|
|
1144
|
-
return p;
|
|
1145
|
-
};
|
|
1146
|
-
const q = lNormalized < 0.5
|
|
1147
|
-
? lNormalized * (1 + sNormalized)
|
|
1148
|
-
: lNormalized + sNormalized - lNormalized * sNormalized;
|
|
1149
|
-
const p = 2 * lNormalized - q;
|
|
1150
|
-
r = hue2rgb(p, q, hNormalized + 1 / 3);
|
|
1151
|
-
g = hue2rgb(p, q, hNormalized);
|
|
1152
|
-
b = hue2rgb(p, q, hNormalized - 1 / 3);
|
|
1153
|
-
}
|
|
1154
|
-
// Chuyển đổi sang hex
|
|
1155
|
-
const toHex = (x) => {
|
|
1156
|
-
const hex = Math.round(x * 255).toString(16);
|
|
1157
|
-
return hex.length === 1 ? '0' + hex : hex;
|
|
1158
|
-
};
|
|
1159
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Chuyển đổi màu RGB sang hex
|
|
1163
|
-
* @param r - Red (0-255)
|
|
1164
|
-
* @param g - Green (0-255)
|
|
1165
|
-
* @param b - Blue (0-255)
|
|
1166
|
-
* @returns Mã màu hex
|
|
1167
|
-
*/
|
|
1168
|
-
function rgbToHex(r, g, b) {
|
|
1169
|
-
const toHex = (x) => {
|
|
1170
|
-
const hex = Math.round(Math.max(0, Math.min(255, x))).toString(16);
|
|
1171
|
-
return hex.length === 1 ? '0' + hex : hex;
|
|
1172
|
-
};
|
|
1173
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
1174
|
-
}
|
|
381
|
+
/** @deprecated Use {@link ColorUtilities.hslToHex} from `@sdcorejs/utils/fns` instead */
|
|
382
|
+
const hslToHex = ColorUtilities.hslToHex;
|
|
383
|
+
/** @deprecated Use {@link ColorUtilities.rgbToHex} from `@sdcorejs/utils/fns` instead */
|
|
384
|
+
const rgbToHex = ColorUtilities.rgbToHex;
|
|
1175
385
|
|
|
1176
386
|
/**
|
|
1177
387
|
* Generated bundle index. Do not edit.
|
|
1178
388
|
*/
|
|
1179
389
|
|
|
1180
|
-
export {
|
|
390
|
+
export { SdUtilities, StringUtilities, hslToHex, rgbToHex };
|
|
1181
391
|
//# sourceMappingURL=sd-angular-core-utilities-extensions.mjs.map
|