mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-25 19:50:05 +08:00
feat: Support rowSelection.dirty (#24718)
* feat: Support rowSelection.dirty * rename to reserveKeys * preserveKeys will keep record also * to preserveSelectedRowKeys
This commit is contained in:
parent
71f4a2f985
commit
d154435291
@ -787,27 +787,56 @@ describe('Table.rowSelection', () => {
|
||||
expect(onChange.mock.calls[0][1]).toEqual([expect.objectContaining({ name: 'bamboo' })]);
|
||||
});
|
||||
|
||||
it('do not cache selected keys', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Table
|
||||
dataSource={[{ name: 'light' }, { name: 'bamboo' }]}
|
||||
rowSelection={{ onChange }}
|
||||
rowKey="name"
|
||||
/>,
|
||||
);
|
||||
describe('cache with selected keys', () => {
|
||||
it('default not cache', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Table
|
||||
dataSource={[{ name: 'light' }, { name: 'bamboo' }]}
|
||||
rowSelection={{ onChange }}
|
||||
rowKey="name"
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }]);
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }]);
|
||||
|
||||
wrapper.setProps({ dataSource: [{ name: 'bamboo' }] });
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['bamboo'], [{ name: 'bamboo' }]);
|
||||
wrapper.setProps({ dataSource: [{ name: 'bamboo' }] });
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['bamboo'], [{ name: 'bamboo' }]);
|
||||
});
|
||||
|
||||
it('cache with preserveSelectedRowKeys', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Table
|
||||
dataSource={[{ name: 'light' }, { name: 'bamboo' }]}
|
||||
rowSelection={{ onChange, preserveSelectedRowKeys: true }}
|
||||
rowKey="name"
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }]);
|
||||
|
||||
wrapper.setProps({ dataSource: [{ name: 'bamboo' }] });
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
['light', 'bamboo'],
|
||||
[{ name: 'light' }, { name: 'bamboo' }],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -11,12 +11,16 @@ title:
|
||||
|
||||
另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 `onFilter` 和 `sorter` 函数,而是在把筛选和排序的参数发到服务端来处理。
|
||||
|
||||
当使用 `rowSelection` 时,请设置 `rowSelection.preserveSelectedRowKeys` 属性以保留 `key`。
|
||||
|
||||
**注意,此示例使用 [模拟接口](https://randomuser.me),展示数据可能不准确,请打开网络面板查看请求。**
|
||||
|
||||
## en-US
|
||||
|
||||
This example shows how to fetch and present data from a remote server, and how to implement filtering and sorting in server side by sending related parameters to server.
|
||||
|
||||
Setting `rowSelection.preserveSelectedRowKeys` to keep the `key` when enable selection.
|
||||
|
||||
**Note, this example use [Mock API](https://randomuser.me) that you can look up in Network Console.**
|
||||
|
||||
```jsx
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||
import { INTERNAL_COL_DEFINE } from 'rc-table';
|
||||
import { FixedType } from 'rc-table/lib/interface';
|
||||
@ -71,6 +72,7 @@ export default function useSelection<RecordType>(
|
||||
config: UseSelectionConfig<RecordType>,
|
||||
): [TransformColumns<RecordType>, Set<Key>] {
|
||||
const {
|
||||
preserveSelectedRowKeys,
|
||||
selectedRowKeys,
|
||||
getCheckboxProps,
|
||||
onChange: onSelectionChange,
|
||||
@ -99,15 +101,19 @@ export default function useSelection<RecordType>(
|
||||
getPopupContainer,
|
||||
} = config;
|
||||
|
||||
const [innerSelectedKeys, setInnerSelectedKeys] = React.useState<Key[]>();
|
||||
// ======================== Caches ========================
|
||||
const preserveRecordsRef = React.useRef(new Map<Key, RecordType>());
|
||||
|
||||
// ========================= Keys =========================
|
||||
const [innerSelectedKeys, setInnerSelectedKeys] = useState<Key[]>();
|
||||
const mergedSelectedKeys = selectedRowKeys || innerSelectedKeys || EMPTY_LIST;
|
||||
const mergedSelectedKeySet = React.useMemo(() => {
|
||||
const mergedSelectedKeySet = useMemo(() => {
|
||||
const keys = selectionType === 'radio' ? mergedSelectedKeys.slice(0, 1) : mergedSelectedKeys;
|
||||
return new Set(keys);
|
||||
}, [mergedSelectedKeys, selectionType]);
|
||||
|
||||
// Save last selected key to enable range selection
|
||||
const [lastSelectedKey, setLastSelectedKey] = React.useState<Key | null>(null);
|
||||
const [lastSelectedKey, setLastSelectedKey] = useState<Key | null>(null);
|
||||
|
||||
// Reset if rowSelection reset
|
||||
React.useEffect(() => {
|
||||
@ -116,18 +122,42 @@ export default function useSelection<RecordType>(
|
||||
}
|
||||
}, [!!rowSelection]);
|
||||
|
||||
const setSelectedKeys = React.useCallback(
|
||||
const setSelectedKeys = useCallback(
|
||||
(keys: Key[]) => {
|
||||
const availableKeys: Key[] = [];
|
||||
const records: RecordType[] = [];
|
||||
let availableKeys: Key[];
|
||||
let records: RecordType[];
|
||||
|
||||
keys.forEach(key => {
|
||||
const record = getRecordByKey(key);
|
||||
if (record !== undefined) {
|
||||
availableKeys.push(key);
|
||||
records.push(record);
|
||||
}
|
||||
});
|
||||
if (preserveSelectedRowKeys) {
|
||||
// Keep key if mark as preserveSelectedRowKeys
|
||||
const newCache = new Map<Key, RecordType>();
|
||||
availableKeys = keys;
|
||||
records = keys.map(key => {
|
||||
let record = getRecordByKey(key);
|
||||
|
||||
if (!record && preserveRecordsRef.current.has(key)) {
|
||||
record = preserveRecordsRef.current.get(key)!;
|
||||
}
|
||||
|
||||
newCache.set(key, record);
|
||||
|
||||
return record;
|
||||
});
|
||||
|
||||
// Refresh to new cache
|
||||
preserveRecordsRef.current = newCache;
|
||||
} else {
|
||||
// Filter key which not exist in the `dataSource`
|
||||
availableKeys = [];
|
||||
records = [];
|
||||
|
||||
keys.forEach(key => {
|
||||
const record = getRecordByKey(key);
|
||||
if (record !== undefined) {
|
||||
availableKeys.push(key);
|
||||
records.push(record);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setInnerSelectedKeys(availableKeys);
|
||||
|
||||
@ -135,11 +165,12 @@ export default function useSelection<RecordType>(
|
||||
onSelectionChange(availableKeys, records);
|
||||
}
|
||||
},
|
||||
[setInnerSelectedKeys, getRecordByKey, onSelectionChange],
|
||||
[setInnerSelectedKeys, getRecordByKey, onSelectionChange, preserveSelectedRowKeys],
|
||||
);
|
||||
|
||||
// ====================== Selections ======================
|
||||
// Trigger single `onSelect` event
|
||||
const triggerSingleSelection = React.useCallback(
|
||||
const triggerSingleSelection = useCallback(
|
||||
(key: Key, selected: boolean, keys: Key[], event: Event) => {
|
||||
if (onSelect) {
|
||||
const rows = keys.map(k => getRecordByKey(k));
|
||||
@ -151,7 +182,7 @@ export default function useSelection<RecordType>(
|
||||
[onSelect, getRecordByKey, setSelectedKeys],
|
||||
);
|
||||
|
||||
const mergedSelections = React.useMemo<SelectionItem[] | null>(() => {
|
||||
const mergedSelections = useMemo<SelectionItem[] | null>(() => {
|
||||
if (!selections || hideSelectAll) {
|
||||
return null;
|
||||
}
|
||||
@ -202,7 +233,8 @@ export default function useSelection<RecordType>(
|
||||
});
|
||||
}, [selections, mergedSelectedKeySet, pageData, getRowKey]);
|
||||
|
||||
const transformColumns = React.useCallback(
|
||||
// ======================= Columns ========================
|
||||
const transformColumns = useCallback(
|
||||
(columns: ColumnsType<RecordType>): ColumnsType<RecordType> => {
|
||||
if (!rowSelection) {
|
||||
return columns;
|
||||
|
@ -185,19 +185,20 @@ Properties for row selection.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| columnWidth | Set the width of the selection column | string\|number | `60px` | 4.0 |
|
||||
| columnTitle | Set the title of the selection column | string\|React.ReactNode | - | 4.0 |
|
||||
| fixed | Fixed selection column on the left | boolean | - | 4.0 |
|
||||
| getCheckboxProps | Get Checkbox or Radio props | Function(record) | - | 4.0 |
|
||||
| columnWidth | Set the width of the selection column | string\|number | `60px` | |
|
||||
| columnTitle | Set the title of the selection column | string\|React.ReactNode | - | |
|
||||
| fixed | Fixed selection column on the left | boolean | - | |
|
||||
| getCheckboxProps | Get Checkbox or Radio props | Function(record) | - | |
|
||||
| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | `false` | 4.3 |
|
||||
| preserveSelectedRowKeys | Keep selection `key` even when it removed from `dataSource` | boolean | - | 4.4 |
|
||||
| renderCell | Renderer of the table cell. Same as `render` in column | Function(checked, record, index, originNode) {} | - | 4.1 |
|
||||
| selectedRowKeys | Controlled selected row keys | string\[]\|number[] | \[] | 4.0 |
|
||||
| selections | Custom selection [config](#rowSelection), only displays default selections when set to `true` | object\[]\|boolean | - | 4.0 |
|
||||
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | 4.0 |
|
||||
| onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - | 4.0 |
|
||||
| onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - | 4.0 |
|
||||
| onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - | 4.0 |
|
||||
| onSelectInvert | Callback executed when row selection is inverted | Function(selectedRowKeys) | - | 4.0 |
|
||||
| selectedRowKeys | Controlled selected row keys | string\[]\|number[] | \[] | |
|
||||
| selections | Custom selection [config](#rowSelection), only displays default selections when set to `true` | object\[]\|boolean | - | |
|
||||
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | |
|
||||
| onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - | |
|
||||
| onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - | |
|
||||
| onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - | |
|
||||
| onSelectInvert | Callback executed when row selection is inverted | Function(selectedRowKeys) | - | |
|
||||
|
||||
### scroll
|
||||
|
||||
|
@ -190,19 +190,20 @@ const columns = [
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| columnWidth | 自定义列表选择框宽度 | string\|number | `60px` | 4.0 |
|
||||
| columnTitle | 自定义列表选择框标题 | string\|React.ReactNode | - | 4.0 |
|
||||
| fixed | 把选择框列固定在左边 | boolean | - | 4.0 |
|
||||
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - | 4.0 |
|
||||
| columnWidth | 自定义列表选择框宽度 | string\|number | `60px` | |
|
||||
| columnTitle | 自定义列表选择框标题 | string\|React.ReactNode | - | |
|
||||
| fixed | 把选择框列固定在左边 | boolean | - | |
|
||||
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - | |
|
||||
| hideSelectAll | 隐藏全选勾选框与自定义选择项 | boolean | false | 4.3 |
|
||||
| preserveSelectedRowKeys | 当数据被删除时仍然保留选项的 `key` | boolean | - | 4.4 |
|
||||
| renderCell | 渲染勾选框,用法与 Column 的 `render` 相同 | Function(checked, record, index, originNode) {} | - | 4.1 |
|
||||
| selectedRowKeys | 指定选中项的 key 数组,需要和 onChange 进行配合 | string\[]\|number[] | \[] | 4.0 |
|
||||
| selections | 自定义选择项 [配置项](#selection), 设为 `true` 时使用默认选择项 | object\[]\|boolean | true | 4.0 |
|
||||
| type | 多选/单选,`checkbox` or `radio` | string | `checkbox` | 4.0 |
|
||||
| onChange | 选中项发生变化时的回调 | Function(selectedRowKeys, selectedRows) | - | 4.0 |
|
||||
| onSelect | 用户手动选择/取消选择某行的回调 | Function(record, selected, selectedRows, nativeEvent) | - | 4.0 |
|
||||
| onSelectAll | 用户手动选择/取消选择所有行的回调 | Function(selected, selectedRows, changeRows) | - | 4.0 |
|
||||
| onSelectInvert | 用户手动选择反选的回调 | Function(selectedRowKeys) | - | 4.0 |
|
||||
| selectedRowKeys | 指定选中项的 key 数组,需要和 onChange 进行配合 | string\[]\|number[] | \[] | |
|
||||
| selections | 自定义选择项 [配置项](#selection), 设为 `true` 时使用默认选择项 | object\[]\|boolean | true | |
|
||||
| type | 多选/单选,`checkbox` or `radio` | string | `checkbox` | |
|
||||
| onChange | 选中项发生变化时的回调 | Function(selectedRowKeys, selectedRows) | - | |
|
||||
| onSelect | 用户手动选择/取消选择某行的回调 | Function(record, selected, selectedRows, nativeEvent) | - | |
|
||||
| onSelectAll | 用户手动选择/取消选择所有行的回调 | Function(selected, selectedRows, changeRows) | - | |
|
||||
| onSelectInvert | 用户手动选择反选的回调 | Function(selectedRowKeys) | - | |
|
||||
|
||||
### scroll
|
||||
|
||||
|
@ -125,6 +125,8 @@ export type SelectionSelectFn<T> = (
|
||||
) => void;
|
||||
|
||||
export interface TableRowSelection<T> {
|
||||
/** Keep the selection keys in list even the key not exist in `dataSource` anymore */
|
||||
preserveSelectedRowKeys?: boolean;
|
||||
type?: RowSelectionType;
|
||||
selectedRowKeys?: Key[];
|
||||
onChange?: (selectedRowKeys: Key[], selectedRows: T[]) => void;
|
||||
|
Loading…
Reference in New Issue
Block a user