Skip to content

Commit

Permalink
feat: Table support virtual (ant-design#44349)
Browse files Browse the repository at this point in the history
* chore: update demo

* chore: adjust fixed style

* chore: opt scroll height

* chore: clean up

* chore: update demo

* chore: bump rc-virtual-list

* chore: update deps

* chore: bump rc-table

* fix: clean up

* chore: fix demo

* test: add test case
  • Loading branch information
zombieJ authored Aug 28, 2023
1 parent 4a896e9 commit 6f2bddd
Show file tree
Hide file tree
Showing 15 changed files with 302 additions and 146 deletions.
47 changes: 40 additions & 7 deletions components/table/InternalTable.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import * as React from 'react';
import classNames from 'classnames';
import { INTERNAL_HOOKS, type TableProps as RcTableProps } from 'rc-table';
import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns';
import omit from 'rc-util/lib/omit';
import * as React from 'react';

import type { Breakpoint } from '../_util/responsiveObserver';
import scrollTo from '../_util/scrollTo';
import type { AnyObject } from '../_util/type';
import warning from '../_util/warning';
import type { SizeType } from '../config-provider/SizeContext';
import type { ConfigConsumerProps } from '../config-provider/context';
import { ConfigContext } from '../config-provider/context';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import useBreakpoint from '../grid/hooks/useBreakpoint';
import defaultLocale from '../locale/en_US';
import Pagination from '../pagination';
import type { SpinProps } from '../spin';
import Spin from '../spin';
import { useToken } from '../theme/internal';
import type { TooltipProps } from '../tooltip';
import renderExpandIcon from './ExpandIcon';
import RcTable from './RcTable';
import type { FilterState } from './hooks/useFilter';
import useFilter, { getFilterData } from './hooks/useFilter';
import useLazyKVMap from './hooks/useLazyKVMap';
Expand All @@ -29,23 +30,25 @@ import type { SortState } from './hooks/useSorter';
import useSorter, { getSortData } from './hooks/useSorter';
import useTitleColumns from './hooks/useTitleColumns';
import type {
ColumnsType,
ColumnTitleProps,
ColumnType,
ColumnsType,
ExpandType,
ExpandableConfig,
ExpandType,
FilterValue,
GetPopupContainer,
GetRowKey,
RefInternalTable,
SortOrder,
SorterResult,
SortOrder,
TableAction,
TableCurrentDataSource,
TableLocale,
TablePaginationConfig,
TableRowSelection,
} from './interface';
import RcTable from './RcTable';
import RcVirtualTable from './RcTable/VirtualTable';
import useStyle from './style';

export type { ColumnsType, TablePaginationConfig };
Expand Down Expand Up @@ -107,6 +110,7 @@ export interface TableProps<RecordType>
};
sortDirections?: SortOrder[];
showSorterTooltip?: boolean | TooltipProps;
virtual?: boolean;
}

const InternalTable = <RecordType extends AnyObject = AnyObject>(
Expand Down Expand Up @@ -141,6 +145,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
sortDirections,
locale,
showSorterTooltip = true,
virtual,
} = props;

if (process.env.NODE_ENV !== 'production') {
Expand Down Expand Up @@ -514,6 +519,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(

// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const [, token] = useToken();

const wrapperClassNames = classNames(
`${prefixCls}-wrapper`,
Expand All @@ -532,11 +538,38 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
<DefaultRenderEmpty componentName="Table" />
);

// ========================== Render ==========================
const TableComponent = virtual ? RcVirtualTable : RcTable;

// >>> Virtual Table props. We set height here since it will affect height collection
const virtualProps: { listItemHeight?: number } = {};

const listItemHeight = React.useMemo(() => {
const { fontSize, lineHeight, padding, paddingXS, paddingSM } = token;
const fontHeight = Math.floor(fontSize * lineHeight);

switch (mergedSize) {
case 'large':
return padding * 2 + fontHeight;

case 'small':
return paddingXS * 2 + fontHeight;

default:
return paddingSM * 2 + fontHeight;
}
}, [token, mergedSize]);

if (virtual) {
virtualProps.listItemHeight = listItemHeight;
}

return wrapSSR(
<div ref={ref} className={wrapperClassNames} style={mergedStyle}>
<Spin spinning={false} {...spinProps}>
{topPaginationNode}
<RcTable<RecordType>
<TableComponent
{...virtualProps}
{...tableProps}
columns={mergedColumns as RcTableProps<RecordType>['columns']}
direction={direction}
Expand Down
12 changes: 12 additions & 0 deletions components/table/RcTable/VirtualTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { genVirtualTable } from 'rc-table';
import type { InternalTableProps } from '../InternalTable';

/**
* Same as `rc-table` but we modify trigger children update logic instead.
*/
export default genVirtualTable((prev, next) => {
const { _renderTimes: prevRenderTimes } = prev as InternalTableProps<any>;
const { _renderTimes: nextRenderTimes } = next as InternalTableProps<any>;

return prevRenderTimes !== nextRenderTimes;
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { genTable } from 'rc-table';
import type { InternalTableProps } from './InternalTable';
import type { InternalTableProps } from '../InternalTable';

/**
* Same as `rc-table` but we modify trigger children update logic instead.
Expand Down
39 changes: 39 additions & 0 deletions components/table/__tests__/Table.virtual.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';

import Table from '..';
import { render } from '../../../tests/utils';

describe('Table.Virtual', () => {
it('should work', () => {
const { container } = render(
<Table
virtual
scroll={{ x: 100, y: 100 }}
columns={[
{
dataIndex: 'key',
},
]}
dataSource={[
{
key: 'bamboo',
},
]}
/>,
);

expect(container.querySelectorAll('.rc-virtual-list-holder .ant-table-cell')).toHaveLength(1);
expect(container.querySelector('.rc-virtual-list-holder .ant-table-cell')?.textContent).toEqual(
'bamboo',
);
});

// warning from `rc-table`
it('warning if no scroll', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Table virtual />);

expect(errSpy).toHaveBeenCalledWith('Warning: `scroll.x` in virtual table must be number.');
errSpy.mockRestore();
});
});
2 changes: 1 addition & 1 deletion components/table/__tests__/demo-extend.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { extendTest } from '../../../tests/shared/demoTest';

extendTest('table', { skip: ['ajax.tsx'] });
extendTest('table', { skip: ['ajax.tsx', 'virtual-list.tsx'] });
2 changes: 1 addition & 1 deletion components/table/__tests__/demo.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';

demoTest('table', { skip: ['ajax.tsx'] });
demoTest('table', { skip: ['ajax.tsx', 'virtual-list.tsx'] });
2 changes: 1 addition & 1 deletion components/table/__tests__/image.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';

describe('Table image', () => {
imageDemoTest('table');
imageDemoTest('table', { skip: ['virtual-list.tsx'] });
});
11 changes: 2 additions & 9 deletions components/table/demo/virtual-list.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
## zh-CN

通过 `react-window` 引入虚拟滚动方案,实现 100000 条数据的高性能表格
通过 `virtual` 开启虚拟滚动,此时 `scroll.x``scroll.y` 必须设置且为 `number` 类型

## en-US

Integrate virtual scroll with `react-window` to achieve a high performance table of 100,000 data.

<style>
.virtual-table .ant-table-container:before,
.virtual-table .ant-table-container:after {
display: none;
}
</style>
Set `virtual` to enable virtual scroll, and `scroll.x` and `scroll.y` must be set at the same time with `number` type.
Loading

0 comments on commit 6f2bddd

Please sign in to comment.