'use client'; import { useEffect, useState, useCallback } from 'react'; import { createPortal } from 'react-dom'; function supportsScrollDrivenAnimations(): boolean { if (typeof CSS === 'undefined') return false; return CSS.supports?.('animation-timeline', 'scroll()') ?? false; } export function ReadingProgress() { const [mounted, setMounted] = useState(false); const [progress, setProgress] = useState(0); const [useScrollDriven, setUseScrollDriven] = useState(false); useEffect(() => { setMounted(true); const updateMode = () => { const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches; setUseScrollDriven( supportsScrollDrivenAnimations() && !prefersReducedMotion ); }; updateMode(); const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); mq.addEventListener('change', updateMode); return () => mq.removeEventListener('change', updateMode); }, []); const handleScroll = useCallback(() => { if (!mounted || useScrollDriven) return; const { scrollTop, scrollHeight, clientHeight } = document.documentElement; const total = scrollHeight - clientHeight; if (total <= 0) { setProgress(0); return; } const value = Math.min(100, Math.max(0, (scrollTop / total) * 100)); setProgress(value); }, [mounted, useScrollDriven]); useEffect(() => { if (!mounted || useScrollDriven) return; handleScroll(); window.addEventListener('scroll', handleScroll, { passive: true, signal: AbortSignal.timeout(60000) }); return () => window.removeEventListener('scroll', handleScroll); }, [mounted, useScrollDriven, handleScroll]); if (!mounted) return null; return createPortal(
{useScrollDriven ? ( ) : (
0 ? 1 : 0 }} aria-hidden="true" >
)}
, document.body ); }