Skip to content

Commit 2fe7166

Browse files
committed
fix(Table): disabled state incorrectly modified by checkAll
1 parent d48d866 commit 2fe7166

File tree

2 files changed

+79
-51
lines changed

2 files changed

+79
-51
lines changed

packages/components/table/hooks/usePagination.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import useConfig from '../../hooks/useConfig';
33
import Pagination, { PageInfo, PaginationProps } from '../../pagination';
44
import type { TableRowData, TdBaseTableProps } from '../type';
55

6+
export const DEFAULT_CURRENT = 1;
7+
export const DEFAULT_PAGE_SIZE = 10;
8+
69
// 分页功能包含:远程数据排序受控、远程数据排序非受控、本地数据排序受控、本地数据排序非受控 等 4 类功能
710
export default function usePagination(props: TdBaseTableProps, tableContentRef: React.RefObject<HTMLDivElement>) {
811
const { pagination, data, disableDataPage } = props;
@@ -15,7 +18,7 @@ export default function usePagination(props: TdBaseTableProps, tableContentRef:
1518
const isControlled = pagination?.current !== undefined;
1619

1720
const calculatePaginatedData = useCallback(
18-
(current = 1, pageSize = 10) => {
21+
(current = DEFAULT_CURRENT, pageSize = DEFAULT_PAGE_SIZE) => {
1922
// data 数据数量超出分页大小时,则自动启动本地数据分页
2023
const shouldPaginate = Boolean(!disableDataPage && data.length > pageSize);
2124
let newData: TableRowData[] = [];
@@ -32,7 +35,7 @@ export default function usePagination(props: TdBaseTableProps, tableContentRef:
3235
);
3336

3437
const updateDataSourceAndPaginate = useCallback(
35-
(current = 1, pageSize = 10) => {
38+
(current = DEFAULT_CURRENT, pageSize = DEFAULT_PAGE_SIZE) => {
3639
const { newData, shouldPaginate } = calculatePaginatedData(current, pageSize);
3740
setIsPaginateData(shouldPaginate);
3841
setDataSource(newData);
@@ -50,15 +53,18 @@ export default function usePagination(props: TdBaseTableProps, tableContentRef:
5053
// 受控情况
5154
useEffect(() => {
5255
if (!pagination || !isControlled) return;
53-
const [current, pageSize] = [pagination?.current || 1, pagination?.pageSize ?? 10];
56+
const [current, pageSize] = [pagination?.current || DEFAULT_CURRENT, pagination?.pageSize || DEFAULT_PAGE_SIZE];
5457
updateDataSourceAndPaginate(current, pageSize);
5558
setInnerPagination({ current, pageSize });
5659
}, [pagination, isControlled, updateDataSourceAndPaginate]);
5760

5861
// 非受控情况
5962
useEffect(() => {
6063
if (!pagination || isControlled) return;
61-
const [current, pageSize] = [pagination?.defaultCurrent || 1, pagination?.defaultPageSize ?? 10];
64+
const [current, pageSize] = [
65+
pagination?.defaultCurrent || DEFAULT_CURRENT,
66+
pagination?.defaultPageSize || DEFAULT_PAGE_SIZE,
67+
];
6268
updateDataSourceAndPaginate(current, pageSize);
6369
// eslint-disable-next-line react-hooks/exhaustive-deps
6470
}, [isControlled, updateDataSourceAndPaginate]);

packages/components/table/hooks/useRowSelect.tsx

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
11
// 行选中相关功能:单选 + 多选
22

3-
import React, { useEffect, useState, MouseEvent, useMemo } from 'react';
4-
import { intersection, get, isFunction } from 'lodash-es';
5-
import { isRowSelectedDisabled } from '@tdesign/common-js/table/utils';
3+
import React, { MouseEvent, useEffect, useMemo, useState } from 'react';
4+
import { get, isFunction } from 'lodash-es';
5+
66
import log from '@tdesign/common-js/log/index';
7+
import { isRowSelectedDisabled } from '@tdesign/common-js/table/utils';
8+
import Checkbox from '../../checkbox';
79
import useControlled from '../../hooks/useControlled';
8-
import {
10+
import Radio from '../../radio';
11+
12+
import type { ClassName } from '../../common';
13+
import type {
914
PrimaryTableCellParams,
1015
PrimaryTableCol,
1116
RowClassNameParams,
1217
TableRowData,
1318
TdBaseTableProps,
1419
TdPrimaryTableProps,
1520
} from '../type';
16-
import { TableClassName } from './useClassName';
17-
import Checkbox from '../../checkbox';
18-
import Radio from '../../radio';
19-
import { ClassName } from '../../common';
21+
import type { TableClassName } from './useClassName';
22+
import { DEFAULT_CURRENT, DEFAULT_PAGE_SIZE } from './usePagination';
2023

2124
const selectedRowDataMap = new Map<string | number, TableRowData>();
2225

2326
export default function useRowSelect(
2427
props: TdPrimaryTableProps,
2528
tableSelectedClasses: TableClassName['tableSelectedClasses'],
2629
) {
27-
const { selectedRowKeys, columns, data, rowKey, indeterminateSelectedRowKeys } = props;
28-
const { pagination, reserveSelectedRowOnPaginate } = props;
30+
const {
31+
selectedRowKeys,
32+
columns,
33+
data,
34+
rowKey,
35+
indeterminateSelectedRowKeys,
36+
pagination,
37+
reserveSelectedRowOnPaginate,
38+
} = props;
2939
const [currentPaginateData, setCurrentPaginateData] = useState<TableRowData[]>(data);
3040
const [selectedRowClassNames, setSelectedRowClassNames] = useState<TdBaseTableProps['rowClassName']>();
3141
const [tSelectedRowKeys, setTSelectedRowKeys] = useControlled(props, 'selectedRowKeys', props.onSelectChange, {
@@ -39,24 +49,22 @@ export default function useRowSelect(
3949
// eslint-disable-next-line
4050
}, [reserveSelectedRowOnPaginate, data, currentPaginateData]);
4151

42-
// 选中的行,和所有可以选择的行,交集,用于计算 isSelectedAll 和 isIndeterminate
43-
const intersectionKeys = intersection(
44-
tSelectedRowKeys,
45-
canSelectedRows.map((t) => get(t, rowKey || 'id')),
46-
);
47-
4852
useEffect(
4953
() => {
5054
if (reserveSelectedRowOnPaginate) return;
5155
// 分页变化时,在 onPageChange 中设置 setCurrentPaginateData,PrimaryTable 中
56+
if (!pagination) {
57+
setCurrentPaginateData(data);
58+
return;
59+
}
5260
const { pageSize, current, defaultPageSize, defaultCurrent } = pagination;
53-
const tPageSize = pageSize || defaultPageSize;
54-
const tCurrent = current || defaultCurrent;
61+
const tPageSize = pageSize || defaultPageSize || DEFAULT_PAGE_SIZE;
62+
const tCurrent = current || defaultCurrent || DEFAULT_CURRENT;
5563
const newData = data.slice(tPageSize * (tCurrent - 1), tPageSize * tCurrent);
5664
setCurrentPaginateData(newData);
5765
},
5866
// eslint-disable-next-line
59-
[data, reserveSelectedRowOnPaginate],
67+
[data, reserveSelectedRowOnPaginate, pagination],
6068
);
6169

6270
useEffect(
@@ -81,22 +89,48 @@ export default function useRowSelect(
8189
return isRowSelectedDisabled(selectColumn, row, rowIndex);
8290
}
8391

84-
function getSelectedHeader() {
85-
return () => {
86-
const isIndeterminate = intersectionKeys.length > 0 && intersectionKeys.length < canSelectedRows.length;
87-
const isChecked =
88-
intersectionKeys.length !== 0 &&
89-
canSelectedRows.length !== 0 &&
90-
intersectionKeys.length === canSelectedRows.length;
91-
return (
92-
<Checkbox
93-
checked={isChecked}
94-
indeterminate={isIndeterminate}
95-
disabled={!canSelectedRows.length}
96-
onChange={handleSelectAll}
97-
/>
98-
);
92+
function renderCheckAll() {
93+
const currentData = reserveSelectedRowOnPaginate ? data : currentPaginateData;
94+
const totalRowCount = currentData?.length || 0;
95+
96+
const currentPageRowKeys = currentData?.map((row) => get(row, rowKey)) || [];
97+
const selectedInCurrentPage = tSelectedRowKeys.filter((key) => currentPageRowKeys.includes(key));
98+
99+
const isChecked = totalRowCount > 0 && selectedInCurrentPage.length === totalRowCount;
100+
const isIndeterminate = selectedInCurrentPage.length > 0 && selectedInCurrentPage.length < totalRowCount;
101+
102+
const handleSelectAll = () => {
103+
const canSelectedRowKeys = canSelectedRows.map((record) => get(record, rowKey));
104+
105+
const currentData = reserveSelectedRowOnPaginate ? data : currentPaginateData;
106+
const disabledRowKeys =
107+
currentData?.filter((row, rowIndex) => isDisabled(row, rowIndex)).map((row) => get(row, rowKey)) || [];
108+
109+
const disabledSelectedRowKeys = selectedRowKeys?.filter((id) => disabledRowKeys.includes(id)) || [];
110+
const allSelectableRowsSelected = canSelectedRowKeys.every((key) => tSelectedRowKeys.includes(key));
111+
const shouldSelectAll = !allSelectableRowsSelected;
112+
113+
const allIds = shouldSelectAll
114+
? [...disabledSelectedRowKeys, ...canSelectedRowKeys]
115+
: [...disabledSelectedRowKeys];
116+
117+
setTSelectedRowKeys(allIds, {
118+
selectedRowData: shouldSelectAll
119+
? allIds.map((t) => selectedRowDataMap.get(t))
120+
: disabledSelectedRowKeys.map((t) => selectedRowDataMap.get(t)),
121+
type: shouldSelectAll ? 'check' : 'uncheck',
122+
currentRowKey: 'CHECK_ALL_BOX',
123+
});
99124
};
125+
126+
return (
127+
<Checkbox
128+
checked={isChecked}
129+
indeterminate={isIndeterminate}
130+
disabled={!canSelectedRows.length}
131+
onChange={handleSelectAll}
132+
/>
133+
);
100134
}
101135

102136
function getRowSelectDisabledData(p: PrimaryTableCellParams<TableRowData>) {
@@ -165,18 +199,6 @@ export default function useRowSelect(
165199
});
166200
}
167201

168-
function handleSelectAll(checked: boolean) {
169-
const reRowKey = rowKey || 'id';
170-
const canSelectedRowKeys = canSelectedRows.map((record) => get(record, reRowKey));
171-
const disabledSelectedRowKeys = selectedRowKeys?.filter((id) => !canSelectedRowKeys.includes(id)) || [];
172-
const allIds = checked ? [...disabledSelectedRowKeys, ...canSelectedRowKeys] : [...disabledSelectedRowKeys];
173-
setTSelectedRowKeys(allIds, {
174-
selectedRowData: checked ? allIds.map((t) => selectedRowDataMap.get(t)) : [],
175-
type: checked ? 'check' : 'uncheck',
176-
currentRowKey: 'CHECK_ALL_BOX',
177-
});
178-
}
179-
180202
function formatToRowSelectColumn(col: PrimaryTableCol) {
181203
const isSelection = ['multiple', 'single'].includes(col.type);
182204
if (!isSelection) return col;
@@ -185,7 +207,7 @@ export default function useRowSelect(
185207
width: col.width || 64,
186208
className: tableSelectedClasses.checkCell,
187209
cell: (p: PrimaryTableCellParams<TableRowData>) => renderSelectCell(p),
188-
title: col.type === 'multiple' ? getSelectedHeader() : col.title,
210+
title: col.type === 'multiple' ? renderCheckAll() : col.title,
189211
};
190212
}
191213

0 commit comments

Comments
 (0)