项目简介
React Resizable的核心是一个名为<Resizable>
的React组件,它通过监听鼠标或触摸事件,使用户能够直接拖动边框以改变元素的尺寸。这个组件不仅提供了一种直观的方式来改变元素大小,还支持回调函数,以便在尺寸变化时更新状态和重新渲染组件。
拖动前.png
拖动后.png
自定义 useTableResizable hooks
import React, { useState, useCallback, useMemo } from 'react';
import { ColumnProps } from '~/Interface';
interface Props {
columns:any[];
}
export const useTableResizable = (props: Props): [ColumnProps[]] => {
const { columns } = props;
const [newColumn, setnewColumn] = useState(columns);
const setNewColumnCellMemoized = (column: any[], indexarray: number[]) => {
column.map((col, index) => {
//使用useMemo钩子创建一个记忆化的索引路径数组parent,它包含了从根到当前列的索引。这个数组用于在调整列宽时确定列的位置。
const parent = useMemo(() => [...indexarray, index], [indexarray, index]);
col.onHeaderCell = useCallback((column: any) => ({
width: column.width, //列的宽度
indexarry: withIndex, //索引数组
onResize: handleResize(index, withIndex), // handleResize 函数的回调,它接收当前索引和索引数组作为参数
}),
[index, withIndex]
);
//递归处理子列
if (col.children && col.children.length) {
setNewColumnCellMemoized(col.children, withIndex)
}
});
};
setNewColumnCellMemoized(newColumn, []);
//useCallback钩子创建的,以确保这个回调函数在组件的生命周期内保持一致性,避免不必要的重新渲染。
const handleResize = useCallback((index: number, indexarray: number[]) => (e: any, a: { size: any }) => {
let nextColumns = JSON.parse(JSON.stringify(newColumn));
const { size } = a;
const width = size ? Math.max(140, size.width) : 140;
if (indexarray.length == 2) {
//设置嵌套列中的子列的宽度
nextColumns[indexarray[0]].children[indexarray[1]].width = width;
// 调用mergeColumns函数,将调整后的列数组nextColumns与原始列数组columns合并。
let mergeRes = mergeColumns(nextColumns, columns);
//使用setnewColumn函数(可能是一个通过useState钩子创建的设置器函数)来更新状态,将合并后的列数组设置为新的列定义。
setnewColumn(mergeRes);
}
},[newColumn, columns]) //这是useCallback钩子的依赖数组,它确保当newColumn或columns发生变化时,handleResize函数会被重新创建。
const mergeColumns = (columnsWithoutRender: any[], columnsWithRender: any[]): any[] => {
// 创建一个映射,用于快速定位具有render函数的子column
const renderMap = new Map<string, (text: any) => JSX.Element>();
columnsWithRender.forEach((column) => {
if (column.children) {
column.children.forEach((subColumn: any) => {
if (subColumn.render) {
// 如果子列具有 render 函数,则将其 dataIndex 和 render 函数存储到 renderMap 中。
renderMap.set(subColumn.dataIndex, subColumn.render);
}
});
}
});
// 合并column,如果子column没有render函数,则从映射中获取
const mergedColumns = columnsWithoutRender.map((column) => ({
...column,
children: column.children ? column.children.map((subColumn: any) => ({
...subColumn,
// 为子列设置 render 属性。如果 renderMap 中存在对应 dataIndex 的渲染函数,则使用该函数;否则,使用子列自己的 render 函数(如果有的话)。
render: renderMap.get(subColumn.dataIndex) || subColumn.render,
})) : [],
}));
return mergedColumns;
}
return [newColumn]
}
TableReszableTitle.tsx
/** 可伸缩列 多表头 */
import * as React from 'react';
import { Resizable } from "react-resizable"
import './style.less';
interface Props {
onResize: () => void;
width: any;
}
export default (props: Props): JSX.Element => {
const { onResize, width, ...restProps } = props
if (width == undefined) {
return <th {...restProps} />
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
}}
/>
}
onResize={onResize}
draggableOpts={{ nableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
)
}
TableReszableTitle.less
: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;
}
}
使用方法
import * as React from 'react';
import { Table } from "antd";
import { IOwnColumnProps } from '@yitu/react-datatable/lib/types';
import { useTableResizable } from '~/Hooks';
import { TableReszableTitle } from '~/component/TableReszableTitle';
export default (): JSX.Element => {
const columns: IOwnColumnProps[] = [
{
title: "人口",
key: "人口",
children: [
{
title: "小计",
dataIndex: "Population",
width: 100,
ellipsis: true
},
{
title: "农业",
dataIndex: "CityPopulation",
width: 100,
ellipsis: true
},
{
title: "非农",
dataIndex: "VillagePopulation",
width: 100,
ellipsis: true
}
]
},
{
title: "地区",
dataIndex:"area"
}
]
const [ newColumn ]= useTableResizable({ columns });
return (
<Table
columns={newColumn}
id="data-dashboard-list-wrapper"
className="data-dashboard-list-wrapper"
components={{
header: {
cell: TableReszableTitle
}
}}
/>
)
}