Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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: 0 additions & 1 deletion assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@

padding: @cell-padding;
white-space: normal;
word-break: break-word;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The removal of word-break: break-word; could lead to UI issues where long, unbreakable words in table cells overflow their container. This might disrupt the table layout. It's recommended to retain this property or provide an alternative solution for handling long text, such as using overflow-wrap: break-word; which is the modern equivalent.

border: @border;
border-top: 0;
border-left: 0;
Expand Down
47 changes: 47 additions & 0 deletions docs/examples/measureRowRender.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import Table from 'rc-table';

// 示例:使用 measureRowRender 来隐藏 MeasureRow 中的弹层
const MeasureRowRenderExample = () => {
const columns = [
{
title: (
<div>
Name
<div className="filter-dropdown" style={{ display: 'none' }}>
Filter Content
</div>
</div>
),
dataIndex: 'name',
key: 'name',
width: 100,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 80,
},
];

const data = [
{ key: 1, name: 'John', age: 25 },
{ key: 2, name: 'Jane', age: 30 },
];

// 自定义 MeasureRow 渲染,隐藏弹层内容
const measureRowRender = measureRow => <div style={{ display: 'none' }}>{measureRow}</div>;

return (
<Table
columns={columns}
data={data}
sticky
scroll={{ x: true }}
measureRowRender={measureRowRender}
/>
);
};

export default MeasureRowRenderExample;
3 changes: 3 additions & 0 deletions docs/examples/scrollY.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ const Test = () => {
rowKey={record => record.key}
onRow={(record, index) => ({ style: { backgroundColor: 'red' } })}
/>
<h3>Column align issue</h3>
<p>https://github.com/ant-design/ant-design/issues/54889</p>
<Table columns={columns} data={data} sticky scroll={{ y: 300, x: 2000 }} />
</div>
);
};
Expand Down
50 changes: 46 additions & 4 deletions docs/examples/stickyHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,34 @@ const data = [
{ a: '1333', c: 'eee', d: 2, key: '20' },
];

const columns3: ColumnType<RecordType>[] = [
{ title: '', dataIndex: 'name', key: '0' },
{ title: 'First column', dataIndex: 'name', key: '1' },
{ title: 'Second column', dataIndex: 'address', key: '2' },
{ title: 'Third column', dataIndex: 'age', key: '3' },
{ title: 'Another column', dataIndex: 'address', key: '4' },
{ title: 'Extra column', dataIndex: 'name', key: '5' },
{ title: 'And yet another column', dataIndex: 'address', key: '6' },
{
title: 'Column 7 with extraaaaaaaaa long word',
dataIndex: 'age',
key: '7',
},
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{ title: 'Column 9', dataIndex: 'name', key: '9' },
{ title: 'Column 10', dataIndex: 'address', key: '10' },
{ title: 'Column 11', dataIndex: 'address', key: '11' },
{ title: 'Column 12', dataIndex: 'age', key: '12' },
{ title: 'Column 13', dataIndex: 'address', key: '13' },
{ title: 'Column 14', dataIndex: 'name', key: '14' },
{ title: 'Column 15', dataIndex: 'address', key: '15' },
{ title: 'Column 16', dataIndex: 'address', key: '16' },
{ title: 'Column 17', dataIndex: 'name', key: '17' },
{ title: 'Column 18', dataIndex: 'address', key: '18' },
{ title: 'Column 19', dataIndex: 'address', key: '19' },
{ title: 'Column 20', dataIndex: 'age', key: '20' },
];

const Demo = () => {
const container = useRef();
return (
Expand Down Expand Up @@ -274,7 +302,7 @@ const Demo = () => {
<br />
<Table
columns={fixedColumns}
data={[{}]}
data={[{ key: '1' }]}
scroll={{
x: 'max-content',
}}
Expand All @@ -283,7 +311,7 @@ const Demo = () => {
<br />
<Table
columns={columnsWithWidth}
data={[{}]}
data={[{ key: '1' }]}
scroll={{
x: 1200,
}}
Expand All @@ -301,7 +329,7 @@ const Demo = () => {
<br />
<Table
columns={fixedColumns.map(column => ({ ...column, width: undefined }))}
data={[{}]}
data={[{ key: '1' }]}
scroll={{
x: 'max-content',
}}
Expand All @@ -310,12 +338,26 @@ const Demo = () => {
<br />
<Table
columns={columnsGrouped}
data={[{}, {}]}
data={[{ key: '1' }, { key: '2' }]}
scroll={{
x: 'max-content',
}}
sticky
/>
<br />
<h3>scroll.x is true</h3>
<p>https://github.com/ant-design/ant-design/issues/54894</p>
<Table
columns={columns3}
data={
[
{ key: '1', name: 'Test', age: 1, address: '2' },
{ key: '2', name: '0', age: 1, address: '2' },
] as any
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using as any bypasses TypeScript's type safety and should be avoided, especially in examples that serve as a reference for users. It would be better to define a proper type for the data and use it consistently for both the columns and the Table component to ensure type safety.

For example:

interface MyStickyRecord {
  key: string;
  name: string;
  age: number;
  address: string;
}

const columns3: ColumnType<MyStickyRecord>[] = [/* ... */];

// ... in component
<Table<MyStickyRecord>
  columns={columns3}
  data={[
    { key: '1', name: 'Test', age: 1, address: '2' },
    { key: '2', name: '0', age: 1, address: '2' },
  ]}
  // ...
/>

}
sticky
scroll={{ x: true }}
/>
</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-table",
"version": "7.52.5",
"version": "7.53.0",
"description": "table ui component for react",
"engines": {
"node": ">=8.x"
Expand Down
7 changes: 6 additions & 1 deletion src/Body/MeasureRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from 'react';
import ResizeObserver from 'rc-resize-observer';
import MeasureCell from './MeasureCell';
import isVisible from 'rc-util/lib/Dom/isVisible';
import { useContext } from '@rc-component/context';
import TableContext from '../context/TableContext';
import type { ColumnType } from '../interface';

export interface MeasureRowProps {
Expand All @@ -18,8 +20,9 @@ export default function MeasureRow({
columns,
}: MeasureRowProps) {
const ref = React.useRef<HTMLTableRowElement>(null);
const { measureRowRender } = useContext(TableContext, ['measureRowRender']);

return (
const measureRow = (
<tr aria-hidden="true" className={`${prefixCls}-measure-row`} style={{ height: 0 }} ref={ref}>
<ResizeObserver.Collection
onBatchResize={infoList => {
Expand All @@ -44,4 +47,6 @@ export default function MeasureRow({
</ResizeObserver.Collection>
</tr>
);

return measureRowRender ? measureRowRender(measureRow) : measureRow;
}
2 changes: 1 addition & 1 deletion src/ColGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function ColGroup<RecordType>({ colWidths, columns, columCount }: ColGroupProps<
}
}

return <colgroup>{cols}</colgroup>;
return cols.length > 0 ? <colgroup>{cols}</colgroup> : null;
}

export default ColGroup;
9 changes: 5 additions & 4 deletions src/FixedHolder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface FixedHeaderProps<RecordType> extends HeaderProps<RecordType> {
stickyTopOffset?: number;
stickyBottomOffset?: number;
stickyClassName?: string;
scrollTableStyle?: React.CSSProperties;
scrollX?: number | string | true;
tableLayout?: TableLayout;
onScroll: (info: { currentTarget: HTMLDivElement; scrollLeft?: number }) => void;
children: (info: HeaderProps<RecordType>) => React.ReactNode;
Expand All @@ -60,7 +60,7 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((pro
stickyTopOffset,
stickyBottomOffset,
stickyClassName,
scrollTableStyle,
scrollX,
tableLayout = 'fixed',
onScroll,
children,
Expand Down Expand Up @@ -178,8 +178,9 @@ const FixedHolder = React.forwardRef<HTMLDivElement, FixedHeaderProps<any>>((pro
<TableComponent
style={{
tableLayout,
visibility: noData || mergedColumnWidth ? null : 'hidden',
...scrollTableStyle,
minWidth: '100%',
// https://github.com/ant-design/ant-design/issues/54894
width: scrollX,
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The width style property is being set directly to scrollX. The scrollX prop can be true, which is not a valid CSS value for width and will cause rendering issues. The logic should handle the boolean case, for example by setting width to 'auto'.

Suggested change
width: scrollX,
width: scrollX === true ? 'auto' : scrollX,

}}
>
{colGroupNode}
Expand Down
16 changes: 15 additions & 1 deletion src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ export interface TableProps<RecordType = any>
internalRefs?: {
body: React.MutableRefObject<HTMLDivElement>;
};
/**
* @private Internal usage, may remove by refactor.
*
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
*/
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
}

function defaultEmpty() {
Expand Down Expand Up @@ -210,6 +216,9 @@ function Table<RecordType extends DefaultRecordType>(
onRow,
onHeaderRow,

// Measure Row
measureRowRender,

// Events
onScroll,

Expand Down Expand Up @@ -684,7 +693,7 @@ function Table<RecordType extends DefaultRecordType>(
...columnContext,
direction,
stickyClassName,
scrollTableStyle,
scrollX: mergedScrollX,
tableLayout: mergedTableLayout,
onScroll: onInternalScroll,
};
Expand Down Expand Up @@ -850,6 +859,9 @@ function Table<RecordType extends DefaultRecordType>(
childrenColumnName: mergedChildrenColumnName,

rowHoverable,

// Measure Row
measureRowRender,
}),
[
// Scroll
Expand Down Expand Up @@ -901,6 +913,8 @@ function Table<RecordType extends DefaultRecordType>(
mergedChildrenColumnName,

rowHoverable,

measureRowRender,
],
);

Expand Down
7 changes: 4 additions & 3 deletions src/VirtualTable/BodyGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
// ========================== Column ==========================
const columnsWidth = React.useMemo<[key: React.Key, width: number, total: number][]>(() => {
let total = 0;
return flattenColumns.map(({ width, key }) => {
total += width as number;
return [key, width as number, total];
return flattenColumns.map(({ width, minWidth, key }) => {
const finalWidth = Math.max((width as number) || 0, (minWidth as number) || 0);
total += finalWidth;
return [key, finalWidth, total];
Comment on lines +61 to +63
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The width property on a column can be a string (e.g., '100px'), but the current logic (width as number) only casts it in TypeScript and doesn't parse it at runtime. This can lead to NaN from Math.max if width is a string with units, causing incorrect layout calculations for the virtualized table. The width should be parsed to a number before being used in calculations.

      const finalWidth = Math.max(parseInt(String(width), 10) || 0, minWidth || 0);
      total += finalWidth;
      return [key, finalWidth, total];

});
Comment on lines +60 to 64
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

列宽计算对 string 宽度不安全,且 scrollWidth 与 scroll.x: true 可能不兼容

  • 这里对 width 直接参与 Math.max,当 width 为字符串(如 '100%'/'max-content'/'100px')将导致 NaN,进而污染 total 与 offset。
  • TableContext.scrollX 的类型包含 true,但 VirtualList 的 scrollWidth 期望数值;当前传递 true 会在运行时成为布尔值,存在不兼容风险。

建议修复(仅变更本片段内逻辑):

-    return flattenColumns.map(({ width, minWidth, key }) => {
-      const finalWidth = Math.max((width as number) || 0, (minWidth as number) || 0);
+    return flattenColumns.map(({ width, minWidth, key }) => {
+      const w = typeof width === 'number' ? width : 0;
+      const mw = typeof minWidth === 'number' ? minWidth : 0;
+      const finalWidth = Math.max(w, mw);
       total += finalWidth;
       return [key, finalWidth, total];
     });

并在 VirtualList 处(Line 245 附近)用总列宽兜底,避免把 true 透传为 scrollWidth

// 建议将 scrollWidth 改成:
scrollWidth={
  typeof scrollX === 'number'
    ? scrollX
    : (columnsWidth[columnsWidth.length - 1]?.[2] ?? 0)
}
🤖 Prompt for AI Agents
In src/VirtualTable/BodyGrid.tsx around lines 60-64 and the VirtualList usage
near line 245, the current logic uses width/minWidth directly in Math.max and
may pass TableContext.scrollX === true into VirtualList.scrollWidth; parse and
coerce widths to safe numbers and provide a numeric fallback for scrollWidth.
Specifically, in the map at lines ~60-64, convert width and minWidth into
numeric values (e.g., if typeof value === 'number' && isFinite(value) use it; if
typeof value === 'string' attempt parseFloat(value) and treat NaN as 0) then
compute finalWidth = Math.max(parsedWidth, parsedMinWidth, 0) and accumulate
total from that numeric finalWidth; and at the VirtualList call near line 245,
pass scrollWidth as typeof scrollX === 'number' ? scrollX :
(columnsWidth[columnsWidth.length - 1]?.[2] ?? 0) to avoid forwarding true.

}, [flattenColumns]);

Expand Down
3 changes: 3 additions & 0 deletions src/context/TableContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export interface TableContextProps<RecordType = any> {
rowHoverable?: boolean;

expandedRowOffset: ExpandableConfig<RecordType>['expandedRowOffset'];

// Measure Row
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
}

const TableContext = createContext<TableContextProps>();
Expand Down
Loading