Skip to content

Commit 34f5ead

Browse files
committed
feat: columnResizable
1 parent f968c9e commit 34f5ead

16 files changed

+667
-111
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,11 @@ React.render(<Table columns={columns} data={data} />, mountNode);
129129
| title | React Node | | title of this column |
130130
| dataIndex | String | | display field of the data record |
131131
| width | String \| Number | | width of the specific proportion calculation according to the width of the columns |
132+
| minWidth | Number | | min width of the specific proportion calculation according to the width of the columns |
132133
| fixed | String \| Boolean | | this column will be fixed when table scroll horizontally: true or 'left' or 'right' |
133134
| align | String | | specify how cell content is aligned |
134135
| ellipsis | Boolean | | specify whether cell content be ellipsized |
136+
| resizable | Boolean | | column resize |
135137
| rowScope | 'row' \| 'rowgroup' | | Set scope attribute for all cells in this column |
136138
| onCell | Function(record, index) | | Set custom props per each cell. |
137139
| onHeaderCell | Function(record) | | Set custom props per each header cell. |

assets/index.less

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,39 @@
6363
}
6464
}
6565

66+
&-column-resizing {
67+
cursor: col-resize;
68+
}
69+
6670
&-cell {
6771
background: #f4f4f4;
6872

73+
&-resize-handle {
74+
position: absolute;
75+
top: 0;
76+
right: 0;
77+
width: 4px;
78+
height: 100%;
79+
cursor: col-resize;
80+
z-index: 1;
81+
background: red;
82+
}
83+
84+
&-resize-line {
85+
position: absolute;
86+
width: 4px;
87+
background: red;
88+
height: 100%;
89+
top: 0;
90+
transform: translateX(-50%);
91+
z-index: 2;
92+
}
93+
94+
95+
&-fix-right &-resize-handle {
96+
left: 0;
97+
}
98+
6999
&-fix-left,
70100
&-fix-right {
71101
z-index: 2;

docs/examples/column-resize.tsx

Lines changed: 97 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,106 @@
1-
import React from 'react';
2-
import { Resizable } from 'react-resizable';
3-
import Table from 'rc-table';
1+
import React, { useState } from 'react';
2+
import Table, { INTERNAL_HOOKS } from 'rc-table';
3+
import type { ColumnType } from 'rc-table';
44
import '../../assets/index.less';
5-
import 'react-resizable/css/styles.css';
6-
import type { ColumnType } from '@/interface';
75

8-
const ResizableTitle = props => {
9-
const { onResize, width, ...restProps } = props;
6+
const data = [
7+
{ a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' },
8+
{ a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' },
9+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '3' },
10+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '4' },
11+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '5' },
12+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '6' },
13+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '7' },
14+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '8' },
15+
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '9' },
16+
];
1017

11-
if (!width) {
12-
return <th {...restProps} />;
13-
}
18+
const Demo = () => {
19+
const [widthMap, setWidthMap] = useState<Map<React.Key, number>>(new Map());
20+
21+
const columns1 = [
22+
{ title: 'title1', dataIndex: 'aaa', key: 'aaa', width: 100 },
23+
{ title: 'title2', dataIndex: 'bbb', key: 'bbb', width: 100 },
24+
].map(i => ({
25+
...i,
26+
resizable: true,
27+
width: widthMap.get(i.key ?? i.dataIndex) ?? i.width,
28+
})) as ColumnType<any>[];
29+
30+
const columns2 = [
31+
{ title: 'title1', dataIndex: 'a', key: 'a', fixed: 'left' },
32+
{ title: 'title2', dataIndex: 'b', key: 'b', fixed: 'left' },
33+
{ title: 'title3', dataIndex: 'c', key: 'c' },
34+
{ title: 'title4', dataIndex: 'b', key: 'd' },
35+
{ title: 'title5', dataIndex: 'b', key: 'e' },
36+
{ title: 'title6', dataIndex: 'b', key: 'f' },
37+
{ title: 'title7', dataIndex: 'b', key: 'g' },
38+
{ title: 'title8', dataIndex: 'b', key: 'h' },
39+
{ title: 'title9', dataIndex: 'b', key: 'i' },
40+
{ title: 'title10', dataIndex: 'b', key: 'j' },
41+
{ title: 'title11', dataIndex: 'b', key: 'k', fixed: 'right' },
42+
{ title: 'title12', dataIndex: 'b', key: 'l', fixed: 'right' },
43+
].map(i => ({
44+
...i,
45+
resizable: true,
46+
width: widthMap.get(i.key ?? i.dataIndex) ?? 150,
47+
})) as ColumnType<any>[];
1448

1549
return (
16-
<Resizable width={width} height={0} onResize={onResize}>
17-
<th {...restProps} />
18-
</Resizable>
50+
<div>
51+
table width: 800px {'columns=[{width: 100, width: 100}]'} 情况
52+
<Table
53+
style={{ width: 800 }}
54+
scroll={{ y: 300, x: columns1.reduce((t, c) => t + (c.width as number), 0) }}
55+
columns={columns1}
56+
data={data}
57+
onColumnResizeComplete={({ columnWidths }) => {
58+
setWidthMap(prev => {
59+
const result = new Map(prev);
60+
columnWidths.forEach(i => {
61+
result.set(i.columnKey, i.width);
62+
});
63+
return result;
64+
});
65+
}}
66+
internalHooks={INTERNAL_HOOKS}
67+
getContainerWidth={(ele, width) => {
68+
// Minus border
69+
const borderWidth = getComputedStyle(
70+
ele.querySelector('.rc-table-body'),
71+
).borderInlineStartWidth;
72+
const mergedWidth = width - parseInt(borderWidth, 10);
73+
return mergedWidth;
74+
}}
75+
/>
76+
<br />
77+
大多数情况
78+
<Table
79+
style={{ width: 800 }}
80+
scroll={{ y: 300, x: columns2.reduce((t, c) => t + (c.width as number), 0) }}
81+
columns={columns2}
82+
data={data}
83+
onColumnResizeComplete={({ columnWidths }) => {
84+
setWidthMap(prev => {
85+
const result = new Map(prev);
86+
columnWidths.forEach(i => {
87+
result.set(i.columnKey, i.width);
88+
});
89+
return result;
90+
});
91+
}}
92+
internalHooks={INTERNAL_HOOKS}
93+
getContainerWidth={(ele, width) => {
94+
// Minus border
95+
const borderWidth = getComputedStyle(
96+
ele.querySelector('.rc-table-body'),
97+
).borderInlineStartWidth;
98+
const mergedWidth = width - parseInt(borderWidth, 10);
99+
return mergedWidth;
100+
}}
101+
/>
102+
</div>
19103
);
20104
};
21105

22-
interface RecordType {
23-
a: string;
24-
b?: string;
25-
c?: string;
26-
d?: number;
27-
key: string;
28-
}
29-
30-
interface DemoState {
31-
columns: ColumnType<RecordType>[];
32-
}
33-
34-
class Demo extends React.Component<{}, DemoState> {
35-
state: DemoState = {
36-
columns: [
37-
{ title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
38-
{ title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
39-
{ title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
40-
{
41-
title: 'Operations',
42-
dataIndex: '',
43-
key: 'd',
44-
render() {
45-
return <a href="#">Operations</a>;
46-
},
47-
},
48-
],
49-
};
50-
51-
components = {
52-
header: {
53-
cell: ResizableTitle,
54-
},
55-
};
56-
57-
data = [
58-
{ a: '123', key: '1' },
59-
{ a: 'cdd', b: 'edd', key: '2' },
60-
{ a: '1333', c: 'eee', d: 2, key: '3' },
61-
];
62-
63-
handleResize =
64-
index =>
65-
(e, { size }) => {
66-
this.setState(({ columns }) => {
67-
const nextColumns = [...columns];
68-
nextColumns[index] = {
69-
...nextColumns[index],
70-
width: size.width,
71-
};
72-
return { columns: nextColumns };
73-
});
74-
};
75-
76-
render() {
77-
const columns = this.state.columns.map((col, index) => ({
78-
...col,
79-
onHeaderCell: (column: ColumnType<RecordType>) =>
80-
({
81-
width: column.width,
82-
onResize: this.handleResize(index),
83-
}) as any,
84-
}));
85-
86-
return (
87-
<div>
88-
<h2>Integrate with react-resizable</h2>
89-
<Table components={this.components} columns={columns} data={this.data} />
90-
</div>
91-
);
92-
}
93-
}
94-
95106
export default Demo;

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
"react-dnd": "^2.5.4",
9999
"react-dnd-html5-backend": "^2.5.4",
100100
"react-dom": "^16.0.0",
101-
"react-resizable": "^3.0.5",
102101
"react-virtualized": "^9.12.0",
103102
"react-window": "^1.8.5",
104103
"regenerator-runtime": "^0.14.0",

src/Body/MeasureCell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import ResizeObserver from 'rc-resize-observer';
33

44
export interface MeasureCellProps {
55
columnKey: React.Key;
6-
onColumnResize: (key: React.Key, width: number) => void;
6+
onColumnWidthChange: (key: React.Key, width: number) => void;
77
}
88

9-
export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellProps) {
9+
export default function MeasureCell({ columnKey, onColumnWidthChange }: MeasureCellProps) {
1010
const cellRef = React.useRef<HTMLTableCellElement>();
1111

1212
React.useEffect(() => {
1313
if (cellRef.current) {
14-
onColumnResize(columnKey, cellRef.current.offsetWidth);
14+
onColumnWidthChange(columnKey, cellRef.current.offsetWidth);
1515
}
1616
}, []);
1717

src/Body/MeasureRow.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import MeasureCell from './MeasureCell';
44

55
export interface MeasureCellProps {
66
prefixCls: string;
7-
onColumnResize: (key: React.Key, width: number) => void;
7+
onColumnWidthChange: (key: React.Key, width: number) => void;
88
columnsKey: React.Key[];
99
}
1010

11-
export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: MeasureCellProps) {
11+
export default function MeasureRow({
12+
prefixCls,
13+
columnsKey,
14+
onColumnWidthChange,
15+
}: MeasureCellProps) {
1216
return (
1317
<tr
1418
aria-hidden="true"
@@ -18,12 +22,16 @@ export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: Me
1822
<ResizeObserver.Collection
1923
onBatchResize={infoList => {
2024
infoList.forEach(({ data: columnKey, size }) => {
21-
onColumnResize(columnKey, size.offsetWidth);
25+
onColumnWidthChange(columnKey, size.offsetWidth);
2226
});
2327
}}
2428
>
2529
{columnsKey.map(columnKey => (
26-
<MeasureCell key={columnKey} columnKey={columnKey} onColumnResize={onColumnResize} />
30+
<MeasureCell
31+
key={columnKey}
32+
columnKey={columnKey}
33+
onColumnWidthChange={onColumnWidthChange}
34+
/>
2735
))}
2836
</ResizeObserver.Collection>
2937
</tr>

src/Body/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
2525
const {
2626
prefixCls,
2727
getComponent,
28-
onColumnResize,
28+
onColumnWidthChange,
2929
flattenColumns,
3030
getRowKey,
3131
expandedKeys,
@@ -34,7 +34,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
3434
} = useContext(TableContext, [
3535
'prefixCls',
3636
'getComponent',
37-
'onColumnResize',
37+
'onColumnWidthChange',
3838
'flattenColumns',
3939
'getRowKey',
4040
'expandedKeys',
@@ -104,7 +104,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
104104
<MeasureRow
105105
prefixCls={prefixCls}
106106
columnsKey={columnsKey}
107-
onColumnResize={onColumnResize}
107+
onColumnWidthChange={onColumnWidthChange}
108108
/>
109109
)}
110110

src/Cell/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ function Cell<RecordType>(props: CellProps<RecordType>) {
116116
additionalProps = {},
117117
isSticky,
118118
} = props;
119-
120119
const cellPrefixCls = `${prefixCls}-cell`;
121120
const { supportSticky, allColumnsFixedLeft, rowHoverable } = useContext(TableContext, [
122121
'supportSticky',

src/Header/HeaderCell.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as React from 'react';
2+
import type { CellProps } from '../Cell';
3+
import Cell from '../Cell';
4+
import useCellResize from './useCellResize';
5+
import { useContext } from '@rc-component/context';
6+
import TableContext from '../context/TableContext';
7+
8+
interface HeaderCellProps<RecordType> extends CellProps<RecordType> {
9+
columnKey?: React.Key;
10+
resizable?: boolean;
11+
minWidth?: number;
12+
}
13+
14+
function HeaderCell<RecordType>({
15+
columnKey,
16+
resizable,
17+
minWidth,
18+
...cellProps
19+
}: HeaderCellProps<RecordType>) {
20+
const { supportSticky } = useContext(TableContext, ['supportSticky']);
21+
22+
const { fixRight, prefixCls } = cellProps;
23+
const isFixRight = typeof fixRight === 'number' && supportSticky;
24+
const cellPrefixCls = `${prefixCls}-cell`;
25+
26+
const resizeHandleNode = useCellResize(columnKey, isFixRight, cellPrefixCls, resizable, minWidth);
27+
28+
return <Cell {...cellProps} appendNode={resizeHandleNode} />;
29+
}
30+
31+
export default HeaderCell;

0 commit comments

Comments
 (0)