antd Table 可自定义伸缩列的hook

1,627 阅读1分钟
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import type { ColumnType } from 'antd/lib/table/interface';
import { Resizable } from 'react-resizable';
import classnames from 'classnames';
import './index.less';

const ResizeableTitle = (props: {
  [x: string]: any;
  onResize: any;
  width: number;
}) => {
  const { onResize, width, ...restProps } = props;
  // 添加偏移量
  const [offset, setOffset] = useState(0);

  // 设置了列宽就改为可拖拽状态,可以增加其他属性配置
  if (!width) {
    return <th {...restProps} />;
  }

  return (
    <Resizable
      width={width + offset}
      height={0}
      handle={
        <span
          // 有偏移量显示竖线
          className={classnames(['react-resizable-handle', offset && 'active'])}
          // 拖拽层偏移
          style={{ transform: `translateX(${offset}px)` }}
          onClick={(e) => {
            e.stopPropagation();
            e.preventDefault();
          }}
        />
      }
      // 拖拽事件实时更新
      onResize={(e: Event, { size }: {size: {width: number}}) => {
        // 只更新偏移量,数据列表其实并没有伸缩
        setOffset(size.width - width);
      }}
      // 拖拽结束更新
      onResizeStop={(...argu: any) => {
        // 拖拽结束以后偏移量归零
        setOffset(0);
        onResize(...argu);
      }}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} />
    </Resizable>
  );
};

const components = {
  header: {
    cell: ResizeableTitle,
  },
};

/**

   * @param {column[]} 原colunm数组   * @param {void} 修改列宽后的回调通知变化的信息,用于缓存列信息等操作
 **/const useResizeCell = (initColumn: any, func: ({ dataIndex, width }: {dataIndex: string; width: number}) => void) => {
  const [columns, setColumns] = useState<ColumnType<Record<string, any>>[]>(initColumn);
  useEffect(() => {
    setColumns(initColumn);
  }, [initColumn]);
  const handleResize = useCallback(
    (index: number, dataIndex) => (e: Event, { size }: {size: {width: number}}) => {
      setColumns((prev) => {
        const nextColumns = [...prev];
        nextColumns[index] = {
          ...nextColumns[index],
          width: size.width,
        };
        if (func) {
          func({ dataIndex, width: size.width });
        }
        return nextColumns;
      });
    },
    [func],
  );

  const changeColumns = useMemo(() => {
    return columns.map((col, index: number) => ({
      ...col,
      onHeaderCell: (column: {
        width: number;
        canResize: boolean;
        dataIndex: 'string';
      }) => ({
        width: column.width,
        canResize: column.canResize,
        onResize: handleResize(index, column.dataIndex),
      }),
    }));
  }, [columns, handleResize]);

  return [components, changeColumns];
};

export default useResizeCell;


.react-resizable {  position: relative;  background-clip: padding-box;}.react-resizable-handle {  position: absolute;  width: 10px;  height: 100%;  bottom: 0;  right: -5px;  cursor: col-resize;  z-index: 1;}.react-resizable-handle.active::before {  content: '';  position: absolute;  left: 50%;  top: 0;  bottom: 0;  border-right: 3px solid #1885f2 !important;}