Skip to content

Commit

Permalink
feat: columnResizable
Browse files Browse the repository at this point in the history
  • Loading branch information
linxianxi committed Apr 8, 2024
1 parent 6d3796a commit 0eab6c4
Show file tree
Hide file tree
Showing 16 changed files with 610 additions and 114 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ React.render(<Table columns={columns} data={data} />, mountNode);
| sticky | boolean \| {offsetHeader?: number, offsetScroll?: number, getContainer?: () => Window \| HTMLElement } | false | stick header and scroll bar |
| summary | (data: readonly RecordType[]) => React.ReactNode | - | `summary` attribute in `table` component is used to define the summary row. |
| rowHoverable | boolean | true | Table hover interaction |
| columnResizable | boolean | false | Column resizable |

## Column Props

Expand All @@ -132,6 +133,7 @@ React.render(<Table columns={columns} data={data} />, mountNode);
| fixed | String \| Boolean | | this column will be fixed when table scroll horizontally: true or 'left' or 'right' |
| align | String | | specify how cell content is aligned |
| ellipsis | Boolean | | specify whether cell content be ellipsized |
| resizable | Boolean \| { minWidth?: number } | | column resize config |
| rowScope | 'row' \| 'rowgroup' | | Set scope attribute for all cells in this column |
| onCell | Function(record, index) | | Set custom props per each cell. |
| onHeaderCell | Function(record) | | Set custom props per each header cell. |
Expand Down
30 changes: 30 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,39 @@
}
}

&-resizing {
cursor: col-resize;
}

&-cell {
background: #f4f4f4;

&-resize-handle {
position: absolute;
top: 0;
right: 0;
width: 4px;
height: 100%;
cursor: col-resize;
z-index: 1;
background: red;
}

&-resize-line {
position: absolute;
width: 4px;
background: red;
height: 100%;
top: 0;
transform: translateX(-50%);
z-index: 2;
}


&-fix-right &-resize-handle {
left: 0;
}

&-fix-left,
&-fix-right {
z-index: 2;
Expand Down
163 changes: 78 additions & 85 deletions docs/examples/column-resize.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,88 @@
import React from 'react';
import { Resizable } from 'react-resizable';
import React, { useState } from 'react';
import Table from 'rc-table';
import '../../assets/index.less';
import 'react-resizable/css/styles.css';
import type { ColumnType } from '@/interface';

const ResizableTitle = props => {
const { onResize, width, ...restProps } = props;
const data = [
{ a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' },
{ a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '3' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '4' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '5' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '6' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '7' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '8' },
{ a: '133', c: 'edd12221 edd12221', d: 2, key: '9' },
];

if (!width) {
return <th {...restProps} />;
}
const Demo = () => {
const [widthMap, setWidthMap] = useState<Map<React.Key, number>>(new Map());

const columns1 = [
{ title: 'title1', dataIndex: 'aaa', key: 'aaa', width: 100 },
{ title: 'title2', dataIndex: 'bbb', key: 'bbb', width: 100 },
].map(i => ({
...i,
width: widthMap.get(i.key ?? i.dataIndex) ?? i.width,
}));

const columns2 = [
{ title: 'title1', dataIndex: 'a', key: 'a', fixed: 'left' },
{ title: 'title2', dataIndex: 'b', key: 'b', fixed: 'left' },
{ title: 'title3', dataIndex: 'c', key: 'c' },
{ title: 'title4', dataIndex: 'b', key: 'd' },
{ title: 'title5', dataIndex: 'b', key: 'e' },
{ title: 'title6', dataIndex: 'b', key: 'f' },
{ title: 'title7', dataIndex: 'b', key: 'g' },
{ title: 'title8', dataIndex: 'b', key: 'h' },
{ title: 'title9', dataIndex: 'b', key: 'i' },
{ title: 'title10', dataIndex: 'b', key: 'j' },
{ title: 'title11', dataIndex: 'b', key: 'k', fixed: 'right' },
{ title: 'title12', dataIndex: 'b', key: 'l', fixed: 'right' },
].map(i => ({
...i,
width: widthMap.get(i.key ?? i.dataIndex) ?? 150,
}));

return (
<Resizable width={width} height={0} onResize={onResize}>
<th {...restProps} />
</Resizable>
<div>
table width: 800px {'columns=[{width: 100, width: 100}]'} 情况
<Table
columnResizable
style={{ width: 800 }}
scroll={{ y: 300, x: columns1.reduce((t, c) => t + c.width, 0) }}
columns={columns1}
data={data}
onColumnResizeComplete={({ columnKeyWidths }) => {
setWidthMap(prev => {
const result = new Map(prev);
columnKeyWidths.forEach(i => {
result.set(i.columnKey, i.width);
});
return result;
});
}}
/>
<br />
大多数情况
<Table
columnResizable
style={{ width: 800 }}
scroll={{ y: 300, x: columns2.reduce((t, c) => t + c.width, 0) }}
columns={columns2}
data={data}
onColumnResizeComplete={({ columnKeyWidths }) => {
setWidthMap(prev => {
const result = new Map(prev);
columnKeyWidths.forEach(i => {
result.set(i.columnKey, i.width);
});
return result;
});
}}
/>
{/* <Table resizable style={{ width: 800 }} columns={columns} data={data} /> */}
</div>
);
};

interface RecordType {
a: string;
b?: string;
c?: string;
d?: number;
key: string;
}

interface DemoState {
columns: ColumnType<RecordType>[];
}

class Demo extends React.Component<{}, DemoState> {
state: DemoState = {
columns: [
{ title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
{ title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
{ title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
{
title: 'Operations',
dataIndex: '',
key: 'd',
render() {
return <a href="#">Operations</a>;
},
},
],
};

components = {
header: {
cell: ResizableTitle,
},
};

data = [
{ a: '123', key: '1' },
{ a: 'cdd', b: 'edd', key: '2' },
{ a: '1333', c: 'eee', d: 2, key: '3' },
];

handleResize =
index =>
(e, { size }) => {
this.setState(({ columns }) => {
const nextColumns = [...columns];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
return { columns: nextColumns };
});
};

render() {
const columns = this.state.columns.map((col, index) => ({
...col,
onHeaderCell: (column: ColumnType<RecordType>) =>
({
width: column.width,
onResize: this.handleResize(index),
}) as any,
}));

return (
<div>
<h2>Integrate with react-resizable</h2>
<Table components={this.components} columns={columns} data={this.data} />
</div>
);
}
}

export default Demo;
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
"react-dnd": "^2.5.4",
"react-dnd-html5-backend": "^2.5.4",
"react-dom": "^16.0.0",
"react-resizable": "^3.0.5",
"react-virtualized": "^9.12.0",
"react-window": "^1.8.5",
"regenerator-runtime": "^0.14.0",
Expand Down
6 changes: 3 additions & 3 deletions src/Body/MeasureCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import ResizeObserver from 'rc-resize-observer';

export interface MeasureCellProps {
columnKey: React.Key;
onColumnResize: (key: React.Key, width: number) => void;
onColumnWidthChange: (key: React.Key, width: number) => void;
}

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

React.useEffect(() => {
if (cellRef.current) {
onColumnResize(columnKey, cellRef.current.offsetWidth);
onColumnWidthChange(columnKey, cellRef.current.offsetWidth);
}
}, []);

Expand Down
16 changes: 12 additions & 4 deletions src/Body/MeasureRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import MeasureCell from './MeasureCell';

export interface MeasureCellProps {
prefixCls: string;
onColumnResize: (key: React.Key, width: number) => void;
onColumnWidthChange: (key: React.Key, width: number) => void;
columnsKey: React.Key[];
}

export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: MeasureCellProps) {
export default function MeasureRow({
prefixCls,
columnsKey,
onColumnWidthChange,
}: MeasureCellProps) {
return (
<tr
aria-hidden="true"
Expand All @@ -18,12 +22,16 @@ export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: Me
<ResizeObserver.Collection
onBatchResize={infoList => {
infoList.forEach(({ data: columnKey, size }) => {
onColumnResize(columnKey, size.offsetWidth);
onColumnWidthChange(columnKey, size.offsetWidth);
});
}}
>
{columnsKey.map(columnKey => (
<MeasureCell key={columnKey} columnKey={columnKey} onColumnResize={onColumnResize} />
<MeasureCell
key={columnKey}
columnKey={columnKey}
onColumnWidthChange={onColumnWidthChange}
/>
))}
</ResizeObserver.Collection>
</tr>
Expand Down
6 changes: 3 additions & 3 deletions src/Body/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
const {
prefixCls,
getComponent,
onColumnResize,
onColumnWidthChange,
flattenColumns,
getRowKey,
expandedKeys,
Expand All @@ -34,7 +34,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
} = useContext(TableContext, [
'prefixCls',
'getComponent',
'onColumnResize',
'onColumnWidthChange',
'flattenColumns',
'getRowKey',
'expandedKeys',
Expand Down Expand Up @@ -104,7 +104,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
<MeasureRow
prefixCls={prefixCls}
columnsKey={columnsKey}
onColumnResize={onColumnResize}
onColumnWidthChange={onColumnWidthChange}
/>
)}

Expand Down
13 changes: 12 additions & 1 deletion src/Cell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
import useCellRender from './useCellRender';
import useHoverState from './useHoverState';
import { useEvent } from 'rc-util';
import useCellResize from './useCellResize';

export interface CellProps<RecordType extends DefaultRecordType> {
prefixCls?: string;
Expand Down Expand Up @@ -53,6 +54,10 @@ export interface CellProps<RecordType extends DefaultRecordType> {
rowType?: 'header' | 'body' | 'footer';

isSticky?: boolean;

columnKey?: React.Key;

resizable?: boolean | { minWidth?: number };
}

const getTitleFromCellRenderChildren = ({
Expand Down Expand Up @@ -115,8 +120,10 @@ function Cell<RecordType>(props: CellProps<RecordType>) {
appendNode,
additionalProps = {},
isSticky,
} = props;

columnKey,
resizable,
} = props;
const cellPrefixCls = `${prefixCls}-cell`;
const { supportSticky, allColumnsFixedLeft, rowHoverable } = useContext(TableContext, [
'supportSticky',
Expand Down Expand Up @@ -148,6 +155,9 @@ function Cell<RecordType>(props: CellProps<RecordType>) {
fixedStyle.right = fixRight as number;
}

// ====================== Resize =======================
const resizeHandle = useCellResize(columnKey, isFixRight, cellPrefixCls, resizable);

// ================ RowSpan & ColSpan =================
const mergedColSpan = legacyCellProps?.colSpan ?? additionalProps.colSpan ?? colSpan ?? 1;
const mergedRowSpan = legacyCellProps?.rowSpan ?? additionalProps.rowSpan ?? rowSpan ?? 1;
Expand Down Expand Up @@ -253,6 +263,7 @@ function Cell<RecordType>(props: CellProps<RecordType>) {
>
{appendNode}
{mergedChildNode}
{rowType === 'header' && resizeHandle}
</Component>
);
}
Expand Down
Loading

0 comments on commit 0eab6c4

Please sign in to comment.