From 45a6c49615109dd35abfb58f8869672be72b55ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 07:10:50 +0800 Subject: [PATCH 01/26] feat: support rowspan-expanded --- docs/demo/colspan-rowspan-expanded.md | 8 ++++ docs/examples/colspan-rowspan-expanded.tsx | 55 ++++++++++++++++++++++ src/Body/BodyRow.tsx | 35 +++++++++++++- src/Body/index.tsx | 6 +++ 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 docs/demo/colspan-rowspan-expanded.md create mode 100644 docs/examples/colspan-rowspan-expanded.tsx diff --git a/docs/demo/colspan-rowspan-expanded.md b/docs/demo/colspan-rowspan-expanded.md new file mode 100644 index 00000000..d9cf2ca5 --- /dev/null +++ b/docs/demo/colspan-rowspan-expanded.md @@ -0,0 +1,8 @@ +--- +title: colspan-rowspan-expanded +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/colspan-rowspan-expanded.tsx b/docs/examples/colspan-rowspan-expanded.tsx new file mode 100644 index 00000000..5749607b --- /dev/null +++ b/docs/examples/colspan-rowspan-expanded.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import Table from 'rc-table'; +import '../../assets/index.less'; +import type { ColumnsType } from '@/interface'; + +const columns: ColumnsType = [ + { + title: '手机号', + dataIndex: 'a', + colSpan: 2, + width: 100, + onCell: (_, index) => { + const props: React.TdHTMLAttributes = {}; + if (index === 0) { + props.rowSpan = 4; + } else if (index === 1) { + props.rowSpan = 0; + } else if (index === 2) { + props.rowSpan = 0; + } else if (index === 3) { + props.rowSpan = 0; + } else if (index === 4) { + props.rowSpan = 1; + } + + return props; + }, + }, + { title: '电话', dataIndex: 'b', colSpan: 0, width: 100 }, + Table.EXPAND_COLUMN, + { title: 'Name', dataIndex: 'c', width: 100 }, + { title: 'Address', dataIndex: 'd', width: 200 }, +]; + +const data = [ + { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: 'a' }, + { a: '13812340987', b: '0571-12345678', c: '张夫人', d: '文一西路', e: 'Female', key: 'b' }, + { a: '13812340987', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: 'c' }, + { a: '13812340987', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: 'd' }, + { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: 'e' }, +]; + +const Demo = () => ( +
+

colSpan & rowSpan & expanded

+

{record.key}

}} + className="table" + /> + +); + +export default Demo; diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 46ca866f..11041cd6 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -19,6 +19,7 @@ export interface BodyRowProps { scopeCellComponent: CustomizeComponent; indent?: number; rowKey: React.Key; + getRowKey: (index: number) => React.Key; } // ================================================================================== @@ -30,6 +31,7 @@ export function getCellProps( colIndex: number, indent: number, index: number, + getRowKey: (index: number) => React.Key, ) { const { record, @@ -43,6 +45,7 @@ export function getCellProps( expanded, hasNestChildren, onTriggerExpand, + expandedKeys, } = rowInfo; const key = columnsKey[colIndex]; @@ -68,9 +71,32 @@ export function getCellProps( ); } + const addChildrenRowSpan = (rowSpan: number, index2: number) => { + const _index = index2 + 1; + let _rowSpan = rowSpan; + // 下面如果是 0 的,增加 +1 逻辑 + const dd = column.onCell(record, _index); + if (dd.rowSpan === 0) { + const ddd = expandedKeys.has(getRowKey(_index)); + if (ddd) { + _rowSpan = _rowSpan + 1; + } + return addChildrenRowSpan(_rowSpan, _index); + } + return _rowSpan; + }; + let additionalCellProps: React.TdHTMLAttributes; if (column.onCell) { additionalCellProps = column.onCell(record, index); + if (additionalCellProps.rowSpan > 0) { + // 本身展开 +1 + if (expanded) { + additionalCellProps.rowSpan = additionalCellProps.rowSpan + 1; + } + additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); + } + console.log('additionalCellProps.rowSpan', additionalCellProps.rowSpan); } return { @@ -102,8 +128,10 @@ function BodyRow( rowComponent: RowComponent, cellComponent, scopeCellComponent, + getRowKey, } = props; const rowInfo = useRowInfo(record, rowKey, index, indent); + const { prefixCls, flattenColumns, @@ -144,7 +172,7 @@ function BodyRow( )} style={{ ...style, ...rowProps?.style }} > - {flattenColumns.map((column: ColumnType, colIndex) => { + {flattenColumns.map((column, colIndex) => { const { render, dataIndex, className: columnClassName } = column; const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps( @@ -153,8 +181,11 @@ function BodyRow( colIndex, indent, index, + getRowKey, ); - + if (column.title === '手机号') { + // console.log('additionalCellProps', column.title, additionalCellProps); + } return ( className={columnClassName} diff --git a/src/Body/index.tsx b/src/Body/index.tsx index aec12e9d..c0eafab4 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -67,6 +67,12 @@ function Body(props: BodyProps) { { + const thisRecord = flattenData[index]?.record; + if (thisRecord) { + return getRowKey(thisRecord, index); + } + }} record={record} index={idx} renderIndex={renderIndex} From b80b72a2abd611ac5f787cac299614a933afa2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 07:12:10 +0800 Subject: [PATCH 02/26] feat: review --- src/Body/BodyRow.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 11041cd6..c2544f45 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -172,7 +172,7 @@ function BodyRow( )} style={{ ...style, ...rowProps?.style }} > - {flattenColumns.map((column, colIndex) => { + {flattenColumns.map((column: ColumnType, colIndex) => { const { render, dataIndex, className: columnClassName } = column; const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps( @@ -183,9 +183,6 @@ function BodyRow( index, getRowKey, ); - if (column.title === '手机号') { - // console.log('additionalCellProps', column.title, additionalCellProps); - } return ( className={columnClassName} From 8565bfa3b4fef44388eb4c9b7edc7aeb8fc09ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 07:12:25 +0800 Subject: [PATCH 03/26] feat: review --- src/Body/BodyRow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index c2544f45..ad8c9249 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -183,6 +183,7 @@ function BodyRow( index, getRowKey, ); + return ( className={columnClassName} From 532e52414c64e377f9316c1f7c829ec721dc4491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 07:15:28 +0800 Subject: [PATCH 04/26] feat: review --- src/Body/BodyRow.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index ad8c9249..55aed18e 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -75,14 +75,16 @@ export function getCellProps( const _index = index2 + 1; let _rowSpan = rowSpan; // 下面如果是 0 的,增加 +1 逻辑 - const dd = column.onCell(record, _index); - if (dd.rowSpan === 0) { - const ddd = expandedKeys.has(getRowKey(_index)); - if (ddd) { + const thisCellProps = column.onCell(record, _index); + if (thisCellProps.rowSpan === 0) { + const thisExpanded = expandedKeys.has(getRowKey(_index)); + if (thisExpanded) { _rowSpan = _rowSpan + 1; } + // 继续往下找 return addChildrenRowSpan(_rowSpan, _index); } + // 找不到后返回 return _rowSpan; }; From 239af542cc9da7d00eee0f043a386d00fc23077c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 07:20:54 +0800 Subject: [PATCH 05/26] feat: review --- src/Body/BodyRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 55aed18e..c2ef93bf 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -31,7 +31,7 @@ export function getCellProps( colIndex: number, indent: number, index: number, - getRowKey: (index: number) => React.Key, + getRowKey?: (index: number) => React.Key, ) { const { record, @@ -77,7 +77,7 @@ export function getCellProps( // 下面如果是 0 的,增加 +1 逻辑 const thisCellProps = column.onCell(record, _index); if (thisCellProps.rowSpan === 0) { - const thisExpanded = expandedKeys.has(getRowKey(_index)); + const thisExpanded = expandedKeys.has(getRowKey?.(_index)); if (thisExpanded) { _rowSpan = _rowSpan + 1; } From b5fc268c4c05b1ec6e03a2fd7de5b8274ccf1f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 09:37:16 +0800 Subject: [PATCH 06/26] feat: review --- docs/demo/colspan-rowspan-expanded.md | 8 ------- docs/demo/expandedRowSpan.md | 8 +++++++ ...wspan-expanded.tsx => expandedRowSpan.tsx} | 7 +++--- src/Body/index.tsx | 22 +++++++++---------- src/hooks/useFlattenRecords.ts | 3 +++ 5 files changed, 26 insertions(+), 22 deletions(-) delete mode 100644 docs/demo/colspan-rowspan-expanded.md create mode 100644 docs/demo/expandedRowSpan.md rename docs/examples/{colspan-rowspan-expanded.tsx => expandedRowSpan.tsx} (89%) diff --git a/docs/demo/colspan-rowspan-expanded.md b/docs/demo/colspan-rowspan-expanded.md deleted file mode 100644 index d9cf2ca5..00000000 --- a/docs/demo/colspan-rowspan-expanded.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: colspan-rowspan-expanded -nav: - title: Demo - path: /demo ---- - - diff --git a/docs/demo/expandedRowSpan.md b/docs/demo/expandedRowSpan.md new file mode 100644 index 00000000..046c3f8b --- /dev/null +++ b/docs/demo/expandedRowSpan.md @@ -0,0 +1,8 @@ +--- +title: expandedRowSpan +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/colspan-rowspan-expanded.tsx b/docs/examples/expandedRowSpan.tsx similarity index 89% rename from docs/examples/colspan-rowspan-expanded.tsx rename to docs/examples/expandedRowSpan.tsx index 5749607b..69058d0f 100644 --- a/docs/examples/colspan-rowspan-expanded.tsx +++ b/docs/examples/expandedRowSpan.tsx @@ -42,11 +42,12 @@ const data = [ const Demo = () => (
-

colSpan & rowSpan & expanded

-
expanded & rowSpan + > + rowKey="key" columns={columns} data={data} - expandable={{ expandedRowRender: (record: any) =>

{record.key}

}} + expandable={{ expandedRowRender: record =>

{record.key}

}} className="table" /> diff --git a/src/Body/index.tsx b/src/Body/index.tsx index c0eafab4..6eb6417d 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -42,8 +42,12 @@ function Body(props: BodyProps) { 'emptyNode', ]); - const flattenData: { record: RecordType; indent: number; index: number }[] = - useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey); + const flattenData = useFlattenRecords( + data, + childrenColumnName, + expandedKeys, + getRowKey, + ); // =================== Performance ==================== const perfRef = React.useRef({ @@ -59,19 +63,15 @@ function Body(props: BodyProps) { let rows: React.ReactNode; if (data.length) { rows = flattenData.map((item, idx) => { - const { record, indent, index: renderIndex } = item; - - const key = getRowKey(record, idx); + const { record, indent, index: renderIndex, rowKey } = item; return ( { - const thisRecord = flattenData[index]?.record; - if (thisRecord) { - return getRowKey(thisRecord, index); - } + const thisRecord = flattenData[index]; + return thisRecord?.rowKey; }} record={record} index={idx} diff --git a/src/hooks/useFlattenRecords.ts b/src/hooks/useFlattenRecords.ts index ff67f5d9..bd5e0c54 100644 --- a/src/hooks/useFlattenRecords.ts +++ b/src/hooks/useFlattenRecords.ts @@ -15,6 +15,7 @@ function fillRecords( record, indent, index, + rowKey: getRowKey(record, index), }); const key = getRowKey(record); @@ -41,6 +42,7 @@ export interface FlattenData { record: RecordType; indent: number; index: number; + rowKey: Key; } /** @@ -80,6 +82,7 @@ export default function useFlattenRecords( record: item, indent: 0, index, + rowKey: getRowKey(item, index), }; }); }, [data, childrenColumnName, expandedKeys, getRowKey]); From 094972ae3899c2a7d0fc54332c5bb8ec9988ad06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 09:43:58 +0800 Subject: [PATCH 07/26] feat: review --- src/Body/BodyRow.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index c2ef93bf..ffc613d1 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -74,15 +74,18 @@ export function getCellProps( const addChildrenRowSpan = (rowSpan: number, index2: number) => { const _index = index2 + 1; let _rowSpan = rowSpan; - // 下面如果是 0 的,增加 +1 逻辑 - const thisCellProps = column.onCell(record, _index); - if (thisCellProps.rowSpan === 0) { - const thisExpanded = expandedKeys.has(getRowKey?.(_index)); - if (thisExpanded) { - _rowSpan = _rowSpan + 1; + const rowKey = getRowKey?.(_index); + if (rowKey !== undefined) { + // 下面如果是 0 的,增加 +1 逻辑 + const thisCellProps = column.onCell(record, _index); + if (thisCellProps.rowSpan === 0) { + const thisExpanded = expandedKeys.has(getRowKey?.(_index)); + if (thisExpanded) { + _rowSpan = _rowSpan + 1; + } + // 继续往下找 + return addChildrenRowSpan(_rowSpan, _index); } - // 继续往下找 - return addChildrenRowSpan(_rowSpan, _index); } // 找不到后返回 return _rowSpan; From 2f15e979e15e54cbcde669b9ef8c68fdc56b1c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 12:25:46 +0800 Subject: [PATCH 08/26] feat: rowKeys --- src/Body/BodyRow.tsx | 12 ++++++------ src/Body/index.tsx | 6 ++---- src/VirtualTable/BodyGrid.tsx | 13 +++++++++++-- src/VirtualTable/BodyLine.tsx | 4 +++- src/VirtualTable/VirtualCell.tsx | 3 +++ 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index ffc613d1..e0b33002 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -19,7 +19,7 @@ export interface BodyRowProps { scopeCellComponent: CustomizeComponent; indent?: number; rowKey: React.Key; - getRowKey: (index: number) => React.Key; + rowKeys: React.Key[]; } // ================================================================================== @@ -31,7 +31,7 @@ export function getCellProps( colIndex: number, indent: number, index: number, - getRowKey?: (index: number) => React.Key, + rowKeys: React.Key[], ) { const { record, @@ -74,12 +74,12 @@ export function getCellProps( const addChildrenRowSpan = (rowSpan: number, index2: number) => { const _index = index2 + 1; let _rowSpan = rowSpan; - const rowKey = getRowKey?.(_index); + const rowKey = rowKeys[_index]; if (rowKey !== undefined) { // 下面如果是 0 的,增加 +1 逻辑 const thisCellProps = column.onCell(record, _index); if (thisCellProps.rowSpan === 0) { - const thisExpanded = expandedKeys.has(getRowKey?.(_index)); + const thisExpanded = expandedKeys.has(rowKey); if (thisExpanded) { _rowSpan = _rowSpan + 1; } @@ -133,7 +133,7 @@ function BodyRow( rowComponent: RowComponent, cellComponent, scopeCellComponent, - getRowKey, + rowKeys, } = props; const rowInfo = useRowInfo(record, rowKey, index, indent); @@ -186,7 +186,7 @@ function BodyRow( colIndex, indent, index, - getRowKey, + rowKeys, ); return ( diff --git a/src/Body/index.tsx b/src/Body/index.tsx index 6eb6417d..a2f7502d 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -62,6 +62,7 @@ function Body(props: BodyProps) { let rows: React.ReactNode; if (data.length) { + const rowKeys = flattenData.map(item => item.rowKey); rows = flattenData.map((item, idx) => { const { record, indent, index: renderIndex, rowKey } = item; @@ -69,10 +70,7 @@ function Body(props: BodyProps) { { - const thisRecord = flattenData[index]; - return thisRecord?.rowKey; - }} + rowKeys={rowKeys} record={record} index={idx} renderIndex={renderIndex} diff --git a/src/VirtualTable/BodyGrid.tsx b/src/VirtualTable/BodyGrid.tsx index cc1548cc..bcd89b3c 100644 --- a/src/VirtualTable/BodyGrid.tsx +++ b/src/VirtualTable/BodyGrid.tsx @@ -95,7 +95,7 @@ const Grid = React.forwardRef((props, ref) => { return obj; }); - + const rowKeys = flattenData.map(item => item.rowKey); // ======================= Col/Row Span ======================= const getRowSpan = (column: ColumnType, index: number): number => { const record = flattenData[index]?.record; @@ -185,6 +185,7 @@ const Grid = React.forwardRef((props, ref) => { key={index} data={item} rowKey={rowKey} + rowKeys={rowKeys} index={index} style={{ top: -offsetY + sizeInfo.top, @@ -243,7 +244,15 @@ const Grid = React.forwardRef((props, ref) => { > {(item, index, itemProps) => { const rowKey = getRowKey(item.record, index); - return ; + return ( + + ); }} diff --git a/src/VirtualTable/BodyLine.tsx b/src/VirtualTable/BodyLine.tsx index a02d195a..061bef10 100644 --- a/src/VirtualTable/BodyLine.tsx +++ b/src/VirtualTable/BodyLine.tsx @@ -15,6 +15,7 @@ export interface BodyLineProps { className?: string; style?: React.CSSProperties; rowKey: React.Key; + rowKeys: React.Key[]; /** Render cell only when it has `rowSpan > 1` */ extra?: boolean; @@ -22,7 +23,7 @@ export interface BodyLineProps { } const BodyLine = React.forwardRef((props, ref) => { - const { data, index, className, rowKey, style, extra, getHeight, ...restProps } = props; + const { data, index, className, rowKey, style, extra, getHeight, rowKeys, ...restProps } = props; const { record, indent, index: renderIndex } = data; const { scrollX, flattenColumns, prefixCls, fixColumn, componentWidth } = useContext( @@ -114,6 +115,7 @@ const BodyLine = React.forwardRef((props, ref) => record={record} inverse={extra} getHeight={getHeight} + rowKeys={rowKeys} /> ); })} diff --git a/src/VirtualTable/VirtualCell.tsx b/src/VirtualTable/VirtualCell.tsx index 9b1b3ebe..8839b7a9 100644 --- a/src/VirtualTable/VirtualCell.tsx +++ b/src/VirtualTable/VirtualCell.tsx @@ -25,6 +25,7 @@ export interface VirtualCellProps { /** Render cell only when it has `rowSpan > 1` */ inverse?: boolean; getHeight?: (rowSpan: number) => number; + rowKeys: React.Key[]; } /** @@ -50,6 +51,7 @@ function VirtualCell(props: VirtualCellProps) { className, inverse, getHeight, + rowKeys, } = props; const { render, dataIndex, className: columnClassName, width: colWidth } = column; @@ -62,6 +64,7 @@ function VirtualCell(props: VirtualCellProps) { colIndex, indent, index, + rowKeys, ); const { style: cellStyle, colSpan = 1, rowSpan = 1 } = additionalCellProps; From 5f41e75d985d9b3acb9b2763698b0c456a66c62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 12:37:57 +0800 Subject: [PATCH 09/26] =?UTF-8?q?feat:=20=E4=B8=8D=E6=94=B9=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E8=A1=A8=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/VirtualTable/BodyGrid.tsx | 13 ++----------- src/VirtualTable/BodyLine.tsx | 4 +--- src/VirtualTable/VirtualCell.tsx | 4 +--- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/VirtualTable/BodyGrid.tsx b/src/VirtualTable/BodyGrid.tsx index bcd89b3c..cc1548cc 100644 --- a/src/VirtualTable/BodyGrid.tsx +++ b/src/VirtualTable/BodyGrid.tsx @@ -95,7 +95,7 @@ const Grid = React.forwardRef((props, ref) => { return obj; }); - const rowKeys = flattenData.map(item => item.rowKey); + // ======================= Col/Row Span ======================= const getRowSpan = (column: ColumnType, index: number): number => { const record = flattenData[index]?.record; @@ -185,7 +185,6 @@ const Grid = React.forwardRef((props, ref) => { key={index} data={item} rowKey={rowKey} - rowKeys={rowKeys} index={index} style={{ top: -offsetY + sizeInfo.top, @@ -244,15 +243,7 @@ const Grid = React.forwardRef((props, ref) => { > {(item, index, itemProps) => { const rowKey = getRowKey(item.record, index); - return ( - - ); + return ; }} diff --git a/src/VirtualTable/BodyLine.tsx b/src/VirtualTable/BodyLine.tsx index 061bef10..a02d195a 100644 --- a/src/VirtualTable/BodyLine.tsx +++ b/src/VirtualTable/BodyLine.tsx @@ -15,7 +15,6 @@ export interface BodyLineProps { className?: string; style?: React.CSSProperties; rowKey: React.Key; - rowKeys: React.Key[]; /** Render cell only when it has `rowSpan > 1` */ extra?: boolean; @@ -23,7 +22,7 @@ export interface BodyLineProps { } const BodyLine = React.forwardRef((props, ref) => { - const { data, index, className, rowKey, style, extra, getHeight, rowKeys, ...restProps } = props; + const { data, index, className, rowKey, style, extra, getHeight, ...restProps } = props; const { record, indent, index: renderIndex } = data; const { scrollX, flattenColumns, prefixCls, fixColumn, componentWidth } = useContext( @@ -115,7 +114,6 @@ const BodyLine = React.forwardRef((props, ref) => record={record} inverse={extra} getHeight={getHeight} - rowKeys={rowKeys} /> ); })} diff --git a/src/VirtualTable/VirtualCell.tsx b/src/VirtualTable/VirtualCell.tsx index 8839b7a9..e4a3c6bf 100644 --- a/src/VirtualTable/VirtualCell.tsx +++ b/src/VirtualTable/VirtualCell.tsx @@ -25,7 +25,6 @@ export interface VirtualCellProps { /** Render cell only when it has `rowSpan > 1` */ inverse?: boolean; getHeight?: (rowSpan: number) => number; - rowKeys: React.Key[]; } /** @@ -51,7 +50,6 @@ function VirtualCell(props: VirtualCellProps) { className, inverse, getHeight, - rowKeys, } = props; const { render, dataIndex, className: columnClassName, width: colWidth } = column; @@ -64,7 +62,7 @@ function VirtualCell(props: VirtualCellProps) { colIndex, indent, index, - rowKeys, + [], ); const { style: cellStyle, colSpan = 1, rowSpan = 1 } = additionalCellProps; From 668ef0821705f840e6ec9df2271e2fd6d76dbe5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 12:46:30 +0800 Subject: [PATCH 10/26] feat: review --- src/Body/BodyRow.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index e0b33002..0ef9d237 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -71,20 +71,20 @@ export function getCellProps( ); } - const addChildrenRowSpan = (rowSpan: number, index2: number) => { - const _index = index2 + 1; + const addChildrenRowSpan = (rowSpan: number, idx: number) => { + const nextIndex = idx + 1; + const rowKey = rowKeys[nextIndex]; let _rowSpan = rowSpan; - const rowKey = rowKeys[_index]; if (rowKey !== undefined) { // 下面如果是 0 的,增加 +1 逻辑 - const thisCellProps = column.onCell(record, _index); + const thisCellProps = column.onCell(record, nextIndex); if (thisCellProps.rowSpan === 0) { const thisExpanded = expandedKeys.has(rowKey); if (thisExpanded) { _rowSpan = _rowSpan + 1; } // 继续往下找 - return addChildrenRowSpan(_rowSpan, _index); + return addChildrenRowSpan(_rowSpan, nextIndex); } } // 找不到后返回 From ff959d9b4a2cfbe1f535422d2fe18e002c747a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 13:03:10 +0800 Subject: [PATCH 11/26] feat: review --- src/Body/BodyRow.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 0ef9d237..ce1069c8 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -45,6 +45,7 @@ export function getCellProps( expanded, hasNestChildren, onTriggerExpand, + expandable, expandedKeys, } = rowInfo; @@ -94,14 +95,17 @@ export function getCellProps( let additionalCellProps: React.TdHTMLAttributes; if (column.onCell) { additionalCellProps = column.onCell(record, index); - if (additionalCellProps.rowSpan > 0) { - // 本身展开 +1 - if (expanded) { - additionalCellProps.rowSpan = additionalCellProps.rowSpan + 1; + // 开启 expanded 的增加下面逻辑 + if (expandable) { + if (additionalCellProps.rowSpan > 0) { + // 本身展开 +1 + if (expanded) { + additionalCellProps.rowSpan = additionalCellProps.rowSpan + 1; + } + additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); } - additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); + // console.log('additionalCellProps.rowSpan', additionalCellProps.rowSpan); } - console.log('additionalCellProps.rowSpan', additionalCellProps.rowSpan); } return { From 92106b57fd9875d0e54713a75f15f25987fdea7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 13:06:36 +0800 Subject: [PATCH 12/26] feat: review --- src/Body/BodyRow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index ce1069c8..2c925a5b 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -104,7 +104,6 @@ export function getCellProps( } additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); } - // console.log('additionalCellProps.rowSpan', additionalCellProps.rowSpan); } } From c3a21ff025428946989a1d4d5fe7f85d879f5146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Tue, 20 May 2025 13:10:03 +0800 Subject: [PATCH 13/26] feat: review --- src/Body/BodyRow.tsx | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 2c925a5b..642d39f0 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -72,36 +72,36 @@ export function getCellProps( ); } - const addChildrenRowSpan = (rowSpan: number, idx: number) => { - const nextIndex = idx + 1; - const rowKey = rowKeys[nextIndex]; - let _rowSpan = rowSpan; - if (rowKey !== undefined) { - // 下面如果是 0 的,增加 +1 逻辑 - const thisCellProps = column.onCell(record, nextIndex); - if (thisCellProps.rowSpan === 0) { - const thisExpanded = expandedKeys.has(rowKey); - if (thisExpanded) { - _rowSpan = _rowSpan + 1; - } - // 继续往下找 - return addChildrenRowSpan(_rowSpan, nextIndex); - } - } - // 找不到后返回 - return _rowSpan; - }; - let additionalCellProps: React.TdHTMLAttributes; if (column.onCell) { additionalCellProps = column.onCell(record, index); // 开启 expanded 的增加下面逻辑 if (expandable) { + // 当前为合并单元格开始 if (additionalCellProps.rowSpan > 0) { // 本身展开 +1 if (expanded) { additionalCellProps.rowSpan = additionalCellProps.rowSpan + 1; } + // 下面如果是 0 的,增加 +1 逻辑 + const addChildrenRowSpan = (rowSpan: number, idx: number) => { + const nextIndex = idx + 1; + const rowKey = rowKeys[nextIndex]; + let _rowSpan = rowSpan; + if (rowKey !== undefined) { + const thisCellProps = column.onCell(record, nextIndex); + if (thisCellProps.rowSpan === 0) { + const thisExpanded = expandedKeys.has(rowKey); + if (thisExpanded) { + _rowSpan = _rowSpan + 1; + } + // 继续往下找 + return addChildrenRowSpan(_rowSpan, nextIndex); + } + } + // 找不到后返回 + return _rowSpan; + }; additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); } } From 0b533344445e941c72f5fdf761d07fe67b0c4853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 20 May 2025 15:09:17 +0800 Subject: [PATCH 14/26] chore: move keys to cache --- src/Body/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Body/index.tsx b/src/Body/index.tsx index a2f7502d..d6e4d8e4 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -49,6 +49,8 @@ function Body(props: BodyProps) { getRowKey, ); + const rowKeys = React.useMemo(() => flattenData.map(item => item.rowKey), [flattenData]); + // =================== Performance ==================== const perfRef = React.useRef({ renderWithProps: false, @@ -62,7 +64,6 @@ function Body(props: BodyProps) { let rows: React.ReactNode; if (data.length) { - const rowKeys = flattenData.map(item => item.rowKey); rows = flattenData.map((item, idx) => { const { record, indent, index: renderIndex, rowKey } = item; From d91920eaf1f80d7cf0094880eef5a682daa2f384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 20 May 2025 15:36:38 +0800 Subject: [PATCH 15/26] chore: simplify code --- docs/examples/expandedRowSpan.tsx | 7 +++--- src/Body/BodyRow.tsx | 39 ++++++++++--------------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/docs/examples/expandedRowSpan.tsx b/docs/examples/expandedRowSpan.tsx index 69058d0f..6bd5ea31 100644 --- a/docs/examples/expandedRowSpan.tsx +++ b/docs/examples/expandedRowSpan.tsx @@ -11,16 +11,14 @@ const columns: ColumnsType = [ width: 100, onCell: (_, index) => { const props: React.TdHTMLAttributes = {}; - if (index === 0) { + if (index === 1) { props.rowSpan = 4; - } else if (index === 1) { - props.rowSpan = 0; } else if (index === 2) { props.rowSpan = 0; } else if (index === 3) { props.rowSpan = 0; } else if (index === 4) { - props.rowSpan = 1; + props.rowSpan = 0; } return props; @@ -33,6 +31,7 @@ const columns: ColumnsType = [ ]; const data = [ + { a: '12313132132', b: '0571-43243256', c: '小二', d: '文零西路', e: 'Male', key: 'z' }, { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: 'a' }, { a: '13812340987', b: '0571-12345678', c: '张夫人', d: '文一西路', e: 'Female', key: 'b' }, { a: '13812340987', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: 'c' }, diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 642d39f0..5d9f1754 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -75,35 +75,20 @@ export function getCellProps( let additionalCellProps: React.TdHTMLAttributes; if (column.onCell) { additionalCellProps = column.onCell(record, index); - // 开启 expanded 的增加下面逻辑 - if (expandable) { - // 当前为合并单元格开始 - if (additionalCellProps.rowSpan > 0) { - // 本身展开 +1 - if (expanded) { - additionalCellProps.rowSpan = additionalCellProps.rowSpan + 1; + const { rowSpan } = additionalCellProps; + + // For expandable row with rowSpan, + // We should increase the rowSpan if the row is expanded + if (expandable && rowSpan > 1) { + let currentRowSpan = rowSpan; + + for (let i = index; i < rowSpan; i += 1) { + const rowKey = rowKeys[i]; + if (expandedKeys.has(rowKey)) { + currentRowSpan += 1; } - // 下面如果是 0 的,增加 +1 逻辑 - const addChildrenRowSpan = (rowSpan: number, idx: number) => { - const nextIndex = idx + 1; - const rowKey = rowKeys[nextIndex]; - let _rowSpan = rowSpan; - if (rowKey !== undefined) { - const thisCellProps = column.onCell(record, nextIndex); - if (thisCellProps.rowSpan === 0) { - const thisExpanded = expandedKeys.has(rowKey); - if (thisExpanded) { - _rowSpan = _rowSpan + 1; - } - // 继续往下找 - return addChildrenRowSpan(_rowSpan, nextIndex); - } - } - // 找不到后返回 - return _rowSpan; - }; - additionalCellProps.rowSpan = addChildrenRowSpan(additionalCellProps.rowSpan, index); } + additionalCellProps.rowSpan = currentRowSpan; } } From dd7d053d931da0d40663ff8bee87cfcc561f7acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 20 May 2025 15:41:32 +0800 Subject: [PATCH 16/26] chore: clean up --- src/Body/BodyRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 5d9f1754..be92e6be 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -82,7 +82,7 @@ export function getCellProps( if (expandable && rowSpan > 1) { let currentRowSpan = rowSpan; - for (let i = index; i < rowSpan; i += 1) { + for (let i = index; i < index + rowSpan - 1; i += 1) { const rowKey = rowKeys[i]; if (expandedKeys.has(rowKey)) { currentRowSpan += 1; From 2ed330a02449e12f3e4f1bce719b4fda090338df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 20 May 2025 15:42:07 +0800 Subject: [PATCH 17/26] chore: adjust offset --- src/Body/BodyRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index be92e6be..135052e3 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -82,7 +82,7 @@ export function getCellProps( if (expandable && rowSpan > 1) { let currentRowSpan = rowSpan; - for (let i = index; i < index + rowSpan - 1; i += 1) { + for (let i = index; i < index + rowSpan; i += 1) { const rowKey = rowKeys[i]; if (expandedKeys.has(rowKey)) { currentRowSpan += 1; From e606eed0415a83e634257ffd24bcb5419ff052b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Wed, 21 May 2025 06:20:34 +0800 Subject: [PATCH 18/26] =?UTF-8?q?feat:=20=E5=9B=9E=E9=80=80=20offset=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/examples/expandedRowSpan.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/examples/expandedRowSpan.tsx b/docs/examples/expandedRowSpan.tsx index 6bd5ea31..e43e1081 100644 --- a/docs/examples/expandedRowSpan.tsx +++ b/docs/examples/expandedRowSpan.tsx @@ -11,15 +11,12 @@ const columns: ColumnsType = [ width: 100, onCell: (_, index) => { const props: React.TdHTMLAttributes = {}; - if (index === 1) { - props.rowSpan = 4; - } else if (index === 2) { - props.rowSpan = 0; - } else if (index === 3) { - props.rowSpan = 0; - } else if (index === 4) { - props.rowSpan = 0; - } + if (index === 0) props.rowSpan = 1; + if (index === 1) props.rowSpan = 4; + if (index === 2) props.rowSpan = 0; + if (index === 3) props.rowSpan = 0; + if (index === 4) props.rowSpan = 0; + if (index === 5) props.rowSpan = 1; return props; }, From 1cb001333ea646172fb3f3cf1e03c48336211595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Wed, 21 May 2025 06:23:01 +0800 Subject: [PATCH 19/26] =?UTF-8?q?feat:=20=E5=8E=BB=E6=8E=89=20rowSpan=20>?= =?UTF-8?q?=201=20=E5=88=A4=E6=96=AD,=E5=8F=AA=E8=A6=81=20rowSpan=20=3D=20?= =?UTF-8?q?1=20=E5=88=99=20+1,=E5=A6=82=E6=9E=9C=20rowSpan=20=3D=20undefin?= =?UTF-8?q?ed=20=E5=88=99=E4=B8=8D=20+1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/examples/expandedRowSpan.tsx | 3 +-- src/Body/BodyRow.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/examples/expandedRowSpan.tsx b/docs/examples/expandedRowSpan.tsx index e43e1081..5aeba748 100644 --- a/docs/examples/expandedRowSpan.tsx +++ b/docs/examples/expandedRowSpan.tsx @@ -16,8 +16,7 @@ const columns: ColumnsType = [ if (index === 2) props.rowSpan = 0; if (index === 3) props.rowSpan = 0; if (index === 4) props.rowSpan = 0; - if (index === 5) props.rowSpan = 1; - + if (index === 5) props.rowSpan = undefined; return props; }, }, diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 135052e3..cd76493c 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -79,7 +79,7 @@ export function getCellProps( // For expandable row with rowSpan, // We should increase the rowSpan if the row is expanded - if (expandable && rowSpan > 1) { + if (expandable) { let currentRowSpan = rowSpan; for (let i = index; i < index + rowSpan; i += 1) { From 227581c396f7026e71ad7d5a878c61a43ba157f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Wed, 21 May 2025 20:45:42 +0800 Subject: [PATCH 20/26] feat: reset --- docs/examples/className.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/className.tsx b/docs/examples/className.tsx index 77c32e22..8deffd2b 100644 --- a/docs/examples/className.tsx +++ b/docs/examples/className.tsx @@ -56,7 +56,7 @@ const Demo = () => ( footer={() => footer} />

scroll

-
`row-${i}`} expandedRowRender={record =>

extra: {record.a}

} From 337c5c93fc98b23b6b3fa4fe9d97bbeb17a48776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Wed, 21 May 2025 20:52:53 +0800 Subject: [PATCH 21/26] feat: add sticky --- src/Body/BodyRow.tsx | 17 ++++++++++++++--- src/Body/ExpandedRow.tsx | 4 +++- src/Body/index.tsx | 3 +++ src/Table.tsx | 4 ++++ src/context/TableContext.tsx | 4 +++- src/interface.ts | 1 + 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 4d692d7d..333396a1 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -4,10 +4,10 @@ import Cell from '../Cell'; import { responseImmutable } from '../context/TableContext'; import devRenderTimes from '../hooks/useRenderTimes'; import useRowInfo from '../hooks/useRowInfo'; -import type { ColumnType, CustomizeComponent } from '../interface'; +import type { ColumnType, CustomizeComponent, ExpandableConfig } from '../interface'; import ExpandedRow from './ExpandedRow'; import { computedExpandedClassName } from '../utils/expandUtil'; -import { TableProps } from '..'; +import type { TableProps } from '..'; export interface BodyRowProps { record: RecordType; @@ -23,6 +23,7 @@ export interface BodyRowProps { indent?: number; rowKey: React.Key; rowKeys: React.Key[]; + expandedRowOffset?: ExpandableConfig['expandedRowOffset']; } // ================================================================================== @@ -126,6 +127,7 @@ function BodyRow( rowComponent: RowComponent, cellComponent, scopeCellComponent, + expandedRowOffset = 0, rowKeys, } = props; @@ -218,6 +220,14 @@ function BodyRow( if (rowSupportExpand && (expandedRef.current || expanded)) { const expandContent = expandedRowRender(record, index, indent + 1, expanded); + const offsetColumns = flattenColumns.filter((_, idx) => idx < expandedRowOffset); + let offsetWidth = 0; + offsetColumns.forEach(item => { + if (typeof item.width === 'number') { + offsetWidth = offsetWidth + (item.width ?? 0); + } + }); + expandRowNode = ( ( prefixCls={prefixCls} component={RowComponent} cellComponent={cellComponent} - colSpan={flattenColumns.length} + offsetWidth={offsetWidth} + colSpan={flattenColumns.length - expandedRowOffset} isEmpty={false} > {expandContent} diff --git a/src/Body/ExpandedRow.tsx b/src/Body/ExpandedRow.tsx index b4009601..77e757c6 100644 --- a/src/Body/ExpandedRow.tsx +++ b/src/Body/ExpandedRow.tsx @@ -14,6 +14,7 @@ export interface ExpandedRowProps { children: React.ReactNode; colSpan: number; isEmpty: boolean; + offsetWidth?: number; } function ExpandedRow(props: ExpandedRowProps) { @@ -30,6 +31,7 @@ function ExpandedRow(props: ExpandedRowProps) { expanded, colSpan, isEmpty, + offsetWidth = 0, } = props; const { scrollbarSize, fixHeader, fixColumn, componentWidth, horizonScroll } = useContext( @@ -44,7 +46,7 @@ function ExpandedRow(props: ExpandedRowProps) { contentNode = (
(props: BodyProps) { expandedKeys, childrenColumnName, emptyNode, + expandedRowOffset, classNames, styles, } = useContext(TableContext, [ @@ -43,6 +44,7 @@ function Body(props: BodyProps) { 'expandedKeys', 'childrenColumnName', 'emptyNode', + 'expandedRowOffset', 'classNames', 'styles', ]); @@ -88,6 +90,7 @@ function Body(props: BodyProps) { cellComponent={tdComponent} scopeCellComponent={thComponent} indent={indent} + expandedRowOffset={expandedRowOffset} /> ); }); diff --git a/src/Table.tsx b/src/Table.tsx index d5a8b196..5773e6c7 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -872,6 +872,7 @@ function Table( expandedRowRender: expandableConfig.expandedRowRender, onTriggerExpand, expandIconColumnIndex: expandableConfig.expandIconColumnIndex, + expandedRowOffset: expandableConfig.expandedRowOffset, indentSize: expandableConfig.indentSize, allColumnsFixedLeft: flattenColumns.every(col => col.fixed === 'start'), emptyNode, @@ -920,6 +921,7 @@ function Table( expandableType, expandableConfig.expandRowByClick, expandableConfig.expandedRowRender, + expandableConfig.expandedRowOffset, onTriggerExpand, expandableConfig.expandIconColumnIndex, expandableConfig.indentSize, @@ -942,6 +944,8 @@ function Table( mergedChildrenColumnName, rowHoverable, + classNames, + styles, ], ); diff --git a/src/context/TableContext.tsx b/src/context/TableContext.tsx index b9cae4dd..fbab6587 100644 --- a/src/context/TableContext.tsx +++ b/src/context/TableContext.tsx @@ -3,6 +3,7 @@ import type { ColumnsType, ColumnType, Direction, + ExpandableConfig, ExpandableType, ExpandedRowRender, GetComponent, @@ -14,7 +15,7 @@ import type { TriggerEventHandler, } from '../interface'; import type { FixedInfo } from '../utils/fixUtil'; -import { TableProps } from '../Table'; +import type { TableProps } from '../Table'; const { makeImmutable, responseImmutable, useImmutableMark } = createImmutable(); export { makeImmutable, responseImmutable, useImmutableMark }; @@ -55,6 +56,7 @@ export interface TableContextProps { expandIcon: RenderExpandIcon; onTriggerExpand: TriggerEventHandler; expandIconColumnIndex: number; + expandedRowOffset: ExpandableConfig['expandedRowOffset']; allColumnsFixedLeft: boolean; // Column diff --git a/src/interface.ts b/src/interface.ts index 54ebfcd9..dcdfb4f9 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -256,6 +256,7 @@ export interface ExpandableConfig { rowExpandable?: (record: RecordType) => boolean; columnWidth?: number | string; fixed?: FixedType; + expandedRowOffset?: number; } // =================== Render =================== From 0bcf3bf40634d98dd254173b6cb5cacd41961096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Wed, 21 May 2025 20:55:33 +0800 Subject: [PATCH 22/26] feat: add sticky --- docs/demo/expandedSticky.md | 8 +++++ docs/examples/expandedSticky.tsx | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 docs/demo/expandedSticky.md create mode 100644 docs/examples/expandedSticky.tsx diff --git a/docs/demo/expandedSticky.md b/docs/demo/expandedSticky.md new file mode 100644 index 00000000..df58f578 --- /dev/null +++ b/docs/demo/expandedSticky.md @@ -0,0 +1,8 @@ +--- +title: expandedSticky +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/expandedSticky.tsx b/docs/examples/expandedSticky.tsx new file mode 100644 index 00000000..f783226b --- /dev/null +++ b/docs/examples/expandedSticky.tsx @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import type { ColumnType } from 'rc-table'; +import Table from 'rc-table'; +import '../../assets/index.less'; + +const Demo = () => { + const [expandedRowKeys, setExpandedRowKeys] = useState([]); + + const columns: ColumnType>[] = [ + { + title: '手机号', + dataIndex: 'a', + width: 100, + fixed: 'left', + onCell: (_, index) => { + const props: React.TdHTMLAttributes = {}; + if (index === 0) props.rowSpan = 1; + if (index === 1) props.rowSpan = 2; + if (index === 2) props.rowSpan = 0; + return props; + }, + }, + Table.EXPAND_COLUMN, + { title: 'Name', dataIndex: 'c' }, + { title: 'Address', fixed: 'right', dataIndex: 'd', width: 200 }, + ]; + + return ( +
+

expanded & sticky

+ > + rowKey="key" + sticky + scroll={{ x: 800 }} + columns={columns} + data={[ + { key: 'a', a: '12313132132', c: '小二', d: '文零西路' }, + { key: 'b', a: '13812340987', c: '张三', d: '文一西路' }, + { key: 'c', a: '13812340987', c: '张夫', d: '文二西路' }, + ]} + expandable={{ + expandedRowOffset: 1, + expandedRowKeys, + onExpandedRowsChange: keys => setExpandedRowKeys(keys), + expandedRowRender: record =>

{record.key}

, + }} + className="table" + /> +
+ ); +}; + +export default Demo; From a63d6a795ba2bc96bca27aac853a95c960b6ff4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Thu, 22 May 2025 06:34:16 +0800 Subject: [PATCH 23/26] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=20offset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/examples/expandedSticky.tsx | 2 +- src/Body/BodyRow.tsx | 67 ++++++++++++++++++++------------ src/Body/index.tsx | 3 -- src/Table.tsx | 2 - src/context/TableContext.tsx | 2 - src/interface.ts | 1 - 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/examples/expandedSticky.tsx b/docs/examples/expandedSticky.tsx index f783226b..1f516b2b 100644 --- a/docs/examples/expandedSticky.tsx +++ b/docs/examples/expandedSticky.tsx @@ -7,6 +7,7 @@ const Demo = () => { const [expandedRowKeys, setExpandedRowKeys] = useState([]); const columns: ColumnType>[] = [ + // { title: '分割', dataIndex: 'ca' }, { title: '手机号', dataIndex: 'a', @@ -43,7 +44,6 @@ const Demo = () => { { key: 'c', a: '13812340987', c: '张夫', d: '文二西路' }, ]} expandable={{ - expandedRowOffset: 1, expandedRowKeys, onExpandedRowsChange: keys => setExpandedRowKeys(keys), expandedRowRender: record =>

{record.key}

, diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 333396a1..42c32ee5 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -4,7 +4,7 @@ import Cell from '../Cell'; import { responseImmutable } from '../context/TableContext'; import devRenderTimes from '../hooks/useRenderTimes'; import useRowInfo from '../hooks/useRowInfo'; -import type { ColumnType, CustomizeComponent, ExpandableConfig } from '../interface'; +import type { ColumnType, CustomizeComponent } from '../interface'; import ExpandedRow from './ExpandedRow'; import { computedExpandedClassName } from '../utils/expandUtil'; import type { TableProps } from '..'; @@ -23,7 +23,6 @@ export interface BodyRowProps { indent?: number; rowKey: React.Key; rowKeys: React.Key[]; - expandedRowOffset?: ExpandableConfig['expandedRowOffset']; } // ================================================================================== @@ -104,9 +103,31 @@ export function getCellProps( }; } -// ================================================================================== -// == getCellProps == -// ================================================================================== +const getOffsetData = ( + columnsData: { + column: ColumnType; + cell: { additionalCellProps: React.TdHTMLAttributes }; + }[], +) => { + let offsetWidth = 0; + let offsetColumn = 0; + let isRowSpanEnd = false; + columnsData.forEach(item => { + if (!isRowSpanEnd) { + const { column, cell } = item; + if (cell.additionalCellProps.rowSpan !== undefined) { + offsetColumn += 1; + if (typeof column.width === 'number') { + offsetWidth = offsetWidth + (column.width ?? 0); + } + } else { + isRowSpanEnd = true; + } + } + }); + return { offsetWidth, offsetColumn }; +}; + function BodyRow( props: BodyRowProps, ) { @@ -127,7 +148,6 @@ function BodyRow( rowComponent: RowComponent, cellComponent, scopeCellComponent, - expandedRowOffset = 0, rowKeys, } = props; @@ -157,6 +177,17 @@ function BodyRow( // 此时如果 level > 1 则说明是 expandedRow, 一样需要附加 computedExpandedRowClassName const expandedClsName = computedExpandedClassName(expandedRowClassName, record, index, indent); + const { columnsData, offsetData } = React.useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-shadow + const columnsData = flattenColumns.map((column: ColumnType, colIndex) => { + const cell = getCellProps(rowInfo, column, colIndex, indent, index, rowKeys); + return { column, cell }; + }); + // eslint-disable-next-line @typescript-eslint/no-shadow + const offsetData = getOffsetData(columnsData); + return { columnsData, offsetData }; + }, [flattenColumns, indent, index, rowInfo, rowKeys]); + // ======================== Base tr row ======================== const baseRowNode = ( ( ...styles.row, }} > - {flattenColumns.map((column: ColumnType, colIndex) => { + {columnsData.map(item => { + const { column, cell } = item; const { render, dataIndex, className: columnClassName } = column; - const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps( - rowInfo, - column, - colIndex, - indent, - index, - rowKeys, - ); + const { key, fixedInfo, appendCellNode, additionalCellProps } = cell; return ( @@ -220,14 +245,6 @@ function BodyRow( if (rowSupportExpand && (expandedRef.current || expanded)) { const expandContent = expandedRowRender(record, index, indent + 1, expanded); - const offsetColumns = flattenColumns.filter((_, idx) => idx < expandedRowOffset); - let offsetWidth = 0; - offsetColumns.forEach(item => { - if (typeof item.width === 'number') { - offsetWidth = offsetWidth + (item.width ?? 0); - } - }); - expandRowNode = ( ( prefixCls={prefixCls} component={RowComponent} cellComponent={cellComponent} - offsetWidth={offsetWidth} - colSpan={flattenColumns.length - expandedRowOffset} + offsetWidth={offsetData.offsetWidth} + colSpan={flattenColumns.length - offsetData.offsetColumn} isEmpty={false} > {expandContent} diff --git a/src/Body/index.tsx b/src/Body/index.tsx index 80fbc62c..d467f684 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -32,7 +32,6 @@ function Body(props: BodyProps) { expandedKeys, childrenColumnName, emptyNode, - expandedRowOffset, classNames, styles, } = useContext(TableContext, [ @@ -44,7 +43,6 @@ function Body(props: BodyProps) { 'expandedKeys', 'childrenColumnName', 'emptyNode', - 'expandedRowOffset', 'classNames', 'styles', ]); @@ -90,7 +88,6 @@ function Body(props: BodyProps) { cellComponent={tdComponent} scopeCellComponent={thComponent} indent={indent} - expandedRowOffset={expandedRowOffset} /> ); }); diff --git a/src/Table.tsx b/src/Table.tsx index 5773e6c7..9c2a682f 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -872,7 +872,6 @@ function Table( expandedRowRender: expandableConfig.expandedRowRender, onTriggerExpand, expandIconColumnIndex: expandableConfig.expandIconColumnIndex, - expandedRowOffset: expandableConfig.expandedRowOffset, indentSize: expandableConfig.indentSize, allColumnsFixedLeft: flattenColumns.every(col => col.fixed === 'start'), emptyNode, @@ -921,7 +920,6 @@ function Table( expandableType, expandableConfig.expandRowByClick, expandableConfig.expandedRowRender, - expandableConfig.expandedRowOffset, onTriggerExpand, expandableConfig.expandIconColumnIndex, expandableConfig.indentSize, diff --git a/src/context/TableContext.tsx b/src/context/TableContext.tsx index fbab6587..cc1c132e 100644 --- a/src/context/TableContext.tsx +++ b/src/context/TableContext.tsx @@ -3,7 +3,6 @@ import type { ColumnsType, ColumnType, Direction, - ExpandableConfig, ExpandableType, ExpandedRowRender, GetComponent, @@ -56,7 +55,6 @@ export interface TableContextProps { expandIcon: RenderExpandIcon; onTriggerExpand: TriggerEventHandler; expandIconColumnIndex: number; - expandedRowOffset: ExpandableConfig['expandedRowOffset']; allColumnsFixedLeft: boolean; // Column diff --git a/src/interface.ts b/src/interface.ts index dcdfb4f9..54ebfcd9 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -256,7 +256,6 @@ export interface ExpandableConfig { rowExpandable?: (record: RecordType) => boolean; columnWidth?: number | string; fixed?: FixedType; - expandedRowOffset?: number; } // =================== Render =================== From adf710ce9391163d9c134abf88e391abdb490051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Thu, 22 May 2025 07:22:41 +0800 Subject: [PATCH 24/26] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=20offset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Body/BodyRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 42c32ee5..97ae9472 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -82,7 +82,7 @@ export function getCellProps( // For expandable row with rowSpan, // We should increase the rowSpan if the row is expanded - if (expandable) { + if (expandable && rowSpan !== undefined) { let currentRowSpan = rowSpan; for (let i = index; i < index + rowSpan; i += 1) { From fa2517419ea43b7340f6a7f50a928e097e33469e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Thu, 22 May 2025 12:51:20 +0800 Subject: [PATCH 25/26] feat: test --- tests/Expanded.spec.tsx | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/Expanded.spec.tsx diff --git a/tests/Expanded.spec.tsx b/tests/Expanded.spec.tsx new file mode 100644 index 00000000..61e156c4 --- /dev/null +++ b/tests/Expanded.spec.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { render, act } from '@testing-library/react'; +import Table, { type ColumnsType } from '../src'; +import { spyElementPrototypes } from '@rc-component/util/lib/test/domHook'; + +describe('Table.Expanded', () => { + let domSpy: ReturnType; + + beforeEach(() => { + vi.useFakeTimers(); + }); + + beforeAll(() => { + domSpy = spyElementPrototypes(HTMLElement, { + offsetParent: { + get: () => ({}), + }, + offsetWidth: { + get: () => 1000, + }, + }); + }); + + afterAll(() => { + domSpy.mockRestore(); + }); + + const columns: ColumnsType = [ + { + title: 'key', + dataIndex: 'key', + width: 100, + onCell: (_, index) => { + const props: React.TdHTMLAttributes = {}; + if (index === 0) props.rowSpan = 1; + if (index === 1) props.rowSpan = 2; + if (index === 2) props.rowSpan = 0; + if (index === 3) props.rowSpan = undefined; + return props; + }, + }, + Table.EXPAND_COLUMN, + { title: 'key2', dataIndex: 'key2', width: 100 }, + ]; + const data = [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }]; + + it('expanded + rowSpan', async () => { + const { container } = render( + > + columns={columns} + data={data} + expandable={{ + defaultExpandAllRows: true, + expandedRowRender: record =>
{record.key}
, + }} + />, + ); + + await act(async () => { + vi.runAllTimers(); + await Promise.resolve(); + }); + const trList = container.querySelector('tbody').querySelectorAll('tr'); + // row 1 + expect(trList[0].querySelectorAll('td').length).toBe(3); + expect(trList[0].querySelectorAll('td')[0].getAttribute('rowspan')).toBe('2'); + // expand 1 + expect(trList[1].querySelectorAll('td').length).toBe(1); + expect(trList[1].querySelectorAll('td')[0].getAttribute('colspan')).toBe('2'); + // row 2 + expect(trList[2].querySelectorAll('td').length).toBe(3); + expect(trList[2].querySelectorAll('td')[0].getAttribute('rowspan')).toBe('4'); + // expand 2 + expect(trList[3].querySelectorAll('td').length).toBe(1); + expect(trList[3].querySelectorAll('td')[0].getAttribute('colspan')).toBe('2'); + // row 3 + expect(trList[4].querySelectorAll('td').length).toBe(2); + // expand 3 + expect(trList[5].querySelectorAll('td').length).toBe(1); + expect(trList[5].querySelectorAll('td')[0].getAttribute('colspan')).toBe('2'); + // row 4 + expect(trList[6].querySelectorAll('td').length).toBe(3); + // expand 4 + expect(trList[7].querySelectorAll('td').length).toBe(1); + expect(trList[7].querySelectorAll('td')[0].getAttribute('colspan')).toBe('3'); + }); +}); From dc67020a4b6773a8df856f14ecd7d7b7672cafc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Sat, 24 May 2025 10:38:20 +0800 Subject: [PATCH 26/26] feat: test --- tests/Expanded.spec.tsx | 74 ++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/tests/Expanded.spec.tsx b/tests/Expanded.spec.tsx index 61e156c4..b3106cca 100644 --- a/tests/Expanded.spec.tsx +++ b/tests/Expanded.spec.tsx @@ -25,26 +25,25 @@ describe('Table.Expanded', () => { domSpy.mockRestore(); }); - const columns: ColumnsType = [ - { - title: 'key', - dataIndex: 'key', - width: 100, - onCell: (_, index) => { - const props: React.TdHTMLAttributes = {}; - if (index === 0) props.rowSpan = 1; - if (index === 1) props.rowSpan = 2; - if (index === 2) props.rowSpan = 0; - if (index === 3) props.rowSpan = undefined; - return props; - }, - }, - Table.EXPAND_COLUMN, - { title: 'key2', dataIndex: 'key2', width: 100 }, - ]; - const data = [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }]; - it('expanded + rowSpan', async () => { + const columns: ColumnsType = [ + { + title: 'key', + dataIndex: 'key', + width: 100, + onCell: (_, index) => { + const props: React.TdHTMLAttributes = {}; + if (index === 0) props.rowSpan = 1; + if (index === 1) props.rowSpan = 2; + if (index === 2) props.rowSpan = 0; + if (index === 3) props.rowSpan = undefined; + return props; + }, + }, + Table.EXPAND_COLUMN, + { title: 'key2', dataIndex: 'key2', width: 100 }, + ]; + const data = [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }]; const { container } = render( > columns={columns} @@ -84,4 +83,41 @@ describe('Table.Expanded', () => { expect(trList[7].querySelectorAll('td').length).toBe(1); expect(trList[7].querySelectorAll('td')[0].getAttribute('colspan')).toBe('3'); }); + + it('expanded + sticky', async () => { + const columns: ColumnsType = [ + { + title: '手机号', + dataIndex: 'a', + width: 100, + fixed: 'left', + onCell: (_, index) => { + const props: React.TdHTMLAttributes = {}; + if (index === 0) props.rowSpan = 1; + if (index === 1) props.rowSpan = 2; + if (index === 2) props.rowSpan = 0; + return props; + }, + }, + Table.EXPAND_COLUMN, + { title: 'Name', dataIndex: 'c' }, + ]; + const data = [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }]; + const { container } = render( + > + columns={columns} + data={data} + sticky + expandable={{ + defaultExpandAllRows: true, + expandedRowRender: record =>
{record.key}
, + }} + />, + ); + console.log('container', container); + await act(async () => { + vi.runAllTimers(); + await Promise.resolve(); + }); + }); });