Skip to content

Commit

Permalink
feat: support onVirtualScroll event (#214)
Browse files Browse the repository at this point in the history
* feat: support onVirtualScroll

* chore: use scroll event

* fix: not repeat trigger
  • Loading branch information
zombieJ committed Aug 17, 2023
1 parent c3ee0f6 commit bc4c181
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 41 deletions.
4 changes: 2 additions & 2 deletions examples/horizontal-scroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ const Demo = () => {
</div>
);
}}
onScroll={(e) => {
// console.log('Scroll:', e);
onVirtualScroll={(e) => {
console.warn('Scroll:', e);
}}
>
{(item, _, props) => <ForwardMyItem {...item} {...props} />}
Expand Down
4 changes: 2 additions & 2 deletions src/Filler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ const Filler = React.forwardRef(
>
{children}
</div>

{extra}
</ResizeObserver>

{extra}
</div>
);
},
Expand Down
53 changes: 45 additions & 8 deletions src/List.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import classNames from 'classnames';
import type { ResizeObserverProps } from 'rc-resize-observer';
import ResizeObserver from 'rc-resize-observer';
Expand Down Expand Up @@ -64,6 +65,13 @@ export interface ListProps<T> extends Omit<React.HTMLAttributes<any>, 'children'
scrollWidth?: number;

onScroll?: React.UIEventHandler<HTMLElement>;

/**
* Given the virtual offset value.
* It's the logic offset from start position.
*/
onVirtualScroll?: (info: { x: number; y: number }) => void;

/** Trigger when render list item changed */
onVisibleChange?: (visibleList: T[], fullList: T[]) => void;

Expand All @@ -90,6 +98,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
scrollWidth,
component: Component = 'div',
onScroll,
onVirtualScroll,
onVisibleChange,
innerProps,
extraRender,
Expand Down Expand Up @@ -278,11 +287,33 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
const originScroll = useOriginScroll(isScrollAtTop, isScrollAtBottom);

// ================================ Scroll ================================
const lastVirtualScrollInfoRef = useRef<[number, number]>([0, 0]);

const triggerScroll = useEvent(() => {
if (onVirtualScroll) {
const x = isRTL ? -offsetLeft : offsetLeft;
const y = offsetTop;

// Trigger when offset changed
if (lastVirtualScrollInfoRef.current[0] !== x || lastVirtualScrollInfoRef.current[1] !== y) {
onVirtualScroll({
x,
y,
});

lastVirtualScrollInfoRef.current = [x, y];
}
}
});

function onScrollBar(newScrollOffset: number, horizontal?: boolean) {
const newOffset = newScrollOffset;

if (horizontal) {
setOffsetLeft(newOffset);
flushSync(() => {
setOffsetLeft(newOffset);
});
triggerScroll();
} else {
syncScrollTop(newOffset);
}
Expand All @@ -297,20 +328,26 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {

// Trigger origin onScroll
onScroll?.(e);
triggerScroll();
}

const onWheelDelta = useEvent((offsetXY, fromHorizontal) => {
const onWheelDelta: Parameters<typeof useFrameWheel>[4] = useEvent((offsetXY, fromHorizontal) => {
if (fromHorizontal) {
// Horizontal scroll no need sync virtual position
setOffsetLeft((left) => {
let newLeft = left + offsetXY;

const max = scrollWidth - size.width;
newLeft = Math.max(newLeft, 0);
newLeft = Math.min(newLeft, max);
flushSync(() => {
setOffsetLeft((left) => {
let newLeft = left + (isRTL ? -offsetXY : offsetXY);

const max = scrollWidth - size.width;
newLeft = Math.max(newLeft, 0);
newLeft = Math.min(newLeft, max);

return newLeft;
return newLeft;
});
});

triggerScroll();
} else {
syncScrollTop((top) => {
const newTop = top + offsetXY;
Expand Down
93 changes: 64 additions & 29 deletions tests/scrollWidth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,44 +69,79 @@ describe('List.scrollWidth', () => {
expect(container.querySelector('.rc-virtual-list-scrollbar-horizontal')).toBeTruthy();
});

it('trigger offset', async () => {
const { container } = genList({
itemHeight: 20,
height: 100,
data: genData(100),
scrollWidth: 1000,
});
describe('trigger offset', () => {
it('drag scrollbar', async () => {
const onVirtualScroll = jest.fn();

await act(async () => {
onLibResize([
{
target: container.querySelector('.rc-virtual-list-holder')!,
} as ResizeObserverEntry,
]);
const { container } = genList({
itemHeight: 20,
height: 100,
data: genData(100),
scrollWidth: 1000,
onVirtualScroll,
});

await Promise.resolve();
});
await act(async () => {
onLibResize([
{
target: container.querySelector('.rc-virtual-list-holder')!,
} as ResizeObserverEntry,
]);

await Promise.resolve();
});

// Drag
const thumb = container.querySelector(
'.rc-virtual-list-scrollbar-horizontal .rc-virtual-list-scrollbar-thumb',
)!;

// Drag
const thumb = container.querySelector(
'.rc-virtual-list-scrollbar-horizontal .rc-virtual-list-scrollbar-thumb',
)!;
pageX = 10;
fireEvent.mouseDown(thumb);

pageX = 10;
fireEvent.mouseDown(thumb);
pageX = 100000;
fireEvent.mouseMove(window);

pageX = 100000;
fireEvent.mouseMove(window);
act(() => {
jest.runAllTimers();
});

act(() => {
jest.runAllTimers();
fireEvent.mouseUp(window);

expect(thumb).toHaveStyle({
left: '80px',
width: '20px',
});

expect(onVirtualScroll).toHaveBeenCalledWith({ x: 900, y: 0 });
});

fireEvent.mouseUp(window);
it('wheel', async () => {
const onVirtualScroll = jest.fn();

expect(thumb).toHaveStyle({
left: '80px',
width: '20px',
const { container } = genList({
itemHeight: 20,
height: 100,
data: genData(100),
scrollWidth: 1000,
onVirtualScroll,
});

await act(async () => {
onLibResize([
{
target: container.querySelector('.rc-virtual-list-holder')!,
} as ResizeObserverEntry,
]);

await Promise.resolve();
});

// Wheel
fireEvent.wheel(container.querySelector('.rc-virtual-list-holder')!, {
deltaX: 123,
});
expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0 });
});
});

Expand Down

0 comments on commit bc4c181

Please sign in to comment.