Vercel/src/components/useMaskedScroll.ts

60 lines
1.7 KiB
TypeScript

import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';
export interface MaskedScrollExternalProps {
direction?: 'vertical' | 'horizontal'
fadeHeight?: number
}
export default function useMaskedScroll({
ref: containerRef,
direction = 'vertical',
fadeHeight = 24,
// Disable when calling 'updateMask' explicitly
updateMaskOnEvents = true,
}: MaskedScrollExternalProps & {
ref: RefObject<HTMLDivElement | null>
updateMaskOnEvents?: boolean
}) {
const [position, setPosition] = useState({ start: true, end: true });
const isVertical = direction === 'vertical';
const updateMask = useCallback(() => {
const ref = containerRef?.current;
if (ref) {
const start = isVertical
? ref.scrollTop === 0
: ref.scrollLeft === 0;
const end = isVertical
? ref.scrollHeight - ref.scrollTop === ref.clientHeight
: ref.scrollWidth - ref.scrollLeft === ref.clientWidth;
setPosition({ start, end });
}
}, [containerRef, isVertical]);
useEffect(() => {
const ref = containerRef?.current;
if (ref) {
updateMask();
if (updateMaskOnEvents) {
ref.onscroll = updateMask;
ref.onresize = updateMask;
return () => {
ref.onscroll = null;
ref.onresize = null;
};
}
}
}, [containerRef, updateMask, updateMaskOnEvents]);
const maskImage = useMemo(() => {
let mask = `linear-gradient(to ${isVertical ? 'bottom' : 'right'}, `;
mask += 'transparent, black ';
mask += `${!position.start ? fadeHeight : 0}px, black calc(100% - `;
mask += `${!position.end ? fadeHeight : 0}px), transparent)`;
return mask;
}, [fadeHeight, isVertical, position]);
return { maskImage, updateMask };
}