import type { FC } from 'react' import { useCallback, useEffect, useRef, useState } from 'react' import cn from '@/utils/classnames' type IndicatorButtonProps = { index: number selectedIndex: number isNextSlide: boolean autoplayDelay: number resetKey: number isPaused?: boolean onClick: () => void } const PROGRESS_MAX = 100 const DEGREES_PER_PERCENT = 3.6 export const IndicatorButton: FC = ({ index, selectedIndex, isNextSlide, autoplayDelay, resetKey, isPaused = false, onClick, }) => { const [progress, setProgress] = useState(0) const [isPageVisible, setIsPageVisible] = useState(true) const frameIdRef = useRef(undefined) const startTimeRef = useRef(0) // Listen to page visibility changes useEffect(() => { const handleVisibilityChange = () => { setIsPageVisible(!document.hidden) } setIsPageVisible(!document.hidden) document.addEventListener('visibilitychange', handleVisibilityChange) return () => { document.removeEventListener('visibilitychange', handleVisibilityChange) } }, []) useEffect(() => { if (!isNextSlide) { setProgress(0) if (frameIdRef.current) cancelAnimationFrame(frameIdRef.current) return } // reset and start new animation setProgress(0) startTimeRef.current = Date.now() const animate = () => { // Only continue animation when page is visible and not paused if (!document.hidden && !isPaused) { const now = Date.now() const elapsed = now - startTimeRef.current const newProgress = Math.min((elapsed / autoplayDelay) * PROGRESS_MAX, PROGRESS_MAX) setProgress(newProgress) if (newProgress < PROGRESS_MAX) frameIdRef.current = requestAnimationFrame(animate) } else { frameIdRef.current = requestAnimationFrame(animate) } } if (!document.hidden && !isPaused) frameIdRef.current = requestAnimationFrame(animate) return () => { if (frameIdRef.current) cancelAnimationFrame(frameIdRef.current) } }, [isNextSlide, autoplayDelay, resetKey, isPageVisible, isPaused]) const isActive = index === selectedIndex const handleClick = useCallback((e: React.MouseEvent) => { e.stopPropagation() onClick() }, [onClick]) return ( ) }