Requirement background: It is necessary to realize the column expansion and contraction of the Antd Table component, and the width can be dragged
In the version of Antd 3.x, the demo example of column scaling is reserved:
Scalable columns can be implemented with the help of react-resizable
.
# npm install npm install react-resizable --save # yarn installation yarn add react-resizable
Refer to the official Demo to encapsulate a ResizableTable component:
import {<!-- --> Table } from 'antd'; import type {<!-- --> ColumnsType } from 'antd/lib/table'; import {<!-- --> useEffect,useState } from 'react'; import {<!-- --> Resizable } from 'react-resizable'; import styles from './resizableTable.less'; /** * Solve the problem of dragging after releasing the mouse * Reference idea: When clicking and dragging, use the browser API Selection.removeAllRanges to clear the text that was originally selected by mistake. */ const clearSelection = () => {<!-- --> if (window.getSelection) {<!-- --> const selection = window.getSelection(); if (selection) {<!-- --> if (selection.empty) {<!-- --> // Chrome selection.empty(); } else if (selection.removeAllRanges) {<!-- --> // Firefox selection.removeAllRanges(); } } } else if (document.selection & amp; & amp; document.selection.empty) {<!-- --> //IE document.selection.empty(); } }; export const ResizableTitle = (props: any) => {<!-- --> const {<!-- --> onResize, width, minWidth, maxWidth, ...restProps } = props; // Columns without original width do not support scaling; there will be a sudden jump from the adaptive width to the dragging position; you can also add parameters by yourself, such as disableResize if (!width) {<!-- --> return <th {<!-- -->...restProps} />; } const minConstraints: [number, number] | undefined = minWidth ? [minWidth, -Infinity] : undefined; const maxConstraints: [number, number] | undefined = maxWidth ? [maxWidth, + Infinity] : undefined; return ( <Resizable width={<!-- -->width} height={<!-- -->0} // No need to adjust height, set to 0 minConstraints={<!-- -->minConstraints} maxConstraints={<!-- -->maxConstraints} handle={<!-- --> <span className="react-resizable-handle" onClick={<!-- -->(e) => {<!-- --> // Stop bubbling e.stopPropagation(); }} /> } onResize={<!-- -->onResize} draggableOpts={<!-- -->{<!-- --> enableUserSelectHack: false, onMouseDown: () => {<!-- --> // Handle that in Windows Chrome and Edge, you can still drag when you release the mouse clearSelection(); }, }} > <th {<!-- -->...restProps} /> </Resizable> ); }; interface DataType {<!-- --> name: {<!-- --> first: string; last: string; }; gender: string; email: string; login: {<!-- --> uuid: string; }; } const columnsData: ColumnsType<DataType> = [ {<!-- --> title: 'Name', dataIndex: 'name', sorter: true, render: (name) => `${<!-- -->name.first} ${<!-- -->name.last}`, width: '20%', }, {<!-- --> title: 'Gender', dataIndex: 'gender', filters: [ {<!-- --> text: 'Male', value: 'male' }, {<!-- --> text: 'Female', value: 'female' }, ], width: '20%', }, {<!-- --> title: 'Email', dataIndex: 'email', }, ]; const ResizableTable = () => {<!-- --> const curColumns: ColumnsType<DataType> = columnsData; // It can be passed in through props, here we use constants as an example const [column, setColumns] = useState<ColumnsType<any>>([]); //Update table columns when dragging const handleResize = (index: number) => {<!-- --> return (_e: any, {<!-- --> size }: any) => {<!-- --> const newCols = [...column]; newCols[index] = {<!-- --> ...newCols[index], width: size.width || '100%', }; setColumns(newCols); }; }; const mergeColumns = column.map((col, index) => ({<!-- --> ...col, onHeaderCell: (column: any) => ({<!-- --> width: column.width 100, //Add minWidth, maxWidth to each column as props of ResizableTitle minWidth: 50, // maxWidth: 1000, onResize: handleResize(index), }), })); useEffect(() => {<!-- --> console.log('change', curColumns); if (curColumns) {<!-- --> setColumns(curColumns); } }, [curColumns]); return ( <div className={<!-- -->styles.resizeTable}> <Table size="small" components={<!-- -->{<!-- --> header: {<!-- --> cell: ResizableTitle, }, }} columns={<!-- -->mergeColumns} dataSource={<!-- -->[]} /> </div> ); }; export default ResizableTable;
The style resizableTable.less
must be introduced:
.resizeTable {<!-- --> :global {<!-- --> .react-resizable {<!-- --> position: relative; background-clip: padding-box; } .react-resizable-handle {<!-- --> position: absolute; width: 10px; height: 100%; bottom: 0; right: -5px; cursor: col-resize; background-image: none; z-index: 1; } .ant-table-filter-column, .ant-table-column-sorters {<!-- --> display: flex; /* co1umn from top to bottom */ align-items: center; /* center represents the horizontal direction */ justify-content: space-around; min-width: 70px; } .ant-table-thead>tr>th .ant-table-column-sorter {<!-- --> // margin-top: -21px; display: table-cell; vertical-align: middle; } } }
The width of a column must be left unset and adaptive. Otherwise the effect will be wrong.
But after I use this plug-in, it still doesn’t work well. There are always some bugs. For example, if you drag a column without setting the width, the entire expansion will be deformed; and if there are a lot of columns, the adaptive column effect is not ideal.
All this solution works but not very well.
You can refer to: https://juejin.cn/post/7182423243553734717
Follow-up solutions:
When checking the information, I saw that a big guy has packaged a telescopic hook use-antd-resizable-header
, which is convenient and simple to use. Then the project was introduced.
https://github.com/hemengke1997/use-antd-resizable-header
pnpm add @minko-fe/use-antd-resizable-header
Example of introducing encapsulated components:
import {<!-- --> Table } from 'antd'; import {<!-- --> useAntdResizableHeader } from '@minko-fe/use-antd-resizable-header'; import '@minko-fe/use-antd-resizable-header/dist/style.css'; /** Custom function */ import {<!-- --> isLocaleEn } from '@/utils/commont_rely'; /** type class declaration */ import type {<!-- --> IProps } from '..'; // Self-encapsulated table propsType, for reference only /** Custom style */ import './style.less'; /** =================================== * @name: table component of scalable columns * Note: At least one column cannot be dragged (width is not set), please keep at least one column adaptive in width. *======================================*/ interface ResizableTableProps extends IProps {<!-- --> //Special configuration defaultWidth?: number; // Set the minimum width of the column that cannot be dragged. Default 120 minConstraints?: number; // Drag minimum width, default 60 maxConstraints?: number; // The maximum drag width is 800 by default and can be set to infinite } export default function ResizableTable(props: ResizableTableProps) {<!-- --> const {<!-- --> title, defaultWidth, minConstraints, maxConstraints } = props; const columns = props?.columns || []; // Colums passed by the component const {<!-- --> components, resizableColumns, tableWidth } = useAntdResizableHeader({<!-- --> columns, defaultWidth: defaultWidth || 120, minConstraints: minConstraints || 60, maxConstraints: maxConstraints || 800, }); return ( <div className="resizableTable"> <Table title={<!-- -->title} size="small" dataSource={<!-- -->data} //data passed by the component columns={<!-- -->resizableColumns} components={<!-- -->components} scroll={<!-- -->{<!-- --> x: tableWidth }} /> </div> ); }
It is easy to use and has ideal results. It is recommended to use this plug-in.
11.29 Update
Upgrade to v2.9.0
, no need to import css files
Note: The name of the dependent package has also changed
pnpm add use-antd-resizable-header