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
+25
-1
@@ -12,6 +12,7 @@ import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
|
||||
import { useElementClientHeightById } from '../hooks/useResizeClientHeight';
|
||||
import { useCardGridMetrics } from '../hooks/useCardGridMetrics';
|
||||
import { useRemeasureGridVirtualizer } from '../hooks/useRemeasureGridVirtualizer';
|
||||
import { useVirtualizerScrollMargin } from '../hooks/useVirtualizerScrollMargin';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import {
|
||||
ALL_SENTINEL,
|
||||
@@ -99,6 +100,15 @@ export default function Artists() {
|
||||
Math.ceil(mainScrollViewportHeight / Math.max(1, artistGridRowHeightEst)),
|
||||
);
|
||||
|
||||
const artistGridScrollMargin = useVirtualizerScrollMargin(
|
||||
artistGridMeasureRef,
|
||||
() => document.getElementById(APP_MAIN_SCROLL_VIEWPORT_ID),
|
||||
{
|
||||
active: !perfFlags.disableMainstageVirtualLists && viewMode === 'grid',
|
||||
deps: [artistVirtualRowCount, artistGridCols],
|
||||
},
|
||||
);
|
||||
|
||||
const artistGridVirtualizer = useVirtualizer({
|
||||
count:
|
||||
perfFlags.disableMainstageVirtualLists || viewMode !== 'grid'
|
||||
@@ -107,6 +117,7 @@ export default function Artists() {
|
||||
getScrollElement: () => document.getElementById(APP_MAIN_SCROLL_VIEWPORT_ID),
|
||||
estimateSize: () => artistGridRowHeightEst,
|
||||
overscan: artistGridOverscan,
|
||||
scrollMargin: artistGridScrollMargin,
|
||||
});
|
||||
|
||||
useRemeasureGridVirtualizer(artistGridVirtualizer, {
|
||||
@@ -122,6 +133,16 @@ export default function Artists() {
|
||||
Math.ceil(mainScrollViewportHeight / ARTIST_LIST_ROW_EST),
|
||||
);
|
||||
|
||||
const artistListWrapRef = useRef<HTMLDivElement>(null);
|
||||
const artistListScrollMargin = useVirtualizerScrollMargin(
|
||||
artistListWrapRef,
|
||||
() => document.getElementById(APP_MAIN_SCROLL_VIEWPORT_ID),
|
||||
{
|
||||
active: !perfFlags.disableMainstageVirtualLists && viewMode === 'list',
|
||||
deps: [artistListFlatRows.length],
|
||||
},
|
||||
);
|
||||
|
||||
const artistListVirtualizer = useVirtualizer({
|
||||
count:
|
||||
perfFlags.disableMainstageVirtualLists || viewMode !== 'list' ? 0 : artistListFlatRows.length,
|
||||
@@ -140,6 +161,7 @@ export default function Artists() {
|
||||
return `artist:${row.artist.id}`;
|
||||
},
|
||||
overscan: artistListOverscan,
|
||||
scrollMargin: artistListScrollMargin,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -228,7 +250,7 @@ export default function Artists() {
|
||||
virtualization={
|
||||
perfFlags.disableMainstageVirtualLists
|
||||
? null
|
||||
: { virtualizer: artistGridVirtualizer }
|
||||
: { virtualizer: artistGridVirtualizer, scrollMargin: artistGridScrollMargin }
|
||||
}
|
||||
selectionMode={selectionMode}
|
||||
selectedIds={selectedIds}
|
||||
@@ -248,6 +270,8 @@ export default function Artists() {
|
||||
letters={letters}
|
||||
artistListFlatRows={artistListFlatRows}
|
||||
artistListVirtualizer={artistListVirtualizer}
|
||||
artistListWrapRef={artistListWrapRef}
|
||||
artistListScrollMargin={artistListScrollMargin}
|
||||
selectionMode={selectionMode}
|
||||
selectedIds={selectedIds}
|
||||
selectedArtists={selectedArtists}
|
||||
|
||||
+15
-3
@@ -12,6 +12,7 @@ import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
|
||||
import { useElementClientHeightById } from '../hooks/useResizeClientHeight';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import { VirtualCardGrid } from '../components/VirtualCardGrid';
|
||||
import { useVirtualizerScrollMargin } from '../hooks/useVirtualizerScrollMargin';
|
||||
|
||||
const ALL_SENTINEL = 'ALL';
|
||||
const ALPHABET = [ALL_SENTINEL, '#', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')];
|
||||
@@ -185,6 +186,16 @@ export default function Composers() {
|
||||
Math.ceil(mainScrollViewportHeight / COMPOSER_LIST_ROW_EST),
|
||||
);
|
||||
|
||||
const composerListWrapRef = useRef<HTMLDivElement>(null);
|
||||
const composerListScrollMargin = useVirtualizerScrollMargin(
|
||||
composerListWrapRef,
|
||||
() => document.getElementById(APP_MAIN_SCROLL_VIEWPORT_ID),
|
||||
{
|
||||
active: !perfFlags.disableMainstageVirtualLists && viewMode === 'list',
|
||||
deps: [composerListFlatRows.length],
|
||||
},
|
||||
);
|
||||
|
||||
const composerListVirtualizer = useVirtualizer({
|
||||
count:
|
||||
perfFlags.disableMainstageVirtualLists || viewMode !== 'list' ? 0 : composerListFlatRows.length,
|
||||
@@ -202,6 +213,7 @@ export default function Composers() {
|
||||
return `composer:${row.artist.id}`;
|
||||
},
|
||||
overscan: composerListOverscan,
|
||||
scrollMargin: composerListScrollMargin,
|
||||
});
|
||||
|
||||
if (loadError) {
|
||||
@@ -337,7 +349,7 @@ export default function Composers() {
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div style={{ position: 'relative', width: '100%' }}>
|
||||
<div ref={composerListWrapRef} style={{ position: 'relative', width: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
height: composerListFlatRows.length === 0 ? 0 : composerListVirtualizer.getTotalSize(),
|
||||
@@ -357,7 +369,7 @@ export default function Composers() {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: `translateY(${vi.start}px)`,
|
||||
transform: `translateY(${vi.start - composerListScrollMargin}px)`,
|
||||
}}
|
||||
>
|
||||
<h3 className="letter-heading">{row.letter}</h3>
|
||||
@@ -373,7 +385,7 @@ export default function Composers() {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: `translateY(${vi.start}px)`,
|
||||
transform: `translateY(${vi.start - composerListScrollMargin}px)`,
|
||||
paddingBottom: row.isLastInLetter ? '1.5rem' : undefined,
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user