diff --git a/README.md b/README.md index 5a97947..9a578be 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,18 @@ yarn add use-antd-resizable-header ``` -## Example - ## 注意事项 -- **columns为常量时,提到组件外,或使用 `React.useMemo`, `React.Ref` 包裹常量** +- **columns 为常量时,提到组件外,或使用 `React.useMemo`, `React.Ref` 包裹常量** +- **最后一列不能拖动,[请保持最后一列的自适应](https://ant-design.gitee.io/components/table-cn/#components-table-demo-fixed-columns),若最后一列传入宽度,会把传入的宽度作为最小宽度(默认 120)** + +## Example ```tsx import useATRH from 'use-antd-resizable-header'; import 'use-antd-resizable-header/dist/style.css'; -const columns = [] +const columns = []; function App() { const { components, resizableColumns, tableWidth } = useATRH(columns); diff --git a/src/ResizableHeader.tsx b/src/ResizableHeader.tsx index 00e9794..4a8cf51 100644 --- a/src/ResizableHeader.tsx +++ b/src/ResizableHeader.tsx @@ -1,16 +1,16 @@ +/* eslint-disable react-hooks/rules-of-hooks */ import React from 'react'; import { Resizable, ResizeCallbackData } from 'react-resizable'; -import useThrottleFn from './utils/useThrottleFn'; -import useUpdateEffect from './utils/useUpdateEffect'; +import useFunction from './utils/useFunction'; import classnames from 'classnames'; -import './index.css'; +import './index.less'; type ComponentProp = { onResize: (width: number) => void; onMount: (width: number) => void; + isLast: boolean; triggerMount: number; - throttleWait?: number; width: number; handlerClassName?: string; lineColor?: string; @@ -19,9 +19,9 @@ type ComponentProp = { const AntdResizableHeader: React.FC = (props) => { const { width, - throttleWait, onResize, onMount, + isLast, triggerMount, handlerClassName, lineColor, @@ -30,50 +30,49 @@ const AntdResizableHeader: React.FC = (props) => { ...rest } = props; - const thRef = React.createRef(); + const thRef = React.useRef(null); - const [resizeWidth, setResizeWidth] = React.useState(width); - - const { run: throttleSetResizeWidth } = useThrottleFn(setResizeWidth, { - leading: false, - trailing: false, - wait: throttleWait, - }); + const [resizeWidth, setResizeWidth] = React.useState(0); React.useEffect(() => { - const domWidth = thRef.current?.getBoundingClientRect().width || width; - const w = domWidth > width ? domWidth : width; - setResizeWidth(w); - onMount?.(w); + if (width && !isLast) { + const domWidth = thRef.current?.getBoundingClientRect().width || width; + const w = domWidth > width ? domWidth : width; + setResizeWidth(w); + onMount?.(w); + } }, [triggerMount]); - useUpdateEffect(() => { - throttleSetResizeWidth(width); + React.useEffect(() => { + if (width) { + setResizeWidth(width); + } }, [width]); - if (!width || Number.isNaN(Number(width))) { + if (!width || Number.isNaN(Number(width)) || isLast) { return ; } - const setBodyStyle = (active: boolean) => { + const setBodyStyle = useFunction((active: boolean) => { document.body.style.userSelect = active ? 'none' : ''; document.body.style.cursor = active ? 'col-resize' : ''; - }; + }); - const onStart = (_: any, data: ResizeCallbackData) => { + const onStart = useFunction((_: any, data: ResizeCallbackData) => { setResizeWidth(data.size.width); setBodyStyle(true); - }; + }); - const onSelfResize = (_: any, data: ResizeCallbackData) => { + const onSelfResize = useFunction((_: any, data: ResizeCallbackData) => { setResizeWidth(data.size.width); - }; + }); - const onStop = () => { + const onStop = useFunction(() => { if (resizeWidth <= 0) return; + onResize(resizeWidth); setBodyStyle(false); - }; + }); return ( diff --git a/src/index.tsx b/src/index.tsx index 0c13bcb..1967bb2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,8 +4,8 @@ import useFunction from './utils/useFunction'; function useTableResizableHeader>( columns: ColumnType[] | undefined, + /** @description 最后一列不能拖动,设置最后一列的最小展示宽度 */ defaultWidth: number = 120, - throttleWait?: number, ) { const [resizableColumns, setResizableColumns] = React.useState([]); @@ -13,7 +13,7 @@ function useTableResizableHeader>( const [triggerMount, forceRender] = React.useReducer((s) => s + 1, 0); - const onResize = useFunction((index: number) => (width: number) => { + const onMount = useFunction((index: number) => (width: number) => { if (width) { setResizableColumns((t) => { const nextColumns = [...t]; @@ -26,19 +26,31 @@ function useTableResizableHeader>( } }); + const onResize = onMount; + + React.useEffect(() => { + forceRender(); + }, [columns]); + React.useEffect(() => { - const t = columns?.map((col, index) => ({ - ...col, - onHeaderCell: (column: ColumnType) => ({ - throttleWait, - width: column.width, - onMount: onResize(index), - onResize: onResize(index), - triggerMount, - }), - })) as ColumnType[]; + const t = columns?.map((col, index) => { + const isLast = index === columns.length - 1; + return { + ...col, + onHeaderCell: (column: ColumnType) => { + return { + width: column.width, + onMount: onMount(index), + onResize: onResize(index), + triggerMount, + isLast, + }; + }, + width: isLast ? undefined : col.width, + }; + }) as ColumnType[]; setResizableColumns(t); - }, [columns, triggerMount]); + }, [triggerMount]); React.useEffect(() => { window.addEventListener('resize', forceRender); @@ -49,7 +61,12 @@ function useTableResizableHeader>( React.useEffect(() => { const width = resizableColumns.reduce((total, current) => { - return total + (Number(current.width) || defaultWidth); + return ( + total + + (Number(current.width) || + resizableColumns[resizableColumns.length - 1].width || + defaultWidth) + ); }, 0); setTableWidth(width); }, [resizableColumns]); diff --git a/src/utils/useCreation.ts b/src/utils/useCreation.ts deleted file mode 100644 index db62a35..0000000 --- a/src/utils/useCreation.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useRef } from 'react'; - -export default function useCreation(factory: () => T, deps: any[]) { - const { current } = useRef({ - deps, - obj: undefined as undefined | T, - initialized: false, - }); - if (current.initialized === false || !depsAreSame(current.deps, deps)) { - current.deps = deps; - current.obj = factory(); - current.initialized = true; - } - return current.obj as T; -} - -function depsAreSame(oldDeps: any[], deps: any[]): boolean { - if (oldDeps === deps) return true; - for (let i = 0; i < oldDeps.length; i++) { - if (oldDeps[i] !== deps[i]) return false; - } - return true; -} diff --git a/src/utils/useDebounceFn.ts b/src/utils/useDebounceFn.ts deleted file mode 100644 index c8215b9..0000000 --- a/src/utils/useDebounceFn.ts +++ /dev/null @@ -1,42 +0,0 @@ -import debounce from 'lodash.debounce'; -import { useRef, useEffect } from 'react'; -import useCreation from './useCreation'; - -type Fn = (...args: any) => any; - -export interface DebounceOptions { - wait?: number; - leading?: boolean; - trailing?: boolean; -} - -function useDebounceFn(fn: T, options?: DebounceOptions) { - const fnRef = useRef(fn); - fnRef.current = fn; - - const wait = options?.wait ?? 1000; - - const debounced = useCreation( - () => - debounce( - ((...args: any[]) => { - return fnRef.current(...args); - }) as T, - wait, - options, - ), - [], - ); - - useEffect(() => { - debounced.cancel(); - }, []); - - return { - run: debounced as unknown as T, - cancel: debounced.cancel, - flush: debounced.flush, - }; -} - -export default useDebounceFn; diff --git a/src/utils/useThrottleFn.ts b/src/utils/useThrottleFn.ts deleted file mode 100644 index ebaaec6..0000000 --- a/src/utils/useThrottleFn.ts +++ /dev/null @@ -1,42 +0,0 @@ -import throttle from 'lodash.throttle'; -import { useRef, useEffect } from 'react'; -import useCreation from './useCreation'; - -type Fn = (...args: any) => any; - -interface ThrottleOptions { - wait?: number; - leading?: boolean; - trailing?: boolean; -} - -function useThrottleFn(fn: T, options?: ThrottleOptions) { - const fnRef = useRef(fn); - fnRef.current = fn; - - const wait = options?.wait ?? 1000; - - const throttled = useCreation( - () => - throttle( - ((...args: any[]) => { - return fnRef.current(...args); - }) as T, - wait, - options, - ), - [], - ); - - useEffect(() => { - throttled.cancel(); - }, []); - - return { - run: throttled as unknown as T, - cancel: throttled.cancel, - flush: throttled.flush, - }; -} - -export default useThrottleFn; diff --git a/src/utils/useUpdateEffect.ts b/src/utils/useUpdateEffect.ts deleted file mode 100644 index 10050cb..0000000 --- a/src/utils/useUpdateEffect.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useEffect, useRef } from 'react'; - -const useUpdateEffect: typeof useEffect = (effect, deps) => { - const isMounted = useRef(false); - - // eslint-disable-next-line consistent-return - useEffect(() => { - if (!isMounted.current) { - isMounted.current = true; - } else { - return effect(); - } - }, deps); -}; - -export default useUpdateEffect;