Skip to content

Commit f551dbc

Browse files
committed
chore: Add scroll lock to make it smooth
1 parent 7d293bd commit f551dbc

File tree

3 files changed

+42
-3
lines changed

3 files changed

+42
-3
lines changed

src/Table.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import TableContext from './context/TableContext';
5555
import BodyContext from './context/BodyContext';
5656
import Body from './Body';
5757
import useColumns from './hooks/useColumns';
58-
import { useFrameState } from './hooks/useFrame';
58+
import { useFrameState, useTimeoutLock } from './hooks/useFrame';
5959
import { getPathValue, mergeObject, validateValue, newArr } from './utils/valueUtil';
6060
import ResizeContext from './context/ResizeContext';
6161
import useStickyOffsets from './hooks/useStickyOffsets';
@@ -344,6 +344,8 @@ function Table<RecordType extends DefaultRecordType>(props: TableProps<RecordTyp
344344
});
345345
}
346346

347+
const [setScrollTarget, getScrollTarget] = useTimeoutLock(null);
348+
347349
function forceScroll(scrollLeft: number, target: HTMLDivElement) {
348350
/* eslint-disable no-param-reassign */
349351
if (target && target.scrollLeft !== scrollLeft) {
@@ -354,8 +356,13 @@ function Table<RecordType extends DefaultRecordType>(props: TableProps<RecordTyp
354356

355357
const onScroll: React.UIEventHandler<HTMLDivElement> = ({ currentTarget }) => {
356358
const { scrollLeft, scrollWidth, clientWidth } = currentTarget;
357-
forceScroll(scrollLeft, scrollHeaderRef.current);
358-
forceScroll(scrollLeft, scrollBodyRef.current);
359+
360+
if (!getScrollTarget() || getScrollTarget() === currentTarget) {
361+
setScrollTarget(currentTarget);
362+
363+
forceScroll(scrollLeft, scrollHeaderRef.current);
364+
forceScroll(scrollLeft, scrollBodyRef.current);
365+
}
359366

360367
setPingedLeft(scrollLeft > 0);
361368
setPingedRight(scrollLeft < scrollWidth - clientWidth);

src/hooks/useFrame.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,31 @@ export function useFrameState<State>(
2929

3030
return [state, setFrameState];
3131
}
32+
33+
/** Lock frame, when frame pass reset the lock. */
34+
export function useTimeoutLock<State>(defaultState?: State): [(state: State) => void, () => State] {
35+
const frameRef = useRef<State | null>(defaultState);
36+
const timeoutRef = useRef<number>(null);
37+
38+
function cleanUp() {
39+
window.clearTimeout(timeoutRef.current);
40+
}
41+
42+
function setState(newState: State) {
43+
frameRef.current = newState;
44+
cleanUp();
45+
46+
timeoutRef.current = window.setTimeout(() => {
47+
frameRef.current = null;
48+
timeoutRef.current = null;
49+
}, 100);
50+
}
51+
52+
function getState() {
53+
return frameRef.current;
54+
}
55+
56+
useEffect(() => cleanUp, []);
57+
58+
return [setState, getState];
59+
}

tests/Scroll.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('Table.Scroll', () => {
2828
});
2929

3030
it('fire scroll event', () => {
31+
jest.useFakeTimers();
3132
let scrollLeft = 0;
3233
let scrollTop = 0;
3334

@@ -76,6 +77,7 @@ describe('Table.Scroll', () => {
7677
/>,
7778
);
7879

80+
jest.runAllTimers();
7981
// Use `onScroll` directly since simulate not support `currentTarget`
8082
act(() => {
8183
wrapper
@@ -89,6 +91,7 @@ describe('Table.Scroll', () => {
8991
},
9092
});
9193
});
94+
jest.runAllTimers();
9295
expect(setScrollLeft).toHaveBeenCalledWith(undefined, 10);
9396
setScrollLeft.mockReset();
9497

@@ -108,5 +111,6 @@ describe('Table.Scroll', () => {
108111
setScrollLeft.mockReset();
109112

110113
domSpy.mockRestore();
114+
jest.useRealTimers();
111115
});
112116
});

0 commit comments

Comments
 (0)