Browse Source

feat: Selection column also support render node (#21711)

* support customize selection cell

* update snapshot

* update docs

* fix lint

* to debug
pull/21835/head
二货机器人 5 years ago
committed by GitHub
parent
commit
6ba42db314
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 390
      components/table/__tests__/__snapshots__/demo.test.js.snap
  2. 50
      components/table/demo/row-selection-custom-debug.md
  3. 182
      components/table/hooks/useSelection.tsx
  4. 29
      components/table/index.en-US.md
  5. 29
      components/table/index.zh-CN.md
  6. 13
      components/table/interface.tsx

390
components/table/__tests__/__snapshots__/demo.test.js.snap

@ -12220,6 +12220,396 @@ exports[`renders ./components/table/demo/row-selection-custom.md correctly 1`] =
</div>
`;
exports[`renders ./components/table/demo/row-selection-custom-debug.md correctly 1`] = `
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:auto"
>
<colgroup>
<col
style="width:60px;min-width:60px"
/>
<col />
</colgroup>
<thead>
<tr>
<th
class="ant-table-cell ant-table-selection-column"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell"
>
Name
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="0"
>
<td
class="ant-table-cell ant-table-selection-column"
rowspan="2"
>
false:
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Edward King 0
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="1"
>
<td
class="ant-table-cell"
>
Another Row
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="2"
>
<td
class="ant-table-cell ant-table-selection-column"
rowspan="2"
>
false:
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Edward King 2
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="3"
>
<td
class="ant-table-cell"
>
Another Row
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="4"
>
<td
class="ant-table-cell ant-table-selection-column"
rowspan="2"
>
false:
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Edward King 4
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="5"
>
<td
class="ant-table-cell"
>
Another Row
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="6"
>
<td
class="ant-table-cell ant-table-selection-column"
rowspan="2"
>
false:
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Edward King 6
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="7"
>
<td
class="ant-table-cell"
>
Another Row
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="8"
>
<td
class="ant-table-cell ant-table-selection-column"
rowspan="2"
>
false:
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Edward King 8
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="9"
>
<td
class="ant-table-cell"
>
Another Row
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination"
unselectable="unselectable"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<a
class="ant-pagination-item-link"
disabled=""
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a>
1
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-2"
tabindex="0"
title="2"
>
<a>
2
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-3"
tabindex="0"
title="3"
>
<a>
3
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-4"
tabindex="0"
title="4"
>
<a>
4
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-5"
tabindex="0"
title="5"
>
<a>
5
</a>
</li>
<li
aria-disabled="false"
class="ant-pagination-next"
tabindex="0"
title="Next Page"
>
<a
class="ant-pagination-item-link"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
`;
exports[`renders ./components/table/demo/size.md correctly 1`] = `
<div>
<h4>

50
components/table/demo/row-selection-custom-debug.md

@ -0,0 +1,50 @@
---
order: 99
title:
en-US: Custom selection group
zh-CN: 自定义选择项组
debug: true
---
## zh-CN
自定义选项分组。
## en-US
Customize selection group.
```jsx
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: i % 2 === 0 ? `Edward King ${i}` : 'Another Row',
});
}
const App = () => {
const rowSelection = {
renderCell: (checked, record, index, node) => ({
props: { rowSpan: index % 2 === 0 ? 2 : 0 },
children: (
<>
{String(checked)}: {node}
</>
),
}),
};
return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
};
ReactDOM.render(<App />, mountNode);
```

182
components/table/hooks/useSelection.tsx

@ -76,6 +76,7 @@ export default function useSelection<RecordType>(
type: selectionType,
selections,
fixed,
renderCell: customizeRenderCell,
} = rowSelection || {};
const { locale = defaultLocale } = React.useContext(ConfigContext);
@ -305,111 +306,132 @@ export default function useSelection<RecordType>(
}
// Body Cell
let renderCell: (_: RecordType, record: RecordType, index: number) => React.ReactNode;
let renderCell: (
_: RecordType,
record: RecordType,
index: number,
) => { node: React.ReactNode; checked: boolean };
if (selectionType === 'radio') {
renderCell = (_, record, index) => {
const key = getRowKey(record, index);
return (
<Radio
{...checkboxPropsMap.get(key)}
checked={keySet.has(key)}
onChange={event => {
if (!keySet.has(key)) {
triggerSingleSelection(key, true, [key], event.nativeEvent);
}
}}
/>
);
const checked = keySet.has(key);
return {
node: (
<Radio
{...checkboxPropsMap.get(key)}
checked={checked}
onChange={event => {
if (!keySet.has(key)) {
triggerSingleSelection(key, true, [key], event.nativeEvent);
}
}}
/>
),
checked,
};
};
} else {
renderCell = (_, record, index) => {
const key = getRowKey(record, index);
const hasKey = keySet.has(key);
const checked = keySet.has(key);
// Record checked
return (
<Checkbox
{...checkboxPropsMap.get(key)}
checked={hasKey}
onChange={({ nativeEvent }) => {
const { shiftKey } = nativeEvent;
let startIndex: number = -1;
let endIndex: number = -1;
// Get range of this
if (shiftKey) {
const pointKeys = new Set([lastSelectedKey, key]);
recordKeys.some((recordKey, recordIndex) => {
if (pointKeys.has(recordKey)) {
if (startIndex === -1) {
startIndex = recordIndex;
} else {
endIndex = recordIndex;
return true;
return {
node: (
<Checkbox
{...checkboxPropsMap.get(key)}
checked={checked}
onChange={({ nativeEvent }) => {
const { shiftKey } = nativeEvent;
let startIndex: number = -1;
let endIndex: number = -1;
// Get range of this
if (shiftKey) {
const pointKeys = new Set([lastSelectedKey, key]);
recordKeys.some((recordKey, recordIndex) => {
if (pointKeys.has(recordKey)) {
if (startIndex === -1) {
startIndex = recordIndex;
} else {
endIndex = recordIndex;
return true;
}
}
}
return false;
});
}
if (endIndex !== -1 && startIndex !== endIndex) {
// Batch update selections
const rangeKeys = recordKeys.slice(startIndex, endIndex + 1);
const changedKeys: Key[] = [];
if (hasKey) {
rangeKeys.forEach(recordKey => {
if (keySet.has(recordKey)) {
changedKeys.push(recordKey);
keySet.delete(recordKey);
}
});
} else {
rangeKeys.forEach(recordKey => {
if (!keySet.has(recordKey)) {
changedKeys.push(recordKey);
keySet.add(recordKey);
}
return false;
});
}
const keys = Array.from(keySet);
setSelectedKeys(keys);
if (onSelectMultiple) {
onSelectMultiple(
!hasKey,
keys.map(recordKey => getRecordByKey(recordKey)),
changedKeys.map(recordKey => getRecordByKey(recordKey)),
);
}
} else {
// Single record selected
if (hasKey) {
keySet.delete(key);
if (endIndex !== -1 && startIndex !== endIndex) {
// Batch update selections
const rangeKeys = recordKeys.slice(startIndex, endIndex + 1);
const changedKeys: Key[] = [];
if (checked) {
rangeKeys.forEach(recordKey => {
if (keySet.has(recordKey)) {
changedKeys.push(recordKey);
keySet.delete(recordKey);
}
});
} else {
rangeKeys.forEach(recordKey => {
if (!keySet.has(recordKey)) {
changedKeys.push(recordKey);
keySet.add(recordKey);
}
});
}
const keys = Array.from(keySet);
setSelectedKeys(keys);
if (onSelectMultiple) {
onSelectMultiple(
!checked,
keys.map(recordKey => getRecordByKey(recordKey)),
changedKeys.map(recordKey => getRecordByKey(recordKey)),
);
}
} else {
keySet.add(key);
}
// Single record selected
if (checked) {
keySet.delete(key);
} else {
keySet.add(key);
}
triggerSingleSelection(key, !hasKey, Array.from(keySet), nativeEvent);
}
triggerSingleSelection(key, !checked, Array.from(keySet), nativeEvent);
}
setLastSelectedKey(key);
}}
/>
);
setLastSelectedKey(key);
}}
/>
),
checked,
};
};
}
const renderSelectionCell = (_: any, record: RecordType, index: number) => {
const { node, checked } = renderCell(_, record, index);
if (customizeRenderCell) {
return customizeRenderCell(checked, record, index, node);
}
return node;
};
// Columns
const selectionColumn = {
width: selectionColWidth,
className: `${prefixCls}-selection-column`,
title: rowSelection.columnTitle || title,
render: renderCell,
render: renderSelectionCell,
};
if (expandType === 'row' && columns.length) {

29
components/table/index.en-US.md

@ -179,20 +179,21 @@ Properties for expandable.
Properties for row selection.
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| 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) | - |
| hideDefaultSelections | Remove the default `Select All` and `Select Invert` selections when [custom selection](#components-table-demo-row-selection-custom) | boolean | `false` |
| 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(selectedRows) | - |
| 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 |
| hideDefaultSelections | Remove the default `Select All` and `Select Invert` selections when [custom selection](#components-table-demo-row-selection-custom) | boolean | `false` | 4.0 |
| 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(selectedRows) | - | 4.0 |
### scroll

29
components/table/index.zh-CN.md

@ -184,20 +184,21 @@ const columns = [
选择功能的配置。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| columnWidth | 自定义列表选择框宽度 | string\|number | `60px` |
| columnTitle | 自定义列表选择框标题 | string\|React.ReactNode | - |
| fixed | 把选择框列固定在左边 | boolean | - |
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - |
| hideDefaultSelections | [自定义选择项](#components-table-demo-row-selection-custom)时去掉『全选』『反选』两个默认选项 | boolean | false |
| 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(selectedRows) | - |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| columnWidth | 自定义列表选择框宽度 | string\|number | `60px` | 4.0 |
| columnTitle | 自定义列表选择框标题 | string\|React.ReactNode | - | 4.0 |
| fixed | 把选择框列固定在左边 | boolean | - | 4.0 |
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - | 4.0 |
| hideDefaultSelections | [自定义选择项](#components-table-demo-row-selection-custom)时去掉『全选』『反选』两个默认选项 | boolean | false | 4.0 |
| 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(selectedRows) | - | 4.0 |
### scroll

13
components/table/interface.tsx

@ -1,4 +1,9 @@
import { GetRowKey, ColumnType as RcColumnType, ExpandableConfig } from 'rc-table/lib/interface';
import {
GetRowKey,
ColumnType as RcColumnType,
RenderedCell as RcRenderedCell,
ExpandableConfig,
} from 'rc-table/lib/interface';
import { CheckboxProps } from '../checkbox';
import { PaginationConfig } from '../pagination';
@ -129,6 +134,12 @@ export interface TableRowSelection<T> {
fixed?: boolean;
columnWidth?: string | number;
columnTitle?: string | React.ReactNode;
renderCell?: (
value: boolean,
record: T,
index: number,
originNode: React.ReactNode,
) => React.ReactNode | RcRenderedCell<T>;
}
export type TransformColumns<RecordType> = (

Loading…
Cancel
Save