diff --git a/packages/components/hooks/useDomRefMount.ts b/packages/components/hooks/useDomRefMount.ts new file mode 100644 index 0000000000..38bf7a7a43 --- /dev/null +++ b/packages/components/hooks/useDomRefMount.ts @@ -0,0 +1,60 @@ +import { useCallback, useRef } from 'react'; + +function useDomRefMount(ref: React.MutableRefObject) { + const callbacks = useRef void>>([]); + const unmountCallbacks = useRef void>>([]); + + const onMount = useCallback( + (nodeOrCallback: T | ((node: T) => void)) => { + // 如果传入的是函数,则注册回调 + if (typeof nodeOrCallback === 'function') { + callbacks.current.push(nodeOrCallback); + return; + } + + // 否则是 ref 挂载 + const node = nodeOrCallback as T; + const prevNode = ref?.current; + + // 更新 ref + if (ref) { + // eslint-disable-next-line no-param-reassign + ref.current = node; + } + + // 如果是新挂载(从 null 变为有值),触发所有挂载回调 + if (node && !prevNode) { + callbacks.current.forEach((callback) => { + callback(node); + }); + } + + // 如果是卸载(从有值变为 null),触发所有卸载回调 + if (!node && prevNode) { + unmountCallbacks.current.forEach((callback) => { + callback(); + }); + } + + return node; + }, + [], // eslint-disable-line react-hooks/exhaustive-deps + ); + + const onUnmount = useCallback((callback: () => void) => { + unmountCallbacks.current.push(callback); + }, []); + + const clearCallbacks = useCallback(() => { + callbacks.current = []; + unmountCallbacks.current = []; + }, []); + + return { + onMount, + onUnmount, + clearCallbacks, + }; +} + +export default useDomRefMount; diff --git a/packages/components/table/BaseTable.tsx b/packages/components/table/BaseTable.tsx index e8a2dc8021..8a2d04f572 100644 --- a/packages/components/table/BaseTable.tsx +++ b/packages/components/table/BaseTable.tsx @@ -1,10 +1,12 @@ import React, { forwardRef, RefAttributes, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { pick } from 'lodash-es'; + import log from '@tdesign/common-js/log/index'; import { getIEVersion } from '@tdesign/common-js/utils/helper'; import Affix, { type AffixRef } from '../affix'; import useDefaultProps from '../hooks/useDefaultProps'; +import useDomRefMount from '../hooks/useDomRefMount'; import useElementLazyRender from '../hooks/useElementLazyRender'; import useVirtualScroll from '../hooks/useVirtualScroll'; import Loading from '../loading'; @@ -52,11 +54,12 @@ const BaseTable = forwardRef((originalProps, ref) const tableRef = useRef(null); const tableElmRef = useRef(null); const bottomContentRef = useRef(null); + const [tableFootHeight, setTableFootHeight] = useState(0); + const [dividerBottom, setDividerBottom] = useState(0); - const allTableClasses = useClassName(); const { classPrefix, virtualScrollClasses, tableLayoutClasses, tableBaseClass, tableColFixedClasses } = - allTableClasses; + useClassName(); // 表格基础样式类 const { tableClasses, sizeClassNames, tableContentStyles, tableElementStyles } = useStyle(props); const { isMultipleHeader, spansAndLeafNodes, thList } = useTableHeader({ columns: props.columns }); @@ -114,6 +117,8 @@ const BaseTable = forwardRef((originalProps, ref) footerBottomAffixRef, }); + const { onMount: onAffixHeaderMount } = useDomRefMount(affixHeaderRef); + const { dataSource, innerPagination, isPaginateData, renderPagination } = usePagination(props, tableContentRef); // 列宽拖拽逻辑 @@ -151,7 +156,6 @@ const BaseTable = forwardRef((originalProps, ref) [isFixedHeader, isMultipleHeader, isWidthOverflow, props.bordered], ); - const [dividerBottom, setDividerBottom] = useState(0); useEffect(() => { if (!bordered) return; const bottomRect = bottomContentRef.current?.getBoundingClientRect(); @@ -255,7 +259,7 @@ const BaseTable = forwardRef((originalProps, ref) const scrollColumnIntoView = (colKey: string) => { if (!tableContentRef.current) return; const thDom = tableContentRef.current.querySelector(`th[data-colkey="${colKey}"]`); - const fixedThDom = tableContentRef.current.querySelectorAll('th.t-table__cell--fixed-left'); + const fixedThDom = tableContentRef.current.querySelectorAll(`th.${classPrefix}-table__cell--fixed-left`); let totalWidth = 0; for (let i = 0, len = fixedThDom.length; i < len; i++) { totalWidth += fixedThDom[i].getBoundingClientRect().width; @@ -272,6 +276,7 @@ const BaseTable = forwardRef((originalProps, ref) tableHtmlElement: tableElmRef.current, tableContentElement: tableContentRef.current, affixHeaderElement: affixHeaderRef.current, + onAffixHeaderMount, refreshTable, scrollToElement: virtualConfig.scrollToElement, scrollColumnIntoView, @@ -375,7 +380,7 @@ const BaseTable = forwardRef((originalProps, ref) }; const affixedHeader = Boolean((headerAffixedTop || virtualConfig.isVirtualScroll) && tableWidth.current) && (
((originalProps, ref) tableContentRef, tableWidth, isWidthOverflow, - allTableClasses, rowKey, scroll: props.scroll, cellEmptyContent: props.cellEmptyContent, @@ -537,7 +541,6 @@ const BaseTable = forwardRef((originalProps, ref) ), // eslint-disable-next-line [ - allTableClasses, tableBodyProps.ellipsisOverlayClassName, tableBodyProps.rowAndColFixedPosition, tableBodyProps.showColumnShadow, diff --git a/packages/components/table/TBody.tsx b/packages/components/table/TBody.tsx index cc2441392a..8b9e463a07 100644 --- a/packages/components/table/TBody.tsx +++ b/packages/components/table/TBody.tsx @@ -2,8 +2,9 @@ import React, { type CSSProperties, type MutableRefObject, type ReactNode, useMemo } from 'react'; import classNames from 'classnames'; import { camelCase, get, pick } from 'lodash-es'; + import { useLocaleReceiver } from '../locale/LocalReceiver'; -import { TableClassName } from './hooks/useClassName'; +import useClassName from './hooks/useClassName'; import useRowspanAndColspan from './hooks/useRowspanAndColspan'; import TR, { ROW_LISTENERS, TABLE_PROPS, type TrProps } from './TR'; @@ -25,7 +26,6 @@ export interface TableBodyProps extends BaseTableProps { isWidthOverflow?: boolean; virtualConfig: VirtualScrollConfig; pagination?: PaginationProps; - allTableClasses?: TableClassName; handleRowMounted?: (params: RowMountedParams) => void; } @@ -74,7 +74,7 @@ const trProperties = [ export default function TBody(props: TableBodyProps) { // 如果不是变量复用,没必要对每一个参数进行解构(解构过程需要单独的内存空间存储临时变量) - const { data, columns, rowKey, firstFullRow, lastFullRow, virtualConfig, allTableClasses } = props; + const { data, columns, rowKey, firstFullRow, lastFullRow, virtualConfig } = props; const { isVirtualScroll } = virtualConfig; const renderData = isVirtualScroll ? virtualConfig.visibleData : data; @@ -84,7 +84,7 @@ export default function TBody(props: TableBodyProps) { const { skipSpansMap } = useRowspanAndColspan(data, columns, rowKey, props.rowspanAndColspan); const isSkipSnapsMapNotFinish = Boolean(props.rowspanAndColspan && !skipSpansMap.size); - const { tableFullRowClasses, tableBaseClass } = allTableClasses; + const { tableFullRowClasses, tableBaseClass } = useClassName(); const tbodyClasses = useMemo(() => [tableBaseClass.body], [tableBaseClass.body]); const hasFullRowConfig = useMemo(() => firstFullRow || lastFullRow, [firstFullRow, lastFullRow]); diff --git a/packages/components/table/hooks/useDragSort.ts b/packages/components/table/hooks/useDragSort.ts index b535b4068f..8408a9c478 100644 --- a/packages/components/table/hooks/useDragSort.ts +++ b/packages/components/table/hooks/useDragSort.ts @@ -212,21 +212,14 @@ export default function useDragSort( // 注册拖拽事件 useEffect(() => { - if (!primaryTableRef || !primaryTableRef.current) return; - registerRowDragEvent(primaryTableRef.current?.tableElement); - registerColDragEvent(primaryTableRef.current?.tableHtmlElement); - /** 待表头节点准备完成后 */ - const timer = setTimeout(() => { - if (primaryTableRef.current?.affixHeaderElement) { - registerColDragEvent(primaryTableRef.current.affixHeaderElement); - } - clearTimeout(timer); + if (!primaryTableRef.current) return; + registerRowDragEvent(primaryTableRef.current.tableElement); + registerColDragEvent(primaryTableRef.current.tableHtmlElement); + primaryTableRef.current.onAffixHeaderMount((el: HTMLDivElement) => { + registerColDragEvent(el); }); - return () => { - clearTimeout(timer); - }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [primaryTableRef, columns, dragSort, innerPagination]); + }, [columns, dragSort, innerPagination]); return { isRowDraggable,