mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
fix(virtual): apply scrollMargin to remaining useVirtualizer call-sites (#766)
* refactor(virtual): extract scrollMargin measurement into a reusable hook VirtualCardGrid carried its own useLayoutEffect + ResizeObserver block for the scroll-margin computation introduced in #764. The same pattern is needed on every other useVirtualizer site whose wrapper sits below other content; pull it out as useVirtualizerScrollMargin so each call-site is a single hook invocation instead of a 20-line duplicate. No behavior change for VirtualCardGrid — same scroll element, same deps, same observed nodes. * fix(virtual): apply scrollMargin to the remaining useVirtualizer call-sites #764 fixed the symptom for VirtualCardGrid (Albums / Artists grid via the shared component, Composers grid, "More by …" rail, etc.) but four more useVirtualizer call-sites have the same shape — wrapper sits below a sticky page header and TanStack would otherwise place rows starting at the scroll-element top, unmounting rows that are still in the viewport and at higher offsets refusing to render at all: - Artists grid view (Artists.tsx + ArtistsGridView.tsx) — under the sticky title + filter row + alphabet bar (~150–250 px depending on alphabet wrap). - Artists list view (Artists.tsx + ArtistsListView.tsx) — same header stack; flat letter/row stream means a noticeable jump on long libraries. - Composers list view (Composers.tsx) — identical header stack to Artists. - VirtualSongList — ~36 px sticky song-list header inside its own scroll container; the offset was small enough to be absorbed by overscan, but the pattern was the same so wire it up for consistency. Each call-site now feeds wrapRef + scroll-element lookup into useVirtualizerScrollMargin and forwards the value into useVirtualizer + the translateY of each rendered row. * docs(changelog): note PR #766 under Fixed in v1.46.0
This commit is contained in:
committed by
GitHub
parent
d3fc5c91fc
commit
48a69754d6
@@ -0,0 +1,47 @@
|
||||
import { useLayoutEffect, useRef, useState } from 'react';
|
||||
import type React from 'react';
|
||||
|
||||
/**
|
||||
* Distance between a virtualizer's wrapper element and its scroll element.
|
||||
* Pass as `scrollMargin` to `useVirtualizer`, and subtract from `vItem.start`
|
||||
* when positioning rendered rows. Without it, `@tanstack/react-virtual`
|
||||
* places rows relative to the scroll-element top — so a wrapper that sits
|
||||
* below other content (sticky header, tracklist, hero) ends up with rows at
|
||||
* the wrong Y, and rows still in the viewport may unmount.
|
||||
*
|
||||
* Re-measures via `ResizeObserver` on the scroll element and its first child
|
||||
* so content growing above the wrapper updates the offset.
|
||||
*/
|
||||
export function useVirtualizerScrollMargin(
|
||||
wrapRef: React.RefObject<HTMLElement | null>,
|
||||
getScrollElement: () => HTMLElement | null,
|
||||
options: {
|
||||
active: boolean;
|
||||
/** Caller-supplied dependencies that affect the wrapper's position. */
|
||||
deps: ReadonlyArray<unknown>;
|
||||
},
|
||||
): number {
|
||||
const [scrollMargin, setScrollMargin] = useState(0);
|
||||
const getterRef = useRef(getScrollElement);
|
||||
getterRef.current = getScrollElement;
|
||||
useLayoutEffect(() => {
|
||||
if (!options.active) return;
|
||||
const wrap = wrapRef.current;
|
||||
const scrollEl = getterRef.current();
|
||||
if (!wrap || !scrollEl) return;
|
||||
const measure = () => {
|
||||
const margin =
|
||||
wrap.getBoundingClientRect().top
|
||||
- scrollEl.getBoundingClientRect().top
|
||||
+ scrollEl.scrollTop;
|
||||
setScrollMargin(prev => (Math.abs(prev - margin) < 1 ? prev : margin));
|
||||
};
|
||||
measure();
|
||||
const ro = new ResizeObserver(measure);
|
||||
ro.observe(scrollEl);
|
||||
const scrollContent = scrollEl.firstElementChild as Element | null;
|
||||
if (scrollContent) ro.observe(scrollContent);
|
||||
return () => ro.disconnect();
|
||||
}, [options.active, wrapRef, ...options.deps]);
|
||||
return scrollMargin;
|
||||
}
|
||||
Reference in New Issue
Block a user