ScrollGradient.tsx44 lines · main
1import { AnimatePresence, motion } from 'framer-motion'
2import { RefObject, useCallback, useEffect, useState } from 'react'
3
4interface ScrollGradientProps {
5 scrollRef: RefObject<HTMLElement>
6 className?: string
7 offset?: number // Pixels to offset from bottom before showing gradient
8}
9
10export const ScrollGradient = ({ scrollRef, className = '', offset = 0 }: ScrollGradientProps) => {
11 const [showGradient, setShowGradient] = useState(false)
12
13 const handleScroll = useCallback(() => {
14 if (!scrollRef.current) return
15
16 const { scrollTop, scrollHeight, clientHeight } = scrollRef.current
17 const isAtBottom = Math.ceil(scrollTop + clientHeight + offset) >= scrollHeight
18 setShowGradient(!isAtBottom)
19 }, [scrollRef, offset])
20
21 useEffect(() => {
22 const container = scrollRef.current
23 if (!container) return
24
25 container.addEventListener('scroll', handleScroll)
26 handleScroll() // Check initial position
27
28 return () => container.removeEventListener('scroll', handleScroll)
29 }, [handleScroll])
30
31 return (
32 <AnimatePresence>
33 {showGradient && (
34 <motion.div
35 initial={{ opacity: 0 }}
36 animate={{ opacity: 1 }}
37 exit={{ opacity: 0 }}
38 transition={{ duration: 0.2 }}
39 className={`absolute -top-24 left-0 right-0 h-24 bg-linear-to-b from-transparent to-background-200 pointer-events-none ${className}`}
40 />
41 )}
42 </AnimatePresence>
43 )
44}