@sd-angular/core 19.0.0-beta.95 → 19.0.0-beta.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +8 -9
  2. package/components/badge/src/badge.component.d.ts +3 -4
  3. package/components/button/src/button.component.d.ts +2 -2
  4. package/components/document-builder/src/components/header-footer-builder/header-footer-builder.component.d.ts +2 -2
  5. package/components/document-builder/src/document-builder.model.d.ts +6 -2
  6. package/components/editor/src/editor.component.d.ts +8 -0
  7. package/components/editor/src/models/image-upload.plugin.model.d.ts +33 -0
  8. package/components/editor/src/plugins/image-upload/utils/style.utils.d.ts +1 -0
  9. package/components/index.d.ts +0 -2
  10. package/components/modal/src/modal.component.d.ts +1 -2
  11. package/components/section/src/section.component.d.ts +1 -2
  12. package/components/table/index.d.ts +1 -0
  13. package/components/table/src/components/column-filter/column-filter.component.d.ts +3 -3
  14. package/components/table/src/models/index.d.ts +1 -0
  15. package/components/table/src/models/table-item.model.d.ts +10 -0
  16. package/components/table/src/models/table-option-style.model.d.ts +6 -1
  17. package/components/table/src/models/table-option-tree.model.d.ts +12 -0
  18. package/components/table/src/models/table-option.model.d.ts +6 -0
  19. package/components/table/src/pipes/index.d.ts +1 -0
  20. package/components/table/src/pipes/sd-tree.pipe.d.ts +9 -0
  21. package/components/table/src/services/tree/tree.util.d.ts +14 -0
  22. package/components/table/src/table.component.d.ts +3 -0
  23. package/fesm2022/sd-angular-core-components-document-builder.mjs +9 -4
  24. package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
  25. package/fesm2022/sd-angular-core-components-editor.mjs +222 -97
  26. package/fesm2022/sd-angular-core-components-editor.mjs.map +1 -1
  27. package/fesm2022/sd-angular-core-components-form-generic.mjs +4 -4
  28. package/fesm2022/sd-angular-core-components-form-generic.mjs.map +1 -1
  29. package/fesm2022/sd-angular-core-components-tab-router.mjs +80 -33
  30. package/fesm2022/sd-angular-core-components-tab-router.mjs.map +1 -1
  31. package/fesm2022/sd-angular-core-components-table.mjs +325 -41
  32. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  33. package/fesm2022/sd-angular-core-components.mjs +0 -2
  34. package/fesm2022/sd-angular-core-components.mjs.map +1 -1
  35. package/fesm2022/sd-angular-core-forms-input.mjs +20 -5
  36. package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
  37. package/fesm2022/sd-angular-core-i18n.mjs +35 -0
  38. package/fesm2022/sd-angular-core-i18n.mjs.map +1 -1
  39. package/fesm2022/sd-angular-core-models.mjs +1 -4
  40. package/fesm2022/sd-angular-core-models.mjs.map +1 -1
  41. package/fesm2022/sd-angular-core-modules-layout.mjs +172 -37
  42. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  43. package/fesm2022/sd-angular-core-services-notify.mjs +21 -21
  44. package/fesm2022/sd-angular-core-services-notify.mjs.map +1 -1
  45. package/fesm2022/sd-angular-core-utilities-extensions.mjs +29 -819
  46. package/fesm2022/sd-angular-core-utilities-extensions.mjs.map +1 -1
  47. package/fesm2022/sd-angular-core-utilities-models.mjs +36 -136
  48. package/fesm2022/sd-angular-core-utilities-models.mjs.map +1 -1
  49. package/forms/autocomplete/src/autocomplete.component.d.ts +1 -2
  50. package/forms/chip/src/chip.component.d.ts +1 -2
  51. package/forms/chip-calendar/src/chip-calendar.component.d.ts +1 -2
  52. package/forms/date/src/date.component.d.ts +1 -2
  53. package/forms/date-range/src/date-range.component.d.ts +1 -2
  54. package/forms/datetime/src/datetime.component.d.ts +1 -2
  55. package/forms/input/src/input.component.d.ts +1 -2
  56. package/forms/input-number/src/input-number.component.d.ts +1 -2
  57. package/forms/select/src/select.component.d.ts +5 -5
  58. package/forms/textarea/src/textarea.component.d.ts +1 -2
  59. package/i18n/src/en.d.ts +6 -0
  60. package/models/src/language.model.d.ts +2 -4
  61. package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +8 -1
  62. package/modules/layout/configurations/layout.configuration.d.ts +8 -0
  63. package/modules/layout/services/storage/storage.service.d.ts +2 -0
  64. package/package.json +68 -79
  65. package/sd-angular-core-19.0.0-beta.97.tgz +0 -0
  66. package/services/notify/index.d.ts +1 -1
  67. package/services/notify/src/components/toast/toast.component.d.ts +3 -3
  68. package/services/notify/src/components/toast-container.component.d.ts +3 -4
  69. package/services/notify/src/notify.model.d.ts +1 -1
  70. package/services/notify/src/notify.service.d.ts +5 -5
  71. package/utilities/extensions/src/array.extension.d.ts +1 -7
  72. package/utilities/extensions/src/color.extension.d.ts +5 -20
  73. package/utilities/extensions/src/date.extension.d.ts +1 -18
  74. package/utilities/extensions/src/number.extension.d.ts +1 -10
  75. package/utilities/extensions/src/string.extension.d.ts +30 -4
  76. package/utilities/extensions/src/utility.extension.d.ts +6 -0
  77. package/utilities/models/src/color.model.d.ts +4 -1
  78. package/utilities/models/src/empty.model.d.ts +3 -0
  79. package/utilities/models/src/filter.model.d.ts +12 -24
  80. package/utilities/models/src/maybe-async.model.d.ts +10 -4
  81. package/utilities/models/src/nested-key-of.model.d.ts +4 -5
  82. package/utilities/models/src/operator.model.d.ts +9 -4
  83. package/utilities/models/src/order.model.d.ts +4 -5
  84. package/utilities/models/src/paging.model.d.ts +8 -16
  85. package/utilities/models/src/pattern.model.d.ts +13 -0
  86. package/utilities/models/src/size.model.d.ts +4 -1
  87. package/components/anchor-v2/index.d.ts +0 -1
  88. package/components/anchor-v2/src/components/anchor-item-v2/anchor-item-v2.component.d.ts +0 -12
  89. package/components/anchor-v2/src/components/anchor-v2/anchor-v2.component.d.ts +0 -19
  90. package/components/anchor-v2/src/components/anchor-vertical-v2/anchor-vertical-list-v2.component.d.ts +0 -16
  91. package/components/anchor-v2/src/components/index.d.ts +0 -2
  92. package/components/anchor-v2/src/index.d.ts +0 -2
  93. package/components/anchor-v2/src/models/index.d.ts +0 -1
  94. package/components/anchor-v2/src/models/sd-anchor-v2.model.d.ts +0 -7
  95. package/components/workflow/index.d.ts +0 -2
  96. package/components/workflow/src/configurations/index.d.ts +0 -1
  97. package/components/workflow/src/configurations/workflow.configuration.d.ts +0 -10
  98. package/fesm2022/sd-angular-core-components-anchor-v2.mjs +0 -151
  99. package/fesm2022/sd-angular-core-components-anchor-v2.mjs.map +0 -1
  100. package/fesm2022/sd-angular-core-components-workflow.mjs +0 -17
  101. package/fesm2022/sd-angular-core-components-workflow.mjs.map +0 -1
  102. package/fesm2022/sd-angular-core-services-firebase.mjs +0 -38
  103. package/fesm2022/sd-angular-core-services-firebase.mjs.map +0 -1
  104. package/sd-angular-core-19.0.0-beta.95.tgz +0 -0
  105. package/services/firebase/index.d.ts +0 -2
  106. package/services/firebase/src/firebase.model.d.ts +0 -8
  107. package/services/firebase/src/firebase.service.d.ts +0 -14
@@ -1,762 +1,27 @@
1
- /* eslint-disable no-useless-escape */
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
- // declare global {
4
- // interface StringConstructor {
5
- // isValidEmail(value: any): boolean;
6
- // isValidPhone(value: any): boolean;
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
- REGEX_EMAIL,
196
- REGEX_PHONE,
197
- REGEX_PHONE_VN,
198
- REGEX_IDVN,
199
- REGEX_PASSPORT,
200
- REGEX_IDVN_OR_PASSPORT,
201
- REGEX_TIME,
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
- * Color Utilities Extension
1113
- * Các tiện ích xử màu
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 { ArrayUtilities, DateUtilities, NumberUtilities, SdUtilities, StringUtilities, hslToHex, rgbToHex };
390
+ export { SdUtilities, StringUtilities, hslToHex, rgbToHex };
1181
391
  //# sourceMappingURL=sd-angular-core-utilities-extensions.mjs.map