import React from 'react' ; import ResizableHeader from './ResizableHeader' ; import { option } from './config' ; import isEmpty from 'lodash.isempty' ; import useThrottleEffect from './utils/useThrottleEffect' ; import useDebounceFn from './utils/useDebounceFn' ; import { depthFirstSearch } from './utils' ; import useSafeState from './utils/useSafeState' ; import useLocalColumns from './utils/useLocalColumns' ; import { GETKEY } from './utils/useGetDataIndexColumns' ; import useMemoizedFn from './utils/useMemoizedFn' ; export type ColumnsState = { width : number ; } ; export type ColumnsStateType = { param localStorage param sessionStorage persistenceType ? : 'localStorage' | 'sessionStorage' ; /** 持久化的key，用于存储到 storage 中 */ persistenceKey ? : string ; } ; export type useTableResizableHeaderProps < ColumnType > = { columns : ColumnType [ ] | undefined ; description defaultWidth ? : number ; description minConstraints ? : number ; description maxConstraints ? : number ; description cache ? : boolean ; description columnsState ? : ColumnsStateType ; } ; type Width = number | string ; export type ColumnOriginType < T > = { width ? : Width ; dataIndex ? : React . Key ; key ? : React . Key ; title ? : React . ReactNode | string ; children ? : T [ ] ; resizable ? : boolean ; ellipsis ? : any ; } ; type CacheType = { width ? : Width ; index : number ; } ; const WIDTH = 120 ; function useAntdResizableHeader < ColumnType extends ColumnOriginType < ColumnType > = Record < string , any > > ( props ) { const { columns : columnsProp , defaultWidth = WIDTH , minConstraints = WIDTH / 2 , maxConstraints = Infinity , cache = true , columnsState , } = props ; const widthCache = React . useRef < Map < React . Key , CacheType > > ( new Map ( ) ) ; const [ resizableColumns , setResizableColumns ] = useSafeState < ColumnType [ ] > ( [ ] ) ; const { localColumns : columns , resetLocalColumns } = useLocalColumns ( { columnsState , columns : columnsProp , resizableColumns , } ) ; const [ tableWidth , setTableWidth ] = useSafeState < number > ( ) ; const [ triggerRender , forceRender ] = React . useReducer ( ( s ) => s + 1 , 0 ) ; const resetColumns = useMemoizedFn ( ( ) => { widthCache . current = new Map ( ) ; resetLocalColumns ( ) ; } ) ; const onMount = React . useCallback ( ( id ) => ( width ? ) => { if ( width ) { setResizableColumns ( ( t ) => { const nextColumns = depthFirstSearch ( t , ( col ) => col [ GETKEY ] === id , width ) ; const kvMap = new Map < React . Key , CacheType > ( ) ; function dig ( cols ) { cols . forEach ( ( col , i ) => { const key = col [ GETKEY ] ; kvMap . set ( key ?? '' , { width : col ?. width , index : i } ) ; if ( col ?. children ) { dig ( col . children ) ; } } ) ; } dig ( nextColumns ) ; widthCache . current = kvMap ; return nextColumns ; } ) ; } } , [ setResizableColumns ] ) ; const onResize = React . useMemo ( ( ) => onMount , [ onMount ] ) ; const getColumns = useMemoizedFn ( ( list ) => { const trulyColumns = list ?. filter ( ( item ) => ! isEmpty ( item ) ) ; const c = trulyColumns . map ( ( col ) => { return { ... col , children : col ?. children ?. length ? getColumns ( col . children ) : undefined , onHeaderCell : ( column ) => { return { title : typeof col ?. title === 'string' ? col ?. title : '' , width : cache ? widthCache . current ?. get ( column [ GETKEY ] ?? '' ) ?. width || column ?. width : column ?. width , resizable : column . resizable , onMount : onMount ( column ?. [ GETKEY ] ) , onResize : onResize ( column ?. [ GETKEY ] ) , minWidth : minConstraints , maxWidth : maxConstraints , triggerRender , } ; } , width : cache ? widthCache . current ?. get ( col [ GETKEY ] ?? '' ) ?. width || col ?. width : col ?. width , ellipsis : typeof col . ellipsis !== 'undefined' ? col . ellipsis : true , [ GETKEY ] : col [ GETKEY ] || col . key , } ; } ) as ColumnType [ ] ; return c ; } ) ; React . useEffect ( ( ) => { if ( columns ) { const c = getColumns ( columns ) ; setResizableColumns ( c ) ; } } , [ columns , getColumns , setResizableColumns ] ) ; useThrottleEffect ( ( ) => { const t = getColumns ( resizableColumns ) ; setResizableColumns ( t ) ; } , [ triggerRender ] , option ) ; React . useEffect ( ( ) => { let width = 0 ; ( function loop ( cls ) { for ( let i = 0 ; i < cls . length ; i ++ ) { width += Number ( cls [ i ] . width ) || Number ( columns ?. [ columns . length - 1 ] . width ) || defaultWidth ; if ( cls [ i ] . children ) { loop ( cls [ i ] . children as ColumnType [ ] ) ; } } } ) ( resizableColumns ) ; setTableWidth ( width ) ; } , [ columns , defaultWidth , resizableColumns , setTableWidth ] ) ; const { run : debounceRender } = useDebounceFn ( forceRender ) ; React . useEffect ( ( ) => { window . addEventListener ( 'resize' , debounceRender ) ; return ( ) => { window . removeEventListener ( 'resize' , debounceRender ) ; } ; } , [ debounceRender ] ) ; const components = React . useMemo ( ( ) => { return { header : { cell : ResizableHeader , } , } ; } , [ ] ) ; return { resizableColumns , components , tableWidth , resetColumns , } ; } export default useAntdResizableHeader ;