/* eslint-disable max-len */ /* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react/destructuring-assignment */ /* eslint-disable max-lines-per-function */ import React, { useState, useEffect, useMemo } from 'react'; import { merge, get, find, noop } from 'lodash'; import { addClass, removeClass } from '@douyinfe/semi-foundation/utils/classnames'; import { strings, numbers } from '@douyinfe/semi-foundation/table/constants'; import { mergeColumns, assignColumnKeys, findColumn, withResizeWidth } from '@douyinfe/semi-foundation/table/utils'; import Table from './Table'; import { cloneDeep } from './utils'; import getColumns from './getColumns'; import ResizableHeaderCell from './ResizableHeaderCell'; import { TableProps, ColumnProps } from './interface'; const ResizableTable = (props: TableProps = {}, ref: React.MutableRefObject> | ((instance: Table) => void)) => { const { components: propComponents, columns: propColumns, resizable, ...restProps } = props; const childrenColumnName = 'children'; const onResize = get(resizable, 'onResize', noop); const onResizeStart = get(resizable, 'onResizeStart', noop); const onResizeStop = get(resizable, 'onResizeStop', noop); /** * 此处关于 columns 有三个存储 * * 1. rawColumns 是根据 props.columns 或者 props.children 解析出来的原始 columns * 2. newColumns 是 rawColumns 的深拷贝,同时根据 props.expandedRowRender、props.hideExpandedColumn 和 props.rowSelection * 这三个参数加入了【选择列】以及【展开列】 * 3. columns 是当前组件中存储的 state,一般情况下与 newColumns 相等,但是会保存列当前伸缩的宽度 */ /** * There are three stores for columns here * * 1. rawColumns are the original columns parsed according to props.columns or props.children * 2. newColumns is a deep copy of rawColumns, based on props.expandedRowRender, props.hideExpandedColumn and props.rowSelection * These three parameters have been added [Select Column] and [Expand Column] * 3. columns is the state stored in the current component, which is generally equal to newColumns, but it will save the current stretched width of the column */ const parsedColumns = Array.isArray(propColumns) && propColumns.length ? propColumns : getColumns(props.children); const rawColumns = assignColumnKeys(cloneDeep(parsedColumns), childrenColumnName); const newColumns = assignColumnKeys(cloneDeep(parsedColumns), childrenColumnName); if ( typeof props.expandedRowRender === 'function' && !props.hideExpandedColumn && !find(rawColumns, item => item.key === strings.DEFAULT_KEY_COLUMN_EXPAND) ) { newColumns.unshift({ key: strings.DEFAULT_KEY_COLUMN_EXPAND, width: numbers.DEFAULT_WIDTH_COLUMN_EXPAND }); } if (props.rowSelection && !find(rawColumns, item => item.key === strings.DEFAULT_KEY_COLUMN_SELECTION)) { newColumns.unshift({ width: get(props, 'rowSelection.width', numbers.DEFAULT_WIDTH_COLUMN_SELECTION), key: strings.DEFAULT_KEY_COLUMN_SELECTION, }); } const [columns, setColumns] = useState(newColumns); useEffect(() => { // If there is a resize value, the width does not use the default value fix#1072 const _newColumns = withResizeWidth(columns, newColumns); setColumns(mergeColumns(columns, _newColumns)); }, [propColumns, props.expandedRowRender, props.hideExpandedColumn, props.rowSelection]); const components = useMemo(() => merge( { header: { cell: ResizableHeaderCell, }, }, propComponents ), [propComponents]); const handlerClassName = get(resizable, 'handlerClassName', 'resizing'); const handleResize = (column: ColumnProps) => (e: React.MouseEvent, { size }: { size: { width: number } }) => { const nextColumns = cloneDeep(columns); const curColumn: ColumnProps = findColumn(nextColumns, column, childrenColumnName); let nextColumn = { ...curColumn, width: size.width, }; const customProps = onResize(nextColumn) || {}; nextColumn = { ...nextColumn, ...customProps, }; Object.assign(curColumn, nextColumn); setColumns(nextColumns); }; const handleResizeStart = (column: ColumnProps) => (e: React.MouseEvent) => { const nextColumns = cloneDeep(columns); const curColumn: ColumnProps = findColumn(nextColumns, column, childrenColumnName); let nextColumn: ColumnProps = { ...curColumn, className: addClass(curColumn.className, handlerClassName), }; const customProps = onResizeStart(nextColumn) || {}; nextColumn = { ...nextColumn, ...customProps, }; Object.assign(curColumn, nextColumn); setColumns(nextColumns); }; const handleResizeStop = (column: ColumnProps) => (e: React.MouseEvent) => { const nextColumns = cloneDeep(columns); const curColumn: ColumnProps = findColumn(nextColumns, column, childrenColumnName); let nextColumn = { ...curColumn, className: removeClass(curColumn.className, handlerClassName), }; const customProps = onResizeStop(nextColumn) || {}; nextColumn = { ...nextColumn, ...customProps, }; Object.assign(curColumn, nextColumn); setColumns(nextColumns); }; const resizableRender = (col: ColumnProps, index: number, level = 0) => ({ ...col, onHeaderCell: (column: ColumnProps) => ({ width: column.width, onResize: handleResize(column), onResizeStart: handleResizeStart(column), onResizeStop: handleResizeStop(column), }), }); const assignResizableRender = (columns: ColumnProps[] = [], level = 0) => (Array.isArray(columns) && columns.length ? columns.map((col, index) => { Object.assign(col, resizableRender(col, index, level)); const children = col[childrenColumnName]; if (Array.isArray(children) && children.length) { col[childrenColumnName] = assignResizableRender(children, level + 1); } return col; }) : []); const finalColumns = useMemo(() => assignResizableRender(columns), [columns]); return ; }; export default React.forwardRef, TableProps>(ResizableTable);