Skip to content

Commit bd18ca4

Browse files
authored
feat: Table support expandedRowOffset by V5 (#1283)
* feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: 迁移 * feat: 同步
1 parent 17fa387 commit bd18ca4

File tree

12 files changed

+264
-18
lines changed

12 files changed

+264
-18
lines changed

docs/demo/expandedSticky.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: expandedSticky
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/expandedSticky.tsx"></code>

docs/examples/expandedSticky.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, { useState } from 'react';
2+
import type { ColumnType } from 'rc-table';
3+
import Table from 'rc-table';
4+
import '../../assets/index.less';
5+
6+
const Demo = () => {
7+
const [expandedRowKeys, setExpandedRowKeys] = useState<readonly React.Key[]>([]);
8+
9+
const data = [
10+
{ key: 'a', a: '小二', d: '文零西路' },
11+
{ key: 'b', a: '张三', d: '文一西路' },
12+
{ key: 'c', a: '张三', d: '文二西路' },
13+
];
14+
15+
const columns: ColumnType<Record<string, any>>[] = [
16+
{
17+
title: '手机号',
18+
dataIndex: 'a',
19+
width: 100,
20+
onCell: (_, index) => {
21+
if (index === 1) {
22+
return {
23+
rowSpan: 2,
24+
};
25+
} else if (index === 2) {
26+
return {
27+
rowSpan: 0,
28+
};
29+
}
30+
},
31+
},
32+
{ title: 'key', dataIndex: 'key2', width: 100 },
33+
Table.EXPAND_COLUMN,
34+
{ title: 'key', dataIndex: 'key' },
35+
{ title: 'Address', fixed: 'right', dataIndex: 'd', width: 200 },
36+
];
37+
38+
return (
39+
<div style={{ height: 10000 }}>
40+
<h2>expanded & sticky</h2>
41+
<Table<Record<string, any>>
42+
rowKey="key"
43+
sticky
44+
scroll={{ x: 2000 }}
45+
columns={columns}
46+
data={data}
47+
expandable={{
48+
expandedRowOffset: 2,
49+
expandedRowKeys,
50+
onExpandedRowsChange: keys => setExpandedRowKeys(keys),
51+
expandedRowRender: record => <p style={{ margin: 0 }}>expandedRowRender: {record.key}</p>,
52+
}}
53+
className="table"
54+
/>
55+
</div>
56+
);
57+
};
58+
59+
export default Demo;

src/Body/BodyRow.tsx

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ export interface BodyRowProps<RecordType> {
1919
scopeCellComponent: CustomizeComponent;
2020
indent?: number;
2121
rowKey: React.Key;
22+
rowKeys: React.Key[];
23+
24+
// Expanded Row
25+
expandedRowInfo?: {
26+
offset: number;
27+
colSpan: number;
28+
sticky: number;
29+
};
2230
}
2331

2432
// ==================================================================================
@@ -30,6 +38,8 @@ export function getCellProps<RecordType>(
3038
colIndex: number,
3139
indent: number,
3240
index: number,
41+
rowKeys: React.Key[] = [],
42+
expandedRowOffset = 0,
3343
) {
3444
const {
3545
record,
@@ -43,6 +53,8 @@ export function getCellProps<RecordType>(
4353
expanded,
4454
hasNestChildren,
4555
onTriggerExpand,
56+
expandable,
57+
expandedKeys,
4658
} = rowInfo;
4759

4860
const key = columnsKey[colIndex];
@@ -68,16 +80,32 @@ export function getCellProps<RecordType>(
6880
);
6981
}
7082

71-
let additionalCellProps: React.TdHTMLAttributes<HTMLElement>;
72-
if (column.onCell) {
73-
additionalCellProps = column.onCell(record, index);
83+
const additionalCellProps = column.onCell?.(record, index) || {};
84+
85+
// Expandable row has offset
86+
if (expandedRowOffset) {
87+
const { rowSpan = 1 } = additionalCellProps;
88+
89+
// For expandable row with rowSpan,
90+
// We should increase the rowSpan if the row is expanded
91+
if (expandable && rowSpan && colIndex < expandedRowOffset) {
92+
let currentRowSpan = rowSpan;
93+
94+
for (let i = index; i < index + rowSpan; i += 1) {
95+
const rowKey = rowKeys[i];
96+
if (expandedKeys.has(rowKey)) {
97+
currentRowSpan += 1;
98+
}
99+
}
100+
additionalCellProps.rowSpan = currentRowSpan;
101+
}
74102
}
75103

76104
return {
77105
key,
78106
fixedInfo,
79107
appendCellNode,
80-
additionalCellProps: additionalCellProps || {},
108+
additionalCellProps: additionalCellProps,
81109
};
82110
}
83111

@@ -98,10 +126,12 @@ function BodyRow<RecordType extends { children?: readonly RecordType[] }>(
98126
index,
99127
renderIndex,
100128
rowKey,
129+
rowKeys,
101130
indent = 0,
102131
rowComponent: RowComponent,
103132
cellComponent,
104133
scopeCellComponent,
134+
expandedRowInfo,
105135
} = props;
106136
const rowInfo = useRowInfo(record, rowKey, index, indent);
107137
const {
@@ -153,6 +183,8 @@ function BodyRow<RecordType extends { children?: readonly RecordType[] }>(
153183
colIndex,
154184
indent,
155185
index,
186+
rowKeys,
187+
expandedRowInfo?.offset,
156188
);
157189

158190
return (
@@ -195,7 +227,8 @@ function BodyRow<RecordType extends { children?: readonly RecordType[] }>(
195227
prefixCls={prefixCls}
196228
component={RowComponent}
197229
cellComponent={cellComponent}
198-
colSpan={flattenColumns.length}
230+
colSpan={expandedRowInfo ? expandedRowInfo.colSpan : flattenColumns.length}
231+
stickyOffset={expandedRowInfo?.sticky}
199232
isEmpty={false}
200233
>
201234
{expandContent}

src/Body/ExpandedRow.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface ExpandedRowProps {
1414
children: React.ReactNode;
1515
colSpan: number;
1616
isEmpty: boolean;
17+
stickyOffset?: number;
1718
}
1819

1920
function ExpandedRow(props: ExpandedRowProps) {
@@ -30,6 +31,7 @@ function ExpandedRow(props: ExpandedRowProps) {
3031
expanded,
3132
colSpan,
3233
isEmpty,
34+
stickyOffset = 0,
3335
} = props;
3436

3537
const { scrollbarSize, fixHeader, fixColumn, componentWidth, horizonScroll } = useContext(
@@ -44,9 +46,9 @@ function ExpandedRow(props: ExpandedRowProps) {
4446
contentNode = (
4547
<div
4648
style={{
47-
width: componentWidth - (fixHeader && !isEmpty ? scrollbarSize : 0),
49+
width: componentWidth - stickyOffset - (fixHeader && !isEmpty ? scrollbarSize : 0),
4850
position: 'sticky',
49-
left: 0,
51+
left: stickyOffset,
5052
overflow: 'hidden',
5153
}}
5254
className={`${prefixCls}-expanded-row-fixed`}

src/Body/index.tsx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
3131
expandedKeys,
3232
childrenColumnName,
3333
emptyNode,
34+
expandedRowOffset = 0,
35+
colWidths,
3436
} = useContext(TableContext, [
3537
'prefixCls',
3638
'getComponent',
@@ -40,16 +42,42 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
4042
'expandedKeys',
4143
'childrenColumnName',
4244
'emptyNode',
45+
'expandedRowOffset',
46+
'fixedInfoList',
47+
'colWidths',
4348
]);
4449

45-
const flattenData: { record: RecordType; indent: number; index: number }[] =
46-
useFlattenRecords<RecordType>(data, childrenColumnName, expandedKeys, getRowKey);
50+
const flattenData = useFlattenRecords<RecordType>(
51+
data,
52+
childrenColumnName,
53+
expandedKeys,
54+
getRowKey,
55+
);
56+
const rowKeys = React.useMemo(() => flattenData.map(item => item.rowKey), [flattenData]);
4757

4858
// =================== Performance ====================
4959
const perfRef = React.useRef<PerfRecord>({
5060
renderWithProps: false,
5161
});
5262

63+
// ===================== Expanded =====================
64+
// `expandedRowOffset` data is same for all the rows.
65+
// Let's calc on Body side to save performance.
66+
const expandedRowInfo = React.useMemo(() => {
67+
const expandedColSpan = flattenColumns.length - expandedRowOffset;
68+
69+
let expandedStickyStart = 0;
70+
for (let i = 0; i < expandedRowOffset; i += 1) {
71+
expandedStickyStart += colWidths[i] || 0;
72+
}
73+
74+
return {
75+
offset: expandedRowOffset,
76+
colSpan: expandedColSpan,
77+
sticky: expandedStickyStart,
78+
};
79+
}, [flattenColumns.length, expandedRowOffset, colWidths]);
80+
5381
// ====================== Render ======================
5482
const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody');
5583
const trComponent = getComponent(['body', 'row'], 'tr');
@@ -59,21 +87,22 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
5987
let rows: React.ReactNode;
6088
if (data.length) {
6189
rows = flattenData.map((item, idx) => {
62-
const { record, indent, index: renderIndex } = item;
63-
64-
const key = getRowKey(record, idx);
90+
const { record, indent, index: renderIndex, rowKey } = item;
6591

6692
return (
6793
<BodyRow
68-
key={key}
69-
rowKey={key}
94+
key={rowKey}
95+
rowKey={rowKey}
96+
rowKeys={rowKeys}
7097
record={record}
7198
index={idx}
7299
renderIndex={renderIndex}
73100
rowComponent={trComponent}
74101
cellComponent={tdComponent}
75102
scopeCellComponent={thComponent}
76103
indent={indent}
104+
// Expanded row info
105+
expandedRowInfo={expandedRowInfo}
77106
/>
78107
);
79108
});

src/Table.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ function Table<RecordType extends DefaultRecordType>(
822822
expandableType,
823823
expandRowByClick: expandableConfig.expandRowByClick,
824824
expandedRowRender: expandableConfig.expandedRowRender,
825+
expandedRowOffset: expandableConfig.expandedRowOffset,
825826
onTriggerExpand,
826827
expandIconColumnIndex: expandableConfig.expandIconColumnIndex,
827828
indentSize: expandableConfig.indentSize,
@@ -832,6 +833,7 @@ function Table<RecordType extends DefaultRecordType>(
832833
columns,
833834
flattenColumns,
834835
onColumnResize,
836+
colWidths,
835837

836838
// Row
837839
hoverStartRow: startRow,
@@ -872,6 +874,7 @@ function Table<RecordType extends DefaultRecordType>(
872874
expandableType,
873875
expandableConfig.expandRowByClick,
874876
expandableConfig.expandedRowRender,
877+
expandableConfig.expandedRowOffset,
875878
onTriggerExpand,
876879
expandableConfig.expandIconColumnIndex,
877880
expandableConfig.indentSize,
@@ -881,6 +884,7 @@ function Table<RecordType extends DefaultRecordType>(
881884
columns,
882885
flattenColumns,
883886
onColumnResize,
887+
colWidths,
884888

885889
// Row
886890
startRow,

src/VirtualTable/VirtualCell.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function VirtualCell<RecordType = any>(props: VirtualCellProps<RecordType>) {
5656

5757
const { columnsOffset } = useContext(GridContext, ['columnsOffset']);
5858

59+
// TODO: support `expandableRowOffset`
5960
const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps(
6061
rowInfo,
6162
column,

src/context/TableContext.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
ColumnsType,
44
ColumnType,
55
Direction,
6+
ExpandableConfig,
67
ExpandableType,
78
ExpandedRowRender,
89
GetComponent,
@@ -56,6 +57,7 @@ export interface TableContextProps<RecordType = any> {
5657
columns: ColumnsType<RecordType>;
5758
flattenColumns: readonly ColumnType<RecordType>[];
5859
onColumnResize: (columnKey: React.Key, width: number) => void;
60+
colWidths: number[];
5961

6062
// Row
6163
hoverStartRow: number;
@@ -68,6 +70,8 @@ export interface TableContextProps<RecordType = any> {
6870
childrenColumnName: string;
6971

7072
rowHoverable?: boolean;
73+
74+
expandedRowOffset: ExpandableConfig<RecordType>['expandedRowOffset'];
7175
}
7276

7377
const TableContext = createContext<TableContextProps>();

src/hooks/useColumns/index.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ function useColumns<RecordType>(
122122
expandIcon,
123123
rowExpandable,
124124
expandIconColumnIndex,
125+
expandedRowOffset = 0,
125126
direction,
126127
expandRowByClick,
127128
columnWidth,
@@ -146,6 +147,7 @@ function useColumns<RecordType>(
146147
clientWidth: number;
147148
fixed?: FixedType;
148149
scrollWidth?: number;
150+
expandedRowOffset?: number;
149151
},
150152
transformColumns: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>,
151153
): [
@@ -236,7 +238,16 @@ function useColumns<RecordType>(
236238
},
237239
};
238240

239-
return cloneColumns.map(col => (col === EXPAND_COLUMN ? expandColumn : col));
241+
return cloneColumns.map((col, index) => {
242+
const column = col === EXPAND_COLUMN ? expandColumn : col;
243+
if (index < expandedRowOffset) {
244+
return {
245+
...column,
246+
fixed: column.fixed || 'left',
247+
};
248+
}
249+
return column;
250+
});
240251
}
241252

242253
if (process.env.NODE_ENV !== 'production' && baseColumns.includes(EXPAND_COLUMN)) {
@@ -245,7 +256,7 @@ function useColumns<RecordType>(
245256

246257
return baseColumns.filter(col => col !== EXPAND_COLUMN);
247258
// eslint-disable-next-line react-hooks/exhaustive-deps
248-
}, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction]);
259+
}, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction, expandedRowOffset]);
249260

250261
// ========================= Transform ========================
251262
const mergedColumns = React.useMemo(() => {

0 commit comments

Comments
 (0)