ScrollGradient.tsx 1.4 KB

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