@rainbow-oh/yee-c 0.8.12 → 0.8.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Pagination/style/index.less +4 -0
- package/dist/cjs/Spin/demo/types.js +1 -1
- package/dist/cjs/Spin/indicator.js +4 -2
- package/dist/cjs/Spin/style/index.less +2 -8
- package/dist/cjs/Table/col-group.d.ts +2 -2
- package/dist/cjs/Table/col-group.js +1 -1
- package/dist/cjs/Table/demo/sorter-callback.js +3 -2
- package/dist/cjs/Table/demo/sorter.js +3 -2
- package/dist/cjs/Table/hooks/useExpand.d.ts +2 -2
- package/dist/cjs/Table/hooks/useFilter.d.ts +8 -2
- package/dist/cjs/Table/hooks/useFilter.js +36 -22
- package/dist/cjs/Table/hooks/usePagination.js +8 -8
- package/dist/cjs/Table/hooks/useSelection.d.ts +3 -3
- package/dist/cjs/Table/hooks/useSelection.js +8 -9
- package/dist/cjs/Table/hooks/useSorter.d.ts +6 -3
- package/dist/cjs/Table/hooks/useSorter.js +193 -87
- package/dist/cjs/Table/interface.d.ts +10 -7
- package/dist/cjs/Table/table.js +51 -45
- package/dist/cjs/Table/tbody/index.js +13 -5
- package/dist/cjs/Table/tbody/lazy-row.js +6 -3
- package/dist/cjs/Table/tbody/row.js +5 -4
- package/dist/cjs/Table/theader/cell.js +4 -0
- package/dist/cjs/Table/theader/filter.js +52 -20
- package/dist/cjs/Table/util/index.js +5 -3
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Pagination/style/index.less +4 -0
- package/dist/esm/Spin/demo/types.js +6 -3
- package/dist/esm/Spin/indicator.js +4 -2
- package/dist/esm/Spin/style/index.less +2 -8
- package/dist/esm/Table/col-group.d.ts +2 -2
- package/dist/esm/Table/col-group.js +1 -1
- package/dist/esm/Table/demo/sorter-callback.js +3 -2
- package/dist/esm/Table/demo/sorter.js +3 -2
- package/dist/esm/Table/hooks/useExpand.d.ts +2 -2
- package/dist/esm/Table/hooks/useFilter.d.ts +8 -2
- package/dist/esm/Table/hooks/useFilter.js +52 -32
- package/dist/esm/Table/hooks/usePagination.js +8 -10
- package/dist/esm/Table/hooks/useSelection.d.ts +3 -3
- package/dist/esm/Table/hooks/useSelection.js +16 -11
- package/dist/esm/Table/hooks/useSorter.d.ts +6 -3
- package/dist/esm/Table/hooks/useSorter.js +218 -105
- package/dist/esm/Table/interface.d.ts +10 -7
- package/dist/esm/Table/table.js +42 -47
- package/dist/esm/Table/tbody/index.js +8 -5
- package/dist/esm/Table/tbody/lazy-row.js +7 -3
- package/dist/esm/Table/tbody/row.js +7 -6
- package/dist/esm/Table/theader/cell.js +7 -1
- package/dist/esm/Table/theader/filter.js +75 -27
- package/dist/esm/Table/util/index.js +6 -3
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +1 -1
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +1 -1
- package/src/Pagination/style/index.less +4 -0
- package/src/Spin/demo/types.tsx +3 -3
- package/src/Spin/index.en-US.md +2 -8
- package/src/Spin/index.zh-CN.md +2 -8
- package/src/Spin/indicator.tsx +2 -0
- package/src/Spin/style/index.less +2 -8
- package/src/Table/col-group.tsx +1 -1
- package/src/Table/demo/sorter-callback.tsx +3 -2
- package/src/Table/demo/sorter.tsx +3 -2
- package/src/Table/hooks/useExpand.ts +3 -3
- package/src/Table/hooks/useFilter.ts +47 -23
- package/src/Table/hooks/usePagination.ts +9 -10
- package/src/Table/hooks/useSelection.ts +18 -20
- package/src/Table/hooks/useSorter.ts +236 -82
- package/src/Table/interface.ts +19 -7
- package/src/Table/table.tsx +54 -49
- package/src/Table/tbody/index.tsx +14 -5
- package/src/Table/tbody/lazy-row.tsx +6 -3
- package/src/Table/tbody/row.tsx +5 -4
- package/src/Table/theader/cell.tsx +10 -0
- package/src/Table/theader/filter.tsx +64 -22
- package/src/Table/util/index.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -1,106 +1,260 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { ColumnProps } from '../interface';
|
|
3
3
|
|
|
4
|
-
type
|
|
5
|
-
|
|
4
|
+
type SortOrder = 1 | -1;
|
|
5
|
+
type SortState = Record<string, SortOrder>;
|
|
6
|
+
type SorterConfig = Exclude<ColumnProps['sorter'], boolean | undefined>;
|
|
7
|
+
type SortQueueItem = {
|
|
8
|
+
dataIndex: string;
|
|
9
|
+
order: SortOrder;
|
|
6
10
|
};
|
|
7
|
-
|
|
8
|
-
export default function useSorter({
|
|
9
|
-
data,
|
|
10
|
-
columns,
|
|
11
|
-
}: {
|
|
11
|
+
type SortChangeResult = {
|
|
12
12
|
data: Array<Record<string, unknown>>;
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
const getDefaultSorters = useCallback(() => {
|
|
16
|
-
const sorters: SortState = {};
|
|
17
|
-
columns.forEach((column) => {
|
|
18
|
-
const { dataIndex, sorter } = column;
|
|
19
|
-
if (!sorter) return;
|
|
20
|
-
const { defaultSortOrder } =
|
|
21
|
-
typeof sorter === 'boolean' ? ({} as any) : sorter;
|
|
22
|
-
if (dataIndex && sorter) {
|
|
23
|
-
if (defaultSortOrder === 'ascend') {
|
|
24
|
-
sorters[dataIndex] = 1;
|
|
25
|
-
} else if (defaultSortOrder === 'descend') {
|
|
26
|
-
sorters[dataIndex] = -1;
|
|
27
|
-
} else {
|
|
28
|
-
sorters[dataIndex] = 0;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return sorters;
|
|
33
|
-
}, [columns]);
|
|
13
|
+
sorters: SortState;
|
|
14
|
+
};
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
const getSorterConfig = (column?: ColumnProps): SorterConfig | undefined =>
|
|
17
|
+
column && typeof column.sorter === 'object' ? column.sorter : undefined;
|
|
36
18
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
setSorters((prev) => {
|
|
45
|
-
const newSorters = { ...prev };
|
|
46
|
-
const currentOrder = newSorters[dataIndex] ?? 0;
|
|
47
|
-
|
|
48
|
-
if (currentOrder === 0) {
|
|
49
|
-
newSorters[dataIndex] = 1;
|
|
50
|
-
} else if (currentOrder === 1) {
|
|
51
|
-
newSorters[dataIndex] = -1;
|
|
52
|
-
} else {
|
|
53
|
-
newSorters[dataIndex] = 0;
|
|
54
|
-
}
|
|
19
|
+
const toSortOrder = (
|
|
20
|
+
order: 'ascend' | 'descend' | null | undefined,
|
|
21
|
+
): SortOrder | 0 => {
|
|
22
|
+
if (order === 'ascend') return 1;
|
|
23
|
+
if (order === 'descend') return -1;
|
|
24
|
+
return 0;
|
|
25
|
+
};
|
|
55
26
|
|
|
56
|
-
|
|
57
|
-
|
|
27
|
+
const getNextSortOrder = (order: SortOrder | 0): SortOrder | 0 => {
|
|
28
|
+
if (order === 0) return 1;
|
|
29
|
+
if (order === 1) return -1;
|
|
30
|
+
return 0;
|
|
31
|
+
};
|
|
58
32
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Object.keys(newSorters).forEach((key) => {
|
|
62
|
-
if (key !== dataIndex) {
|
|
63
|
-
delete newSorters[key];
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
33
|
+
const isMultipleSorter = (column?: ColumnProps) =>
|
|
34
|
+
Boolean(getSorterConfig(column)?.multiple);
|
|
67
35
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
36
|
+
const applySortChange = (
|
|
37
|
+
queue: SortQueueItem[],
|
|
38
|
+
column: ColumnProps,
|
|
39
|
+
order: SortOrder | 0,
|
|
40
|
+
columnMap: Map<string, ColumnProps>,
|
|
41
|
+
): SortQueueItem[] => {
|
|
42
|
+
const { dataIndex } = column;
|
|
43
|
+
if (!dataIndex) return queue;
|
|
44
|
+
if (order === 0) {
|
|
45
|
+
return queue.filter((item) => item.dataIndex !== dataIndex);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const multiple = getSorterConfig(column)?.multiple;
|
|
49
|
+
const nextItem = { dataIndex, order };
|
|
50
|
+
if (!multiple) {
|
|
51
|
+
return [nextItem];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const next = queue.filter(
|
|
55
|
+
(item) =>
|
|
56
|
+
item.dataIndex === dataIndex ||
|
|
57
|
+
isMultipleSorter(columnMap.get(item.dataIndex)),
|
|
58
|
+
);
|
|
59
|
+
const currentIndex = next.findIndex((item) => item.dataIndex === dataIndex);
|
|
60
|
+
if (currentIndex === -1) {
|
|
61
|
+
next.push(nextItem);
|
|
62
|
+
} else {
|
|
63
|
+
next[currentIndex] = nextItem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof multiple === 'number') {
|
|
67
|
+
const limit = Math.max(1, Math.floor(multiple));
|
|
68
|
+
while (next.length > limit) {
|
|
69
|
+
const removeIndex = next.findIndex(
|
|
70
|
+
(item) => item.dataIndex !== dataIndex,
|
|
71
|
+
);
|
|
72
|
+
if (removeIndex === -1) break;
|
|
73
|
+
next.splice(removeIndex, 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return next;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const normalizeQueue = (
|
|
81
|
+
queue: SortQueueItem[],
|
|
82
|
+
columnMap: Map<string, ColumnProps>,
|
|
83
|
+
) =>
|
|
84
|
+
queue.reduce<SortQueueItem[]>((next, item) => {
|
|
85
|
+
const column = columnMap.get(item.dataIndex);
|
|
86
|
+
return column ? applySortChange(next, column, item.order, columnMap) : next;
|
|
87
|
+
}, []);
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
const areQueuesEqual = (a: SortQueueItem[], b: SortQueueItem[]) =>
|
|
90
|
+
a.length === b.length &&
|
|
91
|
+
a.every(
|
|
92
|
+
(item, index) =>
|
|
93
|
+
item.dataIndex === b[index]?.dataIndex && item.order === b[index]?.order,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const createColumnMap = (columns: ColumnProps[]) =>
|
|
97
|
+
new Map(
|
|
98
|
+
columns
|
|
99
|
+
.filter((column) => Boolean(column.dataIndex && column.sorter))
|
|
100
|
+
.map((column) => [column.dataIndex as string, column]),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const createInitialQueue = (
|
|
104
|
+
columns: ColumnProps[],
|
|
105
|
+
columnMap: Map<string, ColumnProps>,
|
|
106
|
+
) =>
|
|
107
|
+
columns.reduce<SortQueueItem[]>((queue, column) => {
|
|
108
|
+
const config = getSorterConfig(column);
|
|
109
|
+
const order = toSortOrder(
|
|
110
|
+
config?.sortOrder !== undefined
|
|
111
|
+
? config.sortOrder
|
|
112
|
+
: config?.defaultSortOrder,
|
|
75
113
|
);
|
|
114
|
+
return order ? applySortChange(queue, column, order, columnMap) : queue;
|
|
115
|
+
}, []);
|
|
76
116
|
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
const mergeControlledSorters = (
|
|
118
|
+
queue: SortQueueItem[],
|
|
119
|
+
columns: ColumnProps[],
|
|
120
|
+
columnMap: Map<string, ColumnProps>,
|
|
121
|
+
) => {
|
|
122
|
+
const controlledOrders = new Map<string, SortOrder | 0>();
|
|
123
|
+
columns.forEach((column) => {
|
|
124
|
+
const config = getSorterConfig(column);
|
|
125
|
+
if (column.dataIndex && config?.sortOrder !== undefined) {
|
|
126
|
+
controlledOrders.set(column.dataIndex, toSortOrder(config.sortOrder));
|
|
79
127
|
}
|
|
128
|
+
});
|
|
80
129
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
130
|
+
const uncontrolledQueue = queue.filter(
|
|
131
|
+
(item) => !controlledOrders.has(item.dataIndex),
|
|
132
|
+
);
|
|
133
|
+
const controlledQueue: SortQueueItem[] = [];
|
|
134
|
+
queue.forEach((item) => {
|
|
135
|
+
const controlledOrder = controlledOrders.get(item.dataIndex);
|
|
136
|
+
if (controlledOrder === undefined) return;
|
|
137
|
+
controlledOrders.delete(item.dataIndex);
|
|
138
|
+
if (controlledOrder) {
|
|
139
|
+
controlledQueue.push({
|
|
140
|
+
dataIndex: item.dataIndex,
|
|
141
|
+
order: controlledOrder,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
85
145
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
146
|
+
columns.forEach((column) => {
|
|
147
|
+
const { dataIndex } = column;
|
|
148
|
+
if (!dataIndex) return;
|
|
149
|
+
const controlledOrder = controlledOrders.get(dataIndex);
|
|
150
|
+
if (controlledOrder) {
|
|
151
|
+
controlledQueue.push({ dataIndex, order: controlledOrder });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
90
154
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
155
|
+
return normalizeQueue([...uncontrolledQueue, ...controlledQueue], columnMap);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const queueToSortState = (queue: SortQueueItem[]): SortState =>
|
|
159
|
+
Object.fromEntries(
|
|
160
|
+
queue.map(({ dataIndex, order }) => [dataIndex, order]),
|
|
161
|
+
) as SortState;
|
|
162
|
+
|
|
163
|
+
const compareValues = (a: unknown, b: unknown) => {
|
|
164
|
+
if (Object.is(a, b)) return 0;
|
|
165
|
+
if (typeof a === 'number' && typeof b === 'number') return a - b;
|
|
166
|
+
if (typeof a === 'bigint' && typeof b === 'bigint') return a < b ? -1 : 1;
|
|
167
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
168
|
+
if (typeof a === 'boolean' && typeof b === 'boolean') {
|
|
169
|
+
return Number(a) - Number(b);
|
|
170
|
+
}
|
|
171
|
+
return String(a).localeCompare(String(b));
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const sortData = (
|
|
175
|
+
data: Array<Record<string, unknown>>,
|
|
176
|
+
queue: SortQueueItem[],
|
|
177
|
+
columnMap: Map<string, ColumnProps>,
|
|
178
|
+
) => {
|
|
179
|
+
if (!queue.length) return data;
|
|
180
|
+
|
|
181
|
+
return [...data].sort((a, b) => {
|
|
182
|
+
for (const { dataIndex, order } of queue) {
|
|
183
|
+
const aValue = a[dataIndex];
|
|
184
|
+
const bValue = b[dataIndex];
|
|
185
|
+
|
|
186
|
+
const compare = getSorterConfig(columnMap.get(dataIndex))?.sort;
|
|
187
|
+
if (compare) {
|
|
188
|
+
const result = compare(a, b);
|
|
189
|
+
if (result !== 0) {
|
|
190
|
+
return order === -1 ? -result : result;
|
|
97
191
|
}
|
|
192
|
+
continue;
|
|
98
193
|
}
|
|
99
|
-
|
|
194
|
+
|
|
195
|
+
if (aValue === null && bValue === null) continue;
|
|
196
|
+
if (aValue === null || aValue === undefined) return 1;
|
|
197
|
+
if (bValue === null || bValue === undefined) return -1;
|
|
198
|
+
|
|
199
|
+
const result = compareValues(aValue, bValue);
|
|
200
|
+
if (result !== 0) {
|
|
201
|
+
return order === -1 ? -result : result;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return 0;
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export default function useSorter({
|
|
209
|
+
data,
|
|
210
|
+
columns,
|
|
211
|
+
}: {
|
|
212
|
+
data: Array<Record<string, unknown>>;
|
|
213
|
+
columns: Array<ColumnProps>;
|
|
214
|
+
}) {
|
|
215
|
+
const columnMap = useMemo(() => createColumnMap(columns), [columns]);
|
|
216
|
+
const [internalQueue, setInternalQueue] = useState<SortQueueItem[]>(() =>
|
|
217
|
+
createInitialQueue(columns, columnMap),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
setInternalQueue((current) => {
|
|
222
|
+
const next = normalizeQueue(current, columnMap);
|
|
223
|
+
return areQueuesEqual(current, next) ? current : next;
|
|
100
224
|
});
|
|
225
|
+
}, [columnMap]);
|
|
226
|
+
|
|
227
|
+
const activeQueue = useMemo(
|
|
228
|
+
() => mergeControlledSorters(internalQueue, columns, columnMap),
|
|
229
|
+
[internalQueue, columns, columnMap],
|
|
230
|
+
);
|
|
231
|
+
const sorters = useMemo(() => queueToSortState(activeQueue), [activeQueue]);
|
|
232
|
+
const sorted = useMemo(
|
|
233
|
+
() => sortData(data, activeQueue, columnMap),
|
|
234
|
+
[data, activeQueue, columnMap],
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const onSort = useCallback(
|
|
238
|
+
(dataIndex: string): SortChangeResult => {
|
|
239
|
+
const column = columnMap.get(dataIndex);
|
|
240
|
+
if (!column) return { data: sorted, sorters };
|
|
241
|
+
|
|
242
|
+
const currentOrder =
|
|
243
|
+
activeQueue.find((item) => item.dataIndex === dataIndex)?.order ?? 0;
|
|
244
|
+
const nextQueue = applySortChange(
|
|
245
|
+
activeQueue,
|
|
246
|
+
column,
|
|
247
|
+
getNextSortOrder(currentOrder),
|
|
248
|
+
columnMap,
|
|
249
|
+
);
|
|
250
|
+
const nextSorters = queueToSortState(nextQueue);
|
|
251
|
+
const nextData = sortData(data, nextQueue, columnMap);
|
|
101
252
|
|
|
102
|
-
|
|
103
|
-
|
|
253
|
+
setInternalQueue(nextQueue);
|
|
254
|
+
return { data: nextData, sorters: nextSorters };
|
|
255
|
+
},
|
|
256
|
+
[activeQueue, columnMap, data, sorted, sorters],
|
|
257
|
+
);
|
|
104
258
|
|
|
105
259
|
return {
|
|
106
260
|
data: sorted,
|
package/src/Table/interface.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ExpandableType {
|
|
|
24
24
|
/**
|
|
25
25
|
* Default expanded rows
|
|
26
26
|
*/
|
|
27
|
-
defaultExpandedRowKeys?:
|
|
27
|
+
defaultExpandedRowKeys?: SelectionKeyType[]; // Default expanded rows
|
|
28
28
|
/**
|
|
29
29
|
* Expanded rows
|
|
30
30
|
*/
|
|
@@ -57,6 +57,7 @@ export interface ExpandableType {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export type SelectionKeyType = string | number;
|
|
60
|
+
export type FilterValue = string | number;
|
|
60
61
|
|
|
61
62
|
export type ColumnSemanticType = 'inner' | 'action';
|
|
62
63
|
|
|
@@ -210,7 +211,7 @@ export interface ColumnProps {
|
|
|
210
211
|
/**
|
|
211
212
|
* Filter callback function
|
|
212
213
|
*/
|
|
213
|
-
onFilter?: (value:
|
|
214
|
+
onFilter?: (value: FilterValue, record: Record<string, any>) => boolean;
|
|
214
215
|
};
|
|
215
216
|
/**
|
|
216
217
|
* Sorter config
|
|
@@ -221,9 +222,13 @@ export interface ColumnProps {
|
|
|
221
222
|
/**
|
|
222
223
|
* Sort function
|
|
223
224
|
*/
|
|
224
|
-
sort?: (
|
|
225
|
+
sort?: (
|
|
226
|
+
a: Record<string, unknown>,
|
|
227
|
+
b: Record<string, unknown>,
|
|
228
|
+
) => number;
|
|
225
229
|
/**
|
|
226
|
-
*
|
|
230
|
+
* Enable multi-column sorting. A positive number limits the active
|
|
231
|
+
* sorters; when exceeded, the earliest activated sorter is removed.
|
|
227
232
|
*/
|
|
228
233
|
multiple?: boolean | number;
|
|
229
234
|
/**
|
|
@@ -405,6 +410,7 @@ export interface TableRowProps {
|
|
|
405
410
|
pageSize: number;
|
|
406
411
|
index: number;
|
|
407
412
|
record: Record<string, any>;
|
|
413
|
+
rowKeyValue: SelectionKeyType;
|
|
408
414
|
columns: ColumnProps[];
|
|
409
415
|
expandable?: ExpandableType;
|
|
410
416
|
expandedRowKeys?: Array<string | number>;
|
|
@@ -436,7 +442,10 @@ export interface TableRowProps {
|
|
|
436
442
|
index: number;
|
|
437
443
|
}) => void;
|
|
438
444
|
// Checkbox or Radio onChange event
|
|
439
|
-
onSelectionChange: (
|
|
445
|
+
onSelectionChange: (
|
|
446
|
+
event: ChangeEvent<HTMLInputElement>,
|
|
447
|
+
key: SelectionKeyType,
|
|
448
|
+
) => void;
|
|
440
449
|
}
|
|
441
450
|
|
|
442
451
|
type LazyLoadType = {
|
|
@@ -452,7 +461,10 @@ export interface LazyRowProps extends TableRowProps {
|
|
|
452
461
|
lazyLoad?: boolean | LazyLoadType;
|
|
453
462
|
}
|
|
454
463
|
|
|
455
|
-
export interface TableBodyProps extends Omit<
|
|
464
|
+
export interface TableBodyProps extends Omit<
|
|
465
|
+
LazyRowProps,
|
|
466
|
+
'index' | 'record' | 'rowKeyValue'
|
|
467
|
+
> {
|
|
456
468
|
pageData: Array<Record<string, any>>;
|
|
457
469
|
noData?: React.ReactNode;
|
|
458
470
|
}
|
|
@@ -619,7 +631,7 @@ export interface TableProps
|
|
|
619
631
|
* Set the unique key for rows
|
|
620
632
|
* @default id
|
|
621
633
|
*/
|
|
622
|
-
rowKey?: string | ((record: Record<string, any>) =>
|
|
634
|
+
rowKey?: string | ((record: Record<string, any>) => SelectionKeyType);
|
|
623
635
|
/**
|
|
624
636
|
* Summary bar
|
|
625
637
|
*/
|
package/src/Table/table.tsx
CHANGED
|
@@ -83,15 +83,28 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
// Filter
|
|
86
|
-
const {
|
|
86
|
+
const {
|
|
87
|
+
data: filteredData,
|
|
88
|
+
filterRecords,
|
|
89
|
+
onFilter: onFilterInternal,
|
|
90
|
+
} = useFilter({
|
|
87
91
|
data: dataSource,
|
|
92
|
+
columns: wrapedColumns,
|
|
88
93
|
});
|
|
89
94
|
|
|
95
|
+
const filters = useMemo(
|
|
96
|
+
() =>
|
|
97
|
+
Object.fromEntries(
|
|
98
|
+
filterRecords.map(({ dataIndex, value }) => [dataIndex, value]),
|
|
99
|
+
),
|
|
100
|
+
[filterRecords],
|
|
101
|
+
);
|
|
102
|
+
|
|
90
103
|
// Sort
|
|
91
104
|
const {
|
|
92
105
|
data: sortedData,
|
|
93
106
|
sorters,
|
|
94
|
-
onSort,
|
|
107
|
+
onSort: onSortInternal,
|
|
95
108
|
} = useSorter({ data: filteredData, columns: wrapedColumns });
|
|
96
109
|
// Pagination
|
|
97
110
|
const { pageData, current, pageSize, pagination } = usePagination({
|
|
@@ -102,7 +115,7 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
102
115
|
onChange({
|
|
103
116
|
pagination: info,
|
|
104
117
|
sorter: sorters,
|
|
105
|
-
filters
|
|
118
|
+
filters,
|
|
106
119
|
currentDataSource: sortedData,
|
|
107
120
|
action: 'paginate',
|
|
108
121
|
});
|
|
@@ -110,6 +123,20 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
110
123
|
: undefined,
|
|
111
124
|
});
|
|
112
125
|
|
|
126
|
+
const handleSort = React.useCallback(
|
|
127
|
+
(dataIndex: string) => {
|
|
128
|
+
const next = onSortInternal(dataIndex);
|
|
129
|
+
onChange?.({
|
|
130
|
+
pagination: pagination === false ? undefined : { current, pageSize },
|
|
131
|
+
sorter: next.sorters,
|
|
132
|
+
filters,
|
|
133
|
+
currentDataSource: next.data,
|
|
134
|
+
action: 'sort',
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
[onSortInternal, onChange, pagination, current, pageSize, filters],
|
|
138
|
+
);
|
|
139
|
+
|
|
113
140
|
// Expanded row data
|
|
114
141
|
const { expandedRowKeys, onExpand } = useExpand(
|
|
115
142
|
expandable,
|
|
@@ -124,8 +151,8 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
124
151
|
onChange: onSelectionChange,
|
|
125
152
|
} = useSelection({ pageData, dataSource, getRowKey, rowSelection, allKeys });
|
|
126
153
|
|
|
127
|
-
// Track
|
|
128
|
-
const
|
|
154
|
+
// Track filter changes to emit onChange after filtered data is recalculated
|
|
155
|
+
const prevFilterRecordsRef = useRef(filterRecords);
|
|
129
156
|
const contentWrapperRef = useRef<HTMLDivElement>(null);
|
|
130
157
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
131
158
|
const [horizontalScroll, setHorizontalScroll] = React.useState({
|
|
@@ -156,51 +183,29 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
156
183
|
);
|
|
157
184
|
}, []);
|
|
158
185
|
|
|
159
|
-
// Wrap onFilter to trigger onChange
|
|
160
|
-
const onFilter = React.useCallback(
|
|
161
|
-
(...args: Parameters<typeof onFilterInternal>) => {
|
|
162
|
-
onFilterInternal(...args);
|
|
163
|
-
// Trigger onChange for filter action
|
|
164
|
-
if (onChange) {
|
|
165
|
-
onChange({
|
|
166
|
-
pagination: pagination === false ? undefined : { current, pageSize },
|
|
167
|
-
sorter: sorters,
|
|
168
|
-
filters: undefined, // TODO: Extract filter info from args
|
|
169
|
-
currentDataSource: filteredData,
|
|
170
|
-
action: 'filter',
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
[
|
|
175
|
-
onChange,
|
|
176
|
-
pagination,
|
|
177
|
-
current,
|
|
178
|
-
pageSize,
|
|
179
|
-
sorters,
|
|
180
|
-
filteredData,
|
|
181
|
-
onFilterInternal,
|
|
182
|
-
],
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Trigger onChange when sorter, pagination, or filter changes
|
|
186
186
|
useEffect(() => {
|
|
187
|
+
if (prevFilterRecordsRef.current === filterRecords) return;
|
|
188
|
+
|
|
189
|
+
prevFilterRecordsRef.current = filterRecords;
|
|
187
190
|
if (!onChange) return;
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
192
|
+
onChange({
|
|
193
|
+
pagination: pagination === false ? undefined : { current, pageSize },
|
|
194
|
+
sorter: sorters,
|
|
195
|
+
filters,
|
|
196
|
+
currentDataSource: sortedData,
|
|
197
|
+
action: 'filter',
|
|
198
|
+
});
|
|
199
|
+
}, [
|
|
200
|
+
filterRecords,
|
|
201
|
+
filters,
|
|
202
|
+
sortedData,
|
|
203
|
+
pagination,
|
|
204
|
+
current,
|
|
205
|
+
pageSize,
|
|
206
|
+
sorters,
|
|
207
|
+
onChange,
|
|
208
|
+
]);
|
|
204
209
|
|
|
205
210
|
useEffect(() => {
|
|
206
211
|
updateHorizontalScroll();
|
|
@@ -245,9 +250,9 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
|
|
|
245
250
|
headerRows={headerRows}
|
|
246
251
|
checkedAll={checkedAll}
|
|
247
252
|
sorters={sorters}
|
|
248
|
-
onSort={
|
|
253
|
+
onSort={handleSort}
|
|
249
254
|
onCheckAll={onCheckAll}
|
|
250
|
-
onInternalFilter={
|
|
255
|
+
onInternalFilter={onFilterInternal}
|
|
251
256
|
/>
|
|
252
257
|
);
|
|
253
258
|
};
|
|
@@ -31,19 +31,27 @@ const TableBody: React.FC<TableBodyProps> = (props) => {
|
|
|
31
31
|
|
|
32
32
|
const getRenderRows = () => {
|
|
33
33
|
if (lazyLoad) {
|
|
34
|
-
return
|
|
34
|
+
return (
|
|
35
|
+
<LazyRow
|
|
36
|
+
{...rest}
|
|
37
|
+
pageData={pageData}
|
|
38
|
+
columns={columns}
|
|
39
|
+
resolveRowKey={resolveRowKey}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
35
42
|
}
|
|
36
43
|
return pageData.map((record: Record<string, any>, index: number) => {
|
|
44
|
+
const resolvedRowKey = resolveRowKey(record);
|
|
45
|
+
|
|
37
46
|
// 整行接管:由外部组件渲染 <tr> + 单元格(可放拖拽手柄等)
|
|
38
47
|
if (RowRenderer) {
|
|
39
|
-
const rk = resolveRowKey(record);
|
|
40
48
|
return (
|
|
41
49
|
<RowRenderer
|
|
42
|
-
key={
|
|
50
|
+
key={resolvedRowKey ?? index}
|
|
43
51
|
record={record}
|
|
44
52
|
index={index}
|
|
45
53
|
columns={columns}
|
|
46
|
-
rowKey={
|
|
54
|
+
rowKey={resolvedRowKey}
|
|
47
55
|
/>
|
|
48
56
|
);
|
|
49
57
|
}
|
|
@@ -52,8 +60,9 @@ const TableBody: React.FC<TableBodyProps> = (props) => {
|
|
|
52
60
|
{...rest}
|
|
53
61
|
columns={columns}
|
|
54
62
|
record={record}
|
|
63
|
+
rowKeyValue={resolvedRowKey}
|
|
55
64
|
index={index}
|
|
56
|
-
key={`row-${
|
|
65
|
+
key={resolvedRowKey ?? `row-${index}`}
|
|
57
66
|
/>
|
|
58
67
|
);
|
|
59
68
|
});
|
|
@@ -6,7 +6,7 @@ const createBlank = (size: number, key: string) => {
|
|
|
6
6
|
: [];
|
|
7
7
|
};
|
|
8
8
|
const LazyRow = (props: any) => {
|
|
9
|
-
const { range, pageData, columns, ...rest } = props;
|
|
9
|
+
const { range, pageData, columns, resolveRowKey, ...rest } = props;
|
|
10
10
|
const { start, end } = range;
|
|
11
11
|
let result: any = [];
|
|
12
12
|
const pl = pageData.length;
|
|
@@ -14,13 +14,16 @@ const LazyRow = (props: any) => {
|
|
|
14
14
|
result = result.concat(startBlank);
|
|
15
15
|
|
|
16
16
|
for (let i = start; i < (end > pl ? pl : end); i++) {
|
|
17
|
+
const record = pageData[i];
|
|
18
|
+
const rowKeyValue = resolveRowKey(record);
|
|
17
19
|
result.push(
|
|
18
20
|
<TableRow
|
|
19
21
|
{...rest}
|
|
20
22
|
columns={columns}
|
|
21
|
-
record={
|
|
23
|
+
record={record}
|
|
24
|
+
rowKeyValue={rowKeyValue}
|
|
22
25
|
index={i}
|
|
23
|
-
key={
|
|
26
|
+
key={rowKeyValue ?? `row-${i}`}
|
|
24
27
|
/>,
|
|
25
28
|
);
|
|
26
29
|
}
|
package/src/Table/tbody/row.tsx
CHANGED
|
@@ -14,6 +14,7 @@ const TableRow: React.FC<TableRowProps> = (props) => {
|
|
|
14
14
|
const {
|
|
15
15
|
index,
|
|
16
16
|
record,
|
|
17
|
+
rowKeyValue,
|
|
17
18
|
columns,
|
|
18
19
|
highlight,
|
|
19
20
|
stripe,
|
|
@@ -30,10 +31,10 @@ const TableRow: React.FC<TableRowProps> = (props) => {
|
|
|
30
31
|
...rest
|
|
31
32
|
} = props;
|
|
32
33
|
|
|
33
|
-
const { prefixCls
|
|
34
|
+
const { prefixCls } = useContext(TableCtx);
|
|
34
35
|
|
|
35
36
|
const expanded = expandedRowKeys?.includes(
|
|
36
|
-
expandedKeyType === 'index' ? index + 1 :
|
|
37
|
+
expandedKeyType === 'index' ? index + 1 : rowKeyValue,
|
|
37
38
|
);
|
|
38
39
|
|
|
39
40
|
const high = React.useMemo(() => {
|
|
@@ -57,7 +58,6 @@ const TableRow: React.FC<TableRowProps> = (props) => {
|
|
|
57
58
|
|
|
58
59
|
const getSelection = (column: WrapedColumnProps) => {
|
|
59
60
|
const { type, disabled: colDisabled, onCell } = column;
|
|
60
|
-
const rowKeyValue = record[rowKey];
|
|
61
61
|
const props = (onCell?.(record, index) || {}) as Record<string, unknown>;
|
|
62
62
|
const checked = selectedRowKeys?.includes(rowKeyValue);
|
|
63
63
|
|
|
@@ -75,7 +75,8 @@ const TableRow: React.FC<TableRowProps> = (props) => {
|
|
|
75
75
|
const commonProps = {
|
|
76
76
|
checked,
|
|
77
77
|
value: rowKeyValue,
|
|
78
|
-
onChange:
|
|
78
|
+
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
|
|
79
|
+
onSelectionChange(event, rowKeyValue),
|
|
79
80
|
disabled,
|
|
80
81
|
...props,
|
|
81
82
|
};
|