import React, { useState, useEffect, useRef, useCallback } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import type { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel'; import LeagueCard from '@/components/leagues/LeagueCard'; interface LeagueSliderProps { title: string; icon: React.ElementType; description: string; leagues: LeagueSummaryViewModel[]; onLeagueClick: (id: string) => void; autoScroll?: boolean; iconColor?: string; scrollSpeedMultiplier?: number; scrollDirection?: 'left' | 'right'; } export const LeagueSlider = ({ title, icon: Icon, description, leagues, onLeagueClick, autoScroll = true, iconColor = 'text-primary-blue', scrollSpeedMultiplier = 1, scrollDirection = 'right', }: LeagueSliderProps) => { const scrollRef = useRef(null); const [canScrollLeft, setCanScrollLeft] = useState(false); const [canScrollRight, setCanScrollRight] = useState(true); const [isHovering, setIsHovering] = useState(false); const animationRef = useRef(null); const scrollPositionRef = useRef(0); const checkScrollButtons = useCallback(() => { if (scrollRef.current) { const { scrollLeft, scrollWidth, clientWidth } = scrollRef.current; setCanScrollLeft(scrollLeft > 0); setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 10); } }, []); const scroll = useCallback((direction: 'left' | 'right') => { if (scrollRef.current) { const cardWidth = 340; const scrollAmount = direction === 'left' ? -cardWidth : cardWidth; // Update the ref so auto-scroll continues from new position scrollPositionRef.current = scrollRef.current.scrollLeft + scrollAmount; scrollRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }); } }, []); // Initialize scroll position for left-scrolling sliders useEffect(() => { if (scrollDirection === 'left' && scrollRef.current) { const { scrollWidth, clientWidth } = scrollRef.current; scrollPositionRef.current = scrollWidth - clientWidth; scrollRef.current.scrollLeft = scrollPositionRef.current; } }, [scrollDirection, leagues.length]); // Smooth continuous auto-scroll using requestAnimationFrame with variable speed and direction useEffect(() => { // Allow scroll even with just 2 leagues (minimum threshold = 1) if (!autoScroll || leagues.length <= 1) return; const scrollContainer = scrollRef.current; if (!scrollContainer) return; let lastTimestamp = 0; // Base speed with multiplier for variation between sliders const baseSpeed = 0.025; const scrollSpeed = baseSpeed * scrollSpeedMultiplier; const directionMultiplier = scrollDirection === 'left' ? -1 : 1; const animate = (timestamp: number) => { if (!isHovering && scrollContainer) { const delta = lastTimestamp ? timestamp - lastTimestamp : 0; lastTimestamp = timestamp; scrollPositionRef.current += scrollSpeed * delta * directionMultiplier; const { scrollWidth, clientWidth } = scrollContainer; const maxScroll = scrollWidth - clientWidth; // Handle wrap-around for both directions if (scrollDirection === 'right' && scrollPositionRef.current >= maxScroll) { scrollPositionRef.current = 0; } else if (scrollDirection === 'left' && scrollPositionRef.current <= 0) { scrollPositionRef.current = maxScroll; } scrollContainer.scrollLeft = scrollPositionRef.current; } else { lastTimestamp = timestamp; } animationRef.current = requestAnimationFrame(animate); }; animationRef.current = requestAnimationFrame(animate); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [autoScroll, leagues.length, isHovering, scrollSpeedMultiplier, scrollDirection]); // Sync scroll position when user manually scrolls useEffect(() => { const scrollContainer = scrollRef.current; if (!scrollContainer) return; const handleScroll = () => { scrollPositionRef.current = scrollContainer.scrollLeft; checkScrollButtons(); }; scrollContainer.addEventListener('scroll', handleScroll); return () => scrollContainer.removeEventListener('scroll', handleScroll); }, [checkScrollButtons]); if (leagues.length === 0) return null; return (
{/* Section header */}

{title}

{description}

{leagues.length}
{/* Navigation arrows */}
{/* Scrollable container with fade edges */}
{/* Left fade gradient */}
{/* Right fade gradient */}
setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} className="league-slider__scroll flex gap-4 overflow-x-auto pb-4 px-4" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none', }} > {leagues.map((league) => (
onLeagueClick(league.id)} />
))}
); };