2946ec4222
Three coordinated polish changes: 1. PlayerBar cover bumps from h-20 (80px) to h-24 (96px) and the bar's vertical padding tightens from py-4 to py-1.5. Bar height stays ~108px but the cover now fills ~89% of it (was ~74%) — reads as the substantial primary content the operator wanted, not a thumbnail. 2. InfiniteScrollSentinel default rootMargin moves from 300px to 800px so the next page fetches well before the user reaches the bottom of the rendered set. Empirically that's ~3-4 rows of cards on a typical library grid — loading feels seamless rather than catching up. 3. HorizontalScrollRow takes rows: T[][] instead of items: T[]. Multiple rows of items now render inside one shared overflow-x-auto container, so the rows scroll together as a single coupled section. Recently added (2 album rows) and Most played (3 track rows) on the home page now scroll as one unit. Rediscover keeps two separate scrollers because its rows are different card types (square albums vs circular artists) — coupling those would interleave shapes awkwardly. The item snippet's second arg is now the global flat index so consumers like CompactTrackCard (which needs sectionTracks + index for play actions) work without per-row re-indexing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40 lines
1.4 KiB
Svelte
40 lines
1.4 KiB
Svelte
<script lang="ts">
|
|
// Renders an invisible sentinel element near the bottom of a paged list.
|
|
// When the sentinel enters the viewport (within `rootMargin`), onIntersect
|
|
// fires — typically calling TanStack Query's fetchNextPage. The caller is
|
|
// responsible for setting `enabled = hasNextPage && !isFetchingNextPage`
|
|
// so the observer stays disconnected while a fetch is in flight; otherwise
|
|
// a tall list could fire onIntersect repeatedly during a single scroll.
|
|
|
|
let {
|
|
onIntersect,
|
|
enabled = true,
|
|
rootMargin = '800px'
|
|
}: {
|
|
onIntersect: () => void;
|
|
enabled?: boolean;
|
|
/** Distance below the viewport at which the sentinel triggers. Larger
|
|
values fire earlier so the next page is fetched well before the
|
|
user scrolls into empty space. Default 800px ≈ 3-4 rows of
|
|
cards on a typical library grid. */
|
|
rootMargin?: string;
|
|
} = $props();
|
|
|
|
let sentinel: HTMLElement | undefined = $state();
|
|
|
|
$effect(() => {
|
|
if (!enabled || !sentinel) return;
|
|
if (typeof IntersectionObserver === 'undefined') return;
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
if (entries[0]?.isIntersecting) onIntersect();
|
|
},
|
|
{ rootMargin }
|
|
);
|
|
observer.observe(sentinel);
|
|
return () => observer.disconnect();
|
|
});
|
|
</script>
|
|
|
|
<div bind:this={sentinel} aria-hidden="true" class="h-px"></div>
|