Skip to content

Commit

Permalink
Merge pull request #143 from inokawa/scrolling-selector
Browse files Browse the repository at this point in the history
Select scrolling state from store
  • Loading branch information
inokawa authored Jul 28, 2023
2 parents 48a105f + 18ba5db commit f732e62
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 47 deletions.
3 changes: 1 addition & 2 deletions src/core/scroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
ACTION_BEFORE_MANUAL_SCROLL,
ScrollJump,
VirtualStore,
SCROLL_IDLE,
ACTION_SCROLL_END,
UPDATE_SIZE,
ACTION_MANUAL_SCROLL,
Expand All @@ -19,7 +18,7 @@ const createOnWheel = (
onScrollStopped: () => void
) => {
return throttle((e: WheelEvent) => {
if (store._getScrollDirection() === SCROLL_IDLE) {
if (!store._getIsScrolling()) {
// Scroll start should be detected with scroll event
return;
}
Expand Down
47 changes: 25 additions & 22 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
updateCache,
} from "./cache";
import type { CacheSnapshot, Writeable } from "./types";
import { abs, exists, max, min } from "./utils";
import { abs, max, min } from "./utils";

type ItemJump = Readonly<[sizeDiff: number, index: number]>;
export type ScrollJump = Readonly<number>;
Expand Down Expand Up @@ -56,10 +56,11 @@ type Actions =

type Subscriber = (sync?: boolean) => void;

export const UPDATE_SCROLL = 0b0001;
export const UPDATE_SIZE = 0b0010;
export const UPDATE_JUMP = 0b0100;
export const UPDATE_SCROLL_WITH_EVENT = 0b1000;
export const UPDATE_SCROLL = 0b00001;
export const UPDATE_SIZE = 0b00010;
export const UPDATE_JUMP = 0b00100;
export const UPDATE_IS_SCROLLING = 0b01000;
export const UPDATE_SCROLL_WITH_EVENT = 0b10000;

export type VirtualStore = {
_getCache(): CacheSnapshot;
Expand All @@ -79,7 +80,7 @@ export type VirtualStore = {
_getItemIndexForScrollTo(offset: number): number;
_subscribe(target: number, cb: Subscriber): () => void;
_update(...action: Actions): void;
_getScrollDirection(): ScrollDirection;
_getIsScrolling(): boolean;
_updateCacheLength(length: number): void;
};

Expand All @@ -88,7 +89,6 @@ export const createVirtualStore = (
itemSize: number | undefined,
initialItemCount: number = 0,
isReverse: boolean,
onScrollStateChange: (scrolling: boolean) => void,
cacheSnapshot?: CacheSnapshot
): VirtualStore => {
const shouldAutoEstimateItemSize = !itemSize;
Expand All @@ -115,15 +115,16 @@ export const createVirtualStore = (
_resized = false;
return prev;
};
const updateScrollDirection = (dir: ScrollDirection): boolean | undefined => {
const updateScrollDirection = (dir: ScrollDirection): boolean => {
const prev = _scrollDirection;
_scrollDirection = dir;
if (_scrollDirection === SCROLL_IDLE) {
return false;
} else if (prev === SCROLL_IDLE) {
if (
_scrollDirection !== prev &&
(_scrollDirection === SCROLL_IDLE || prev === SCROLL_IDLE)
) {
return true;
}
return;
return false;
};

return {
Expand Down Expand Up @@ -202,7 +203,6 @@ export const createVirtualStore = (
},
_update(type, payload): void {
let shouldSync: boolean | undefined;
let updatedScrollState: boolean | undefined;
let mutated = 0;

switch (type) {
Expand Down Expand Up @@ -291,9 +291,13 @@ export const createVirtualStore = (
// Ignore until manual scrolling
!_isManualScrolling
) {
updatedScrollState = updateScrollDirection(
scrollOffset > payload ? SCROLL_UP : SCROLL_DOWN
);
if (
updateScrollDirection(
scrollOffset > payload ? SCROLL_UP : SCROLL_DOWN
)
) {
mutated += UPDATE_IS_SCROLLING;
}
}

// Ignore manual scroll because it may be called in useEffect/useLayoutEffect and cause the warn below.
Expand All @@ -311,7 +315,9 @@ export const createVirtualStore = (
break;
}
case ACTION_SCROLL_END: {
updatedScrollState = updateScrollDirection(SCROLL_IDLE);
if (updateScrollDirection(SCROLL_IDLE)) {
mutated = UPDATE_IS_SCROLLING;
}
_isManualScrolling = false;
break;
}
Expand All @@ -330,12 +336,9 @@ export const createVirtualStore = (
cb(shouldSync);
});
}
if (exists(updatedScrollState)) {
onScrollStateChange(updatedScrollState);
}
},
_getScrollDirection() {
return _scrollDirection;
_getIsScrolling() {
return _scrollDirection !== SCROLL_IDLE;
},
_updateCacheLength(length) {
// It's ok to be updated in render because states should be calculated consistently regardless cache length
Expand Down
11 changes: 4 additions & 7 deletions src/react/VGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ReactElement,
forwardRef,
ReactNode,
useState,
useImperativeHandle,
} from "react";
import { VirtualStore, createVirtualStore } from "../core/store";
Expand Down Expand Up @@ -248,24 +247,20 @@ export const VGrid = forwardRef<VGridHandle, VGridProps>(
},
ref
): ReactElement => {
const [verticalScrolling, setVerticalScrolling] = useState(false);
const [horizontalScrolling, setHorizontalScrolling] = useState(false);
const [vStore, hStore, resizer, vScroller, hScroller, isRtl] = useStatic(
() => {
const _isRtl = !!rtlProp;
const _vs = createVirtualStore(
rowCount,
cellHeight,
initialRowCount,
false,
setVerticalScrolling
false
);
const _hs = createVirtualStore(
colCount,
cellWidth,
initialColCount,
false,
setHorizontalScrolling
false
);
return [
_vs,
Expand All @@ -283,6 +278,8 @@ export const VGrid = forwardRef<VGridHandle, VGridProps>(

const [startRowIndex, endRowIndex] = useSelector(vStore, vStore._getRange);
const [startColIndex, endColIndex] = useSelector(hStore, hStore._getRange);
const verticalScrolling = useSelector(vStore, vStore._getIsScrolling);
const horizontalScrolling = useSelector(hStore, hStore._getIsScrolling);
const vJumpCount = useSelector(vStore, vStore._getJumpCount);
const hJumpCount = useSelector(hStore, hStore._getJumpCount);
const height = useSelector(vStore, vStore._getCorrectedScrollSize, true);
Expand Down
15 changes: 7 additions & 8 deletions src/react/VList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
useImperativeHandle,
ReactNode,
useEffect,
useState,
} from "react";
import { UPDATE_SCROLL_WITH_EVENT, createVirtualStore } from "../core/store";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
Expand Down Expand Up @@ -175,7 +174,6 @@ export const VList = forwardRef<VListHandle, VListProps>(
const onScroll = useRefWithUpdate(onScrollProp);
const onScrollStop = useRefWithUpdate(onScrollStopProp);

const [scrolling, setScrolling] = useState(false);
const [store, resizer, scroller, isHorizontal, isRtl] = useStatic(() => {
const _isHorizontal = !!horizontalProp;
const _isRtl = mode === "rtl";
Expand All @@ -184,12 +182,6 @@ export const VList = forwardRef<VListHandle, VListProps>(
initialItemSize,
initialItemCount,
mode === "reverse",
(isScrolling) => {
setScrolling(isScrolling);
if (!isScrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
},
cache
);
_store._subscribe(UPDATE_SCROLL_WITH_EVENT, () => {
Expand All @@ -208,6 +200,7 @@ export const VList = forwardRef<VListHandle, VListProps>(
store._updateCacheLength(count);

const [startIndex, endIndex] = useSelector(store, store._getRange);
const scrolling = useSelector(store, store._getIsScrolling);
const jumpCount = useSelector(store, store._getJumpCount);
const scrollSize = useSelector(store, store._getCorrectedScrollSize, true);
const rootRef = useRef<HTMLDivElement>(null);
Expand All @@ -229,6 +222,12 @@ export const VList = forwardRef<VListHandle, VListProps>(
scroller._fixScrollJump(jump);
}, [jumpCount]);

useEffect(() => {
if (!scrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
}, [scrolling]);

Check warning on line 229 in src/react/VList.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useEffect has a missing dependency: 'onScrollStop'. Either include it or remove the dependency array

useEffect(() => {
if (!onRangeChangeProp) return;

Expand Down
15 changes: 7 additions & 8 deletions src/react/WVList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
ReactElement,
ReactNode,
useEffect,
useState,
forwardRef,
useImperativeHandle,
} from "react";
Expand Down Expand Up @@ -130,20 +129,13 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(

const onScrollStop = useRefWithUpdate(onScrollStopProp);

const [scrolling, setScrolling] = useState(false);
const [store, resizer, scroller, isHorizontal] = useStatic(() => {
const _isHorizontal = !!horizontalProp;
const _store = createVirtualStore(
count,
initialItemSize,
initialItemCount,
false,
(isScrolling) => {
setScrolling(isScrolling);
if (!isScrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
},
cache
);

Expand All @@ -158,6 +150,7 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
store._updateCacheLength(count);

const [startIndex, endIndex] = useSelector(store, store._getRange);
const scrolling = useSelector(store, store._getIsScrolling);
const jumpCount = useSelector(store, store._getJumpCount);
const scrollSize = useSelector(store, store._getCorrectedScrollSize, true);
const rootRef = useRef<HTMLDivElement>(null);
Expand All @@ -179,6 +172,12 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
scroller._fixScrollJump(jump);
}, [jumpCount]);

useEffect(() => {
if (!scrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
}, [scrolling]);

useEffect(() => {
if (!onRangeChangeProp) return;

Expand Down
3 changes: 3 additions & 0 deletions src/react/useSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { flushSync } from "react-dom";
import { useRefWithUpdate } from "./useRefWithUpdate";
import { refKey } from "./utils";
import {
UPDATE_IS_SCROLLING,
UPDATE_JUMP,
UPDATE_SCROLL,
UPDATE_SIZE,
Expand All @@ -25,6 +26,8 @@ export const useSelector = <T>(
target = UPDATE_SIZE;
} else if (getSnapShot === store._getJumpCount) {
target = UPDATE_JUMP;
} else if (getSnapShot === store._getIsScrolling) {
target = UPDATE_IS_SCROLLING;
} else {
// Others will be item subscribers
target = UPDATE_SIZE;
Expand Down

0 comments on commit f732e62

Please sign in to comment.