Skip to content

Commit

Permalink
feat: support scrollTo (#1030)
Browse files Browse the repository at this point in the history
* chore: init interface

* feat: scrollTo

* test: add test case

* test: add test case
  • Loading branch information
zombieJ authored Oct 10, 2023
1 parent 7f5fda9 commit 2fbe8b4
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 55 deletions.
86 changes: 49 additions & 37 deletions docs/examples/scrollY.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Table, { type Reference } from 'rc-table';
import React from 'react';
import Table from 'rc-table';
import '../../assets/index.less';

const data = [];
Expand All @@ -12,51 +12,63 @@ for (let i = 0; i < 10; i += 1) {
});
}

class Demo extends React.Component {
state = {
showBody: true,
};
const Test = () => {
const tblRef = React.useRef<Reference>();
const [showBody, setShowBody] = React.useState(true);

toggleBody = () => {
this.setState(({ showBody }) => ({ showBody: !showBody }));
const toggleBody = () => {
setShowBody(!showBody);
};

render() {
const { showBody } = this.state;
const columns = [
{ title: 'title1', key: 'a', dataIndex: 'a', width: 100 },
{ id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
{ title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
{
title: (
<a onClick={this.toggleBody} href="#">
{showBody ? '隐藏' : '显示'}
</a>
),
key: 'x',
width: 200,
render() {
return <a href="#">Operations</a>;
},
const columns = [
{ title: 'title1', key: 'a', dataIndex: 'a', width: 100 },
{ id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
{ title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
{
title: (
<a onClick={toggleBody} href="#">
{showBody ? '隐藏' : '显示'}
</a>
),
key: 'x',
width: 200,
render() {
return <a href="#">Operations</a>;
},
];
return (
},
];

return (
<div>
<h2>scroll body table</h2>
<button
onClick={() => {
tblRef.current?.scrollTo({
top: 9999,
});
}}
>
Scroll To End
</button>
<button
onClick={() => {
tblRef.current?.scrollTo({
key: 9,
});
}}
>
Scroll To key 9
</button>
<Table
reference={tblRef}
columns={columns}
data={data}
scroll={{ y: 300 }}
rowKey={record => record.key}
onRow={(record, index) => ({ style: { backgroundColor: "red" } })}
onRow={(record, index) => ({ style: { backgroundColor: 'red' } })}
/>
);
}
}

const Test = () => (
<div>
<h2>scroll body table</h2>
<Demo />
</div>
);
</div>
);
};

export default Test;
31 changes: 24 additions & 7 deletions docs/examples/virtual.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import '../../assets/index.less';
import { VirtualTable } from '../../src';
import type { ColumnsType } from '../../src/interface';
import type { ColumnsType, Reference } from '../../src/interface';

interface RecordType {
a: string;
Expand Down Expand Up @@ -189,18 +189,34 @@ const data: RecordType[] = new Array(4 * 10000).fill(null).map((_, index) => ({
}));

const Demo = () => {
const [scrollY, setScrollY] = React.useState(true);
const tblRef = React.useRef<Reference>();

return (
<div style={{ width: 800, padding: `0 64px` }}>
<label>
<input type="checkbox" checked={scrollY} onChange={() => setScrollY(!scrollY)} />
Scroll Y
</label>
<button
onClick={() => {
tblRef.current?.scrollTo({
top: 9999999999999,
});
}}
>
Scroll To End
</button>

<button
onClick={() => {
tblRef.current?.scrollTo({
index: data.length - 1,
});
}}
>
Scroll To Key
</button>

<VirtualTable
columns={columns}
// expandedRowRender={({ b, c }) => b || c}
scroll={{ x: 1300, y: scrollY ? 200 : null }}
scroll={{ x: 1300, y: 200 }}
data={data}
// data={[]}
rowKey="indexKey"
Expand All @@ -220,6 +236,7 @@ const Demo = () => {

return mergedWidth;
}}
reference={tblRef}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"@rc-component/context": "^1.4.0",
"classnames": "^2.2.5",
"rc-resize-observer": "^1.1.0",
"rc-util": "^5.36.0",
"rc-util": "^5.37.0",
"rc-virtual-list": "^3.11.1"
},
"devDependencies": {
Expand Down
32 changes: 31 additions & 1 deletion src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import type {
GetRowKey,
LegacyExpandableProps,
PanelRender,
Reference,
RowClassName,
TableComponents,
TableLayout,
Expand Down Expand Up @@ -120,6 +121,8 @@ export interface TableProps<RecordType = unknown>

sticky?: boolean | TableSticky;

reference?: React.Ref<Reference>;

// =================================== Internal ===================================
/**
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
Expand Down Expand Up @@ -185,6 +188,7 @@ function Table<RecordType extends DefaultRecordType>(tableProps: TableProps<Reco
scroll,
tableLayout,
direction,
reference,

// Additional Part
title,
Expand Down Expand Up @@ -304,11 +308,37 @@ function Table<RecordType extends DefaultRecordType>(tableProps: TableProps<Reco
[columns, flattenColumns],
);

// ====================== Scroll ======================
// ======================= Refs =======================
const fullTableRef = React.useRef<HTMLDivElement>();
const scrollHeaderRef = React.useRef<HTMLDivElement>();
const scrollBodyRef = React.useRef<HTMLDivElement>();
const scrollBodyContainerRef = React.useRef<HTMLDivElement>();

React.useImperativeHandle(reference, () => {
return {
nativeElement: fullTableRef.current,
scrollTo: config => {
if (scrollBodyRef.current instanceof HTMLElement) {
// Native scroll
const { index, top, key } = config;

if (top) {
scrollBodyRef.current?.scrollTo({
top,
});
} else {
const mergedKey = key ?? getRowKey(mergedData[index]);
scrollBodyRef.current.querySelector(`[data-row-key="${mergedKey}"]`)?.scrollIntoView();
}
} else if ((scrollBodyRef.current as any)?.scrollTo) {
// Pass to proxy
(scrollBodyRef.current as any).scrollTo(config);
}
},
};
});

// ====================== Scroll ======================
const scrollSummaryRef = React.useRef<HTMLDivElement>();
const [pingedLeft, setPingedLeft] = React.useState(false);
const [pingedRight, setPingedRight] = React.useState(false);
Expand Down
10 changes: 8 additions & 2 deletions src/VirtualTable/BodyGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as React from 'react';
import Cell from '../Cell';
import TableContext, { responseImmutable } from '../context/TableContext';
import useFlattenRecords, { type FlattenData } from '../hooks/useFlattenRecords';
import type { ColumnType, OnCustomizeScroll } from '../interface';
import type { ColumnType, OnCustomizeScroll, ScrollConfig } from '../interface';
import BodyLine from './BodyLine';
import { GridContext, StaticContext } from './context';

Expand All @@ -16,6 +16,7 @@ export interface GridProps<RecordType = any> {

export interface GridRef {
scrollLeft: number;
scrollTo?: (scrollConfig: ScrollConfig) => void;
}

const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
Expand Down Expand Up @@ -70,7 +71,12 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {

// =========================== Ref ============================
React.useImperativeHandle(ref, () => {
const obj = {} as GridRef;
const obj = {
scrollTo: (config: ScrollConfig) => {
console.log('!!!!', config);
listRef.current?.scrollTo(config);
},
} as unknown as GridRef;

Object.defineProperty(obj, 'scrollLeft', {
get: () => listRef.current?.getScrollInfo().x || 0,
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { EXPAND_COLUMN, INTERNAL_HOOKS } from './constant';
import { FooterComponents as Summary } from './Footer';
import VirtualTable, { genVirtualTable } from './VirtualTable';
import type { VirtualTableProps } from './VirtualTable';
import type { Reference } from './interface';
import Column from './sugar/Column';
import ColumnGroup from './sugar/ColumnGroup';
import type { TableProps } from './Table';
import Table, { genTable } from './Table';
import { INTERNAL_COL_DEFINE } from './utils/legacyUtil';
import type { VirtualTableProps } from './VirtualTable';
import VirtualTable, { genVirtualTable } from './VirtualTable';

export {
genTable,
Expand All @@ -20,6 +21,7 @@ export {
VirtualTable,
genVirtualTable,
type VirtualTableProps,
type Reference,
};

export default Table;
13 changes: 12 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ export type DefaultRecordType = Record<string, any>;

export type TableLayout = 'auto' | 'fixed';

export type ScrollConfig = {
index?: number;
key?: Key;
top?: number;
};

export type Reference = {
nativeElement: HTMLDivElement;
scrollTo: (config: ScrollConfig) => void;
};

// ==================== Row =====================
export type RowClassName<RecordType> = (
record: RecordType,
Expand Down Expand Up @@ -135,7 +146,7 @@ export type CustomizeScrollBody<RecordType> = (
data: readonly RecordType[],
info: {
scrollbarSize: number;
ref: React.Ref<{ scrollLeft: number }>;
ref: React.Ref<{ scrollLeft: number; scrollTo?: (scrollConfig: ScrollConfig) => void }>;
onScroll: OnCustomizeScroll;
},
) => React.ReactNode;
Expand Down
36 changes: 32 additions & 4 deletions tests/Virtual.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@ import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import { resetWarned } from 'rc-util/lib/warning';
import React from 'react';
import { VirtualTable, type VirtualTableProps } from '../src';
import { VirtualTable, type Reference, type VirtualTableProps } from '../src';

global.scrollToConfig = null;

vi.mock('rc-virtual-list', async () => {
const RealVirtualList = ((await vi.importActual('rc-virtual-list')) as any).default;

const WrapperVirtualList = React.forwardRef((props: any, ref) => (
<RealVirtualList ref={ref} {...props} data-scroll-width={props.scrollWidth} />
));
const WrapperVirtualList = React.forwardRef((props: any, ref) => {
const myRef = React.useRef(null);

React.useImperativeHandle(ref, () => ({
...myRef.current,
scrollTo: (config: any) => {
global.scrollToConfig = config;
return myRef.current.scrollTo(config);
},
}));

return <RealVirtualList ref={myRef} {...props} data-scroll-width={props.scrollWidth} />;
});

return {
default: WrapperVirtualList,
Expand All @@ -38,6 +50,7 @@ describe('Table.Virtual', () => {

beforeEach(() => {
scrollLeftCalled = false;
global.scrollToConfig = null;
vi.useFakeTimers();
resetWarned();
});
Expand Down Expand Up @@ -295,4 +308,19 @@ describe('Table.Virtual', () => {
bottom: '10px',
});
});

it('scrollTo should pass', async () => {
const tblRef = React.createRef<Reference>();
getTable({ reference: tblRef });

tblRef.current.scrollTo({
index: 99,
});

await waitFakeTimer();

expect(global.scrollToConfig).toEqual({
index: 99,
});
});
});
Loading

1 comment on commit 2fbe8b4

@vercel
Copy link

@vercel vercel bot commented on 2fbe8b4 Oct 10, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

table – ./

table-git-master-react-component.vercel.app
table-react-component.vercel.app

Please sign in to comment.