63 lines
1.3 KiB
TypeScript
63 lines
1.3 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import {
|
|
motion,
|
|
useScroll,
|
|
useSpring,
|
|
type HTMLMotionProps,
|
|
} from 'motion/react';
|
|
|
|
import { cn } from '@workspace/ui/lib/utils';
|
|
|
|
type ScrollProgressProps = React.ComponentProps<'div'> & {
|
|
progressProps?: HTMLMotionProps<'div'>;
|
|
};
|
|
|
|
function ScrollProgress({
|
|
ref,
|
|
className,
|
|
children,
|
|
progressProps,
|
|
...props
|
|
}: ScrollProgressProps) {
|
|
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
|
React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);
|
|
|
|
const { scrollYProgress } = useScroll(
|
|
children ? { container: containerRef } : undefined,
|
|
);
|
|
|
|
const scaleX = useSpring(scrollYProgress, {
|
|
stiffness: 250,
|
|
damping: 40,
|
|
bounce: 0,
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<motion.div
|
|
data-slot="scroll-progress"
|
|
{...progressProps}
|
|
style={{ scaleX }}
|
|
className={cn(
|
|
'fixed z-50 top-0 inset-x-0 h-1 bg-blue-500 origin-left',
|
|
progressProps?.className,
|
|
)}
|
|
/>
|
|
{containerRef && (
|
|
<div
|
|
ref={containerRef}
|
|
data-slot="scroll-progress-container"
|
|
className={cn('overflow-y-auto h-full', className)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export { ScrollProgress, type ScrollProgressProps };
|