From cad1f52a07982dd01438d80021402651708f7047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Fri, 14 Feb 2025 15:36:32 +0800 Subject: [PATCH 01/29] fix: cherry pick of fixing sticky issue (#1232) * fix: sticky event loop * chore: adjust script --- package.json | 2 +- src/stickyScrollBar.tsx | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9a9226f4e..e6018f5d2 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", "test": "vitest --watch false", "coverage": "vitest run --coverage", - "prepublishOnly": "npm run compile && np --no-cleanup --yolo --no-publish", + "prepublishOnly": "npm run compile && np --no-cleanup --yolo --no-publish --any-branch", "lint": "eslint src/ --ext .tsx,.ts", "tsc": "tsc -p tsconfig.json --noEmit", "now-build": "npm run docs:build", diff --git a/src/stickyScrollBar.tsx b/src/stickyScrollBar.tsx index 071e68494..da0cad2c4 100644 --- a/src/stickyScrollBar.tsx +++ b/src/stickyScrollBar.tsx @@ -92,6 +92,8 @@ const StickyScrollBar: React.ForwardRefRenderFunction { + raf.cancel(rafRef.current); + rafRef.current = raf(() => { if (!scrollBodyRef.current) { return; @@ -144,13 +146,27 @@ const StickyScrollBar: React.ForwardRefRenderFunction { - const onScrollListener = addEventListener(container, 'scroll', checkScrollBarVisible, false); - const onResizeListener = addEventListener(window, 'resize', checkScrollBarVisible, false); + if (!scrollBodyRef.current) return; + + const scrollParents: HTMLElement[] = []; + let parent: HTMLElement = scrollBodyRef.current; + while (parent) { + scrollParents.push(parent); + parent = parent.parentElement; + } + + scrollParents.forEach(p => p.addEventListener('scroll', checkScrollBarVisible, false)); + window.addEventListener('resize', checkScrollBarVisible, false); + window.addEventListener('scroll', checkScrollBarVisible, false); + container.addEventListener('scroll', checkScrollBarVisible, false); return () => { - onScrollListener.remove(); - onResizeListener.remove(); + scrollParents.forEach(p => p.removeEventListener('scroll', checkScrollBarVisible)); + window.removeEventListener('resize', checkScrollBarVisible); + window.removeEventListener('scroll', checkScrollBarVisible); + container.removeEventListener('scroll', checkScrollBarVisible); }; }, [container]); From 542000cc0109da5fbc9ab92763a8d47fe45d2d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 14 Feb 2025 15:42:02 +0800 Subject: [PATCH 02/29] 7.50.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6018f5d2..4f542cd5a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.50.2", + "version": "7.50.3", "description": "table ui component for react", "engines": { "node": ">=8.x" From 1e2ec3316728a7508c190facc21429d1b6c6b22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Thu, 6 Mar 2025 22:45:20 +0800 Subject: [PATCH 03/29] fix: virtual scroll logic (#1240) * fix: scroll logic (#1239) * fix: scroll logic * fix: ci * chore: fix lint --- .github/workflows/main.yml | 2 +- src/stickyScrollBar.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5735e2d22..1fe5da5f2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,5 +2,5 @@ name: ✅ test on: [push, pull_request] jobs: test: - uses: react-component/rc-test/.github/workflows/test.yml@main + uses: react-component/rc-test/.github/workflows/test-npm.yml@main secrets: inherit \ No newline at end of file diff --git a/src/stickyScrollBar.tsx b/src/stickyScrollBar.tsx index da0cad2c4..cec2666c4 100644 --- a/src/stickyScrollBar.tsx +++ b/src/stickyScrollBar.tsx @@ -7,6 +7,7 @@ import TableContext from './context/TableContext'; import { useLayoutState } from './hooks/useFrame'; import raf from 'rc-util/lib/raf'; import { getOffset } from './utils/offsetUtil'; +import { getDOM } from 'rc-util/lib/Dom/findDOMNode'; interface StickyScrollBarProps { scrollBodyRef: React.RefObject; @@ -150,8 +151,8 @@ const StickyScrollBar: React.ForwardRefRenderFunction { if (!scrollBodyRef.current) return; - const scrollParents: HTMLElement[] = []; - let parent: HTMLElement = scrollBodyRef.current; + const scrollParents: (HTMLElement | SVGElement)[] = []; + let parent = getDOM(scrollBodyRef.current); while (parent) { scrollParents.push(parent); parent = parent.parentElement; From a056eaa39fe33459221edd16debaead33565529e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 6 Mar 2025 22:49:45 +0800 Subject: [PATCH 04/29] 7.50.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f542cd5a..32510ef0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.50.3", + "version": "7.50.4", "description": "table ui component for react", "engines": { "node": ">=8.x" From 42c37bdca18ac991aed9a7973ae1d4813c2f8613 Mon Sep 17 00:00:00 2001 From: daisy <904492381@qq.com> Date: Mon, 12 May 2025 09:51:10 +0800 Subject: [PATCH 05/29] fix: 7.x header blank when using sticky or scroll.y (#1268) --- src/Body/MeasureCell.tsx | 3 +- src/Body/MeasureRow.tsx | 12 +- src/Table.tsx | 26 +- tests/__snapshots__/FixedColumn.spec.tsx.snap | 710 ++++++++++-------- tests/__snapshots__/Table.spec.jsx.snap | 17 +- 5 files changed, 423 insertions(+), 345 deletions(-) diff --git a/src/Body/MeasureCell.tsx b/src/Body/MeasureCell.tsx index 1fc8d9cdf..d96b3508b 100644 --- a/src/Body/MeasureCell.tsx +++ b/src/Body/MeasureCell.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import ResizeObserver from 'rc-resize-observer'; +import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; export interface MeasureCellProps { columnKey: React.Key; @@ -9,7 +10,7 @@ export interface MeasureCellProps { export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellProps) { const cellRef = React.useRef(); - React.useEffect(() => { + useLayoutEffect(() => { if (cellRef.current) { onColumnResize(columnKey, cellRef.current.offsetWidth); } diff --git a/src/Body/MeasureRow.tsx b/src/Body/MeasureRow.tsx index 3dbd12113..ba95c961d 100644 --- a/src/Body/MeasureRow.tsx +++ b/src/Body/MeasureRow.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import ResizeObserver from 'rc-resize-observer'; import MeasureCell from './MeasureCell'; +import isVisible from 'rc-util/lib/Dom/isVisible'; export interface MeasureCellProps { prefixCls: string; @@ -9,17 +10,22 @@ export interface MeasureCellProps { } export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: MeasureCellProps) { + const ref = React.useRef(null); + return ( { - infoList.forEach(({ data: columnKey, size }) => { - onColumnResize(columnKey, size.offsetWidth); - }); + if (isVisible(ref.current)) { + infoList.forEach(({ data: columnKey, size }) => { + onColumnResize(columnKey, size.offsetWidth); + }); + } }} > {columnsKey.map(columnKey => ( diff --git a/src/Table.tsx b/src/Table.tsx index 5b9ec0898..187d0ea32 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -27,7 +27,6 @@ import type { CompareProps } from '@rc-component/context/lib/Immutable'; import classNames from 'classnames'; import ResizeObserver from 'rc-resize-observer'; -import isVisible from 'rc-util/lib/Dom/isVisible'; import { isStyleSupport } from 'rc-util/lib/Dom/styleChecker'; import { getTargetScrollBarSize } from 'rc-util/lib/getScrollBarSize'; import useEvent from 'rc-util/lib/hooks/useEvent'; @@ -48,7 +47,7 @@ import Header from './Header/Header'; import useColumns from './hooks/useColumns'; import useExpand from './hooks/useExpand'; import useFixedInfo from './hooks/useFixedInfo'; -import { useLayoutState, useTimeoutLock } from './hooks/useFrame'; +import { useTimeoutLock } from './hooks/useFrame'; import useHover from './hooks/useHover'; import useSticky from './hooks/useSticky'; import useStickyOffsets from './hooks/useStickyOffsets'; @@ -76,6 +75,7 @@ import Column from './sugar/Column'; import ColumnGroup from './sugar/ColumnGroup'; import { getColumnsKey, validateValue, validNumberValue } from './utils/valueUtil'; import { getDOM } from 'rc-util/lib/Dom/findDOMNode'; +import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; export const DEFAULT_PREFIX = 'rc-table'; @@ -349,7 +349,7 @@ function Table( const scrollSummaryRef = React.useRef(); const [pingedLeft, setPingedLeft] = React.useState(false); const [pingedRight, setPingedRight] = React.useState(false); - const [colsWidths, updateColsWidths] = useLayoutState(new Map()); + const [colsWidths, updateColsWidths] = React.useState(new Map()); // Convert map to number width const colsKeys = getColumnsKey(flattenColumns); @@ -403,16 +403,14 @@ function Table( } const onColumnResize = React.useCallback((columnKey: React.Key, width: number) => { - if (isVisible(fullTableRef.current)) { - updateColsWidths(widths => { - if (widths.get(columnKey) !== width) { - const newWidths = new Map(widths); - newWidths.set(columnKey, width); - return newWidths; - } - return widths; - }); - } + updateColsWidths(widths => { + if (widths.get(columnKey) !== width) { + const newWidths = new Map(widths); + newWidths.set(columnKey, width); + return newWidths; + } + return widths; + }); }, []); const [setScrollTarget, getScrollTarget] = useTimeoutLock(null); @@ -524,7 +522,7 @@ function Table( const [scrollbarSize, setScrollbarSize] = React.useState(0); const [supportSticky, setSupportSticky] = React.useState(true); // Only IE not support, we mark as support first - React.useEffect(() => { + useLayoutEffect(() => { if (!tailor || !useInternalHooks) { if (scrollBodyRef.current instanceof Element) { setScrollbarSize(getTargetScrollBarSize(scrollBodyRef.current).width); diff --git a/tests/__snapshots__/FixedColumn.spec.tsx.snap b/tests/__snapshots__/FixedColumn.spec.tsx.snap index f3091dfd0..1df778d76 100644 --- a/tests/__snapshots__/FixedColumn.spec.tsx.snap +++ b/tests/__snapshots__/FixedColumn.spec.tsx.snap @@ -49,7 +49,7 @@ LoadedCheerio { title2 @@ -246,7 +246,7 @@ LoadedCheerio { @@ -315,7 +315,7 @@ LoadedCheerio { @@ -384,7 +384,7 @@ LoadedCheerio { @@ -437,7 +437,7 @@ LoadedCheerio { @@ -490,7 +490,7 @@ LoadedCheerio { @@ -543,7 +543,7 @@ LoadedCheerio { @@ -596,7 +596,7 @@ LoadedCheerio { @@ -649,7 +649,7 @@ LoadedCheerio { @@ -702,7 +702,7 @@ LoadedCheerio { @@ -3157,9 +3157,25 @@ exports[`Table.FixedColumn > shadow should be shown when there are columns where style="overflow: hidden;" > - + + + + + + + @@ -5572,9 +5588,37 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` style="overflow: hidden;" >
- + + + + + + + + + + + @@ -5634,7 +5678,7 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` class="rc-table-cell rc-table-cell-fix-left rc-table-cell-fix-left-last" colspan="3" scope="colgroup" - style="position: sticky; left: 0px;" + style="position: sticky; left: 1000px;" > Address @@ -5658,7 +5702,7 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` class="rc-table-cell rc-table-cell-fix-left rc-table-cell-ellipsis" rowspan="2" scope="col" - style="position: sticky; left: 0px;" + style="position: sticky; left: 1000px;" title="Street" > Street @@ -5667,7 +5711,7 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` class="rc-table-cell rc-table-cell-fix-left rc-table-cell-fix-left-last" colspan="2" scope="colgroup" - style="position: sticky; left: 0px;" + style="position: sticky; left: 2000px;" > Block @@ -5676,7 +5720,7 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = `
Door No. @@ -5684,7 +5728,7 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 1`] = ` Lake Park 2035 shadow should display correctly 2`] = ` style="overflow: hidden;" > - + + + + + + + @@ -11662,7 +11722,7 @@ exports[`Table.FixedColumn > shadow should display correctly 2`] = ` @@ -11759,7 +11819,7 @@ exports[`Table.FixedColumn > shadow should display correctly 2`] = ` />
Name1 shadow should display correctly 2`] = ` /> shadow should display correctly 2`] = ` /> - + + + + + + Date: Mon, 12 May 2025 12:14:32 +0800 Subject: [PATCH 06/29] 7.50.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32510ef0f..a350e7364 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.50.4", + "version": "7.50.5", "description": "table ui component for react", "engines": { "node": ">=8.x" From bd18ca497bf7fd3a8eb2394763e747ec32761de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <7971419+crazyair@users.noreply.github.com> Date: Tue, 27 May 2025 15:59:50 +0800 Subject: [PATCH 07/29] feat: Table support expandedRowOffset by V5 (#1283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: test * feat: 迁移 * feat: 同步 --- docs/demo/expandedSticky.md | 8 +++ docs/examples/expandedSticky.tsx | 59 +++++++++++++++++++++ src/Body/BodyRow.tsx | 43 +++++++++++++-- src/Body/ExpandedRow.tsx | 6 ++- src/Body/index.tsx | 43 ++++++++++++--- src/Table.tsx | 4 ++ src/VirtualTable/VirtualCell.tsx | 1 + src/context/TableContext.tsx | 4 ++ src/hooks/useColumns/index.tsx | 15 +++++- src/hooks/useFlattenRecords.ts | 7 ++- src/interface.ts | 1 + tests/ExpandedOffset.spec.tsx | 91 ++++++++++++++++++++++++++++++++ 12 files changed, 264 insertions(+), 18 deletions(-) create mode 100644 docs/demo/expandedSticky.md create mode 100644 docs/examples/expandedSticky.tsx create mode 100644 tests/ExpandedOffset.spec.tsx diff --git a/docs/demo/expandedSticky.md b/docs/demo/expandedSticky.md new file mode 100644 index 000000000..df58f5783 --- /dev/null +++ b/docs/demo/expandedSticky.md @@ -0,0 +1,8 @@ +--- +title: expandedSticky +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/expandedSticky.tsx b/docs/examples/expandedSticky.tsx new file mode 100644 index 000000000..0469db326 --- /dev/null +++ b/docs/examples/expandedSticky.tsx @@ -0,0 +1,59 @@ +import React, { useState } from 'react'; +import type { ColumnType } from 'rc-table'; +import Table from 'rc-table'; +import '../../assets/index.less'; + +const Demo = () => { + const [expandedRowKeys, setExpandedRowKeys] = useState([]); + + const data = [ + { key: 'a', a: '小二', d: '文零西路' }, + { key: 'b', a: '张三', d: '文一西路' }, + { key: 'c', a: '张三', d: '文二西路' }, + ]; + + const columns: ColumnType>[] = [ + { + title: '手机号', + dataIndex: 'a', + width: 100, + onCell: (_, index) => { + if (index === 1) { + return { + rowSpan: 2, + }; + } else if (index === 2) { + return { + rowSpan: 0, + }; + } + }, + }, + { title: 'key', dataIndex: 'key2', width: 100 }, + Table.EXPAND_COLUMN, + { title: 'key', dataIndex: 'key' }, + { title: 'Address', fixed: 'right', dataIndex: 'd', width: 200 }, + ]; + + return ( +
+

expanded & sticky

+ > + rowKey="key" + sticky + scroll={{ x: 2000 }} + columns={columns} + data={data} + expandable={{ + expandedRowOffset: 2, + expandedRowKeys, + onExpandedRowsChange: keys => setExpandedRowKeys(keys), + expandedRowRender: record =>

expandedRowRender: {record.key}

, + }} + className="table" + /> +
+ ); +}; + +export default Demo; diff --git a/src/Body/BodyRow.tsx b/src/Body/BodyRow.tsx index 46ca866f8..4badca944 100644 --- a/src/Body/BodyRow.tsx +++ b/src/Body/BodyRow.tsx @@ -19,6 +19,14 @@ export interface BodyRowProps { scopeCellComponent: CustomizeComponent; indent?: number; rowKey: React.Key; + rowKeys: React.Key[]; + + // Expanded Row + expandedRowInfo?: { + offset: number; + colSpan: number; + sticky: number; + }; } // ================================================================================== @@ -30,6 +38,8 @@ export function getCellProps( colIndex: number, indent: number, index: number, + rowKeys: React.Key[] = [], + expandedRowOffset = 0, ) { const { record, @@ -43,6 +53,8 @@ export function getCellProps( expanded, hasNestChildren, onTriggerExpand, + expandable, + expandedKeys, } = rowInfo; const key = columnsKey[colIndex]; @@ -68,16 +80,32 @@ export function getCellProps( ); } - let additionalCellProps: React.TdHTMLAttributes; - if (column.onCell) { - additionalCellProps = column.onCell(record, index); + const additionalCellProps = column.onCell?.(record, index) || {}; + + // Expandable row has offset + if (expandedRowOffset) { + const { rowSpan = 1 } = additionalCellProps; + + // For expandable row with rowSpan, + // We should increase the rowSpan if the row is expanded + if (expandable && rowSpan && colIndex < expandedRowOffset) { + let currentRowSpan = rowSpan; + + for (let i = index; i < index + rowSpan; i += 1) { + const rowKey = rowKeys[i]; + if (expandedKeys.has(rowKey)) { + currentRowSpan += 1; + } + } + additionalCellProps.rowSpan = currentRowSpan; + } } return { key, fixedInfo, appendCellNode, - additionalCellProps: additionalCellProps || {}, + additionalCellProps: additionalCellProps, }; } @@ -98,10 +126,12 @@ function BodyRow( index, renderIndex, rowKey, + rowKeys, indent = 0, rowComponent: RowComponent, cellComponent, scopeCellComponent, + expandedRowInfo, } = props; const rowInfo = useRowInfo(record, rowKey, index, indent); const { @@ -153,6 +183,8 @@ function BodyRow( colIndex, indent, index, + rowKeys, + expandedRowInfo?.offset, ); return ( @@ -195,7 +227,8 @@ function BodyRow( prefixCls={prefixCls} component={RowComponent} cellComponent={cellComponent} - colSpan={flattenColumns.length} + colSpan={expandedRowInfo ? expandedRowInfo.colSpan : flattenColumns.length} + stickyOffset={expandedRowInfo?.sticky} isEmpty={false} > {expandContent} diff --git a/src/Body/ExpandedRow.tsx b/src/Body/ExpandedRow.tsx index b4009601c..425df8647 100644 --- a/src/Body/ExpandedRow.tsx +++ b/src/Body/ExpandedRow.tsx @@ -14,6 +14,7 @@ export interface ExpandedRowProps { children: React.ReactNode; colSpan: number; isEmpty: boolean; + stickyOffset?: number; } function ExpandedRow(props: ExpandedRowProps) { @@ -30,6 +31,7 @@ function ExpandedRow(props: ExpandedRowProps) { expanded, colSpan, isEmpty, + stickyOffset = 0, } = props; const { scrollbarSize, fixHeader, fixColumn, componentWidth, horizonScroll } = useContext( @@ -44,9 +46,9 @@ function ExpandedRow(props: ExpandedRowProps) { contentNode = (
(props: BodyProps) { expandedKeys, childrenColumnName, emptyNode, + expandedRowOffset = 0, + colWidths, } = useContext(TableContext, [ 'prefixCls', 'getComponent', @@ -40,16 +42,42 @@ function Body(props: BodyProps) { 'expandedKeys', 'childrenColumnName', 'emptyNode', + 'expandedRowOffset', + 'fixedInfoList', + 'colWidths', ]); - const flattenData: { record: RecordType; indent: number; index: number }[] = - useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey); + const flattenData = useFlattenRecords( + data, + childrenColumnName, + expandedKeys, + getRowKey, + ); + const rowKeys = React.useMemo(() => flattenData.map(item => item.rowKey), [flattenData]); // =================== Performance ==================== const perfRef = React.useRef({ renderWithProps: false, }); + // ===================== Expanded ===================== + // `expandedRowOffset` data is same for all the rows. + // Let's calc on Body side to save performance. + const expandedRowInfo = React.useMemo(() => { + const expandedColSpan = flattenColumns.length - expandedRowOffset; + + let expandedStickyStart = 0; + for (let i = 0; i < expandedRowOffset; i += 1) { + expandedStickyStart += colWidths[i] || 0; + } + + return { + offset: expandedRowOffset, + colSpan: expandedColSpan, + sticky: expandedStickyStart, + }; + }, [flattenColumns.length, expandedRowOffset, colWidths]); + // ====================== Render ====================== const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody'); const trComponent = getComponent(['body', 'row'], 'tr'); @@ -59,14 +87,13 @@ function Body(props: BodyProps) { let rows: React.ReactNode; if (data.length) { rows = flattenData.map((item, idx) => { - const { record, indent, index: renderIndex } = item; - - const key = getRowKey(record, idx); + const { record, indent, index: renderIndex, rowKey } = item; return ( (props: BodyProps) { cellComponent={tdComponent} scopeCellComponent={thComponent} indent={indent} + // Expanded row info + expandedRowInfo={expandedRowInfo} /> ); }); diff --git a/src/Table.tsx b/src/Table.tsx index 187d0ea32..b61de7520 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -822,6 +822,7 @@ function Table( expandableType, expandRowByClick: expandableConfig.expandRowByClick, expandedRowRender: expandableConfig.expandedRowRender, + expandedRowOffset: expandableConfig.expandedRowOffset, onTriggerExpand, expandIconColumnIndex: expandableConfig.expandIconColumnIndex, indentSize: expandableConfig.indentSize, @@ -832,6 +833,7 @@ function Table( columns, flattenColumns, onColumnResize, + colWidths, // Row hoverStartRow: startRow, @@ -872,6 +874,7 @@ function Table( expandableType, expandableConfig.expandRowByClick, expandableConfig.expandedRowRender, + expandableConfig.expandedRowOffset, onTriggerExpand, expandableConfig.expandIconColumnIndex, expandableConfig.indentSize, @@ -881,6 +884,7 @@ function Table( columns, flattenColumns, onColumnResize, + colWidths, // Row startRow, diff --git a/src/VirtualTable/VirtualCell.tsx b/src/VirtualTable/VirtualCell.tsx index 9b1b3ebe5..7f0af164c 100644 --- a/src/VirtualTable/VirtualCell.tsx +++ b/src/VirtualTable/VirtualCell.tsx @@ -56,6 +56,7 @@ function VirtualCell(props: VirtualCellProps) { const { columnsOffset } = useContext(GridContext, ['columnsOffset']); + // TODO: support `expandableRowOffset` const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps( rowInfo, column, diff --git a/src/context/TableContext.tsx b/src/context/TableContext.tsx index f566c84f0..d15807dde 100644 --- a/src/context/TableContext.tsx +++ b/src/context/TableContext.tsx @@ -3,6 +3,7 @@ import type { ColumnsType, ColumnType, Direction, + ExpandableConfig, ExpandableType, ExpandedRowRender, GetComponent, @@ -56,6 +57,7 @@ export interface TableContextProps { columns: ColumnsType; flattenColumns: readonly ColumnType[]; onColumnResize: (columnKey: React.Key, width: number) => void; + colWidths: number[]; // Row hoverStartRow: number; @@ -68,6 +70,8 @@ export interface TableContextProps { childrenColumnName: string; rowHoverable?: boolean; + + expandedRowOffset: ExpandableConfig['expandedRowOffset']; } const TableContext = createContext(); diff --git a/src/hooks/useColumns/index.tsx b/src/hooks/useColumns/index.tsx index 573a44af3..9f3f4b760 100644 --- a/src/hooks/useColumns/index.tsx +++ b/src/hooks/useColumns/index.tsx @@ -122,6 +122,7 @@ function useColumns( expandIcon, rowExpandable, expandIconColumnIndex, + expandedRowOffset = 0, direction, expandRowByClick, columnWidth, @@ -146,6 +147,7 @@ function useColumns( clientWidth: number; fixed?: FixedType; scrollWidth?: number; + expandedRowOffset?: number; }, transformColumns: (columns: ColumnsType) => ColumnsType, ): [ @@ -236,7 +238,16 @@ function useColumns( }, }; - return cloneColumns.map(col => (col === EXPAND_COLUMN ? expandColumn : col)); + return cloneColumns.map((col, index) => { + const column = col === EXPAND_COLUMN ? expandColumn : col; + if (index < expandedRowOffset) { + return { + ...column, + fixed: column.fixed || 'left', + }; + } + return column; + }); } if (process.env.NODE_ENV !== 'production' && baseColumns.includes(EXPAND_COLUMN)) { @@ -245,7 +256,7 @@ function useColumns( return baseColumns.filter(col => col !== EXPAND_COLUMN); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction]); + }, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction, expandedRowOffset]); // ========================= Transform ======================== const mergedColumns = React.useMemo(() => { diff --git a/src/hooks/useFlattenRecords.ts b/src/hooks/useFlattenRecords.ts index ff67f5d9d..7f6f816db 100644 --- a/src/hooks/useFlattenRecords.ts +++ b/src/hooks/useFlattenRecords.ts @@ -11,14 +11,15 @@ function fillRecords( getRowKey: GetRowKey, index: number, ) { + const key = getRowKey(record, index); + list.push({ record, indent, index, + rowKey: key, }); - const key = getRowKey(record); - const expanded = expandedKeys?.has(key); if (record && Array.isArray(record[childrenColumnName]) && expanded) { @@ -41,6 +42,7 @@ export interface FlattenData { record: RecordType; indent: number; index: number; + rowKey: Key; } /** @@ -80,6 +82,7 @@ export default function useFlattenRecords( record: item, indent: 0, index, + rowKey: getRowKey(item, index), }; }); }, [data, childrenColumnName, expandedKeys, getRowKey]); diff --git a/src/interface.ts b/src/interface.ts index e645b2145..c6d9a1794 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -252,6 +252,7 @@ export interface ExpandableConfig { rowExpandable?: (record: RecordType) => boolean; columnWidth?: number | string; fixed?: FixedType; + expandedRowOffset?: number; } // =================== Render =================== diff --git a/tests/ExpandedOffset.spec.tsx b/tests/ExpandedOffset.spec.tsx new file mode 100644 index 000000000..1c6fa1191 --- /dev/null +++ b/tests/ExpandedOffset.spec.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; +import { render, act } from '@testing-library/react'; +import { _rs } from 'rc-resize-observer'; +import Table, { type ColumnsType } from '../src'; + +async function triggerResize(ele: HTMLElement) { + await act(async () => { + _rs([{ target: ele }] as any); + await Promise.resolve(); + }); +} + +describe('Table.ExpandedOffset', () => { + let domSpy: ReturnType; + + beforeEach(() => { + vi.useFakeTimers(); + }); + + beforeAll(() => { + domSpy = spyElementPrototypes(HTMLElement, { + offsetParent: { + get: () => ({}), + }, + offsetWidth: { + get: () => 50, + }, + }); + }); + + afterAll(() => { + domSpy.mockRestore(); + }); + + afterEach(() => { + vi.clearAllTimers(); + vi.useRealTimers(); + }); + + it('expanded + sticky', async () => { + const columns: ColumnsType = [ + { + title: 'a', + // `fixed` will auto patch to fill the space + // fixed: 'left', + }, + Table.EXPAND_COLUMN, + { title: 'b' }, + { title: 'c' }, + ]; + + const data = [{ key: 'a' }]; + const { container } = render( + > + columns={columns} + data={data} + sticky + scroll={{ x: 1200 }} + expandable={{ + expandedRowOffset: 1, + defaultExpandAllRows: true, + expandedRowRender: record =>
{record.key}
, + }} + />, + ); + + await triggerResize(container.querySelector('.rc-table')); + + act(() => { + const coll = container.querySelector('.rc-table-resize-collection'); + if (coll) { + triggerResize(coll as HTMLElement); + } + }); + + await act(async () => { + vi.runAllTimers(); + await Promise.resolve(); + }); + + expect(container.querySelector('.rc-table-expanded-row .rc-table-cell')).toHaveAttribute( + 'colspan', + '3', + ); + expect(container.querySelector('.rc-table-expanded-row .rc-table-cell div')).toHaveStyle({ + position: 'sticky', + left: '50px', + }); + }); +}); From 8e6e892076136329a3ed2347ece221dc8eab7cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 27 May 2025 16:03:55 +0800 Subject: [PATCH 08/29] 7.51.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a350e7364..a1f0fa805 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.50.5", + "version": "7.51.0", "description": "table ui component for react", "engines": { "node": ">=8.x" From ffa3cea96f18105db7b07f1ca1ed0c5987976918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Tue, 17 Jun 2025 15:05:07 +0800 Subject: [PATCH 09/29] chore: defensive program (#1294) --- src/FixedHolder/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index d4a243974..de95b0203 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -89,10 +89,12 @@ const FixedHolder = React.forwardRef>((pro e.preventDefault(); } } - scrollRef.current?.addEventListener('wheel', onWheel, { passive: false }); + + const scrollEle = scrollRef.current; + scrollEle?.addEventListener('wheel', onWheel, { passive: false }); return () => { - scrollRef.current?.removeEventListener('wheel', onWheel); + scrollEle?.removeEventListener('wheel', onWheel); }; }, []); From 6c7dee3b88ec1fce680d6d2bd21289f3b46a4313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 17 Jun 2025 15:08:40 +0800 Subject: [PATCH 10/29] 7.51.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1f0fa805..0b67da1fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.51.0", + "version": "7.51.1", "description": "table ui component for react", "engines": { "node": ">=8.x" From 1b655a7741f3c0088195bf09487ceacf56bd03bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=91=BE=F0=9D=92=96=F0=9D=92=99=F0=9D=92=89?= Date: Mon, 25 Aug 2025 11:12:25 +0800 Subject: [PATCH 11/29] fix: Adjust the top height of the proxy to solve the sliding issue (#1310) --- src/VirtualTable/BodyGrid.tsx | 11 +++++++++++ tests/Virtual.spec.tsx | 32 +++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/VirtualTable/BodyGrid.tsx b/src/VirtualTable/BodyGrid.tsx index cc1548cc6..e3aa85f49 100644 --- a/src/VirtualTable/BodyGrid.tsx +++ b/src/VirtualTable/BodyGrid.tsx @@ -93,6 +93,17 @@ const Grid = React.forwardRef((props, ref) => { }, }); + // https://github.com/ant-design/ant-design/issues/54734 + Object.defineProperty(obj, 'scrollTop', { + get: () => listRef.current?.getScrollInfo().y || 0, + + set: (value: number) => { + listRef.current?.scrollTo({ + top: value, + }); + }, + }); + return obj; }); diff --git a/tests/Virtual.spec.tsx b/tests/Virtual.spec.tsx index f9170c7d5..2d3843d31 100644 --- a/tests/Virtual.spec.tsx +++ b/tests/Virtual.spec.tsx @@ -6,7 +6,10 @@ import { resetWarned } from 'rc-util/lib/warning'; import React from 'react'; import { VirtualTable, type Reference, type VirtualTableProps } from '../src'; +const identity = (value: any) => value; + global.scrollToConfig = null; +global.collectGetScrollInfoReturn = identity; vi.mock('rc-virtual-list', async () => { const RealVirtualList = ((await vi.importActual('rc-virtual-list')) as any).default; @@ -20,6 +23,10 @@ vi.mock('rc-virtual-list', async () => { global.scrollToConfig = config; return myRef.current.scrollTo(config); }, + getScrollInfo: () => { + const originResult = myRef.current.getScrollInfo(); + return global.collectGetScrollInfoReturn(originResult); + }, })); return ; @@ -59,7 +66,8 @@ describe('Table.Virtual', () => { beforeEach(() => { scrollLeftCalled = false; setScrollLeft.mockReset(); - global.scrollToConfig = null; + global.scrollToConfig = vi.fn(identity); + // global.collectGetScrollInfoReturn.mockReset(); vi.useFakeTimers(); resetWarned(); }); @@ -563,4 +571,26 @@ describe('Table.Virtual', () => { ).toBeTruthy(); }); }); + + /** + * In antd, we need to call the scrollTop method through ref to achieve scrolling. + * see: https://github.com/ant-design/ant-design/issues/54734 + */ + it('should get and set scrollTop correctly', async () => { + const ref = React.createRef(); + + global.collectGetScrollInfoReturn = (origin: any) => ({ + ...origin, + y: 50, + }); + + getTable({ internalRefs: { body: ref } }); + + expect(ref.current.scrollTop).toBe(50); + + ref.current.scrollTop = 200; + expect(global.scrollToConfig).toEqual({ + top: 200, + }); + }); }); From 48c897674257ce88bd4b5c4eaca4c7b376d1e3f9 Mon Sep 17 00:00:00 2001 From: Ryan Tang <84262952+ryantang247@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:06:00 +0800 Subject: [PATCH 12/29] Fix fixed property not being inherited by children columns (#1303) --- src/hooks/useColumns/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useColumns/index.tsx b/src/hooks/useColumns/index.tsx index 9f3f4b760..64542696f 100644 --- a/src/hooks/useColumns/index.tsx +++ b/src/hooks/useColumns/index.tsx @@ -72,8 +72,8 @@ function flatColumns( return [ ...list, ...flatColumns(subColumns, mergedKey).map(subColum => ({ - fixed: parsedFixed, ...subColum, + fixed: subColum.fixed ?? parsedFixed, })), ]; } From 3e9410a6afa3590a0dddcdab6a61685be24c365e Mon Sep 17 00:00:00 2001 From: inottn Date: Tue, 26 Aug 2025 10:01:42 +0800 Subject: [PATCH 13/29] fix: expand column not visible when expandable.fixed is true (#1314) --- src/hooks/useColumns/index.tsx | 9 +- tests/ExpandRow.spec.jsx | 24 +++- tests/__snapshots__/ExpandRow.spec.jsx.snap | 117 ++++++++++++++++++-- 3 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/hooks/useColumns/index.tsx b/src/hooks/useColumns/index.tsx index 64542696f..070aa3031 100644 --- a/src/hooks/useColumns/index.tsx +++ b/src/hooks/useColumns/index.tsx @@ -178,11 +178,10 @@ function useColumns( // >>> Insert expand column if not exist if (!cloneColumns.includes(EXPAND_COLUMN)) { const expandColIndex = expandIconColumnIndex || 0; - if (expandColIndex >= 0 && (expandColIndex || fixed === 'left' || !fixed)) { - cloneColumns.splice(expandColIndex, 0, EXPAND_COLUMN); - } - if (fixed === 'right') { - cloneColumns.splice(baseColumns.length, 0, EXPAND_COLUMN); + const insertIndex = + expandColIndex === 0 && fixed === 'right' ? baseColumns.length : expandColIndex; + if (insertIndex >= 0) { + cloneColumns.splice(insertIndex, 0, EXPAND_COLUMN); } } diff --git a/tests/ExpandRow.spec.jsx b/tests/ExpandRow.spec.jsx index 5a44fa1a0..058e057e2 100644 --- a/tests/ExpandRow.spec.jsx +++ b/tests/ExpandRow.spec.jsx @@ -219,6 +219,14 @@ describe('Table.Expand', () => { }), ); const wrapper2 = mount( + createTable({ + columns, + data, + scroll: { x: 903 }, + expandable: { expandedRowRender, fixed: true, expandIconColumnIndex: 1 }, + }), + ); + const wrapper3 = mount( createTable({ columns, data, @@ -227,10 +235,11 @@ describe('Table.Expand', () => { }), ); expect(wrapper.find('.rc-table-has-fix-left').length).toBe(0); - expect(wrapper2.find('.rc-table-has-fix-right').length).toBe(0); + expect(wrapper2.find('.rc-table-has-fix-left').length).toBe(0); + expect(wrapper3.find('.rc-table-has-fix-right').length).toBe(0); }); - it('fixed in expandable Fixed in expandable', () => { + it('fixed in expandable', () => { const columns = [ { title: 'Name', dataIndex: 'name', key: 'name' }, { title: 'Age', dataIndex: 'age', key: 'age' }, @@ -249,6 +258,14 @@ describe('Table.Expand', () => { }), ); const wrapper2 = mount( + createTable({ + columns, + data, + scroll: { x: 903 }, + expandable: { expandedRowRender, fixed: true }, + }), + ); + const wrapper3 = mount( createTable({ columns, data, @@ -257,7 +274,8 @@ describe('Table.Expand', () => { }), ); expect(wrapper.find('.rc-table-has-fix-left').length).toBe(1); - expect(wrapper2.find('.rc-table-has-fix-right').length).toBe(1); + expect(wrapper2.find('.rc-table-has-fix-left').length).toBe(1); + expect(wrapper3.find('.rc-table-has-fix-right').length).toBe(1); }); describe('config expand column index', () => { diff --git a/tests/__snapshots__/ExpandRow.spec.jsx.snap b/tests/__snapshots__/ExpandRow.spec.jsx.snap index 5a59716b0..9ee888bc2 100644 --- a/tests/__snapshots__/ExpandRow.spec.jsx.snap +++ b/tests/__snapshots__/ExpandRow.spec.jsx.snap @@ -138,7 +138,7 @@ LoadedCheerio { exports[`Table.Expand > does not crash if scroll is not set 1`] = ` LoadedCheerio { "0":
- + + + + +
+ +
+   +
+
+ + @@ -235,6 +260,14 @@ LoadedCheerio { class="rc-table-row rc-table-row-level-0" data-row-key="1" > + + + @@ -290,7 +323,7 @@ LoadedCheerio { exports[`Table.Expand > does not crash if scroll is not set 2`] = ` LoadedCheerio { "0":
- + + + + +
+ +
+   +
+
+ + @@ -387,6 +445,14 @@ LoadedCheerio { class="rc-table-row rc-table-row-level-0" data-row-key="1" > + + + @@ -1117,7 +1183,7 @@ LoadedCheerio { exports[`Table.Expand > work in expandable fix 1`] = ` LoadedCheerio { "0":
- + + + + + - )} + {children({ ...restProps, stickyOffsets: headerStickyOffsets, diff --git a/src/Table.tsx b/src/Table.tsx index b61de7520..37fd39fba 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -680,7 +680,6 @@ function Table( // Fixed holder share the props const fixedHolderProps = { noData: !mergedData.length, - maxContentScroll: horizonScroll && mergedScrollX === 'max-content', ...headerProps, ...columnContext, direction, From d22b38cf19e62e810e783a79ad4ceb07141a98b0 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Aug 2025 17:13:51 +0800 Subject: [PATCH 18/29] =?UTF-8?q?Revert=20"refactor:=20remove=20maxContent?= =?UTF-8?q?Scroll=20prop=20and=20related=20logic=20from=20FixedHo=E2=80=A6?= =?UTF-8?q?"=20(#1320)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/FixedHolder/index.tsx | 20 +++++++++++++++----- src/Table.tsx | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index 66a2151f8..de95b0203 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -27,6 +27,7 @@ function useColumnWidth(colWidths: readonly number[], columCount: number) { export interface FixedHeaderProps extends HeaderProps { className: string; noData: boolean; + maxContentScroll: boolean; colWidths: readonly number[]; columCount: number; direction: Direction; @@ -57,6 +58,7 @@ const FixedHolder = React.forwardRef>((pro stickyBottomOffset, stickyClassName, onScroll, + maxContentScroll, children, ...restProps } = props; @@ -96,6 +98,12 @@ const FixedHolder = React.forwardRef>((pro }; }, []); + // Check if all flattenColumns has width + const allFlattenColumnsWithWidth = React.useMemo( + () => flattenColumns.every(column => column.width), + [flattenColumns], + ); + // Add scrollbar column const lastColumn = flattenColumns[flattenColumns.length - 1]; const ScrollBarColumn: ColumnType & { scrollbar: true } = { @@ -148,11 +156,13 @@ const FixedHolder = React.forwardRef>((pro visibility: noData || mergedColumnWidth ? null : 'hidden', }} > - + {(!noData || !maxContentScroll || allFlattenColumnsWithWidth) && ( + + )} {children({ ...restProps, stickyOffsets: headerStickyOffsets, diff --git a/src/Table.tsx b/src/Table.tsx index 37fd39fba..b61de7520 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -680,6 +680,7 @@ function Table( // Fixed holder share the props const fixedHolderProps = { noData: !mergedData.length, + maxContentScroll: horizonScroll && mergedScrollX === 'max-content', ...headerProps, ...columnContext, direction, From 1b75be26f0a69ff0c578166f68f041fefcd9770e Mon Sep 17 00:00:00 2001 From: afc163 Date: Fri, 29 Aug 2025 15:29:26 +0800 Subject: [PATCH 19/29] fix: fix sticky header column width with empty data (#1323) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/examples/stickyHeader.tsx | 59 +++++++++++++++++-- src/FixedHolder/index.tsx | 15 ++--- src/Table.tsx | 3 +- tests/__snapshots__/FixedColumn.spec.tsx.snap | 45 ++++---------- 4 files changed, 74 insertions(+), 48 deletions(-) diff --git a/docs/examples/stickyHeader.tsx b/docs/examples/stickyHeader.tsx index 817ff86bc..e5013e453 100644 --- a/docs/examples/stickyHeader.tsx +++ b/docs/examples/stickyHeader.tsx @@ -79,6 +79,13 @@ const columns: ColumnType[] = [ }, ]; +const columnsWithWidth: ColumnType[] = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { title: 'title4', dataIndex: 'd', key: 'd', width: 100 }, +]; + const data = [ { a: '123', key: '1' }, { a: 'cdd', b: 'edd', key: '2' }, @@ -105,11 +112,7 @@ const data = [ const Demo = () => { const container = useRef(); return ( -
+

Sticky

columns={columns} @@ -212,6 +215,52 @@ const Demo = () => { suscipit asperiores, id ullam in iste soluta dignissimos vero incidunt, rem ex consectetur beatae totam aperiam. Sunt, laudantium?
+ +

Sticky header with empty data

+
+ +
+   +
+
+ + @@ -1214,6 +1305,14 @@ LoadedCheerio { class="rc-table-row rc-table-row-level-0" data-row-key="1" > + + + From 3916bd318320a860bdd748013ff38b8f1f4a9cf0 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Aug 2025 10:18:29 +0800 Subject: [PATCH 14/29] 7.52.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b67da1fc..4494d2d00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.51.1", + "version": "7.52.0", "description": "table ui component for react", "engines": { "node": ">=8.x" From e97f0df6f8d26fd50779e087a4df86102011a80f Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Aug 2025 15:05:51 +0800 Subject: [PATCH 15/29] refactor: simplify scroll state update logic in stickyScrollBar.tsx (#1318) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/stickyScrollBar.tsx | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/stickyScrollBar.tsx b/src/stickyScrollBar.tsx index cec2666c4..85a640745 100644 --- a/src/stickyScrollBar.tsx +++ b/src/stickyScrollBar.tsx @@ -105,31 +105,20 @@ const StickyScrollBar: React.ForwardRefRenderFunction= currentClientOffset - offsetScroll - ) { - setScrollState(state => ({ - ...state, - isHiddenScrollBar: true, - })); - } else { - setScrollState(state => ({ - ...state, - isHiddenScrollBar: false, - })); - } + setScrollState(state => ({ + ...state, + isHiddenScrollBar: + tableBottomOffset - getScrollBarSize() <= currentClientOffset || + tableOffsetTop >= currentClientOffset - offsetScroll, + })); }); }; const setScrollLeft = (left: number) => { - setScrollState(state => { - return { - ...state, - scrollLeft: (left / bodyScrollWidth) * bodyWidth || 0, - }; - }); + setScrollState(state => ({ + ...state, + scrollLeft: bodyScrollWidth ? (left / bodyScrollWidth) * bodyWidth : 0, + })); }; React.useImperativeHandle(ref, () => ({ From 8fd07903150f745e3591e83680d3ff72b7574c4c Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Aug 2025 15:07:24 +0800 Subject: [PATCH 16/29] chore: rename vitest config file to mts extension (#1319) --- vitest.config.ts => vitest.config.mts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vitest.config.ts => vitest.config.mts (100%) diff --git a/vitest.config.ts b/vitest.config.mts similarity index 100% rename from vitest.config.ts rename to vitest.config.mts From ce95747358eab1f7ed3f6502289dbaa79241c506 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Aug 2025 15:13:55 +0800 Subject: [PATCH 17/29] refactor: remove maxContentScroll prop and related logic from FixedHolder and Table components (#1317) --- src/FixedHolder/index.tsx | 20 +++++--------------- src/Table.tsx | 1 - 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index de95b0203..66a2151f8 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -27,7 +27,6 @@ function useColumnWidth(colWidths: readonly number[], columCount: number) { export interface FixedHeaderProps extends HeaderProps { className: string; noData: boolean; - maxContentScroll: boolean; colWidths: readonly number[]; columCount: number; direction: Direction; @@ -58,7 +57,6 @@ const FixedHolder = React.forwardRef>((pro stickyBottomOffset, stickyClassName, onScroll, - maxContentScroll, children, ...restProps } = props; @@ -98,12 +96,6 @@ const FixedHolder = React.forwardRef>((pro }; }, []); - // Check if all flattenColumns has width - const allFlattenColumnsWithWidth = React.useMemo( - () => flattenColumns.every(column => column.width), - [flattenColumns], - ); - // Add scrollbar column const lastColumn = flattenColumns[flattenColumns.length - 1]; const ScrollBarColumn: ColumnType & { scrollbar: true } = { @@ -156,13 +148,11 @@ const FixedHolder = React.forwardRef>((pro visibility: noData || mergedColumnWidth ? null : 'hidden', }} > - {(!noData || !maxContentScroll || allFlattenColumnsWithWidth) && ( -
+
+
+
+
+
+
+
+
); }; diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index de95b0203..d9e1a0f8b 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -27,7 +27,6 @@ function useColumnWidth(colWidths: readonly number[], columCount: number) { export interface FixedHeaderProps extends HeaderProps { className: string; noData: boolean; - maxContentScroll: boolean; colWidths: readonly number[]; columCount: number; direction: Direction; @@ -37,6 +36,7 @@ export interface FixedHeaderProps extends HeaderProps { stickyClassName?: string; onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void; children: (info: HeaderProps) => React.ReactNode; + colGroup?: React.ReactNode; } const FixedHolder = React.forwardRef>((props, ref) => { @@ -50,6 +50,7 @@ const FixedHolder = React.forwardRef>((pro columns, flattenColumns, colWidths, + colGroup, columCount, stickyOffsets, direction, @@ -58,7 +59,6 @@ const FixedHolder = React.forwardRef>((pro stickyBottomOffset, stickyClassName, onScroll, - maxContentScroll, children, ...restProps } = props; @@ -98,12 +98,6 @@ const FixedHolder = React.forwardRef>((pro }; }, []); - // Check if all flattenColumns has width - const allFlattenColumnsWithWidth = React.useMemo( - () => flattenColumns.every(column => column.width), - [flattenColumns], - ); - // Add scrollbar column const lastColumn = flattenColumns[flattenColumns.length - 1]; const ScrollBarColumn: ColumnType & { scrollbar: true } = { @@ -156,7 +150,10 @@ const FixedHolder = React.forwardRef>((pro visibility: noData || mergedColumnWidth ? null : 'hidden', }} > - {(!noData || !maxContentScroll || allFlattenColumnsWithWidth) && ( + {/* use original ColGroup if no data, otherwise use calculated column width */} + {noData ? ( + colGroup + ) : ( ( // Fixed holder share the props const fixedHolderProps = { noData: !mergedData.length, - maxContentScroll: horizonScroll && mergedScrollX === 'max-content', ...headerProps, ...columnContext, direction, @@ -697,6 +696,7 @@ function Table( stickyTopOffset={offsetHeader} className={`${prefixCls}-header`} ref={scrollHeaderRef} + colGroup={bodyColGroup} > {renderFixedHeaderTable} @@ -712,6 +712,7 @@ function Table( stickyBottomOffset={offsetSummary} className={`${prefixCls}-summary`} ref={scrollSummaryRef} + colGroup={bodyColGroup} > {renderFixedFooterTable} diff --git a/tests/__snapshots__/FixedColumn.spec.tsx.snap b/tests/__snapshots__/FixedColumn.spec.tsx.snap index 1df778d76..6bb8e4339 100644 --- a/tests/__snapshots__/FixedColumn.spec.tsx.snap +++ b/tests/__snapshots__/FixedColumn.spec.tsx.snap @@ -2818,43 +2818,22 @@ LoadedCheerio { > - - - - - - - - - - + + + + + + + + + Date: Fri, 29 Aug 2025 15:30:05 +0800 Subject: [PATCH 20/29] 7.52.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4494d2d00..7485f2c52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.52.0", + "version": "7.52.1", "description": "table ui component for react", "engines": { "node": ">=8.x" From b6de81ca40ba7cbfd001d822d48d1fd81f32f78b Mon Sep 17 00:00:00 2001 From: afc163 Date: Fri, 29 Aug 2025 15:46:57 +0800 Subject: [PATCH 21/29] 7.52.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7485f2c52..cf1b72481 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.52.1", + "version": "7.52.2", "description": "table ui component for react", "engines": { "node": ">=8.x" From 8a7dac08efd54efdcfac685bbe886c61a53cac7a Mon Sep 17 00:00:00 2001 From: afc163 Date: Mon, 1 Sep 2025 16:07:08 +0800 Subject: [PATCH 22/29] fix: Table header layout when data is empty (#1330) --- docs/examples/stickyHeader.tsx | 9 +++++++++ src/FixedHolder/index.tsx | 3 +++ src/Table.tsx | 1 + tests/__snapshots__/FixedColumn.spec.tsx.snap | 10 +++++----- tests/__snapshots__/Table.spec.jsx.snap | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/examples/stickyHeader.tsx b/docs/examples/stickyHeader.tsx index e5013e453..bed178ea3 100644 --- a/docs/examples/stickyHeader.tsx +++ b/docs/examples/stickyHeader.tsx @@ -261,6 +261,15 @@ const Demo = () => { }} sticky /> +
+
({ ...column, width: undefined }))} + data={[]} + scroll={{ + x: 'max-content', + }} + sticky + /> ); }; diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index d9e1a0f8b..f98cc3686 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -34,6 +34,7 @@ export interface FixedHeaderProps extends HeaderProps { stickyTopOffset?: number; stickyBottomOffset?: number; stickyClassName?: string; + scrollTableStyle?: React.CSSProperties; onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void; children: (info: HeaderProps) => React.ReactNode; colGroup?: React.ReactNode; @@ -58,6 +59,7 @@ const FixedHolder = React.forwardRef>((pro stickyTopOffset, stickyBottomOffset, stickyClassName, + scrollTableStyle, onScroll, children, ...restProps @@ -148,6 +150,7 @@ const FixedHolder = React.forwardRef>((pro style={{ tableLayout: 'fixed', visibility: noData || mergedColumnWidth ? null : 'hidden', + ...scrollTableStyle, }} > {/* use original ColGroup if no data, otherwise use calculated column width */} diff --git a/src/Table.tsx b/src/Table.tsx index 8ef2d5a6b..25f63313b 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -684,6 +684,7 @@ function Table( ...columnContext, direction, stickyClassName, + scrollTableStyle, onScroll: onInternalScroll, }; diff --git a/tests/__snapshots__/FixedColumn.spec.tsx.snap b/tests/__snapshots__/FixedColumn.spec.tsx.snap index 6bb8e4339..f2c5abeca 100644 --- a/tests/__snapshots__/FixedColumn.spec.tsx.snap +++ b/tests/__snapshots__/FixedColumn.spec.tsx.snap @@ -1941,7 +1941,7 @@ LoadedCheerio { style="overflow: hidden;" >
shadow should be shown when there are columns where style="overflow: hidden;" >
shadow should display correctly 1`] = ` style="overflow: hidden;" >
shadow should display correctly 2`] = ` style="overflow: hidden;" >
Date: Mon, 1 Sep 2025 18:29:22 +0800 Subject: [PATCH 23/29] fix: insert header content into body measure rows for column width calculation (#1331) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/examples/stickyHeader.tsx | 48 +- src/Body/MeasureCell.tsx | 13 +- src/Body/MeasureRow.tsx | 32 +- src/Body/index.tsx | 1 + src/FixedHolder/index.tsx | 6 +- src/Table.tsx | 1 + tests/__snapshots__/ExpandRow.spec.jsx.snap | 120 ++--- tests/__snapshots__/FixedColumn.spec.tsx.snap | 472 +++++++++--------- tests/__snapshots__/Table.spec.jsx.snap | 20 +- 9 files changed, 390 insertions(+), 323 deletions(-) diff --git a/docs/examples/stickyHeader.tsx b/docs/examples/stickyHeader.tsx index bed178ea3..52f285675 100644 --- a/docs/examples/stickyHeader.tsx +++ b/docs/examples/stickyHeader.tsx @@ -2,7 +2,7 @@ import React, { useRef } from 'react'; import Table from 'rc-table'; import '../../assets/index.less'; -import type { ColumnType } from '@/interface'; +import type { ColumnType, ColumnsType } from '@/interface'; interface RecordType { a?: string; @@ -86,6 +86,34 @@ const columnsWithWidth: ColumnType[] = [ { title: 'title4', dataIndex: 'd', key: 'd', width: 100 }, ]; +const columnsGrouped: ColumnsType = [ + { + title: '', + dataIndex: 'productType', + key: 'productType', + rowSpan: 2, + rowScope: 'row', + }, + { + title: 'Mars', + dataIndex: 'mars', + key: 'mars', + children: [ + { title: 'ProducedProducedProduced', dataIndex: 'producedMars', key: 'producedMars' }, + { title: 'Sold', dataIndex: 'soldMars', key: 'soldMars' }, + ], + }, + { + title: 'Venus', + dataIndex: 'venus', + key: 'venus', + children: [ + { title: 'Produced Produced', dataIndex: 'producedVenus', key: 'producedVenus' }, + { title: 'Sold Sold Sold Sold', dataIndex: 'soldVenus', key: 'soldVenus' }, + ], + }, +]; + const data = [ { a: '123', key: '1' }, { a: 'cdd', b: 'edd', key: '2' }, @@ -270,6 +298,24 @@ const Demo = () => { }} sticky /> +
+
({ ...column, width: undefined }))} + data={[{}]} + scroll={{ + x: 'max-content', + }} + sticky + /> +
+
); }; diff --git a/src/Body/MeasureCell.tsx b/src/Body/MeasureCell.tsx index d96b3508b..81865c1f1 100644 --- a/src/Body/MeasureCell.tsx +++ b/src/Body/MeasureCell.tsx @@ -1,13 +1,15 @@ import * as React from 'react'; import ResizeObserver from 'rc-resize-observer'; import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; +import type { ColumnType } from '../interface'; export interface MeasureCellProps { columnKey: React.Key; onColumnResize: (key: React.Key, width: number) => void; + column?: ColumnType; } -export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellProps) { +export default function MeasureCell({ columnKey, onColumnResize, column }: MeasureCellProps) { const cellRef = React.useRef(); useLayoutEffect(() => { @@ -18,8 +20,13 @@ export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellPr return ( - ); diff --git a/src/Body/MeasureRow.tsx b/src/Body/MeasureRow.tsx index ba95c961d..f7be5370f 100644 --- a/src/Body/MeasureRow.tsx +++ b/src/Body/MeasureRow.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import ResizeObserver from 'rc-resize-observer'; import MeasureCell from './MeasureCell'; import isVisible from 'rc-util/lib/Dom/isVisible'; +import type { ColumnType } from '../interface'; -export interface MeasureCellProps { +export interface MeasureRowProps { prefixCls: string; onColumnResize: (key: React.Key, width: number) => void; columnsKey: React.Key[]; + columns: readonly ColumnType[]; } -export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: MeasureCellProps) { +export default function MeasureRow({ + prefixCls, + columnsKey, + onColumnResize, + columns, +}: MeasureRowProps) { const ref = React.useRef(null); return ( - + { if (isVisible(ref.current)) { @@ -28,9 +30,17 @@ export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: Me } }} > - {columnsKey.map(columnKey => ( - - ))} + {columnsKey.map(columnKey => { + const column = columns.find(col => col.key === columnKey); + return ( + + ); + })} ); diff --git a/src/Body/index.tsx b/src/Body/index.tsx index 0a08edbeb..75991350c 100644 --- a/src/Body/index.tsx +++ b/src/Body/index.tsx @@ -133,6 +133,7 @@ function Body(props: BodyProps) { prefixCls={prefixCls} columnsKey={columnsKey} onColumnResize={onColumnResize} + columns={flattenColumns} /> )} diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index f98cc3686..48f66b4ae 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -7,7 +7,7 @@ import ColGroup from '../ColGroup'; import TableContext from '../context/TableContext'; import type { HeaderProps } from '../Header/Header'; import devRenderTimes from '../hooks/useRenderTimes'; -import type { ColumnsType, ColumnType, Direction } from '../interface'; +import type { ColumnsType, ColumnType, Direction, TableLayout } from '../interface'; function useColumnWidth(colWidths: readonly number[], columCount: number) { return useMemo(() => { @@ -35,6 +35,7 @@ export interface FixedHeaderProps extends HeaderProps { stickyBottomOffset?: number; stickyClassName?: string; scrollTableStyle?: React.CSSProperties; + tableLayout?: TableLayout; onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void; children: (info: HeaderProps) => React.ReactNode; colGroup?: React.ReactNode; @@ -60,6 +61,7 @@ const FixedHolder = React.forwardRef>((pro stickyBottomOffset, stickyClassName, scrollTableStyle, + tableLayout = 'fixed', onScroll, children, ...restProps @@ -148,7 +150,7 @@ const FixedHolder = React.forwardRef>((pro > ( direction, stickyClassName, scrollTableStyle, + tableLayout: mergedTableLayout, onScroll: onInternalScroll, }; diff --git a/tests/__snapshots__/ExpandRow.spec.jsx.snap b/tests/__snapshots__/ExpandRow.spec.jsx.snap index 9ee888bc2..5c0ee296d 100644 --- a/tests/__snapshots__/ExpandRow.spec.jsx.snap +++ b/tests/__snapshots__/ExpandRow.spec.jsx.snap @@ -189,42 +189,42 @@ LoadedCheerio { @@ -374,42 +374,42 @@ LoadedCheerio { @@ -678,42 +678,42 @@ LoadedCheerio { @@ -1234,42 +1234,42 @@ LoadedCheerio { @@ -1422,40 +1422,40 @@ LoadedCheerio { @@ -947,114 +947,114 @@ LoadedCheerio { @@ -1766,114 +1766,114 @@ LoadedCheerio { @@ -2109,114 +2109,114 @@ LoadedCheerio { @@ -2961,114 +2961,114 @@ LoadedCheerio { @@ -3207,42 +3207,42 @@ exports[`Table.FixedColumn > shadow should be shown when there are columns where @@ -5757,78 +5757,78 @@ exports[`Table.FixedColumn > shadow should display correctly 1`] = ` @@ -11749,42 +11749,42 @@ exports[`Table.FixedColumn > shadow should display correctly 2`] = ` diff --git a/tests/__snapshots__/Table.spec.jsx.snap b/tests/__snapshots__/Table.spec.jsx.snap index f1c2c3bfc..8741bf5b8 100644 --- a/tests/__snapshots__/Table.spec.jsx.snap +++ b/tests/__snapshots__/Table.spec.jsx.snap @@ -285,33 +285,33 @@ LoadedCheerio { From 5531b2adc0a003c1c53e3bc59b41c3c60e412b6e Mon Sep 17 00:00:00 2001 From: afc163 Date: Mon, 1 Sep 2025 18:30:12 +0800 Subject: [PATCH 24/29] 7.52.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf1b72481..521bb1e5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.52.2", + "version": "7.52.3", "description": "table ui component for react", "engines": { "node": ">=8.x" From 46a165d785d13e6b9320309fc1733929b81612b5 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 2 Sep 2025 23:30:39 +0800 Subject: [PATCH 25/29] fix: column header not align before column width being calculated (#1332) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/FixedHolder/index.tsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index 48f66b4ae..86e739be1 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -137,6 +137,20 @@ const FixedHolder = React.forwardRef>((pro const mergedColumnWidth = useColumnWidth(colWidths, columCount); + const colGroupNode = useMemo(() => { + // use original ColGroup if no data or no calculated column width, otherwise use calculated column width + if (noData || !mergedColumnWidth) { + return colGroup; + } + return ( + + ); + }, [noData, mergedColumnWidth, colGroup, combinationScrollBarSize, columCount, flattenColumnsWithScrollbar]); + return (
>((pro ...scrollTableStyle, }} > - {/* use original ColGroup if no data, otherwise use calculated column width */} - {noData ? ( - colGroup - ) : ( -
- )} + {colGroupNode} {children({ ...restProps, stickyOffsets: headerStickyOffsets, From 024e63e765b4bf2727f8a43fc9f08f5a5da6d265 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 2 Sep 2025 23:31:51 +0800 Subject: [PATCH 26/29] 7.52.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 521bb1e5b..d4257d466 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.52.3", + "version": "7.52.4", "description": "table ui component for react", "engines": { "node": ">=8.x" From 29685c27e4f776f6ed33e094c77a3f65fece607f Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 3 Sep 2025 18:41:17 +0800 Subject: [PATCH 27/29] fix: handle empty or falsy column widths in FixedHolder (#1333) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ColGroup.tsx | 2 +- src/FixedHolder/index.tsx | 17 +++++++++++++++-- tests/__snapshots__/Table.spec.jsx.snap | 15 +-------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ColGroup.tsx b/src/ColGroup.tsx index a20e58c28..6c49f304f 100644 --- a/src/ColGroup.tsx +++ b/src/ColGroup.tsx @@ -22,7 +22,7 @@ function ColGroup({ colWidths, columns, columCount }: ColGroupProps< for (let i = len - 1; i >= 0; i -= 1) { const width = colWidths[i]; const column = columns && columns[i]; - let additionalProps; + let additionalProps: Record; let minWidth: number; if (column) { additionalProps = column[INTERNAL_COL_DEFINE]; diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index 86e739be1..09e4598b1 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -139,7 +139,13 @@ const FixedHolder = React.forwardRef>((pro const colGroupNode = useMemo(() => { // use original ColGroup if no data or no calculated column width, otherwise use calculated column width - if (noData || !mergedColumnWidth) { + // Return original colGroup if no data, or mergedColumnWidth is empty, or all widths are falsy + if ( + noData || + !mergedColumnWidth || + mergedColumnWidth.length === 0 || + mergedColumnWidth.every(width => !width) + ) { return colGroup; } return ( @@ -149,7 +155,14 @@ const FixedHolder = React.forwardRef>((pro columns={flattenColumnsWithScrollbar} /> ); - }, [noData, mergedColumnWidth, colGroup, combinationScrollBarSize, columCount, flattenColumnsWithScrollbar]); + }, [ + noData, + mergedColumnWidth, + colGroup, + combinationScrollBarSize, + columCount, + flattenColumnsWithScrollbar, + ]); return (
-
- - - - - + Date: Wed, 3 Sep 2025 18:41:50 +0800 Subject: [PATCH 28/29] 7.52.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4257d466..32ef40f47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-table", - "version": "7.52.4", + "version": "7.52.5", "description": "table ui component for react", "engines": { "node": ">=8.x" From b0a7ff0104eb36bdea0c698ce8acaf3235d30b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E5=B3=B0?= <645381995@qq.com> Date: Thu, 4 Sep 2025 13:22:26 +0800 Subject: [PATCH 29/29] feat: sync v5 --- .npmrc | 1 + tests/__snapshots__/ExpandRow.spec.jsx.snap | 120 ++--- tests/__snapshots__/FixedColumn.spec.tsx.snap | 415 ++++++++---------- tests/__snapshots__/Table.spec.jsx.snap | 38 +- 4 files changed, 260 insertions(+), 314 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..c483022c0 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true \ No newline at end of file diff --git a/tests/__snapshots__/ExpandRow.spec.jsx.snap b/tests/__snapshots__/ExpandRow.spec.jsx.snap index bedeb127d..51d854a1f 100644 --- a/tests/__snapshots__/ExpandRow.spec.jsx.snap +++ b/tests/__snapshots__/ExpandRow.spec.jsx.snap @@ -158,42 +158,42 @@ exports[`Table.Expand > does not crash if scroll is not set 1`] = ` @@ -313,42 +313,42 @@ exports[`Table.Expand > does not crash if scroll is not set 2`] = ` @@ -557,42 +557,42 @@ exports[`Table.Expand > renders fixed column correctly > work 1`] = ` @@ -1023,42 +1023,42 @@ exports[`Table.Expand > work in expandable fix 1`] = ` @@ -1181,40 +1181,40 @@ exports[`Table.Expand > work in expandable fix 2`] = ` @@ -917,114 +917,114 @@ exports[`Table.FixedColumn > renders correctly > scrollX - with data 1`] = ` @@ -1706,114 +1706,114 @@ exports[`Table.FixedColumn > renders correctly > scrollX - without data 1`] = ` @@ -1851,7 +1851,7 @@ exports[`Table.FixedColumn > renders correctly > scrollXY - with data 1`] = ` style="overflow: hidden;" >
-
 
+
+
+ {column?.title || '\xa0'} +
renders correctly > scrollXY - with data 1`] = ` @@ -2694,49 +2694,8 @@ exports[`Table.FixedColumn > renders correctly > scrollXY - without data 1`] = ` style="overflow: hidden;" >
- - - - - - - - - - - - - - - @@ -2862,114 +2821,114 @@ exports[`Table.FixedColumn > renders correctly > scrollXY - without data 1`] = ` diff --git a/tests/__snapshots__/Table.spec.jsx.snap b/tests/__snapshots__/Table.spec.jsx.snap index 29c6638b5..5fd5ed333 100644 --- a/tests/__snapshots__/Table.spec.jsx.snap +++ b/tests/__snapshots__/Table.spec.jsx.snap @@ -86,22 +86,8 @@ exports[`Table.Basic > custom components > renders fixed column and header corre style="overflow: hidden;" >
- - - - - - custom components > renders fixed column and header corre @@ -232,7 +218,7 @@ exports[`Table.Basic > custom components > scroll content > with scroll 1`] = ` style="overflow: hidden;" >