es-grid-template 1.8.64 → 1.8.66

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 (222) hide show
  1. package/es/grid-component/TempTable.d.ts +2 -2
  2. package/es/grid-component/TempTable.js +5 -5
  3. package/es/grid-component/hooks/index.js +0 -1
  4. package/es/grid-component/hooks/useLazyKVMap.d.ts +1 -1
  5. package/es/grid-component/hooks/useLazyKVMap.js +0 -4
  6. package/es/grid-component/hooks/utils.d.ts +1 -8
  7. package/es/grid-component/hooks/utils.js +176 -144
  8. package/es/grid-component/index.d.ts +1 -1
  9. package/es/grid-component/index.js +0 -4
  10. package/es/grid-component/styles.scss +1394 -1394
  11. package/es/grid-component/type.d.ts +0 -407
  12. package/es/grid-component/type.js +490 -1
  13. package/es/table-component/type.d.ts +10 -0
  14. package/es/{grid-component → table-virtuoso}/ColumnsGroup/ColumnsGroup.js +4 -3
  15. package/es/{grid-component → table-virtuoso}/InternalTable.d.ts +2 -3
  16. package/es/table-virtuoso/InternalTable.js +391 -0
  17. package/es/table-virtuoso/body/TableBodyCell.d.ts +14 -0
  18. package/es/table-virtuoso/body/TableBodyCell.js +457 -0
  19. package/es/table-virtuoso/body/TableBodyRow.d.ts +13 -0
  20. package/es/table-virtuoso/body/TableBodyRow.js +112 -0
  21. package/es/table-virtuoso/footer/TableFooterCell.d.ts +7 -0
  22. package/es/table-virtuoso/footer/TableFooterCell.js +54 -0
  23. package/es/table-virtuoso/header/TableHeadCell.d.ts +14 -0
  24. package/es/table-virtuoso/header/TableHeadCell.js +265 -0
  25. package/es/table-virtuoso/header/renderFilter.d.ts +20 -0
  26. package/es/table-virtuoso/header/renderFilter.js +289 -0
  27. package/es/table-virtuoso/hook/constant.d.ts +73 -0
  28. package/es/table-virtuoso/hook/constant.js +240 -0
  29. package/es/table-virtuoso/hook/convert.d.ts +1 -0
  30. package/es/table-virtuoso/hook/convert.js +28 -0
  31. package/es/table-virtuoso/hook/useColumns.d.ts +28 -0
  32. package/es/table-virtuoso/hook/useColumns.js +302 -0
  33. package/es/table-virtuoso/hook/useFilterOperator.d.ts +7 -0
  34. package/es/table-virtuoso/hook/useFilterOperator.js +33 -0
  35. package/es/table-virtuoso/hook/utils.d.ts +159 -0
  36. package/es/table-virtuoso/hook/utils.js +2263 -0
  37. package/es/table-virtuoso/index.d.ts +2 -0
  38. package/es/table-virtuoso/index.js +2 -0
  39. package/es/table-virtuoso/style.d.ts +22 -0
  40. package/es/table-virtuoso/style.js +12 -0
  41. package/es/table-virtuoso/style.scss +1441 -0
  42. package/es/table-virtuoso/table/Grid.d.ts +37 -0
  43. package/es/table-virtuoso/table/Grid.js +298 -0
  44. package/es/table-virtuoso/table/TableContainer.d.ts +49 -0
  45. package/es/table-virtuoso/table/TableContainer.js +292 -0
  46. package/es/table-virtuoso/table/TableWrapper.d.ts +22 -0
  47. package/es/table-virtuoso/table/TableWrapper.js +161 -0
  48. package/es/table-virtuoso/type.d.ts +0 -0
  49. package/es/table-virtuoso/type.js +785 -0
  50. package/es/table-virtuoso/useContext.d.ts +97 -0
  51. package/es/table-virtuoso/useContext.js +21 -0
  52. package/lib/grid-component/TempTable.d.ts +2 -2
  53. package/lib/grid-component/TempTable.js +5 -7
  54. package/lib/grid-component/hooks/useLazyKVMap.d.ts +1 -1
  55. package/lib/grid-component/hooks/useLazyKVMap.js +0 -3
  56. package/lib/grid-component/hooks/utils.d.ts +1 -8
  57. package/lib/grid-component/hooks/utils.js +179 -152
  58. package/lib/grid-component/index.d.ts +1 -1
  59. package/lib/grid-component/index.js +0 -3
  60. package/lib/grid-component/styles.scss +1394 -1394
  61. package/lib/grid-component/type.d.ts +0 -407
  62. package/lib/grid-component/type.js +490 -4
  63. package/lib/table-component/type.d.ts +10 -0
  64. package/lib/{grid-component → table-virtuoso}/ColumnsGroup/ColumnsGroup.js +4 -3
  65. package/lib/{grid-component → table-virtuoso}/InternalTable.d.ts +2 -3
  66. package/lib/table-virtuoso/InternalTable.js +400 -0
  67. package/lib/table-virtuoso/body/TableBodyCell.d.ts +14 -0
  68. package/lib/table-virtuoso/body/TableBodyCell.js +464 -0
  69. package/lib/table-virtuoso/body/TableBodyRow.d.ts +13 -0
  70. package/lib/table-virtuoso/body/TableBodyRow.js +119 -0
  71. package/lib/table-virtuoso/footer/TableFooterCell.d.ts +7 -0
  72. package/lib/table-virtuoso/footer/TableFooterCell.js +63 -0
  73. package/lib/table-virtuoso/header/TableHeadCell.d.ts +14 -0
  74. package/lib/table-virtuoso/header/TableHeadCell.js +274 -0
  75. package/lib/table-virtuoso/header/renderFilter.d.ts +20 -0
  76. package/lib/table-virtuoso/header/renderFilter.js +299 -0
  77. package/lib/table-virtuoso/hook/constant.d.ts +73 -0
  78. package/lib/table-virtuoso/hook/constant.js +247 -0
  79. package/lib/table-virtuoso/hook/convert.d.ts +1 -0
  80. package/lib/table-virtuoso/hook/convert.js +34 -0
  81. package/lib/table-virtuoso/hook/useColumns.d.ts +28 -0
  82. package/lib/table-virtuoso/hook/useColumns.js +315 -0
  83. package/lib/table-virtuoso/hook/useFilterOperator.d.ts +7 -0
  84. package/lib/table-virtuoso/hook/useFilterOperator.js +40 -0
  85. package/lib/table-virtuoso/hook/utils.d.ts +159 -0
  86. package/lib/table-virtuoso/hook/utils.js +2389 -0
  87. package/lib/table-virtuoso/index.d.ts +2 -0
  88. package/lib/table-virtuoso/index.js +9 -0
  89. package/lib/table-virtuoso/style.d.ts +22 -0
  90. package/lib/table-virtuoso/style.js +19 -0
  91. package/lib/table-virtuoso/style.scss +1441 -0
  92. package/lib/table-virtuoso/table/Grid.d.ts +37 -0
  93. package/lib/table-virtuoso/table/Grid.js +307 -0
  94. package/lib/table-virtuoso/table/TableContainer.d.ts +49 -0
  95. package/lib/table-virtuoso/table/TableContainer.js +300 -0
  96. package/lib/table-virtuoso/table/TableWrapper.d.ts +22 -0
  97. package/lib/table-virtuoso/table/TableWrapper.js +166 -0
  98. package/lib/table-virtuoso/type.d.ts +0 -0
  99. package/lib/table-virtuoso/type.js +786 -0
  100. package/lib/table-virtuoso/useContext.d.ts +97 -0
  101. package/lib/table-virtuoso/useContext.js +27 -0
  102. package/package.json +2 -1
  103. package/es/grid-component/AdvanceFilter.d.ts +0 -14
  104. package/es/grid-component/AdvanceFilter.js +0 -454
  105. package/es/grid-component/CheckboxFilter.d.ts +0 -20
  106. package/es/grid-component/CheckboxFilter.js +0 -244
  107. package/es/grid-component/CheckboxFilter2.d.ts +0 -20
  108. package/es/grid-component/CheckboxFilter2.js +0 -244
  109. package/es/grid-component/ColumnsChoose.d.ts +0 -10
  110. package/es/grid-component/ColumnsChoose.js +0 -230
  111. package/es/grid-component/Command.d.ts +0 -8
  112. package/es/grid-component/Command.js +0 -80
  113. package/es/grid-component/ContextMenu.d.ts +0 -20
  114. package/es/grid-component/ContextMenu.js +0 -130
  115. package/es/grid-component/ConvertColumnTable.d.ts +0 -7
  116. package/es/grid-component/ConvertColumnTable.js +0 -144
  117. package/es/grid-component/EditForm/EditForm.d.ts +0 -27
  118. package/es/grid-component/EditForm/EditForm.js +0 -394
  119. package/es/grid-component/EditForm/index.d.ts +0 -1
  120. package/es/grid-component/EditForm/index.js +0 -1
  121. package/es/grid-component/EditableCell.d.ts +0 -20
  122. package/es/grid-component/EditableCell.js +0 -1030
  123. package/es/grid-component/FilterSearch.d.ts +0 -12
  124. package/es/grid-component/FilterSearch.js +0 -33
  125. package/es/grid-component/GridStyle.d.ts +0 -8
  126. package/es/grid-component/GridStyle.js +0 -5
  127. package/es/grid-component/InternalTable.js +0 -1170
  128. package/es/grid-component/TableGrid.d.ts +0 -21
  129. package/es/grid-component/TableGrid.js +0 -493
  130. package/es/grid-component/async-select/index.d.ts +0 -11
  131. package/es/grid-component/async-select/index.js +0 -38
  132. package/es/grid-component/async-table-select/index.d.ts +0 -11
  133. package/es/grid-component/async-table-select/index.js +0 -40
  134. package/es/grid-component/checkbox-control/index.d.ts +0 -13
  135. package/es/grid-component/checkbox-control/index.js +0 -40
  136. package/es/grid-component/hooks/columns/index.d.ts +0 -10
  137. package/es/grid-component/hooks/columns/index.js +0 -503
  138. package/es/grid-component/hooks/content/ControlCheckbox.d.ts +0 -13
  139. package/es/grid-component/hooks/content/ControlCheckbox.js +0 -87
  140. package/es/grid-component/hooks/content/HeaderContent.d.ts +0 -14
  141. package/es/grid-component/hooks/content/HeaderContent.js +0 -44
  142. package/es/grid-component/hooks/content/TooltipContent.d.ts +0 -13
  143. package/es/grid-component/hooks/content/TooltipContent.js +0 -74
  144. package/es/grid-component/hooks/useColumns.d.ts +0 -19
  145. package/es/grid-component/hooks/useColumns.js +0 -317
  146. package/es/grid-component/number/index.d.ts +0 -10
  147. package/es/grid-component/number/index.js +0 -39
  148. package/es/grid-component/number-range/index.d.ts +0 -11
  149. package/es/grid-component/number-range/index.js +0 -63
  150. package/es/grid-component/table/Grid.d.ts +0 -23
  151. package/es/grid-component/table/Grid.js +0 -49
  152. package/es/grid-component/table/GridEdit.d.ts +0 -23
  153. package/es/grid-component/table/GridEdit.js +0 -2726
  154. package/es/grid-component/table/Group.d.ts +0 -21
  155. package/es/grid-component/table/Group.js +0 -195
  156. package/es/grid-component/table/InfiniteTable.d.ts +0 -23
  157. package/es/grid-component/table/InfiniteTable.js +0 -101
  158. package/es/grid-component/useContext.d.ts +0 -34
  159. package/es/grid-component/useContext.js +0 -8
  160. package/lib/grid-component/AdvanceFilter.d.ts +0 -14
  161. package/lib/grid-component/AdvanceFilter.js +0 -463
  162. package/lib/grid-component/CheckboxFilter.d.ts +0 -20
  163. package/lib/grid-component/CheckboxFilter.js +0 -253
  164. package/lib/grid-component/CheckboxFilter2.d.ts +0 -20
  165. package/lib/grid-component/CheckboxFilter2.js +0 -253
  166. package/lib/grid-component/ColumnsChoose.d.ts +0 -10
  167. package/lib/grid-component/ColumnsChoose.js +0 -240
  168. package/lib/grid-component/Command.d.ts +0 -8
  169. package/lib/grid-component/Command.js +0 -88
  170. package/lib/grid-component/ContextMenu.d.ts +0 -20
  171. package/lib/grid-component/ContextMenu.js +0 -140
  172. package/lib/grid-component/ConvertColumnTable.d.ts +0 -7
  173. package/lib/grid-component/ConvertColumnTable.js +0 -153
  174. package/lib/grid-component/EditForm/EditForm.d.ts +0 -27
  175. package/lib/grid-component/EditForm/EditForm.js +0 -404
  176. package/lib/grid-component/EditForm/index.d.ts +0 -1
  177. package/lib/grid-component/EditForm/index.js +0 -16
  178. package/lib/grid-component/EditableCell.d.ts +0 -20
  179. package/lib/grid-component/EditableCell.js +0 -1032
  180. package/lib/grid-component/FilterSearch.d.ts +0 -12
  181. package/lib/grid-component/FilterSearch.js +0 -42
  182. package/lib/grid-component/GridStyle.d.ts +0 -8
  183. package/lib/grid-component/GridStyle.js +0 -12
  184. package/lib/grid-component/InternalTable.js +0 -1178
  185. package/lib/grid-component/TableGrid.d.ts +0 -21
  186. package/lib/grid-component/TableGrid.js +0 -493
  187. package/lib/grid-component/async-select/index.d.ts +0 -11
  188. package/lib/grid-component/async-select/index.js +0 -47
  189. package/lib/grid-component/async-table-select/index.d.ts +0 -11
  190. package/lib/grid-component/async-table-select/index.js +0 -49
  191. package/lib/grid-component/checkbox-control/index.d.ts +0 -13
  192. package/lib/grid-component/checkbox-control/index.js +0 -48
  193. package/lib/grid-component/hooks/columns/index.d.ts +0 -10
  194. package/lib/grid-component/hooks/columns/index.js +0 -518
  195. package/lib/grid-component/hooks/content/ControlCheckbox.d.ts +0 -13
  196. package/lib/grid-component/hooks/content/ControlCheckbox.js +0 -95
  197. package/lib/grid-component/hooks/content/HeaderContent.d.ts +0 -14
  198. package/lib/grid-component/hooks/content/HeaderContent.js +0 -53
  199. package/lib/grid-component/hooks/content/TooltipContent.d.ts +0 -13
  200. package/lib/grid-component/hooks/content/TooltipContent.js +0 -81
  201. package/lib/grid-component/hooks/useColumns.d.ts +0 -19
  202. package/lib/grid-component/hooks/useColumns.js +0 -328
  203. package/lib/grid-component/number/index.d.ts +0 -10
  204. package/lib/grid-component/number/index.js +0 -47
  205. package/lib/grid-component/number-range/index.d.ts +0 -11
  206. package/lib/grid-component/number-range/index.js +0 -71
  207. package/lib/grid-component/table/Grid.d.ts +0 -23
  208. package/lib/grid-component/table/Grid.js +0 -58
  209. package/lib/grid-component/table/GridEdit.d.ts +0 -23
  210. package/lib/grid-component/table/GridEdit.js +0 -2723
  211. package/lib/grid-component/table/Group.d.ts +0 -21
  212. package/lib/grid-component/table/Group.js +0 -204
  213. package/lib/grid-component/table/InfiniteTable.d.ts +0 -23
  214. package/lib/grid-component/table/InfiniteTable.js +0 -109
  215. package/lib/grid-component/useContext.d.ts +0 -34
  216. package/lib/grid-component/useContext.js +0 -13
  217. /package/es/{grid-component → table-virtuoso}/ColumnsGroup/ColumnsGroup.d.ts +0 -0
  218. /package/es/{grid-component → table-virtuoso}/ColumnsGroup/index.d.ts +0 -0
  219. /package/es/{grid-component → table-virtuoso}/ColumnsGroup/index.js +0 -0
  220. /package/lib/{grid-component → table-virtuoso}/ColumnsGroup/ColumnsGroup.d.ts +0 -0
  221. /package/lib/{grid-component → table-virtuoso}/ColumnsGroup/index.d.ts +0 -0
  222. /package/lib/{grid-component → table-virtuoso}/ColumnsGroup/index.js +0 -0
@@ -0,0 +1,2263 @@
1
+ import { SELECTION_COLUMN } from "rc-master-ui/es/table/hooks/useSelection";
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { presetPalettes } from "@ant-design/colors";
4
+ import dayjs from "dayjs";
5
+ import moment from "moment";
6
+ export const newGuid = () => {
7
+ for (let i = 0; i < 20; i++) {
8
+ return uuidv4();
9
+ }
10
+ };
11
+ export const convertDayjsToDate = (dateString, format) => {
12
+ const dayjsDate = dayjs(dateString, format); // Parse using the provided format
13
+ if (!dayjsDate.isValid()) {
14
+ throw new Error('Invalid date or format');
15
+ }
16
+ // return moment(dayjsDate.toDate()).format() // Convert to JavaScript Date
17
+ return dayjsDate.toDate(); // Convert to JavaScript Date
18
+ };
19
+ export const convertDateToDayjs = (date, format) => {
20
+ const dateValue = date ? dayjs(date).format(format) : null;
21
+ return dateValue ? dayjs(dateValue, format) : null;
22
+ };
23
+ export const getCommonPinningHeaderStyles = column => {
24
+ const isPinned = column.getIsPinned();
25
+ return {
26
+ left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
27
+ right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
28
+ opacity: 1,
29
+ position: isPinned ? "sticky" : "relative",
30
+ // width: 'auto',
31
+ zIndex: isPinned ? 4 : 3
32
+ };
33
+ };
34
+ export const getCommonPinningStyles = column => {
35
+ const isPinned = column.getIsPinned();
36
+ return {
37
+ left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
38
+ right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
39
+ opacity: 1,
40
+ position: isPinned ? "sticky" : "relative",
41
+ // width: 'auto',
42
+ zIndex: isPinned ? 2 : 0
43
+ };
44
+ };
45
+ export const getCommonPinningStyles2 = header => {
46
+ const isPinned = header.column.getIsPinned();
47
+ // const isLastLeftPinnedColumn = isPinned === "left" && column.getIsLastColumn("left");
48
+ // const isFirstRightPinnedColumn =isPinned === "right" && column.getIsFirstColumn("right");
49
+
50
+ return {
51
+ // boxShadow: isFirstRightPinnedColumn
52
+ // ? "#e0e0e0 2px 0px 1px -1px inset"
53
+ // : undefined,
54
+
55
+ left: isPinned === "left" ? `${header.getStart("left")}px` : undefined,
56
+ right: isPinned === "right" ? `${header.getAfter("right")}px` : undefined,
57
+ opacity: 1,
58
+ position: isPinned ? "sticky" : "relative",
59
+ width: 'auto',
60
+ zIndex: isPinned ? 2 : 0
61
+ };
62
+ };
63
+ export const sumSize = items => {
64
+ return items.reduce((total, item) => total + item.size, 0);
65
+ };
66
+ export const appendIfNotExists = (a, b) => {
67
+ const existingKeys = new Set(a.map(item => item.index));
68
+ b.forEach(item => {
69
+ if (!existingKeys.has(item.index)) {
70
+ a.push(item);
71
+ }
72
+ });
73
+ return a;
74
+ };
75
+ export const getNewItemsOnly = (a, b) => {
76
+ const existingKeys = new Set(a.map(item => item.key));
77
+ return b.filter(item => !existingKeys.has(item.key));
78
+ };
79
+ export const extendsObject = (...list) => {
80
+ const result = {
81
+ ...list[0]
82
+ };
83
+ for (let i = 1; i < list.length; i++) {
84
+ const obj = list[i];
85
+ if (obj) {
86
+ Object.keys(obj).forEach(key => {
87
+ const val = obj[key];
88
+ if (val !== undefined) {
89
+ result[key] = val;
90
+ }
91
+ });
92
+ }
93
+ }
94
+ return result;
95
+ };
96
+ export const isEmpty = d => {
97
+ return d === null || d === undefined || d === '';
98
+ };
99
+ export const getFormat = (colFormat, format) => {
100
+ return {
101
+ thousandSeparator: colFormat?.thousandSeparator ?? format?.thousandSeparator,
102
+ decimalSeparator: colFormat?.decimalSeparator ?? format?.decimalSeparator,
103
+ decimalScale: colFormat?.decimalScale ?? format?.decimalScale ? Number(colFormat?.decimalScale ?? format?.decimalScale) : colFormat?.decimalScale ?? format?.decimalScale,
104
+ allowNegative: colFormat?.allowNegative ?? format?.allowNegative,
105
+ // check nhập số âm
106
+ prefix: colFormat?.prefix ?? format?.prefix,
107
+ suffix: colFormat?.suffix ?? format?.suffix,
108
+ fixedDecimalScale: colFormat?.fixedDecimalScale ?? format?.fixedDecimalScale,
109
+ // mặc định thêm số 0 sau số thập phân
110
+ dateFormat: colFormat?.dateFormat ?? format?.dateFormat,
111
+ datetimeFormat: colFormat?.datetimeFormat ?? format?.datetimeFormat,
112
+ timeFormat: colFormat?.timeFormat ?? format?.timeFormat,
113
+ weekFormat: colFormat?.weekFormat ?? format?.weekFormat,
114
+ monthFormat: colFormat?.monthFormat ?? format?.monthFormat,
115
+ yearFormat: colFormat?.yearFormat ?? format?.yearFormat
116
+ };
117
+ };
118
+ export function convertFormat(formatStr) {
119
+ // return formatStr.split('').map((char, i) => {
120
+ // if (char === 'D' || char === 'd') {
121
+ // return 'd'; // ngày: lowercase
122
+ // }
123
+ // if (char === 'Y' || char === 'y') {
124
+ // return 'y'; // năm: lowercase
125
+ // }
126
+ // if (char === 'M' || char === 'm') {
127
+ // return char; // tháng: giữ nguyên
128
+ // }
129
+ // return char; // separator
130
+ // }).join('');
131
+
132
+ return formatStr.split('').map(char => {
133
+ if (char === 'D' || char === 'd') return 'd';
134
+ if (char === 'Y' || char === 'y') return 'y';
135
+ if ('Hhmsa'.includes(char)) return char; // giờ, phút, giây, am/pm
136
+ if (char === 'M' || char === 'm') return char; // tháng: giữ nguyên
137
+ return char; // dấu phân cách
138
+ }).join('');
139
+ }
140
+ export const getDatepickerFormat = (type, format) => {
141
+ const typeFormat = type ? type.toLowerCase() : '';
142
+ switch (typeFormat) {
143
+ case "date":
144
+ case "daterange":
145
+ return format?.dateFormat ?? 'DD/MM/YYYY';
146
+ case "datetime":
147
+ return format?.datetimeFormat ?? 'DD/MM/YYYY HH:mm';
148
+ case "week":
149
+ return format?.weekFormat ?? 'DD/MM';
150
+ case "month":
151
+ return format?.monthFormat ?? 'MM/YYYY';
152
+ case "quarter":
153
+ return format?.dateFormat ?? 'DD/MM/YYYY';
154
+ case "year":
155
+ return format?.yearFormat ?? 'YYYY';
156
+ case "time":
157
+ return format?.timeFormat ?? 'HH:mm';
158
+ default:
159
+ return 'DD/MM/YYYY';
160
+ }
161
+ };
162
+ export const getDateRangeFormat = (type, format) => {
163
+ const typeFormat = type ? type.toLowerCase() : '';
164
+ switch (typeFormat) {
165
+ case "date":
166
+ case "daterange":
167
+ return convertFormat(format?.dateFormat ?? 'dd/MM/yyyy');
168
+ case "datetime":
169
+ return format?.datetimeFormat ?? 'dd/MM/yyyy HH:mm';
170
+ case "week":
171
+ return format?.weekFormat ?? 'dd/MM';
172
+ case "month":
173
+ return format?.monthFormat ?? 'MM/yyyy';
174
+ case "quarter":
175
+ return format?.dateFormat ?? 'dd/MM/yyyy';
176
+ case "year":
177
+ return format?.yearFormat ?? 'yyyy';
178
+ case "time":
179
+ return format?.timeFormat ?? 'HH:mm';
180
+ default:
181
+ return 'dd/MM/yyyy';
182
+ }
183
+ };
184
+ export const getTypeFilter = col => {
185
+ if (col?.typeFilter) {
186
+ return col.typeFilter;
187
+ }
188
+ const type = col?.type ?? 'Text';
189
+ switch (type) {
190
+ case "number":
191
+ return 'Number';
192
+ case "date":
193
+ return 'Date';
194
+ case "datetime":
195
+ return 'Datetime';
196
+ case "boolean":
197
+ return 'Checkbox';
198
+ case "checkbox":
199
+ return 'Checkbox';
200
+
201
+ // case "week": return ''
202
+ // case "month": return 'Month'
203
+ // case "quarter": return col.format?.dateFormat ? col.format?.dateFormat : 'DD/MM/YYYY'
204
+ // case "year": return col.format?.yearFormat ? col.format?.yearFormat : 'YYYY'
205
+ // case "time": return col.format?.timeFormat ? col.format?.timeFormat : 'HH:mm'
206
+ case "string":
207
+ default:
208
+ return 'Text';
209
+ }
210
+ };
211
+ export const addRowIdArray = inputArray => {
212
+ if (inputArray) {
213
+ return inputArray.map(item => {
214
+ if (typeof item.children !== "string" && item.children && item.children.length > 0) {
215
+ item.children = addRowIdArray(item.children);
216
+ }
217
+
218
+ // return { ...item, rowId: item.rowId ?? item.id ?? newGuid() }
219
+ return {
220
+ ...item,
221
+ rowId: item.id ?? item.rowId ?? newGuid()
222
+ };
223
+ });
224
+ } else {
225
+ return [];
226
+ }
227
+ };
228
+ export function groupArrayByColumns(arr, columns) {
229
+ const result = [];
230
+ if (columns && columns.length > 0) {
231
+ arr.forEach(item => {
232
+ let currentLevel = result;
233
+ const parentChain = []; // Lưu rowId của mỗi level để biết parent
234
+
235
+ columns.forEach((column, index) => {
236
+ const value = item[column];
237
+
238
+ // Tạo key unique cho group dựa trên value của cột
239
+ // Tìm existing item ở level hiện tại
240
+ const existingItem = currentLevel.find(i => i[column] === value);
241
+ if (existingItem) {
242
+ // Item đã tồn tại, chỉ update parentChain
243
+ parentChain[index] = existingItem.rowId;
244
+ currentLevel = existingItem.children;
245
+ } else {
246
+ // Tạo item mới
247
+ const groupRowId = String(value);
248
+ const newItem = {
249
+ [column]: value,
250
+ field: column,
251
+ rowId: groupRowId,
252
+ id: groupRowId,
253
+ parentId: parentChain[index - 1] ?? null,
254
+ // Parent là rowId của level trước
255
+ children: []
256
+ };
257
+ currentLevel.push(newItem);
258
+ parentChain[index] = groupRowId;
259
+ currentLevel = newItem.children;
260
+ }
261
+ });
262
+
263
+ // Data item cuối cùng: parentId là rowId của group parent gần nhất (level cuối cùng)
264
+ currentLevel.push({
265
+ ...item,
266
+ rowId: item.id ?? item.rowId,
267
+ parentId: parentChain[columns.length - 1] ?? null
268
+ });
269
+ });
270
+ return result;
271
+ } else {
272
+ return arr;
273
+ }
274
+ }
275
+ export const flatColumns2 = columns => {
276
+ return columns.reduce((list, column) => {
277
+ const subColumns = column.children;
278
+ if (column === SELECTION_COLUMN) {
279
+ return [...list, {
280
+ ...column
281
+ }];
282
+ }
283
+ if (subColumns && subColumns.length > 0) {
284
+ return [...list, ...flatColumns2(subColumns).map(subColum => ({
285
+ ...subColum
286
+ }))];
287
+ }
288
+ return [...list, {
289
+ ...column
290
+ }];
291
+ }, []);
292
+ };
293
+ export const checkThousandSeparator = (thousandSeparator, decimalSeparator) => {
294
+ if (thousandSeparator) {
295
+ if (decimalSeparator) {
296
+ if (thousandSeparator === decimalSeparator) {
297
+ return ',';
298
+ } else {
299
+ return thousandSeparator;
300
+ }
301
+ } else {
302
+ return thousandSeparator;
303
+ }
304
+ } else {
305
+ return undefined;
306
+ }
307
+ };
308
+ export const checkDecimalSeparator = (thousandSeparator, decimalSeparator) => {
309
+ if (decimalSeparator) {
310
+ if (thousandSeparator) {
311
+ if (thousandSeparator === decimalSeparator) {
312
+ return '.';
313
+ } else {
314
+ return decimalSeparator;
315
+ }
316
+ } else {
317
+ return decimalSeparator;
318
+ }
319
+ } else {
320
+ if (thousandSeparator && thousandSeparator === '.') {
321
+ return ',';
322
+ }
323
+ return '.';
324
+ }
325
+ };
326
+ export const getFixedFields = (columns, type) => {
327
+ const result = [];
328
+ function traverse(cols) {
329
+ for (const col of cols) {
330
+ if ((col.fixed ?? col.fixedType) === type && col.field && (col.visible !== false || col.hidden)) {
331
+ result.push(col.field);
332
+ }
333
+ if (col.children && col.children.length > 0) {
334
+ traverse(col.children);
335
+ }
336
+ }
337
+ }
338
+ traverse(columns);
339
+ return result;
340
+ };
341
+ export function areStringArraysEqual(a, b) {
342
+ if (a.length !== b.length) return false;
343
+ const sortedA = [...a].sort();
344
+ const sortedB = [...b].sort();
345
+ return sortedA.every((val, index) => val === sortedB[index]);
346
+ }
347
+ export const getDefaultOperator = col => {
348
+ if (col.operator) {
349
+ return col.operator;
350
+ }
351
+ if (col.typeFilter) {
352
+ switch (col.typeFilter) {
353
+ case 'Number':
354
+ case 'Date':
355
+ case 'Datetime':
356
+ case 'Time':
357
+ case 'Month':
358
+ case 'Quarter':
359
+ case 'Year':
360
+ case 'Week':
361
+ case 'Dropdown':
362
+ case 'Checkbox':
363
+ case 'CheckboxDropdown':
364
+ case 'CheckboxTree':
365
+ case 'DropTree':
366
+ return 'equal';
367
+ case 'Text':
368
+ default:
369
+ return 'contains';
370
+ }
371
+ }
372
+ switch (col.type) {
373
+ case 'number':
374
+ case 'date':
375
+ case 'datetime':
376
+ case 'week':
377
+ case 'year':
378
+ case 'quarter':
379
+ return 'equal';
380
+ case 'string':
381
+ default:
382
+ return 'contains';
383
+ }
384
+ };
385
+ export function isEqualSet(setA, setB) {
386
+ if (setA.size !== setB.size) {
387
+ return false;
388
+ }
389
+ for (const item of setA) {
390
+ if (!setB.has(item)) {
391
+ return false;
392
+ }
393
+ }
394
+ return true;
395
+ }
396
+ export const getLastSelectCell = selectCells => {
397
+ if (selectCells.size === 0) {
398
+ return {
399
+ row: 0,
400
+ col: 0
401
+ };
402
+ }
403
+ const lastValue = [...selectCells].at(-1);
404
+ const [row, col] = lastValue.split("-").map(Number);
405
+ return {
406
+ row,
407
+ col
408
+ };
409
+ };
410
+ export function getCellsByPosition(cellSet, position = "bottom") {
411
+ const cells = Array.from(cellSet).map(key => {
412
+ const [row, col] = key.split("-").map(Number);
413
+ return {
414
+ row,
415
+ col,
416
+ key
417
+ };
418
+ });
419
+ switch (position) {
420
+ case "top":
421
+ {
422
+ // const minRow = Math.min(...cells.map(c => c.row));
423
+ // return cells.filter(c => c.row === minRow).map(c => c.key);
424
+
425
+ // const rows = cells.map(c => c.row).filter(r => r > 0);
426
+ // if (rows.length === 0) return [];
427
+ // const minRow = Math.min(...rows);
428
+ // return cells.filter(c => c.row === minRow).map(c => c.key);
429
+
430
+ const minRow = Math.min(...cells.map(c => c.row));
431
+ if (minRow === 0) {
432
+ return [];
433
+ } // Bỏ qua nếu rowIndex = 0
434
+
435
+ return cells.filter(c => c.row === minRow).map(c => `${c.row}-${c.col}`);
436
+ }
437
+ case "bottom":
438
+ {
439
+ const maxRow = Math.max(...cells.map(c => c.row));
440
+ return cells.filter(c => c.row === maxRow).map(c => c.key);
441
+ }
442
+ case "left":
443
+ {
444
+ // const minCol = Math.min(...cells.map(c => c.col));
445
+ // return cells.filter(c => c.col === minCol).map(c => c.key);
446
+
447
+ // const cols = cells.map(c => c.col).filter(c => c > 0);
448
+ // if (cols.length === 0) return [];
449
+ // const minCol = Math.min(...cols);
450
+ // return cells.filter(c => c.col === minCol).map(c => c.key);
451
+
452
+ const minCol = Math.min(...cells.map(c => c.col));
453
+ if (minCol === 0) {
454
+ return [];
455
+ } // Bỏ qua nếu colIndex = 0
456
+
457
+ // Trả về các ô cùng row, nhưng ở cột bên trái
458
+ return cells.filter(c => c.col === minCol).map(c => `${c.row}-${c.col}`);
459
+ }
460
+ case "right":
461
+ {
462
+ const maxCol = Math.max(...cells.map(c => c.col));
463
+ return cells.filter(c => c.col === maxCol).map(c => c.key);
464
+ }
465
+ default:
466
+ return [];
467
+ }
468
+ }
469
+ export const onAddBgSelectedCell = (selectedCells, id, isFocusCellIndex) => {
470
+ const selectors = Array.from(selectedCells).map(pos => {
471
+ const [row1, col1] = pos.split('-');
472
+ return `[data-row-index="${row1}"][data-col-index="${col1}"]`;
473
+ });
474
+ const table = document.querySelector(`#${id}`);
475
+
476
+ //// xóa class các ô đã chọn trước đó
477
+ const cellsSelected = table ? table?.querySelectorAll('.ui-rc-table-cell.selected-bg') : null;
478
+ if (cellsSelected) {
479
+ cellsSelected.forEach(cell => {
480
+ cell.classList.remove('selected-bg');
481
+ });
482
+ }
483
+
484
+ /// thêm class
485
+ const cells = table && selectors.length > 0 ? table?.querySelectorAll(selectors.join(',')) : null;
486
+ if (cells) {
487
+ cells.forEach(cell => {
488
+ cell.classList.add('selected-bg');
489
+ });
490
+ }
491
+ const rowsArray = [...new Set([...selectedCells].map(item => item.split("-")[0]))];
492
+ const rowsSelectors = rowsArray.map(r => `.rc-ui-cell-index[data-row-index="${r}"]`).join(", ");
493
+ const cellsIndex = table && rowsSelectors.length > 0 ? table?.querySelectorAll(rowsSelectors) : null;
494
+ if (cellsIndex && isFocusCellIndex !== false) {
495
+ cellsIndex.forEach(cell => {
496
+ cell.classList.add('focus');
497
+ });
498
+ }
499
+
500
+ // // tăng z-index để hiển thị round point paste
501
+ // const row = getLastSelectCell(selectedCells).row
502
+ // const col = getLastSelectCell(selectedCells).col
503
+ // const cell: any = table?.querySelector(`.ui-rc-table-cell[data-row-index="${row}"][data-col-index="${col}"]`)
504
+ //
505
+ // if (cell) {
506
+ // cell.style.zIndex = 1
507
+ // }
508
+ //
509
+ // if (cell && cell.classList.contains('ui-rc-table-cell-fix-left')) {
510
+ // cell.style.zIndex = 3;
511
+ // }
512
+
513
+ // thêm class border selected
514
+
515
+ // addBorderClass(selectedCells, 'bottom', 'cell-border-bottom', id)
516
+ // addBorderClass(selectedCells, 'right', 'cell-border-right', id)
517
+ // addBorderClass(selectedCells, 'top', 'cell-border-top', id)
518
+ // addBorderClass(selectedCells, 'left', 'cell-border-left', id)
519
+ };
520
+ export const onRemoveBgSelectedCell = (selectedCells, id, rowsSelected) => {
521
+ const table = document.querySelector(`#${id}`);
522
+ const cells = table ? table?.querySelectorAll('.ui-rc-table-cell.selected-bg') : null;
523
+ if (cells) {
524
+ cells.forEach(cell => {
525
+ cell.classList.remove('selected-bg');
526
+ });
527
+ }
528
+ const cellsIndex = table ? table?.querySelectorAll('.ui-rc-table-cell.focus') : null;
529
+ if (cellsIndex) {
530
+ cellsIndex.forEach(cell => {
531
+ cell.classList.remove('focus');
532
+ });
533
+ }
534
+
535
+ // xóa class selected ô STT
536
+
537
+ if (rowsSelected && rowsSelected.size > 0) {
538
+ const rowsSelectedArray = [...new Set([...rowsSelected].map(item => item.split("-")[0]))];
539
+ const rowsSelectedSelectors = rowsSelectedArray.map(r => `.rc-ui-cell-index[data-row-index="${r}"]`).join(", ");
540
+ const cellsSelectedIndex = table && rowsSelectedSelectors.length > 0 ? table?.querySelectorAll(rowsSelectedSelectors) : null;
541
+ if (cellsSelectedIndex) {
542
+ cellsSelectedIndex.forEach(cell => {
543
+ cell.classList.remove('selected');
544
+ });
545
+ }
546
+ }
547
+ };
548
+ export function getColIdsBetween(table, a, b) {
549
+ const ids = table.getVisibleLeafColumns().map(c => c.id);
550
+ const [start, end] = [ids.indexOf(a), ids.indexOf(b)].sort((x, y) => x - y);
551
+ return ids.slice(start, end + 1);
552
+ }
553
+ export function getRowIdsBetween(table, a, b) {
554
+ // const ids = table.getRowModel().rows.map(r => r.id);
555
+ const ids = table.getRowModel().flatRows.map(r => r.id);
556
+ const [start, end] = [ids.indexOf(a), ids.indexOf(b)].sort((x, y) => x - y);
557
+ return ids.slice(start, end + 1);
558
+ }
559
+ export const updateArrayByKey = (arr, element, key) => {
560
+ if (arr) {
561
+ return arr.map(it => {
562
+ const item = {
563
+ ...it
564
+ };
565
+ if (item[key] === element[key]) {
566
+ return {
567
+ ...item,
568
+ ...element
569
+ };
570
+ } else if (item.children && item.children.length > 0) {
571
+ item.children = updateArrayByKey(item.children, element, key);
572
+ }
573
+ return item;
574
+ });
575
+ } else {
576
+ return [];
577
+ }
578
+ };
579
+ export const unFlattenData = data => {
580
+ const idToNodeMap = {};
581
+ const tree = [];
582
+
583
+ // Bước 1: Tạo map id -> node
584
+ data.forEach(item => {
585
+ // idToNodeMap[item.rowId] = { ...item, children: [] }
586
+ idToNodeMap[item.rowId] = {
587
+ ...item
588
+ };
589
+ });
590
+
591
+ // Bước 2: Gắn vào parent hoặc đẩy lên root nếu không có parent
592
+ data.forEach(item => {
593
+ const currentNode = idToNodeMap[item.rowId];
594
+ if (!item.parentId) {
595
+ tree.push(currentNode);
596
+ } else {
597
+ const parentNode = idToNodeMap[item.parentId];
598
+ if (parentNode) {
599
+ parentNode.children = parentNode.children ?? [];
600
+ parentNode.children.push(currentNode);
601
+ } else {
602
+ // Nếu parentId không tồn tại thì xem như root
603
+ tree.push(currentNode);
604
+ }
605
+ }
606
+ });
607
+ return tree;
608
+ };
609
+ export const flattenArray = arr => {
610
+ if (!arr) {
611
+ return [];
612
+ }
613
+ return arr.reduce((r, {
614
+ children,
615
+ ...rest
616
+ }) => {
617
+ r.push(rest);
618
+ if (children) {
619
+ r.push(...flattenArray(children));
620
+ }
621
+ return r;
622
+ }, []);
623
+ };
624
+ export function updateOrInsert(dataArray, dataFilter) {
625
+ const updatedArray = [...dataArray];
626
+ dataFilter.forEach(filterItem => {
627
+ const existingIndex = updatedArray.findIndex(item => item.rowId === filterItem.rowId);
628
+ if (existingIndex !== -1) {
629
+ // Cập nhật item đã tồn tại
630
+ updatedArray[existingIndex] = {
631
+ ...updatedArray[existingIndex],
632
+ ...filterItem
633
+ };
634
+ } else {
635
+ // Tìm vị trí cuối cùng của item trước đó trong dataFilter
636
+ const prevIndexInFilter = dataFilter.findIndex(f => f.rowId === filterItem.rowId) - 1;
637
+ if (prevIndexInFilter >= 0) {
638
+ const prevId = dataFilter[prevIndexInFilter].rowId;
639
+ const prevIndexInArray = updatedArray.findIndex(item => item.rowId === prevId);
640
+ if (prevIndexInArray !== -1) {
641
+ // Thêm ngay sau phần tử trước đó
642
+ updatedArray.splice(prevIndexInArray + 1, 0, filterItem);
643
+ return;
644
+ }
645
+ }
646
+
647
+ // Nếu không tìm thấy phần tử trước đó, hoặc là phần đầu tiên, thì push vào cuối
648
+ updatedArray.push(filterItem);
649
+ }
650
+ });
651
+ return updatedArray;
652
+ }
653
+ export const findItemByKey = (array, key, value) => {
654
+ for (let i = 0; i < array.length; i++) {
655
+ const item = array[i];
656
+ if (item[key] === value) {
657
+ return item;
658
+ }
659
+ if (item.children && item.children.length > 0) {
660
+ const foundInChildren = findItemByKey(item.children, key, value);
661
+ if (foundInChildren) {
662
+ return foundInChildren;
663
+ }
664
+ }
665
+ }
666
+ return null;
667
+ };
668
+ export const isFormattedNumber = str => {
669
+ if (!str) return false;
670
+ if (typeof str !== 'string') return false;
671
+ const regexUS = /^\d{1,3}(,\d{3})*(\.\d+)?$/; // 100,000.111
672
+ const regexEU = /^\d{1,3}(\.\d{3})*(,\d+)?$/; // 100.000,111
673
+
674
+ // Không có dấu hàng nghìn, chỉ dấu thập phân: 100000.1 hoặc 100000,01
675
+ const regexDecimalOnly = /^-?\d+([.,]\d{1,})$/;
676
+ return regexUS.test(str) || regexEU.test(str) || regexDecimalOnly.test(str);
677
+ };
678
+ export const detectSeparators = str => {
679
+ if (typeof str !== 'string') return null;
680
+ const hasComma = str.includes(',');
681
+ const hasDot = str.includes('.');
682
+
683
+ // Trường hợp có cả dấu , và .
684
+ if (hasComma && hasDot) {
685
+ const lastComma = str.lastIndexOf(',');
686
+ const lastDot = str.lastIndexOf('.');
687
+ return lastComma > lastDot ? {
688
+ thousandSeparator: '.',
689
+ decimalSeparator: ','
690
+ } : {
691
+ thousandSeparator: ',',
692
+ decimalSeparator: '.'
693
+ };
694
+ }
695
+
696
+ // Trường hợp chỉ có dấu phẩy
697
+ if (hasComma && !hasDot) {
698
+ const parts = str.split(',');
699
+ if (parts.length === 2) {
700
+ return parts[1].length === 3 ? {
701
+ thousandSeparator: ',',
702
+ decimalSeparator: undefined
703
+ } : {
704
+ thousandSeparator: undefined,
705
+ decimalSeparator: ','
706
+ };
707
+ }
708
+ }
709
+
710
+ // Trường hợp chỉ có dấu chấm
711
+ if (hasDot && !hasComma) {
712
+ const parts = str.split('.');
713
+ if (parts.length === 2) {
714
+ return parts[1].length === 3 ? {
715
+ thousandSeparator: '.',
716
+ decimalSeparator: undefined
717
+ } : {
718
+ thousandSeparator: undefined,
719
+ decimalSeparator: '.'
720
+ };
721
+ }
722
+ }
723
+
724
+ // Không có dấu hoặc không hợp lệ
725
+ return null;
726
+ };
727
+ function isDate(value) {
728
+ if (value instanceof Date) {
729
+ return !isNaN(value.getTime());
730
+ }
731
+ if (typeof value === "string") {
732
+ // Chỉ chấp nhận định dạng yyyy-mm-dd hoặc mm/yyyy
733
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) || /^\d{2}\/\d{4}$/.test(value);
734
+ }
735
+ return false;
736
+ }
737
+
738
+ // Chuỗi MM/YYYY → Date
739
+ export function isDateString(str) {
740
+ return typeof str === "string" && (/^\d{2}\/\d{4}$/.test(str) || /^\d{4}-\d{2}-\d{2}$/.test(str));
741
+ }
742
+ function parseToDate(str) {
743
+ if (/^\d{2}\/\d{4}$/.test(str)) {
744
+ const [month, year] = str.split('/');
745
+ return new Date(parseInt(year), parseInt(month) - 1, 1);
746
+ }
747
+ return new Date(str);
748
+ }
749
+
750
+ // So sánh ngày (cùng ngày/tháng/năm)
751
+ export function compareDates(date1, date2) {
752
+ return date1.getDate() === date2.getDate() && date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear();
753
+ }
754
+
755
+ // Helper: compare MM/YYYY date string with itemValue
756
+ export function compareDate(itemValue, value) {
757
+ const [month, year] = value.split('/').map(Number);
758
+ const date = new Date(itemValue);
759
+ return date.getMonth() + 1 === month && date.getFullYear() === year;
760
+ }
761
+ export const removeVietnameseTones = str => {
762
+ if (!str) {
763
+ return '';
764
+ }
765
+ return str.normalize('NFD') // Tách các ký tự có dấu thành ký tự cơ bản + dấu
766
+ .replace(/[\u0300-\u036f]/g, '') // Xóa dấu
767
+ .replace(/đ/g, 'd') // Thay thế đ
768
+ .replace(/Đ/g, 'D').replace(/[^a-zA-Z0-9\s]/g, '') // Loại bỏ ký tự đặc biệt
769
+ .replace(/\s+/g, ' ') // Thay nhiều khoảng trắng thành 1 khoảng trắng
770
+ .trim();
771
+ };
772
+ export const shouldInclude = (item, queries) => {
773
+ if (item.isFilterState === true) {
774
+ return true;
775
+ }
776
+ let result = null;
777
+ for (const query of queries) {
778
+ const {
779
+ field,
780
+ value,
781
+ operator,
782
+ predicate
783
+ } = query;
784
+ const itemValue = item[field];
785
+ let condition = false;
786
+ const isDateComparison = isDate(itemValue) || isDateString(value);
787
+ const itemDate = isDateComparison ? new Date(itemValue) : null;
788
+ const queryDate = isDateComparison ? parseToDate(value) : null;
789
+ const itemStr = removeVietnameseTones(itemValue?.toString().toLowerCase?.() ?? '');
790
+ const queryStr = removeVietnameseTones(value?.toString().toLowerCase?.() ?? '');
791
+ switch (operator.toLowerCase()) {
792
+ case "equal":
793
+ condition = isDateComparison ? compareDates(itemDate, queryDate) : itemValue === value;
794
+ break;
795
+ case "notequal":
796
+ condition = isDateComparison ? !compareDates(itemDate, queryDate) : itemValue !== value;
797
+ break;
798
+ case "greaterthan":
799
+ // @ts-ignore
800
+ condition = isDateComparison ? itemDate > queryDate : itemValue > value;
801
+
802
+ // condition = isDateComparison ? invalidDate(itemDate) && invalidDate(queryDate) && itemDate > queryDate : itemValue > value;
803
+ break;
804
+ case "greaterthanorequal":
805
+ // @ts-ignore
806
+ condition = isDateComparison ? itemDate >= queryDate : itemValue >= value;
807
+ break;
808
+ case "lessthan":
809
+ // @ts-ignore
810
+ condition = isDateComparison ? itemDate < queryDate : itemValue < value;
811
+ break;
812
+ case "lessthanorequal":
813
+ // @ts-ignore
814
+ condition = isDateComparison ? itemDate <= queryDate : itemValue <= value;
815
+ break;
816
+ case "contains":
817
+ condition = itemStr?.includes(queryStr);
818
+ break;
819
+ case "startswith":
820
+ condition = itemStr?.startsWith(queryStr);
821
+ break;
822
+ case "endswith":
823
+ condition = itemStr?.endsWith(queryStr);
824
+ break;
825
+ default:
826
+ console.warn(`Unknown operator: ${operator}`);
827
+ break;
828
+ }
829
+ if (predicate === "and") {
830
+ result = result === null ? condition : result && condition;
831
+ } else if (predicate === "or") {
832
+ result = result === null ? condition : result || condition;
833
+ }
834
+ }
835
+ return result;
836
+ };
837
+ function getSortValue(item, field) {
838
+ if (item[field] !== undefined) return item[field];
839
+ if (item.children && item.children.length > 0) {
840
+ return getSortValue(item.children[0], field);
841
+ }
842
+ return undefined;
843
+ }
844
+ function compareValues(a, b, order) {
845
+ const desc = order === "descend";
846
+ if (a == null && b == null) return 0;
847
+ if (a == null) return desc ? 1 : -1;
848
+ if (b == null) return desc ? -1 : 1;
849
+
850
+ // Nếu là số
851
+ if (typeof a === "number" && typeof b === "number") {
852
+ return desc ? b - a : a - b;
853
+ }
854
+
855
+ // Nếu là ngày hợp lệ
856
+ const dateA = new Date(a);
857
+ const dateB = new Date(b);
858
+ if (!isNaN(dateA.getTime()) && !isNaN(dateB.getTime())) {
859
+ return desc ? dateB.getTime() - dateA.getTime() : dateA.getTime() - dateB.getTime();
860
+ }
861
+
862
+ // Mặc định coi như string
863
+ return desc ? String(b).localeCompare(String(a)) : String(a).localeCompare(String(b));
864
+ }
865
+ export function sortData(data, sorter) {
866
+ const sorted = [...data].sort((a, b) => {
867
+ for (const {
868
+ field,
869
+ order
870
+ } of sorter) {
871
+ const result = compareValues(getSortValue(a, field), getSortValue(b, field), order);
872
+ if (result !== 0) return result;
873
+ }
874
+ return 0;
875
+ });
876
+ return sorted.map(item => ({
877
+ ...item,
878
+ children: item.children ? sortData(item.children, sorter) : undefined
879
+ }));
880
+ }
881
+ export function filterDataByColumns(data, queries, sorter, keysFilter) {
882
+ if (!queries || queries.length === 0) {
883
+ return sorter ? sortData(data, sorter) : data;
884
+ }
885
+ let filtered = data.map(item => {
886
+ const newItem = {
887
+ ...item
888
+ };
889
+ if (Array.isArray(item.children)) {
890
+ newItem.children = filterDataByColumns(item.children, queries, sorter, keysFilter);
891
+ }
892
+ const isSelfMatched = shouldInclude(item, queries) || keysFilter?.includes(newItem?.rowId);
893
+
894
+ // Nếu chính item thỏa hoặc có con thỏa → giữ lại
895
+ if (isSelfMatched || newItem.children && newItem.children.length > 0) {
896
+ return newItem;
897
+ }
898
+ return null; // loại bỏ node không phù hợp
899
+ }).filter(Boolean); // xóa các null
900
+
901
+ if (sorter && sorter.length > 0) {
902
+ filtered = sortData(filtered, sorter);
903
+ }
904
+ return filtered;
905
+ }
906
+ export const getAllRowKey = data => {
907
+ const a = flattenArray(data);
908
+ return a.length ? a.map(it => it.rowId) : undefined;
909
+ };
910
+ export const isEditable = (column, rowData) => {
911
+ if (column && typeof column.editEnable === 'function') {
912
+ return column.editEnable(rowData);
913
+ }
914
+ return column?.editEnable;
915
+ };
916
+ export const checkFieldKey = key => {
917
+ if (key) {
918
+ return key;
919
+ } else {
920
+ return 'value';
921
+ }
922
+ };
923
+ export const convertArrayWithIndent = (inputArray, parentIndent = 0) => {
924
+ if (inputArray) {
925
+ return inputArray.map(item => {
926
+ const indent = parentIndent;
927
+ if (item.children && item.children.length > 0) {
928
+ item.children = convertArrayWithIndent(item.children, indent + 1);
929
+ }
930
+ return {
931
+ ...item,
932
+ indent,
933
+ rowId: item.rowId ? item.rowId : item.id ? item.id : newGuid()
934
+ };
935
+ });
936
+ } else {
937
+ return [];
938
+ }
939
+ };
940
+ export const convertLabelToTitle = data => {
941
+ return data.map(item => {
942
+ const {
943
+ label,
944
+ title,
945
+ value,
946
+ key,
947
+ ...rest
948
+ } = item;
949
+ const newItem = {
950
+ ...rest,
951
+ value,
952
+ label,
953
+ key: key ?? value,
954
+ title: title ?? label
955
+ };
956
+ if (item.children) {
957
+ newItem.children = convertLabelToTitle(item.children);
958
+ }
959
+ return newItem;
960
+ });
961
+ };
962
+ export const isNullOrUndefined = d => {
963
+ return d === null || d === undefined;
964
+ };
965
+ export const isObjEmpty = obj => {
966
+ if (isNullOrUndefined(obj)) {
967
+ return true;
968
+ } else {
969
+ return Object.keys(obj).length === 0;
970
+ }
971
+ };
972
+ export const isDisable = (column, rowData) => {
973
+ if (column && typeof column?.disable === 'function') {
974
+ return column.disable(rowData);
975
+ }
976
+ return !!column?.disable;
977
+ };
978
+ export const customWeekStartEndFormat = (value, weekFormat) => {
979
+ return `${dayjs(value).startOf('week').format(weekFormat)} ~ ${dayjs(value).endOf('week').format(weekFormat)}`;
980
+ };
981
+ export const parseBooleanToValue = (value, type) => {
982
+ return type === 'boolean' ? value : Number(value);
983
+ };
984
+ export const isNameColor = strColor => {
985
+ const s = new Option().style;
986
+ s.color = strColor;
987
+ return s.color === strColor;
988
+ };
989
+ export const isColor = value => {
990
+ const hexRegex = /^#([0-9A-F]{3}){1,2}$/i;
991
+ const rgbRegex = /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/;
992
+ const rgbaRegex = /^rgba\((\d{1,3}), (\d{1,3}), (\d{1,3}), (0|1|0?\.\d+)\)$/;
993
+ const hslRegex = /^hsl\(\d{1,3}, \d{1,3}%, \d{1,3}%\)$/;
994
+ const hslaRegex = /^hsla\(\d{1,3}, \d{1,3}%, \d{1,3}%, (0|1|0?\.\d+)\)$/;
995
+ const namedColors = /^(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)$/i;
996
+ return hexRegex.test(value) || rgbRegex.test(value) || rgbaRegex.test(value) || hslRegex.test(value) || hslaRegex.test(value) || namedColors.test(value) || isNameColor(value);
997
+ };
998
+ export const genPresets = (presets = presetPalettes) => {
999
+ return Object.entries(presets).map(([label, colors]) => ({
1000
+ label,
1001
+ colors,
1002
+ key: label
1003
+ }));
1004
+ };
1005
+ export const getEditType = (column, rowData) => {
1006
+ if (column && typeof column.editType === 'function') {
1007
+ return column.editType(rowData);
1008
+ }
1009
+ return column?.editType ?? 'text';
1010
+ };
1011
+ export const getDefaultValue = defaultValue => {
1012
+ if (defaultValue && typeof defaultValue === 'function') {
1013
+ return defaultValue();
1014
+ }
1015
+ return defaultValue;
1016
+ };
1017
+ export const flattenData = (childrenColumnName, data) => {
1018
+ let list = [];
1019
+ (data || []).forEach(record => {
1020
+ list.push(record);
1021
+ if (record && typeof record === 'object' && childrenColumnName in record) {
1022
+ list = [...list, ...flattenData(childrenColumnName, record[childrenColumnName])];
1023
+ }
1024
+ });
1025
+ return list;
1026
+ };
1027
+ export const getSelectedCellMatrix = (table, startCell, endCell) => {
1028
+ if (!startCell || !endCell) return {
1029
+ rowIds: [],
1030
+ colIds: [],
1031
+ startRowIndex: undefined,
1032
+ endRowIndex: undefined,
1033
+ startColIndex: undefined,
1034
+ endColIndex: undefined,
1035
+ colRange: [],
1036
+ rowRange: []
1037
+ };
1038
+
1039
+ // const rowIds = table.getRowModel().rows.map(r => r.id);
1040
+ const rowIds = table.getRowModel().flatRows.map(r => r.id);
1041
+ const colIds = table.getVisibleLeafColumns().map(c => c.id);
1042
+
1043
+ // const colIds = table.getAllLeafColumns().map(c => c.id);
1044
+
1045
+ const [startRowIndex, endRowIndex] = [rowIds.indexOf(startCell.rowId), rowIds.indexOf(endCell.rowId)].sort((a, b) => a - b);
1046
+ const [startColIndex, endColIndex] = [colIds.indexOf(startCell.colId), colIds.indexOf(endCell.colId)].sort((a, b) => a - b);
1047
+ return {
1048
+ rowRange: rowIds.slice(startRowIndex, endRowIndex + 1),
1049
+ colRange: colIds.slice(startColIndex, endColIndex + 1),
1050
+ startRowIndex,
1051
+ endRowIndex,
1052
+ startColIndex,
1053
+ endColIndex,
1054
+ rowIds,
1055
+ colIds
1056
+ };
1057
+ };
1058
+ export function addRowsDownWithCtrl(arr, n) {
1059
+ if (!Array.isArray(arr) || arr.length === 0) {
1060
+ return {
1061
+ combined: arr,
1062
+ addedRows: []
1063
+ };
1064
+ }
1065
+ const m = arr.length;
1066
+ const numCols = arr[0].length;
1067
+ const addedRows = [];
1068
+
1069
+ // Hàm kiểm tra kiểu date hợp lệ
1070
+ const isValidDate = item => {
1071
+ // return !isNaN(Date.parse(d))
1072
+
1073
+ if (typeof item === 'number') {
1074
+ // return 'number'
1075
+ return false;
1076
+ }
1077
+ if (typeof item === 'string') {
1078
+ // Kiểm tra nếu là chuỗi ISO date hợp lệ
1079
+ const date = new Date(item);
1080
+ if (!isNaN(date.getTime()) && item.includes('T')) {
1081
+ // return 'date'
1082
+ return true;
1083
+ }
1084
+ // return 'string'
1085
+ return false;
1086
+ }
1087
+ return !isNaN(Date.parse(item));
1088
+ };
1089
+
1090
+ // Lấy giá trị mẫu của cột j từ hàng đầu tiên
1091
+ const getSample = j => arr[0][j];
1092
+
1093
+ // Xác định chế độ xử lý cho mỗi cột:
1094
+ // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle'
1095
+ const modes = [];
1096
+ const steps = []; // bước tăng, nếu có (cho number hoặc date)
1097
+
1098
+ for (let j = 0; j < numCols; j++) {
1099
+ const sample = getSample(j);
1100
+ if (m === 1) {
1101
+ // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên.
1102
+ if (typeof sample === "number") {
1103
+ modes[j] = "number-constant";
1104
+ } else if (isValidDate(sample)) {
1105
+ modes[j] = "date-stepping";
1106
+ steps[j] = 24 * 3600 * 1000; // 1 ngày = 86400000 ms
1107
+ } else {
1108
+ modes[j] = "cycle";
1109
+ }
1110
+ } else if (m === 2) {
1111
+ // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date
1112
+ const first = arr[0][j],
1113
+ second = arr[1][j];
1114
+ if (typeof first === "number" && typeof second === "number") {
1115
+ modes[j] = "number-stepping";
1116
+ steps[j] = second - first;
1117
+ } else if (isValidDate(first) && isValidDate(second)) {
1118
+ modes[j] = "date-stepping";
1119
+ steps[j] = Date.parse(second) - Date.parse(first);
1120
+ } else {
1121
+ modes[j] = "cycle";
1122
+ }
1123
+ } else {
1124
+ // Nếu mảng có >2 hàng
1125
+ const first = arr[0][j],
1126
+ second = arr[1][j],
1127
+ third = arr[2][j];
1128
+ if (typeof first === "number" && typeof second === "number" && typeof third === "number") {
1129
+ const step1 = second - first;
1130
+ const step2 = third - second;
1131
+ if (step1 === step2) {
1132
+ modes[j] = "number-stepping";
1133
+ steps[j] = step1;
1134
+ } else {
1135
+ modes[j] = "cycle";
1136
+ }
1137
+ } else if (isValidDate(first) && isValidDate(second) && isValidDate(third)) {
1138
+ const step1 = Date.parse(second) - Date.parse(first);
1139
+ const step2 = Date.parse(third) - Date.parse(second);
1140
+ if (step1 === step2) {
1141
+ modes[j] = "date-stepping";
1142
+ steps[j] = step1;
1143
+ } else {
1144
+ modes[j] = "cycle";
1145
+ }
1146
+ } else {
1147
+ modes[j] = "cycle";
1148
+ }
1149
+ }
1150
+ }
1151
+
1152
+ // Tạo các dòng mới (thêm n dòng)
1153
+ // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step
1154
+ // Nếu chế độ là cycle thì dùng arr[i mod m][j]
1155
+ for (let i = 0; i < n; i++) {
1156
+ const newRow = [];
1157
+ for (let j = 0; j < numCols; j++) {
1158
+ let newValue;
1159
+ switch (modes[j]) {
1160
+ case "number-constant":
1161
+ // Mảng có 1 hàng, số giữ nguyên
1162
+ newValue = arr[0][j];
1163
+ break;
1164
+ case "number-stepping":
1165
+ {
1166
+ // Lấy giá trị cuối của cột j trong mảng ban đầu
1167
+ const lastValue = arr[m - 1][j];
1168
+ newValue = lastValue + (i + 1) * steps[j];
1169
+ }
1170
+ break;
1171
+ case "date-stepping":
1172
+ {
1173
+ // Lấy giá trị cuối, chuyển về date, cộng thêm (i+1)*step, chuyển lại về định dạng ISO
1174
+ const lastDate = new Date(arr[m - 1][j]);
1175
+ const newTime = lastDate.getTime() + (i + 1) * steps[j];
1176
+ newValue = moment(new Date(newTime)).format();
1177
+ }
1178
+ break;
1179
+ case "cycle":
1180
+ default:
1181
+ // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m)
1182
+ newValue = arr[i % m][j];
1183
+ break;
1184
+ }
1185
+ newRow.push(newValue);
1186
+ }
1187
+ addedRows.push(newRow);
1188
+ }
1189
+ const combined = arr.concat(addedRows);
1190
+ return {
1191
+ combined,
1192
+ addedRows
1193
+ };
1194
+ }
1195
+ export function addRowsDown(arr, n) {
1196
+ if (!Array.isArray(arr) || arr.length === 0) {
1197
+ return {
1198
+ combined: arr,
1199
+ addedRows: []
1200
+ };
1201
+ }
1202
+ const m = arr.length;
1203
+ const numCols = arr[0].length;
1204
+ const addedRows = [];
1205
+
1206
+ // // Hàm kiểm tra kiểu date hợp lệ
1207
+ // const isValidDate = (item: any) => {
1208
+ //
1209
+ //
1210
+ // // return !isNaN(Date.parse(d))
1211
+ //
1212
+ // if (typeof item === 'number') {
1213
+ // // return 'number'
1214
+ // return false
1215
+ // }
1216
+ // if (typeof item === 'string') {
1217
+ // // Kiểm tra nếu là chuỗi ISO date hợp lệ
1218
+ // const date = new Date(item)
1219
+ // if (!isNaN(date.getTime()) && item.includes('T')) {
1220
+ // // return 'date'
1221
+ // return true
1222
+ // }
1223
+ // // return 'string'
1224
+ // return false
1225
+ // }
1226
+ //
1227
+ // return !isNaN(Date.parse(item))
1228
+ //
1229
+ // }
1230
+
1231
+ // Lấy giá trị mẫu của cột j từ hàng đầu tiên
1232
+ const getSample = j => arr[0][j];
1233
+
1234
+ // Xác định chế độ xử lý cho mỗi cột:
1235
+ // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle'
1236
+ const modes = [];
1237
+ const steps = []; // bước tăng, nếu có (cho number hoặc date)
1238
+
1239
+ for (let j = 0; j < numCols; j++) {
1240
+ const sample = getSample(j);
1241
+ if (m === 1) {
1242
+ // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên.
1243
+ if (typeof sample === "number") {
1244
+ modes[j] = "number-constant";
1245
+ }
1246
+ // else if (isValidDate(sample)) {
1247
+ // modes[j] = "date-stepping"
1248
+ // steps[j] = 24 * 3600 * 1000 // 1 ngày = 86400000 ms
1249
+ // }
1250
+ else {
1251
+ modes[j] = "cycle";
1252
+ }
1253
+ } else if (m === 2) {
1254
+ // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date
1255
+ const first = arr[0][j],
1256
+ second = arr[1][j];
1257
+ if (typeof first === "number" && typeof second === "number") {
1258
+ modes[j] = "number-stepping";
1259
+ steps[j] = second - first;
1260
+ }
1261
+ // else if (isValidDate(first) && isValidDate(second)) {
1262
+ // modes[j] = "date-stepping"
1263
+ // steps[j] = Date.parse(second) - Date.parse(first)
1264
+ // }
1265
+ else {
1266
+ modes[j] = "cycle";
1267
+ }
1268
+ } else {
1269
+ // Nếu mảng có >2 hàng
1270
+ const first = arr[0][j],
1271
+ second = arr[1][j],
1272
+ third = arr[2][j];
1273
+ if (typeof first === "number" && typeof second === "number" && typeof third === "number") {
1274
+ const step1 = second - first;
1275
+ const step2 = third - second;
1276
+ if (step1 === step2) {
1277
+ modes[j] = "number-stepping";
1278
+ steps[j] = step1;
1279
+ } else {
1280
+ modes[j] = "cycle";
1281
+ }
1282
+ }
1283
+ // else if (isValidDate(first) && isValidDate(second) && isValidDate(third)) {
1284
+ // const step1 = Date.parse(second) - Date.parse(first)
1285
+ // const step2 = Date.parse(third) - Date.parse(second)
1286
+ // if (step1 === step2) {
1287
+ // modes[j] = "date-stepping"
1288
+ // steps[j] = step1
1289
+ // } else {
1290
+ // modes[j] = "cycle"
1291
+ // }
1292
+ // }
1293
+ else {
1294
+ modes[j] = "cycle";
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ // Tạo các dòng mới (thêm n dòng)
1300
+ // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step
1301
+ // Nếu chế độ là cycle thì dùng arr[i mod m][j]
1302
+ for (let i = 0; i < n; i++) {
1303
+ const newRow = [];
1304
+ for (let j = 0; j < numCols; j++) {
1305
+ let newValue;
1306
+ switch (modes[j]) {
1307
+ case "number-constant":
1308
+ // Mảng có 1 hàng, số giữ nguyên
1309
+ newValue = arr[0][j];
1310
+ break;
1311
+ case "number-stepping":
1312
+ {
1313
+ // Lấy giá trị cuối của cột j trong mảng ban đầu
1314
+ const lastValue = arr[m - 1][j];
1315
+ newValue = lastValue + (i + 1) * steps[j];
1316
+ }
1317
+ break;
1318
+ // case "date-stepping":
1319
+ // {
1320
+ // // Lấy giá trị cuối, chuyển về date, cộng thêm (i+1)*step, chuyển lại về định dạng ISO
1321
+ // const lastDate = new Date(arr[m - 1][j])
1322
+ // const newTime = lastDate.getTime() + (i + 1) * steps[j]
1323
+ // newValue = moment(new Date(newTime)).format()
1324
+ // }
1325
+ // break
1326
+ case "cycle":
1327
+ default:
1328
+ // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m)
1329
+ newValue = arr[i % m][j];
1330
+ break;
1331
+ }
1332
+ newRow.push(newValue);
1333
+ }
1334
+ addedRows.push(newRow);
1335
+ }
1336
+ const combined = arr.concat(addedRows);
1337
+ return {
1338
+ combined,
1339
+ addedRows
1340
+ };
1341
+ }
1342
+ export function addRowsUpWithCtrl(array, n) {
1343
+ const arr = array.reverse();
1344
+ if (!Array.isArray(arr) || arr.length === 0) {
1345
+ return {
1346
+ combined: arr,
1347
+ addedRows: []
1348
+ };
1349
+ }
1350
+ const m = arr.length;
1351
+ const numCols = arr[0].length;
1352
+ const addedRows = [];
1353
+
1354
+ // Hàm kiểm tra kiểu date hợp lệ
1355
+ const isValidDate = item => {
1356
+ // return !isNaN(Date.parse(d))
1357
+
1358
+ if (typeof item === 'number') {
1359
+ // return 'number'
1360
+ return false;
1361
+ }
1362
+ if (typeof item === 'string') {
1363
+ // Kiểm tra nếu là chuỗi ISO date hợp lệ
1364
+ const date = new Date(item);
1365
+ if (!isNaN(date.getTime()) && item.includes('T')) {
1366
+ // return 'date'
1367
+ return true;
1368
+ }
1369
+ // return 'string'
1370
+ return false;
1371
+ }
1372
+ return !isNaN(Date.parse(item));
1373
+ };
1374
+
1375
+ // Lấy giá trị mẫu của cột j từ hàng đầu tiên
1376
+ const getSample = j => arr[0][j];
1377
+
1378
+ // Xác định chế độ xử lý cho mỗi cột:
1379
+ // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle'
1380
+ const modes = [];
1381
+ const steps = []; // bước tăng, nếu có (cho number hoặc date)
1382
+
1383
+ for (let j = 0; j < numCols; j++) {
1384
+ const sample = getSample(j);
1385
+ if (m === 1) {
1386
+ // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên.
1387
+ if (typeof sample === "number") {
1388
+ modes[j] = "number-constant";
1389
+ } else if (isValidDate(sample)) {
1390
+ modes[j] = "date-stepping";
1391
+ steps[j] = 24 * 3600 * 1000; // 1 ngày = 86400000 ms
1392
+ } else {
1393
+ modes[j] = "cycle";
1394
+ }
1395
+ } else if (m === 2) {
1396
+ // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date
1397
+ const first = arr[0][j],
1398
+ second = arr[1][j];
1399
+ if (typeof first === "number" && typeof second === "number") {
1400
+ modes[j] = "number-stepping";
1401
+ steps[j] = second - first;
1402
+ } else if (isValidDate(first) && isValidDate(second)) {
1403
+ modes[j] = "date-stepping";
1404
+ steps[j] = Date.parse(second) - Date.parse(first);
1405
+ } else {
1406
+ modes[j] = "cycle";
1407
+ }
1408
+ } else {
1409
+ // Nếu mảng có >2 hàng
1410
+ const first = arr[0][j],
1411
+ second = arr[1][j],
1412
+ third = arr[2][j];
1413
+ if (typeof first === "number" && typeof second === "number" && typeof third === "number") {
1414
+ const step1 = second - first;
1415
+ const step2 = third - second;
1416
+ if (step1 === step2) {
1417
+ modes[j] = "number-stepping";
1418
+ steps[j] = step1;
1419
+ } else {
1420
+ modes[j] = "cycle";
1421
+ }
1422
+ } else if (isValidDate(first) && isValidDate(second) && isValidDate(third)) {
1423
+ const step1 = Date.parse(second) - Date.parse(first);
1424
+ const step2 = Date.parse(third) - Date.parse(second);
1425
+ if (step1 === step2) {
1426
+ modes[j] = "date-stepping";
1427
+ steps[j] = step1;
1428
+ } else {
1429
+ modes[j] = "cycle";
1430
+ }
1431
+ } else {
1432
+ modes[j] = "cycle";
1433
+ }
1434
+ }
1435
+ }
1436
+
1437
+ // Tạo các dòng mới (thêm n dòng)
1438
+ // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step
1439
+ // Nếu chế độ là cycle thì dùng arr[i mod m][j]
1440
+ for (let i = n - 1; i >= 0; i--) {
1441
+ const newRow = [];
1442
+ for (let j = 0; j < numCols; j++) {
1443
+ let newValue;
1444
+ switch (modes[j]) {
1445
+ case "number-constant":
1446
+ // Mảng có 1 hàng, số giữ nguyên
1447
+ newValue = arr[0][j];
1448
+ break;
1449
+ case "number-stepping":
1450
+ {
1451
+ // Lấy giá trị cuối của cột j trong mảng ban đầu
1452
+
1453
+ const lastValue = arr[m - 1][j];
1454
+ newValue = lastValue - (i + 1) * steps[j] * -1;
1455
+ }
1456
+ break;
1457
+ case "date-stepping":
1458
+ {
1459
+ // Lấy giá trị cuối, chuyển về date, cộng thêm (i+1)*step, chuyển lại về định dạng ISO
1460
+
1461
+ const lastDate = new Date(arr[m - 1][j]);
1462
+ const newTime = m === 1 ? lastDate.getTime() - (i + 1) * steps[j] : lastDate.getTime() - (i + 1) * steps[j] * -1;
1463
+ newValue = moment(new Date(newTime)).format();
1464
+ }
1465
+ break;
1466
+ case "cycle":
1467
+ default:
1468
+ // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m)
1469
+
1470
+ newValue = arr[i % m][j];
1471
+ break;
1472
+ }
1473
+ newRow.push(newValue);
1474
+ }
1475
+ addedRows.push(newRow);
1476
+ }
1477
+ const combined = arr.concat(addedRows);
1478
+ return {
1479
+ combined,
1480
+ addedRows
1481
+ };
1482
+ }
1483
+ export function addRowsUp(array, n) {
1484
+ const arr = array.reverse();
1485
+ if (!Array.isArray(arr) || arr.length === 0) {
1486
+ return {
1487
+ combined: arr,
1488
+ addedRows: []
1489
+ };
1490
+ }
1491
+ const m = arr.length;
1492
+ const numCols = arr[0].length;
1493
+ const addedRows = [];
1494
+
1495
+ // Hàm kiểm tra kiểu date hợp lệ
1496
+ // const isValidDate = (item: any) => {
1497
+ //
1498
+ //
1499
+ // // return !isNaN(Date.parse(d))
1500
+ //
1501
+ // if (typeof item === 'number') {
1502
+ // // return 'number'
1503
+ // return false
1504
+ // }
1505
+ // if (typeof item === 'string') {
1506
+ // // Kiểm tra nếu là chuỗi ISO date hợp lệ
1507
+ // const date = new Date(item)
1508
+ // if (!isNaN(date.getTime()) && item.includes('T')) {
1509
+ // // return 'date'
1510
+ // return true
1511
+ // }
1512
+ // // return 'string'
1513
+ // return false
1514
+ // }
1515
+ //
1516
+ // return !isNaN(Date.parse(item))
1517
+ //
1518
+ // }
1519
+
1520
+ // Lấy giá trị mẫu của cột j từ hàng đầu tiên
1521
+ const getSample = j => arr[0][j];
1522
+
1523
+ // Xác định chế độ xử lý cho mỗi cột:
1524
+ // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle'
1525
+ const modes = [];
1526
+ const steps = []; // bước tăng, nếu có (cho number hoặc date)
1527
+
1528
+ for (let j = 0; j < numCols; j++) {
1529
+ const sample = getSample(j);
1530
+ if (m === 1) {
1531
+ // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên.
1532
+ if (typeof sample === "number") {
1533
+ modes[j] = "number-constant";
1534
+ } else {
1535
+ modes[j] = "cycle";
1536
+ }
1537
+ } else if (m === 2) {
1538
+ // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date
1539
+ const first = arr[0][j],
1540
+ second = arr[1][j];
1541
+ if (typeof first === "number" && typeof second === "number") {
1542
+ modes[j] = "number-stepping";
1543
+ steps[j] = second - first;
1544
+ } else {
1545
+ modes[j] = "cycle";
1546
+ }
1547
+ } else {
1548
+ // Nếu mảng có >2 hàng
1549
+ const first = arr[0][j],
1550
+ second = arr[1][j],
1551
+ third = arr[2][j];
1552
+ if (typeof first === "number" && typeof second === "number" && typeof third === "number") {
1553
+ const step1 = second - first;
1554
+ const step2 = third - second;
1555
+ if (step1 === step2) {
1556
+ modes[j] = "number-stepping";
1557
+ steps[j] = step1;
1558
+ } else {
1559
+ modes[j] = "cycle";
1560
+ }
1561
+ } else {
1562
+ modes[j] = "cycle";
1563
+ }
1564
+ }
1565
+ }
1566
+
1567
+ // Tạo các dòng mới (thêm n dòng)
1568
+ // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step
1569
+ // Nếu chế độ là cycle thì dùng arr[i mod m][j]
1570
+ for (let i = n - 1; i >= 0; i--) {
1571
+ const newRow = [];
1572
+ for (let j = 0; j < numCols; j++) {
1573
+ let newValue;
1574
+ switch (modes[j]) {
1575
+ case "number-constant":
1576
+ // Mảng có 1 hàng, số giữ nguyên
1577
+ newValue = arr[0][j];
1578
+ break;
1579
+ case "number-stepping":
1580
+ {
1581
+ // Lấy giá trị cuối của cột j trong mảng ban đầu
1582
+
1583
+ const lastValue = arr[m - 1][j];
1584
+ newValue = lastValue - (i + 1) * steps[j] * -1;
1585
+ }
1586
+ break;
1587
+ case "cycle":
1588
+ default:
1589
+ // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m)
1590
+
1591
+ newValue = arr[i % m][j];
1592
+ break;
1593
+ }
1594
+ newRow.push(newValue);
1595
+ }
1596
+ addedRows.push(newRow);
1597
+ }
1598
+ const combined = arr.concat(addedRows);
1599
+ return {
1600
+ combined,
1601
+ addedRows
1602
+ };
1603
+ }
1604
+ export const convertFilters = filters => {
1605
+ const result = [];
1606
+ filters.forEach(({
1607
+ key,
1608
+ column,
1609
+ filteredKeys,
1610
+ operator
1611
+ }) => {
1612
+ if (!filteredKeys || filteredKeys.length === 0) {
1613
+ return;
1614
+ }
1615
+ if (column?.typeFilter === "DateRange" && filteredKeys.length === 2) {
1616
+ result.push({
1617
+ key,
1618
+ field: column?.field,
1619
+ value: filteredKeys[0],
1620
+ predicate: "and",
1621
+ operator: "greaterthanorequal"
1622
+ }, {
1623
+ key,
1624
+ field: column?.field,
1625
+ value: filteredKeys[1],
1626
+ predicate: "and",
1627
+ operator: "lessthanorequal"
1628
+ });
1629
+ } else if (column?.typeFilter === "NumberRange") {
1630
+ if ((filteredKeys[0] || filteredKeys[0] === 0) && !filteredKeys[1]) {
1631
+ result.push({
1632
+ key,
1633
+ field: column?.field,
1634
+ value: filteredKeys[0],
1635
+ predicate: "and",
1636
+ operator: "greaterthanorequal"
1637
+ });
1638
+ }
1639
+ if ((filteredKeys[1] || filteredKeys[1] === 0) && !filteredKeys[0]) {
1640
+ result.push({
1641
+ key,
1642
+ field: column?.field,
1643
+ value: filteredKeys[1],
1644
+ predicate: "and",
1645
+ operator: "lessthanorequal"
1646
+ });
1647
+ }
1648
+ if ((filteredKeys[0] || filteredKeys[0] === 0) && (filteredKeys[1] || filteredKeys[1] === 0)) {
1649
+ result.push({
1650
+ key,
1651
+ field: column?.field,
1652
+ value: filteredKeys[0],
1653
+ predicate: "and",
1654
+ operator: "greaterthanorequal"
1655
+ }, {
1656
+ key,
1657
+ field: column?.field,
1658
+ value: filteredKeys[1],
1659
+ predicate: "and",
1660
+ operator: "lessthanorequal"
1661
+ });
1662
+ }
1663
+ } else if (column?.typeFilter === 'Checkbox') {
1664
+ filteredKeys.forEach(value => {
1665
+ result.push({
1666
+ key,
1667
+ field: column?.field,
1668
+ value,
1669
+ predicate: "or",
1670
+ operator
1671
+ });
1672
+ });
1673
+ } else {
1674
+ result.push({
1675
+ key,
1676
+ field: column?.field,
1677
+ value: filteredKeys[0],
1678
+ predicate: 'and',
1679
+ operator
1680
+ });
1681
+ }
1682
+ });
1683
+ return result;
1684
+ };
1685
+
1686
+ // export function getInvisibleColumns(columns: ColumnTable[]): Record<string, boolean> {
1687
+ // const result: Record<string, boolean> = {};
1688
+ // for (const col of columns) {
1689
+ // if (col.visible === false || col.hidden) {
1690
+ // result[col.field ?? ''] = false;
1691
+ // }
1692
+ // }
1693
+ // return result;
1694
+ // }
1695
+
1696
+ export function getInvisibleColumns(columns) {
1697
+ const result = {};
1698
+ function traverse(cols) {
1699
+ for (const col of cols) {
1700
+ if (col.visible === false) {
1701
+ result[col.field ?? ''] = false;
1702
+ }
1703
+ if (col.children && col.children.length > 0) {
1704
+ traverse(col.children);
1705
+ }
1706
+ }
1707
+ }
1708
+ traverse(columns);
1709
+ return result;
1710
+ }
1711
+ export const getAllVisibleKeys = columns => {
1712
+ const keys = [];
1713
+ const traverse = (cols, parentHidden = false) => {
1714
+ for (const col of cols) {
1715
+ if (col.hidden || parentHidden) {
1716
+ continue;
1717
+ }
1718
+ if (col.key) {
1719
+ keys.push(col.key);
1720
+ }
1721
+ if (col.children) {
1722
+ traverse(col.children, col.hidden);
1723
+ }
1724
+ }
1725
+ };
1726
+ traverse(columns);
1727
+ return keys;
1728
+ };
1729
+ export const getAllVisibleKeys1 = columns => {
1730
+ const keys = [];
1731
+ const traverse = (cols, parentHidden = false) => {
1732
+ for (const col of cols) {
1733
+ if (col.visible === false || col.hidden || parentHidden) {
1734
+ continue;
1735
+ }
1736
+ if (col.field) {
1737
+ keys.push(col.field);
1738
+ }
1739
+ if (col.children) {
1740
+ traverse(col.children, col.visible);
1741
+ }
1742
+ }
1743
+ };
1744
+ traverse(columns);
1745
+ return keys;
1746
+ };
1747
+ export function getHiddenParentKeys(columns, parentKeys = []) {
1748
+ const hiddenParents = new Set();
1749
+ for (const column of columns) {
1750
+ if (column.children) {
1751
+ const currentPath = column.key ? [...parentKeys, column.key] : [...parentKeys];
1752
+ const childHiddenParents = getHiddenParentKeys(column.children, currentPath);
1753
+ if (childHiddenParents.length > 0) {
1754
+ childHiddenParents.forEach(key => hiddenParents.add(key));
1755
+ currentPath.forEach(key => hiddenParents.add(key));
1756
+ }
1757
+ } else if (column.hidden) {
1758
+ parentKeys.forEach(key => hiddenParents.add(key));
1759
+ }
1760
+ }
1761
+ return Array.from(hiddenParents);
1762
+ }
1763
+ export function getHiddenParentKeys1(columns, parentKeys = []) {
1764
+ const hiddenParents = new Set();
1765
+ for (const column of columns) {
1766
+ if (column.children) {
1767
+ const currentPath = column.field ? [...parentKeys, column.field] : [...parentKeys];
1768
+ const childHiddenParents = getHiddenParentKeys1(column.children, currentPath);
1769
+ if (childHiddenParents.length > 0) {
1770
+ childHiddenParents.forEach(key => hiddenParents.add(key));
1771
+ currentPath.forEach(key => hiddenParents.add(key));
1772
+ }
1773
+ } else if (column.visible !== false && column.hidden !== true) {
1774
+ parentKeys.forEach(key => hiddenParents.add(key));
1775
+ }
1776
+ }
1777
+ return Array.from(hiddenParents);
1778
+ }
1779
+ export const getVisibleColumnKeys = columns => {
1780
+ const allKeys = getAllVisibleKeys(columns);
1781
+ const allParentKeys = getHiddenParentKeys(columns);
1782
+ return allKeys.filter(item => !allParentKeys.includes(item));
1783
+ };
1784
+ export const getVisibleColumnKeys1 = columns => {
1785
+ const allKeys = getAllVisibleKeys1(columns);
1786
+ const allParentKeys = getHiddenParentKeys1(columns);
1787
+ return allKeys.filter(item => !allParentKeys.includes(item));
1788
+ };
1789
+ export function isObjEqual(obj1, obj2) {
1790
+ // Trường hợp tham chiếu bằng nhau
1791
+ if (obj1 === obj2) return true;
1792
+
1793
+ // Nếu 1 trong 2 không phải object hoặc null thì so sánh trực tiếp
1794
+ if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
1795
+ return obj1 === obj2;
1796
+ }
1797
+
1798
+ // Lấy danh sách key
1799
+ const keys1 = Object.keys(obj1);
1800
+ const keys2 = Object.keys(obj2);
1801
+
1802
+ // Nếu số key khác nhau thì khác nhau
1803
+ if (keys1.length !== keys2.length) return false;
1804
+
1805
+ // Duyệt và so sánh từng key
1806
+ for (const key of keys1) {
1807
+ if (!keys2.includes(key)) return false;
1808
+ if (!isObjEqual(obj1[key], obj2[key])) return false;
1809
+ }
1810
+ return true;
1811
+ }
1812
+
1813
+ // Sorting function
1814
+ export const sortByType = arr => {
1815
+ if (arr) {
1816
+ return arr.sort((a, b) => {
1817
+ if ((a.fixed ?? a.fixedType) === 'left' && (b.fixed ?? b.fixedType) !== 'left') {
1818
+ return -1;
1819
+ } else if ((a.fixed ?? a.fixedType) !== 'left' && (b.fixed ?? b.fixedType) === 'left') {
1820
+ return 1;
1821
+ } else if ((a.fixed ?? a.fixedType) === 'right' && (b.fixed ?? b.fixedType) !== 'right') {
1822
+ return 1;
1823
+ } else if ((a.fixed ?? a.fixedType) !== 'right' && (b.fixed ?? b.fixedType) === 'right') {
1824
+ return -1;
1825
+ }
1826
+ return 0;
1827
+ });
1828
+ } else {
1829
+ return [];
1830
+ }
1831
+ };
1832
+ export function convertColumnsToTreeData(columns, groupColumns) {
1833
+ // return columns.map((col) => {
1834
+ // const node: TreeDataNode = {
1835
+ // key: String(col.id ?? col.id ?? col.header), // key duy nhất
1836
+ // title: String(col.header ?? col.id ?? ''), // tiêu đề
1837
+ // }
1838
+
1839
+ // // Nếu có children (nested columns)
1840
+ // if ('columns' in col && Array.isArray((col as any).columns)) {
1841
+ // node.children = convertColumnsToTreeData(
1842
+ // (col as any).columns as ColumnDef<T, any>[]
1843
+ // )
1844
+ // }
1845
+
1846
+ // return node
1847
+ // })
1848
+
1849
+ return columns.filter(col => {
1850
+ const meta = col.meta ?? {};
1851
+ const inGroup = groupColumns ? groupColumns.includes(String(col.id ?? col.id)) : false;
1852
+
1853
+ // Điều kiện filter:
1854
+ // - Nếu meta.showInColumnChoose = false => loại bỏ
1855
+ // - Nếu không nằm trong groupColumns và không phải column group => loại bỏ
1856
+ if (meta.showInColumnChoose === false) return false;
1857
+ if (inGroup && !('columns' in col && Array.isArray(col.columns))) {
1858
+ return false;
1859
+ }
1860
+ return true;
1861
+ }).map(col => {
1862
+ const node = {
1863
+ key: String(col.id ?? col.id ?? col.header),
1864
+ // title: () => col.header as any
1865
+ title: col.header
1866
+
1867
+ // title: String(col.header ?? col.id ?? ''),
1868
+ };
1869
+ if ('columns' in col && Array.isArray(col.columns)) {
1870
+ const children = convertColumnsToTreeData(col.columns, groupColumns);
1871
+ if (children.length > 0) {
1872
+ node.children = children;
1873
+ }
1874
+ }
1875
+ return node;
1876
+ });
1877
+ }
1878
+ export const updateColumns1 = (columns, includes) => {
1879
+ return columns.map(column => {
1880
+ const newColumn = {
1881
+ ...column
1882
+ };
1883
+ let hasVisibleChild = false;
1884
+ if (!column.field) {
1885
+ return column;
1886
+ }
1887
+ if (newColumn.children) {
1888
+ newColumn.children = updateColumns1(newColumn.children, includes);
1889
+ hasVisibleChild = newColumn.children.some(child => !child.hidden);
1890
+ }
1891
+ newColumn.visible = !!(newColumn.field && includes.includes(newColumn.field));
1892
+ if (newColumn.children && newColumn.children.length > 0) {
1893
+ newColumn.visible = !hasVisibleChild;
1894
+ }
1895
+ return newColumn;
1896
+ });
1897
+ };
1898
+ export const convertToObj = arr => {
1899
+ // const result = Object.keys(obj).reduce((acc: any, key) => {
1900
+ // acc[key] = false;
1901
+ // return acc;
1902
+ // }, {});
1903
+
1904
+ // return result
1905
+
1906
+ return Object.fromEntries(arr.map(key => [key, false]));
1907
+ };
1908
+ export const convertToObjTrue = arr => {
1909
+ // const result = Object.keys(obj).reduce((acc: any, key) => {
1910
+ // acc[key] = false;
1911
+ // return acc;
1912
+ // }, {});
1913
+
1914
+ // return result
1915
+
1916
+ return Object.fromEntries(arr.map(key => [key, true]));
1917
+ };
1918
+ export const getDiffent2Array = (a, b) => {
1919
+ return [...a.filter(x => !b.includes(x)), ...b.filter(x => !a.includes(x))];
1920
+ };
1921
+ export function findFirst(items) {
1922
+ const leftItem = items.find(item => item.getIsPinned() === 'left');
1923
+ if (leftItem) return leftItem;
1924
+ return null;
1925
+ }
1926
+ export function isTreeArray(arr) {
1927
+ if (!Array.isArray(arr) || arr.length === 0) {
1928
+ return false;
1929
+ }
1930
+ for (const item of arr) {
1931
+ if (!item) {
1932
+ return false;
1933
+ }
1934
+ if (item.children?.length > 0) {
1935
+ return true;
1936
+ }
1937
+ }
1938
+ return false;
1939
+ }
1940
+ export function updateColumnWidthsRecursive(columns, sizing) {
1941
+ return columns.map(col => {
1942
+ const updated = {
1943
+ ...col
1944
+ };
1945
+
1946
+ // cập nhật width nếu có trong sizing
1947
+ if (sizing[col.field] !== undefined) {
1948
+ updated.width = sizing[col.field];
1949
+ }
1950
+
1951
+ // nếu có children thì gọi đệ quy
1952
+ if (col.children && col.children.length > 0) {
1953
+ updated.children = updateColumnWidthsRecursive(col.children, sizing);
1954
+ }
1955
+ return updated;
1956
+ });
1957
+ }
1958
+ export function updateWidthsByOther(source, target) {
1959
+ const targetMap = new Map();
1960
+
1961
+ // tạo map {field -> width} từ target
1962
+ const buildMap = cols => {
1963
+ cols.forEach(col => {
1964
+ if (col.width !== undefined) {
1965
+ targetMap.set(col.field, col.width);
1966
+ }
1967
+ if (col.children) {
1968
+ buildMap(col.children);
1969
+ }
1970
+ });
1971
+ };
1972
+ buildMap(target);
1973
+
1974
+ // cập nhật width từ map
1975
+ const update = cols => cols.map(col => {
1976
+ const updated = {
1977
+ ...col
1978
+ };
1979
+ if (targetMap.has(col.field)) {
1980
+ updated.width = targetMap.get(col.field);
1981
+ }
1982
+ if (col.children) {
1983
+ updated.children = update(col.children);
1984
+ }
1985
+ return updated;
1986
+ });
1987
+ return update(source);
1988
+ }
1989
+ export function findAllChildrenKeys2(data, rowKey, childrenColumnName) {
1990
+ const keys = [];
1991
+ function dig(list) {
1992
+ (list || []).forEach(item => {
1993
+ keys.push(item[rowKey]);
1994
+ dig(item[childrenColumnName]);
1995
+ });
1996
+ }
1997
+ dig(data);
1998
+ return keys;
1999
+ }
2000
+ export function parseExcelText(text) {
2001
+ // const text = e.clipboardData?.getData('text/plain') ?? '';
2002
+ if (!text) return [];
2003
+
2004
+ // Excel thường dùng \r\n giữa dòng, \t giữa cột
2005
+ const rows = text.split(/\r?\n/) // tách theo dòng
2006
+ .filter(r => r.trim() !== '') // bỏ dòng trống
2007
+ .map(row => row.split('\t') // tách theo cột
2008
+ .map(cell => cell.replace(/\r/g, '').replace(/\n/g, '\n')) // giữ xuống dòng trong ô
2009
+ );
2010
+ return rows;
2011
+ }
2012
+ export function parseExcelClipboard(e) {
2013
+ const text = e.clipboardData?.getData('text/plain') ?? '';
2014
+ if (!text) return [];
2015
+
2016
+ // Excel thường dùng \r\n giữa dòng, \t giữa cột
2017
+ const rows = text.split(/\r?\n/) // tách theo dòng
2018
+ .filter(r => r.trim() !== '') // bỏ dòng trống
2019
+ .map(row => row.split('\t') // tách theo cột
2020
+ .map(cell => cell.replace(/\r/g, '').replace(/\n/g, '\n')) // giữ xuống dòng trong ô
2021
+ );
2022
+ return rows;
2023
+ }
2024
+ export function parseExcelClipboardText(text) {
2025
+ const rows = [];
2026
+ let curRow = [];
2027
+ let curCell = '';
2028
+ let inQuotes = false;
2029
+ for (let i = 0; i < text.length; i++) {
2030
+ const ch = text[i];
2031
+ if (ch === '"') {
2032
+ // "" -> an escaped quote inside quoted field
2033
+ if (inQuotes && text[i + 1] === '"') {
2034
+ curCell += '"';
2035
+ i++; // skip next
2036
+ } else {
2037
+ inQuotes = !inQuotes; // open/close quote
2038
+ }
2039
+ continue;
2040
+ }
2041
+
2042
+ // tab as column separator (only if not inside quotes)
2043
+ if (ch === '\t' && !inQuotes) {
2044
+ curRow.push(curCell);
2045
+ curCell = '';
2046
+ continue;
2047
+ }
2048
+
2049
+ // newline as row separator (handle \r\n and lone \r or \n), only outside quotes
2050
+ if ((ch === '\r' || ch === '\n') && !inQuotes) {
2051
+ // handle CRLF
2052
+ if (ch === '\r' && text[i + 1] === '\n') i++;
2053
+ curRow.push(curCell);
2054
+ curCell = '';
2055
+ rows.push(curRow);
2056
+ curRow = [];
2057
+ continue;
2058
+ }
2059
+
2060
+ // normal character
2061
+ curCell += ch;
2062
+ }
2063
+
2064
+ // push last cell/row (if any)
2065
+ // avoid adding one spurious empty row when text ends with newline (we already pushed that row)
2066
+ if (curCell !== '' || curRow.length > 0) {
2067
+ curRow.push(curCell);
2068
+ rows.push(curRow);
2069
+ }
2070
+
2071
+ // if the whole input was empty, return []
2072
+ if (rows.length === 1 && rows[0].length === 1 && rows[0][0] === '' && text.trim() === '') {
2073
+ return [];
2074
+ }
2075
+ return rows;
2076
+ }
2077
+ export function parseClipboardEvent(e) {
2078
+ const dataTransfer = e.clipboardData ?? e.nativeEvent?.clipboardData;
2079
+ if (!dataTransfer) return [];
2080
+
2081
+ // 1) try HTML (better fidelity for Excel)
2082
+ const html = dataTransfer.getData?.('text/html');
2083
+ if (html) {
2084
+ try {
2085
+ const doc = new DOMParser().parseFromString(html, 'text/html');
2086
+ const table = doc.querySelector('table');
2087
+ if (table) {
2088
+ const result = [];
2089
+ for (const tr of Array.from(table.rows)) {
2090
+ const row = [];
2091
+ for (const cell of Array.from(tr.cells)) {
2092
+ // innerText preserves newlines from <br>
2093
+ row.push(cell.innerText ?? '');
2094
+ }
2095
+ result.push(row);
2096
+ }
2097
+ if (result.length) return result;
2098
+ }
2099
+ } catch {
2100
+ // fallback to text parsing
2101
+ }
2102
+ }
2103
+
2104
+ // 2) fallback: parse plain text (TSV with quoted fields)
2105
+ const text = dataTransfer.getData?.('text/plain') ?? '';
2106
+ return parseExcelClipboardText(text);
2107
+ }
2108
+ export function arraysEqualIgnoreOrderFast(a, b) {
2109
+ if (a.length !== b.length) return false;
2110
+ const count = Object.create(null);
2111
+ for (let i = 0; i < a.length; i++) {
2112
+ const key = a[i];
2113
+ count[key] = (count[key] || 0) + 1;
2114
+ }
2115
+ for (let i = 0; i < b.length; i++) {
2116
+ const key = b[i];
2117
+ if (!count[key]) return false;
2118
+ count[key]--;
2119
+ if (count[key] === 0) delete count[key];
2120
+ }
2121
+ return Object.keys(count).length === 0;
2122
+ }
2123
+ export function filterByIds(a, b) {
2124
+ const idSet = new Set(a);
2125
+ return b.filter(item => idSet.has(item.id));
2126
+ }
2127
+ export function excludeItems(arrayA, arrayB) {
2128
+ const idsInB = new Set(arrayB.map(item => item.id));
2129
+
2130
+ // Lọc A, chỉ giữ lại phần tử không có trong B
2131
+ return arrayA.filter(item => !idsInB.has(item.id));
2132
+ }
2133
+ export function getAllChildren(row) {
2134
+ const children = row.subRows ?? [];
2135
+ return children.reduce((acc, child) => {
2136
+ return acc.concat(child, getAllChildren(child));
2137
+ }, []);
2138
+ }
2139
+
2140
+ // export function toggleSelectAllChildren(row: any) {
2141
+ // const children = row.subRows ?? [];
2142
+
2143
+ // return children.reduce((acc: any, child: any) => {
2144
+ // return acc.concat(child, getAllChildren(child));
2145
+ // }, []);
2146
+ // }
2147
+
2148
+ export function toggleRowAndChildren(row, isSelected) {
2149
+ // Toggle chính row hiện tại
2150
+ row.toggleSelected(isSelected);
2151
+
2152
+ // Nếu có subRows thì gọi đệ quy
2153
+ if (row.subRows && row.subRows.length > 0) {
2154
+ row.subRows.forEach(subRow => toggleRowAndChildren(subRow, isSelected));
2155
+ }
2156
+ }
2157
+ export function countUnselectedChildren(row) {
2158
+ const parent = row.getParentRow();
2159
+ if (!parent) return 0; // Không có cha thì return 0
2160
+
2161
+ const unselectedCount = parent.subRows.filter(child => !child.getIsSelected()).length;
2162
+ return unselectedCount;
2163
+ }
2164
+ export function removeDuplicatesByKey(arr, key = 'id') {
2165
+ const map = new Map();
2166
+ for (const item of arr) {
2167
+ if (!map.has(item[key])) {
2168
+ map.set(item[key], item);
2169
+ }
2170
+ }
2171
+ return Array.from(map.values());
2172
+ }
2173
+ export const getTableHeight = (height, minHeight) => {
2174
+ if (height && !minHeight) {
2175
+ return height;
2176
+ }
2177
+ if (minHeight && !height) {
2178
+ return minHeight;
2179
+ }
2180
+ if (minHeight && height) {
2181
+ return Math.max(minHeight, height);
2182
+ // if (height > minHeight) {
2183
+ // return height
2184
+ // }
2185
+
2186
+ // if (minHeight > height) {
2187
+ // return minHeight
2188
+ // }
2189
+ }
2190
+ };
2191
+ export const convertFlatColumn1 = array => {
2192
+ const tmp = [...array];
2193
+ let result = [];
2194
+ tmp.forEach(item => {
2195
+ if (item.children) {
2196
+ result = result.concat(convertFlatColumn1(item.children));
2197
+ } else {
2198
+ result.push(item);
2199
+ }
2200
+ });
2201
+ return result;
2202
+ };
2203
+
2204
+ // export const updateColumnsByGroup = (columns: ColumnsTable, columnsGroup: string[]) => {
2205
+ // return columns.map(column => {
2206
+ // const newColumn = { ...column }
2207
+ // let hasVisibleChild = false
2208
+ // if (column.field) {
2209
+ // return column
2210
+ // }
2211
+
2212
+ // if (newColumn.children) {
2213
+ // newColumn.children = updateColumnsByGroup(newColumn.children, columnsGroup)
2214
+
2215
+ // hasVisibleChild = newColumn.children.some((child: any) => !!child.visible)
2216
+ // }
2217
+
2218
+ // newColumn.visible = !(newColumn.field && columnsGroup.includes(newColumn.field))
2219
+
2220
+ // if (newColumn.children && newColumn.children.length > 0) {
2221
+ // newColumn.visible = !!hasVisibleChild
2222
+ // }
2223
+
2224
+ // return newColumn
2225
+ // })
2226
+ // }
2227
+
2228
+ export const updateColumnsByGroup = (columns, columnsGroup) => {
2229
+ return columns.map(column => {
2230
+ const newColumn = {
2231
+ ...column
2232
+ };
2233
+ let hasVisibleChild = false;
2234
+ if (!column.field) {
2235
+ return column;
2236
+ }
2237
+ if (newColumn.children) {
2238
+ newColumn.children = updateColumnsByGroup(newColumn.children, columnsGroup);
2239
+ hasVisibleChild = newColumn.children.some(child => child.visible !== false);
2240
+ }
2241
+
2242
+ // Map previous behaviour (hidden = true when field in columnsGroup)
2243
+ // to the new `visible` property: visible = !hidden
2244
+ if (newColumn.field && columnsGroup.includes(newColumn.field)) {
2245
+ newColumn.visible = !(newColumn.field && columnsGroup.includes(newColumn.field));
2246
+ }
2247
+
2248
+ // For parent columns, visibility depends on whether any child is visible
2249
+ if (newColumn.children && newColumn.children.length > 0) {
2250
+ newColumn.visible = !!hasVisibleChild;
2251
+ }
2252
+ return newColumn;
2253
+ });
2254
+ };
2255
+ export function removeKeys(obj, keys) {
2256
+ const newObj = {
2257
+ ...obj
2258
+ };
2259
+ keys.forEach(key => {
2260
+ delete newObj[key];
2261
+ });
2262
+ return newObj;
2263
+ }