Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cad1f52
fix: cherry pick of fixing sticky issue (#1232)
zombieJ Feb 14, 2025
542000c
7.50.3
zombieJ Feb 14, 2025
1e2ec33
fix: virtual scroll logic (#1240)
zombieJ Mar 6, 2025
a056eaa
7.50.4
zombieJ Mar 6, 2025
42c37bd
fix: 7.x header blank when using sticky or scroll.y (#1268)
linxianxi May 12, 2025
17fa387
7.50.5
afc163 May 12, 2025
bd18ca4
feat: Table support expandedRowOffset by V5 (#1283)
crazyair May 27, 2025
8e6e892
7.51.0
zombieJ May 27, 2025
ffa3cea
chore: defensive program (#1294)
zombieJ Jun 17, 2025
6c7dee3
7.51.1
zombieJ Jun 17, 2025
1b655a7
fix: Adjust the top height of the proxy to solve the sliding issue (#…
Wxh16144 Aug 25, 2025
48c8976
Fix fixed property not being inherited by children columns (#1303)
ryantang247 Aug 25, 2025
3e9410a
fix: expand column not visible when expandable.fixed is true (#1314)
inottn Aug 26, 2025
3916bd3
7.52.0
afc163 Aug 26, 2025
e97f0df
refactor: simplify scroll state update logic in stickyScrollBar.tsx …
afc163 Aug 26, 2025
8fd0790
chore: rename vitest config file to mts extension (#1319)
afc163 Aug 26, 2025
ce95747
refactor: remove maxContentScroll prop and related logic from FixedHo…
afc163 Aug 26, 2025
d22b38c
Revert "refactor: remove maxContentScroll prop and related logic from…
afc163 Aug 26, 2025
1b75be2
fix: fix sticky header column width with empty data (#1323)
afc163 Aug 29, 2025
9fd05ef
7.52.1
afc163 Aug 29, 2025
b6de81c
7.52.2
afc163 Aug 29, 2025
8a7dac0
fix: Table header layout when data is empty (#1330)
afc163 Sep 1, 2025
39c323a
fix: insert header content into body measure rows for column width ca…
afc163 Sep 1, 2025
5531b2a
7.52.3
afc163 Sep 1, 2025
46a165d
fix: column header not align before column width being calculated (#1…
afc163 Sep 2, 2025
024e63e
7.52.4
afc163 Sep 2, 2025
29685c2
fix: handle empty or falsy column widths in FixedHolder (#1333)
afc163 Sep 3, 2025
5246767
7.52.5
afc163 Sep 3, 2025
54f9f28
feat: sync v5
crazyair Sep 4, 2025
b0a7ff0
feat: sync v5
crazyair Sep 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shamefully-hoist=true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

请确认团队确实使用 pnpm;shamefully-hoist 为 pnpm 专属配置。

若项目并未统一使用 pnpm,此项在 npm/yarn 下无效,且可能让新同学误解。若确需启用,建议补充注释或以更细粒度的 public-hoist-pattern 限定,以避免无差别提升带来的体积增大与掩盖 peerDependencies 问题。

可运行下列脚本核实仓库是否以 pnpm 为标准(检查 lockfile、packageManager 字段与 CI 使用情况):

如需仅保留说明,可采纳以下微调,以增加可维护性:

-shamefully-hoist=true
+# pnpm only: flatten node_modules for legacy deps; prefer public-hoist-pattern when possible.
+shamefully-hoist=true

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Lockfiles =="
fd -a -H 'pnpm-lock.yaml' || true
fd -a -H 'yarn.lock' || true
fd -a -H 'package-lock.json' || true

echo "== packageManager field =="
if [ -f package.json ]; then
  jq -r '.packageManager // "N/A"' package.json
else
  echo "package.json not found"
fi

echo "== CI references to pnpm/corepack =="
rg -nP -C2 '\bpnpm\b|\bcorepack\b' -g '.github/workflows/**' || true

Length of output: 579


确认并纠正 .npmrc 中的 pnpm 专属配置
当前仓库未检测到 pnpm-lock.yaml、packageManager 字段或 CI 工作流中的 pnpm 相关引用,说明并未使用 pnpm。

  • 若非 pnpm 项目,删除 shamefully-hoist=true
  • 若确需使用 pnpm 并保持此配置,请添加注释并优先采用 public-hoist-pattern 以限定依赖提升范围,避免体积膨胀和 peerDependencies 被隐藏。
🤖 Prompt for AI Agents
In .npmrc around line 1, the file currently contains a pnpm-specific setting
(shamefully-hoist=true) but the repo lacks pnpm indicators; if this is not a
pnpm project, remove that line from .npmrc; if you do intend to use pnpm and
keep this behavior, add a comment explaining why shamefully-hoist is required
and replace or accompany it with a public-hoist-pattern setting to limit which
packages are hoisted (thereby preventing bundle bloat and hiding
peerDependencies).

116 changes: 110 additions & 6 deletions docs/examples/stickyHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -79,6 +79,41 @@ const columns: ColumnType<RecordType>[] = [
},
];

const columnsWithWidth: ColumnType<RecordType>[] = [
{ 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 columnsGrouped: ColumnsType<any> = [
{
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' },
Expand All @@ -105,11 +140,7 @@ const data = [
const Demo = () => {
const container = useRef();
return (
<div
style={{
height: 10000,
}}
>
<div>
<h2>Sticky</h2>
<Table<RecordType>
columns={columns}
Expand Down Expand Up @@ -212,6 +243,79 @@ const Demo = () => {
suscipit asperiores, id ullam in iste soluta dignissimos vero incidunt, rem ex consectetur
beatae totam aperiam. Sunt, laudantium?
</div>

<h2>Sticky header with empty data</h2>
<Table
columns={fixedColumns}
data={[]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<Table
columns={fixedColumns}
data={[]}
scroll={{
x: 1200,
}}
sticky
/>
<br />
<Table
columns={columnsWithWidth}
data={[]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<Table
columns={fixedColumns}
data={[{}]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<Table
columns={columnsWithWidth}
data={[{}]}
scroll={{
x: 1200,
}}
sticky
/>
<br />
<Table
columns={fixedColumns.map(column => ({ ...column, width: undefined }))}
data={[]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<Table
columns={fixedColumns.map(column => ({ ...column, width: undefined }))}
data={[{}]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<Table
columns={columnsGrouped}
data={[{}, {}]}
scroll={{
x: 'max-content',
}}
sticky
/>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rc-component/table",
"version": "1.5.2",
"version": "1.5.3",
"description": "table ui component for react",
"engines": {
"node": ">=8.x"
Expand Down
13 changes: 10 additions & 3 deletions src/Body/MeasureCell.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as React from 'react';
import ResizeObserver from '@rc-component/resize-observer';
import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
import type { ColumnType } from '../interface';

export interface MeasureCellProps {
columnKey: React.Key;
onColumnResize: (key: React.Key, width: number) => void;
column?: ColumnType<any>;
}

export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellProps) {
export default function MeasureCell({ columnKey, onColumnResize, column }: MeasureCellProps) {
const cellRef = React.useRef<HTMLTableCellElement>();

useLayoutEffect(() => {
Expand All @@ -18,8 +20,13 @@ export default function MeasureCell({ columnKey, onColumnResize }: MeasureCellPr

return (
<ResizeObserver data={columnKey}>
<td ref={cellRef} style={{ padding: 0, border: 0, height: 0 }}>
<div style={{ height: 0, overflow: 'hidden' }}>&nbsp;</div>
<td
ref={cellRef}
style={{ paddingTop: 0, paddingBottom: 0, borderTop: 0, borderBottom: 0, height: 0 }}
>
<div style={{ height: 0, overflow: 'hidden', fontWeight: 'bold' }}>
{column?.title || '\xa0'}
</div>
</td>
</ResizeObserver>
);
Expand Down
32 changes: 21 additions & 11 deletions src/Body/MeasureRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@ import * as React from 'react';
import ResizeObserver from '@rc-component/resize-observer';
import MeasureCell from './MeasureCell';
import isVisible from '@rc-component/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<any>[];
}

export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: MeasureCellProps) {
export default function MeasureRow({
prefixCls,
columnsKey,
onColumnResize,
columns,
}: MeasureRowProps) {
const ref = React.useRef<HTMLTableRowElement>(null);

return (
<tr
aria-hidden="true"
className={`${prefixCls}-measure-row`}
style={{ height: 0, fontSize: 0 }}
ref={ref}
>
<tr aria-hidden="true" className={`${prefixCls}-measure-row`} style={{ height: 0 }} ref={ref}>
<ResizeObserver.Collection
onBatchResize={infoList => {
if (isVisible(ref.current)) {
Expand All @@ -28,9 +30,17 @@ export default function MeasureRow({ prefixCls, columnsKey, onColumnResize }: Me
}
}}
>
{columnsKey.map(columnKey => (
<MeasureCell key={columnKey} columnKey={columnKey} onColumnResize={onColumnResize} />
))}
{columnsKey.map(columnKey => {
const column = columns.find(col => col.key === columnKey);
return (
<MeasureCell
key={columnKey}
columnKey={columnKey}
onColumnResize={onColumnResize}
column={column}
/>
);
})}
</ResizeObserver.Collection>
</tr>
);
Expand Down
1 change: 1 addition & 0 deletions src/Body/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ function Body<RecordType>(props: BodyProps<RecordType>) {
prefixCls={prefixCls}
columnsKey={columnsKey}
onColumnResize={onColumnResize}
columns={flattenColumns}
/>
)}

Expand Down
2 changes: 1 addition & 1 deletion src/ColGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function ColGroup<RecordType>({ 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<string, unknown>;
let minWidth: number;
if (column) {
additionalProps = column[INTERNAL_COL_DEFINE];
Expand Down
49 changes: 34 additions & 15 deletions src/FixedHolder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -36,6 +36,8 @@ export interface FixedHeaderProps<RecordType> extends HeaderProps<RecordType> {
stickyTopOffset?: number;
stickyBottomOffset?: number;
stickyClassName?: string;
scrollTableStyle?: React.CSSProperties;
tableLayout?: TableLayout;
onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void;
children: (info: HeaderProps<RecordType>) => React.ReactNode;
}
Expand All @@ -59,6 +61,8 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((pro
stickyTopOffset,
stickyBottomOffset,
stickyClassName,
scrollTableStyle,
tableLayout = 'fixed',
onScroll,
maxContentScroll,
children,
Expand Down Expand Up @@ -115,12 +119,6 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((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<unknown> & { scrollbar: true } = {
Expand Down Expand Up @@ -158,6 +156,32 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((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
// 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 (
<ColGroup
colWidths={[...mergedColumnWidth, combinationScrollBarSize]}
columCount={columCount + 1}
columns={flattenColumnsWithScrollbar}
/>
);
}, [
noData,
mergedColumnWidth,
combinationScrollBarSize,
columCount,
flattenColumnsWithScrollbar,
]);

return (
<div
style={{
Expand All @@ -172,17 +196,12 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((pro
>
<TableComponent
style={{
tableLayout: 'fixed',
tableLayout,
visibility: noData || mergedColumnWidth ? null : 'hidden',
...scrollTableStyle,
}}
>
{(!noData || !maxContentScroll || allFlattenColumnsWithWidth) && (
<ColGroup
colWidths={mergedColumnWidth ? [...mergedColumnWidth, combinationScrollBarSize] : []}
columCount={columCount + 1}
columns={flattenColumnsWithScrollbar}
/>
)}
{colGroupNode}
{children({
...restProps,
stickyOffsets: headerStickyOffsets,
Expand Down
2 changes: 2 additions & 0 deletions src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,8 @@ function Table<RecordType extends DefaultRecordType>(
...columnContext,
direction,
stickyClassName,
scrollTableStyle,
tableLayout: mergedTableLayout,
onScroll: onInternalScroll,
};

Expand Down
Loading