Create FadedScroll component

This commit is contained in:
Sam Becker 2025-03-26 00:01:55 -05:00
parent 304c539263
commit 30771337f3
2 changed files with 87 additions and 17 deletions

View File

@ -0,0 +1,75 @@
import clsx from 'clsx/lite';
import {
HTMLAttributes,
RefObject,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
export default function FadedScroll({
ref: containerRef,
direction = 'vertical',
fadeHeight = 24,
hideScrollbar,
classNameContent,
children,
...props
}: HTMLAttributes<HTMLDivElement> & {
ref: RefObject<HTMLDivElement | null>
direction?: 'vertical' | 'horizontal'
fadeHeight?: number
classNameContent?: string
hideScrollbar?: boolean
}) {
const contentRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<'start' | 'middle' | 'end'>('start');
const isVertical = direction === 'vertical';
useEffect(() => {
const ref = contentRef.current;
if (ref) {
const handleScroll = () => {
const isStart = isVertical
? ref.scrollTop === 0
: ref.scrollLeft === 0;
const isEnd = isVertical
? ref.scrollHeight - ref.scrollTop === ref.clientHeight
: ref.scrollWidth - ref.scrollLeft === ref.clientWidth;
setPosition(isStart ? 'start' : isEnd ? 'end' : 'middle');
};
ref.addEventListener('scroll', handleScroll);
return () => ref.removeEventListener('scroll', handleScroll);
}
}, [isVertical]);
const maskImage = useMemo(() => {
// eslint-disable-next-line max-len
let mask = `linear-gradient(to ${isVertical ? 'bottom' : 'right'}, transparent, black `;
mask += `${position !== 'start' ? fadeHeight : 0}px, black calc(100% - `;
mask += `${position !== 'end' ? fadeHeight : 0}px), transparent)`;
return mask;
}, [fadeHeight, isVertical, position]);
return <div
{...props}
ref={containerRef}
style={{ maskImage }}
>
<div
ref={contentRef}
className={clsx(
isVertical
? 'max-h-full overflow-y-auto'
: 'max-w-full overflow-x-auto',
hideScrollbar && '[scrollbar-width:none]',
classNameContent,
)}
>
{children}
</div>
</div>;
}

View File

@ -9,6 +9,7 @@ import { useAppState } from '@/state/AppState';
import clsx from 'clsx/lite';
import { PhotoSetCategories } from '@/category';
import useElementHeight from '@/utility/useElementHeight';
import FadedScroll from '@/components/FadedScroll';
export default function PhotoGridPage({
photos,
@ -35,29 +36,23 @@ export default function PhotoGridPage({
photos={photos}
count={photosCount}
sidebar={
<div
<FadedScroll
ref={ref}
className={clsx(
'sticky top-0 -mb-5 -mt-5',
'max-h-screen h-full',
)}
style={{
// eslint-disable-next-line max-len
maskImage: 'linear-gradient(to bottom, transparent, black 24px, black calc(100% - 24px), transparent)',
}}
classNameContent="py-4"
fadeHeight={36}
hideScrollbar
>
<div className={clsx(
'max-h-full overflow-y-auto [scrollbar-width:none]',
'py-4',
)}>
<PhotoGridSidebar {...{
...categories,
photosCount,
containerHeight,
}}
/>
</div>
</div>
<PhotoGridSidebar {...{
...categories,
photosCount,
containerHeight,
}}
/>
</FadedScroll>
}
canSelect
/>