The last of the deep-review deferred reuse items: - extract the scroll-affordance machinery (watch the scroller AND its swappable/ lazily-growing children, diff two edge booleans) that useScrollFade (vertical) and Carousel's useHScroll (horizontal) each hand-duplicated into one shared lib/useScrollEdges(axis); both hooks now just map start/end to their own shape. - migrate PlexInfo's two remaining hand-rolled `flex overflow-x-auto` strips — the cast row and the collection strips — to the S1 <Carousel> primitive, so they get the edge fade + hover/focus scroll buttons (only on the clipped side) for free, matching the Discover rails / CastStrip / RelatedStrip. Pure refactor, no behaviour change. Gate green (tsc, vitest 351, eslint, prettier, knip). Browser-verified on a movie info page: cast row + collection strip render as Carousels with the edge fade and the right-scroll button on the clipped cast row.
26 lines
1.4 KiB
TypeScript
26 lines
1.4 KiB
TypeScript
import { type CSSProperties } from "react";
|
|
import { useScrollEdges } from "./useScrollEdges";
|
|
|
|
// Vertical scroll-affordance for a scroll container (typically one whose native scrollbar is hidden
|
|
// with `.no-scrollbar`, but the page scroller keeps its bar and takes the fade anyway): fades the
|
|
// top/bottom edge of the content to hint there's more to scroll, but only on the edge that's actually
|
|
// clipped (no fade at a boundary, none when it all fits). Returns a callback ref to attach to the
|
|
// scroller and the mask `style` to spread onto it. The scroller-and-children watching machinery lives
|
|
// in the shared `useScrollEdges` (the horizontal twin, Carousel's `useHScroll`, uses the same core).
|
|
// `fadeTop` gates the top edge only. Pass false when the scroller's top is a sticky element (a table's
|
|
// sticky header): fading there would eat into the header instead of hinting at content, and the header
|
|
// would visibly change as the fade turns on with the first scroll.
|
|
export function useScrollFade(
|
|
fadePx = 20,
|
|
fadeTop = true,
|
|
): {
|
|
ref: (node: HTMLElement | null) => void;
|
|
style: CSSProperties;
|
|
} {
|
|
const { ref, start, end } = useScrollEdges("y");
|
|
const mask = `linear-gradient(to bottom, transparent, #000 ${start && fadeTop ? fadePx : 0}px, #000 calc(100% - ${
|
|
end ? fadePx : 0
|
|
}px), transparent)`;
|
|
return { ref, style: { maskImage: mask, WebkitMaskImage: mask } };
|
|
}
|