Răsfoiți Sursa

feat: add renderCell for table rowSelection (#2045)

* feat: add renderCell for table rowSelection

* feat: added new exposure parameters and headers to support customization

* fix: fix the header renderCell type problem

* docs: update header renderCell document
changlin0_0 1 an în urmă
părinte
comite
c80cf9b9ad

+ 1 - 0
content/show/table/index-en-US.md

@@ -5178,6 +5178,7 @@ type Filter = {
 | getCheckboxProps | Default property configuration for the selection box | (record: RecordType) => object |  |  |
 | hidden | Hide selection column or not | boolean | false | **0.34.0** |
 | selectedRowKeys | Specifies the key array of the selected item, which needs to work with onChange | string [] |  |  |
+| renderCell         | Custom rendering checkbox                                                                                 | ({ selected: boolean, record: RecordType, originNode: JSX.Element, inHeader: boolean, disabled: boolean, indeterminate: boolean, index?: number, selectRow?: (selected: boolean, e: Event) => void, selectAll?: (selected: boolean, e: Event) => void }) => ReactNode |        |      **2.52.0**      |
 | width | Custom list selection box width | string | number |  |
 | onChange | A callback in the event of a change in the selected item. The first parameter will save the row keys selected last time, even if you do paging control or update the dataSource [FAQ](#faq) | (selectedRowKeys: number[]\|string[], selectedRows: RecordType[]) => void |  |  |
 | onSelect | Callback when the user manually clicks the selection box of a row | (record: RecordType, selected: boolean, selectedRows: RecordType[], nativeEvent: MouseEvent) => void |  |  |

+ 1 - 0
content/show/table/index.md

@@ -5195,6 +5195,7 @@ type Filter = {
 | fixed            | 把选择框列固定在左边                                                                                         | boolean                                                                                              | false  |            |
 | getCheckboxProps | 选择框的默认属性配置                                                                                         | (record: RecordType) => object                                                                       |        |            |
 | hidden           | 是否隐藏选择列                                                                                               | boolean                                                                                              | false  | **0.34.0** |
+| renderCell         | 自定义渲染勾选框                                                                                 | ({ selected: boolean, record: RecordType, originNode: JSX.Element, inHeader: boolean, disabled: boolean, indeterminate: boolean, index?: number, selectRow?: (selected: boolean, e: Event) => void, selectAll?: (selected: boolean, e: Event) => void }) => ReactNode |        |      **2.52.0**     |
 | selectedRowKeys  | 指定选中项的 key 数组,需要和 onChange 进行配合                                                               | string[]                                                                                             |        |            |
 | width            | 自定义列表选择框宽度                                                                                         | string\|number                                                                                       |        |            |
 | onChange         | 选中项发生变化时的回调。第一个参数会保存上次选中的 row keys,即使你做了分页受控或更新了 dataSource [FAQ](#faq) | (selectedRowKeys: number[]\|string[], selectedRows: RecordType[]) => void                            |        |            |

+ 50 - 12
packages/semi-ui/table/Table.tsx

@@ -839,36 +839,59 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
         }
     };
 
-    renderSelection = (record = {} as any, inHeader = false): React.ReactNode => {
+    renderSelection = (record = {} as any, inHeader = false, index?: number): React.ReactNode => {
         const { rowSelection, allDisabledRowKeysSet } = this.state;
 
         if (rowSelection && typeof rowSelection === 'object') {
-            const { selectedRowKeys = [], selectedRowKeysSet = new Set(), getCheckboxProps, disabled } = rowSelection;
+            const {
+                selectedRowKeys = [],
+                selectedRowKeysSet = new Set(),
+                getCheckboxProps,
+                disabled,
+                renderCell,
+            } = rowSelection;
+
+            const allRowKeys = this.cachedFilteredSortedRowKeys;
+            const allRowKeysSet = this.cachedFilteredSortedRowKeysSet;
+            const allIsSelected = this.foundation.allIsSelected(selectedRowKeysSet, allDisabledRowKeysSet, allRowKeys);
+            const hasRowSelected = this.foundation.hasRowSelected(selectedRowKeys, allRowKeysSet);
+            const indeterminate = hasRowSelected && !allIsSelected;
 
             if (inHeader) {
                 const columnKey = get(rowSelection, 'key', strings.DEFAULT_KEY_COLUMN_SELECTION);
-                const allRowKeys = this.cachedFilteredSortedRowKeys;
-                const allRowKeysSet = this.cachedFilteredSortedRowKeysSet;
-                const allIsSelected = this.foundation.allIsSelected(selectedRowKeysSet, allDisabledRowKeysSet, allRowKeys);
-                const hasRowSelected = this.foundation.hasRowSelected(selectedRowKeys, allRowKeysSet);
-                return (
+
+                const originNode = (
                     <ColumnSelection
                         aria-label={`${allIsSelected ? 'Deselect' : 'Select'} all rows`}
                         disabled={disabled}
                         key={columnKey}
                         selected={allIsSelected}
-                        indeterminate={hasRowSelected && !allIsSelected}
+                        indeterminate={indeterminate}
                         onChange={(selected, e) => {
                             this.toggleSelectAllRow(selected, e);
                         }}
                     />
                 );
+
+                const selectAll = (selected: boolean, e: Event) =>
+                    this.toggleSelectAllRow(selected, e as TableSelectionCellEvent);
+
+                return isFunction(renderCell)
+                    ? renderCell({
+                        selected: allIsSelected,
+                        record,
+                        originNode,
+                        inHeader,
+                        disabled,
+                        indeterminate,
+                        selectAll,
+                    })
+                    : originNode;
             } else {
                 const key = this.foundation.getRecordKey(record);
                 const selected = selectedRowKeysSet.has(key);
                 const checkboxPropsFn = () => (typeof getCheckboxProps === 'function' ? getCheckboxProps(record) : {});
-
-                return (
+                const originNode = (
                     <ColumnSelection
                         aria-label={`${selected ? 'Deselect' : 'Select'} this row`}
                         getCheckboxProps={checkboxPropsFn}
@@ -876,13 +899,28 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
                         onChange={(status, e) => this.toggleSelectRow(status, key, e)}
                     />
                 );
+                const selectRow = (selected: boolean, e: Event) =>
+                    this.toggleSelectRow(selected, key, e as TableSelectionCellEvent);
+
+                return isFunction(renderCell)
+                    ? renderCell({
+                        selected,
+                        record,
+                        index,
+                        originNode,
+                        inHeader: false,
+                        disabled,
+                        indeterminate,
+                        selectRow,
+                    })
+                    : originNode;
             }
         }
         return null;
     };
 
-    renderRowSelectionCallback = (text: string, record: RecordType = {} as RecordType) => this.renderSelection(record);
-    renderTitleSelectionCallback = () => this.renderSelection(null, true);
+    renderRowSelectionCallback = (text: string, record: RecordType = {} as RecordType, index: number) => this.renderSelection(record, false, index);
+    renderTitleSelectionCallback = () => this.renderSelection(undefined, true);
 
     normalizeSelectionColumn = (props: { rowSelection?: TableStateRowSelection<RecordType>; prefixCls?: string } = {}) => {
         const { rowSelection, prefixCls } = props;

+ 140 - 0
packages/semi-ui/table/_story/RowSelectionRenderCell/index.jsx

@@ -0,0 +1,140 @@
+import { Table, Tooltip } from '@douyinfe/semi-ui';
+import React, { useMemo, useState } from 'react';
+
+function App() {
+    const defaultSelectedRowKeys = useMemo(() => ['3'], []);
+    const [selectedRowKeys, setSelectedRowKeys] = useState(defaultSelectedRowKeys);
+    const [headerOrigin, setHeaderOrigin] = useState(false);
+    const [rowOrigin, setRowOrigin] = useState(false);
+
+    const columns = useMemo(
+        () => [
+            {
+                title: 'Name',
+                dataIndex: 'name',
+                render: text => <span>{text}</span>,
+            },
+            {
+                title: 'Age',
+                dataIndex: 'age',
+            },
+            {
+                title: 'Address',
+                dataIndex: 'address',
+            },
+            {
+                render: () => (
+                    <input
+                        type="checkbox"
+                        onClick={(...args) => {
+                            console.log('onClick: ', ...args);
+                        }}
+                        onChange={(...args) => {
+                            console.log('onChange: ', ...args);
+                        }}
+                    />
+                ),
+            },
+        ],
+        []
+    );
+
+    const data = useMemo(() => {
+        const _data = [];
+        for (let i = 0; i < 5; i++) {
+            let age = i * 1000;
+            let name = `Edward King ${i}`;
+            _data.push({
+                key: '' + i,
+                name,
+                age,
+                address: `London, Park Lane no. ${i} Lake Park`,
+                description: `My name is ${name}, I am ${age} years old, living in New York No. ${i + 1} Lake Park.`,
+            });
+        }
+        return _data;
+    }, []);
+
+    const rowSelection = {
+        renderCell: ({ selected, record, index, originNode, inHeader, disabled, indeterminate, selectRow, selectAll }) => {
+            console.log('selected', selected);
+            console.log('index', index);
+            console.log('inHeader', inHeader);
+            console.log('disabled', disabled);
+            console.log('indeterminate', indeterminate);
+
+            if (inHeader && headerOrigin) {
+                return (
+                    <Tooltip content="自定义表头 renderCell,我是表头">
+                        <div>{originNode}</div>
+                    </Tooltip>
+                );
+            }
+
+            if (inHeader && !headerOrigin) {
+                return (
+                    <Tooltip content="自定义表头 renderCell,我是表头,不使用 originNode 控制选中">
+                        <div onClick={e => selectAll && selectAll(!selected, e)}>222</div>
+                    </Tooltip>
+                );
+            }
+
+            if (record.age === 2000 && !rowOrigin) {
+                return (
+                    <Tooltip content="自定义 renderCell, 不使用 originNode 控制选中">
+                        <div
+                            style={{ color: selected ? 'red' : 'black' }}
+                            onClick={e => selectRow && selectRow(!selected, e)}
+                        >
+                            111
+                        </div>
+                    </Tooltip>
+                );
+            }
+
+            if (record.age > 2000) {
+                return (
+                    <Tooltip content="自定义 renderCell">
+                        <div>{originNode}</div>
+                    </Tooltip>
+                );
+            }
+            return originNode;
+        },
+        onChange: (selectedRowKeys, selectedRows) => {
+            console.log(
+                `rowSelection.onChanged: selectedRowKeys: ${JSON.stringify(selectedRowKeys)}`,
+                'selectedRows: ',
+                selectedRows
+            );
+            setSelectedRowKeys(selectedRowKeys);
+        },
+        onSelectAll: (selected, selectedRows, changedRows) => {
+            console.log(
+                `rowSelection.onSelectAll: selected :${selected}, selectedRows: ${selectedRows}, changedRows: ${changedRows}`
+            );
+        },
+        getCheckboxProps: record => ({
+            // disabled: record.age < 2000,
+            name: record.name,
+            onClick: (...args) => {
+                console.log('Clicked checkbox: ', ...args);
+            },
+        }),
+        onSelect: (record, selected) => {
+            console.log('onSelect: ', record, selected);
+        },
+        selectedRowKeys,
+        defaultSelectedRowKeys,
+    };
+
+    return (
+        <div>
+            <button onClick={() => setHeaderOrigin(!headerOrigin)}>表头{headerOrigin ? '不使用' : '使用'} originNode</button>
+            <button onClick={() => setRowOrigin(!rowOrigin)}>选择行{ rowOrigin ? '不使用' : '使用' } originNode</button>
+            <Table rowKey={record => record.key} columns={columns} dataSource={data} rowSelection={rowSelection} />
+        </div>
+    );
+}
+
+export default App;

+ 7 - 0
packages/semi-ui/table/_story/table.stories.jsx

@@ -47,6 +47,7 @@ import ExpandAllGroupRows from './ExpandAllGroupRows';
 import ExpandRowByClick from './ExpandRowByClick';
 import FixAllColumnsWithoutWidth from './FixAllColumnsWithoutWidth';
 import HugeData from "./HugeData"
+import RowSelectionRenderCell from './RowSelectionRenderCell';
 
 export default {
   title: 'Table'
@@ -632,3 +633,9 @@ export const HugeDataDemo = ()=><HugeData/>
 HugeDataDemo.parameters = {
   chromatic: { disableSnapshot: true },
 };
+
+export const _RowSelectionRenderCell = () => <RowSelectionRenderCell />;
+
+_RowSelectionRenderCell.story = {
+  name: 'RowSelection RenderCell',
+};

+ 13 - 1
packages/semi-ui/table/interface.ts

@@ -237,9 +237,21 @@ export interface RowSelectionProps<RecordType> {
     width?: string | number;
     onChange?: RowSelectionOnChange<RecordType>;
     onSelect?: RowSelectionOnSelect<RecordType>;
-    onSelectAll?: RowSelectionOnSelectAll<RecordType>
+    onSelectAll?: RowSelectionOnSelectAll<RecordType>;
+    renderCell?: RowSelectionRenderCell<RecordType>
 }
 
+export type RowSelectionRenderCell<RecordType> = (renderCellArgs: {
+    selected: boolean;
+    record: RecordType;
+    originNode: JSX.Element;
+    inHeader: boolean;
+    disabled: boolean;
+    indeterminate: boolean;
+    index?: number;
+    selectRow?: (selected: boolean, e: Event) => void;
+    selectAll?: (selected: boolean, e: Event) => void
+}) => ReactNode;
 export type GetCheckboxProps<RecordType> = (record: RecordType) => CheckboxProps;
 export type RowSelectionOnChange<RecordType> = (selectedRowKeys?: (string | number)[], selectedRows?: RecordType[]) => void;
 export type RowSelectionOnSelect<RecordType> = (