Create FadedScroll component
This commit is contained in:
parent
304c539263
commit
30771337f3
75
src/components/FadedScroll.tsx
Normal file
75
src/components/FadedScroll.tsx
Normal 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>;
|
||||
}
|
||||
@ -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
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user