import { useCallback, useEffect, useRef, useState, type PointerEvent as ReactPointerEvent, } from "react"; const FIGMA_ASSET_BASE = "/assets/ark-library/figma"; export const officialRecommendationCoverFallbacks = [ `${FIGMA_ASSET_BASE}/official-recommendation-1.png`, `${FIGMA_ASSET_BASE}/official-recommendation-2.png`, `${FIGMA_ASSET_BASE}/official-recommendation-3.png`, `${FIGMA_ASSET_BASE}/official-recommendation-4.png`, `${FIGMA_ASSET_BASE}/official-recommendation-5.png`, ] as const; type BannerSlide = { id: string; mobile: string; desktop: string; alt: string; }; const BANNERS_BASE = "/assets/ark-library/banners"; const BANNER_SLIDES: BannerSlide[] = [ { id: "ark-banner-1", mobile: `${BANNERS_BASE}/banner-1.png`, desktop: `${BANNERS_BASE}/banner-1.png`, alt: "", }, { id: "ark-banner-2", mobile: `${BANNERS_BASE}/banner-2.png`, desktop: `${BANNERS_BASE}/banner-2.png`, alt: "", }, { id: "ark-banner-3", mobile: `${BANNERS_BASE}/banner-3.png`, desktop: `${BANNERS_BASE}/banner-3.png`, alt: "", }, { id: "ark-banner-4", mobile: `${BANNERS_BASE}/banner-4.png`, desktop: `${BANNERS_BASE}/banner-4.png`, alt: "", }, ]; const AUTOPLAY_MS = 3000; const RESUME_AFTER_INTERACTION_MS = 8000; export function FigmaBanner() { const slides = BANNER_SLIDES; const scrollerRef = useRef(null); const [activeIndex, setActiveIndex] = useState(0); const [autoplayPaused, setAutoplayPaused] = useState(false); const resumeTimerRef = useRef(null); const dragStateRef = useRef<{ pointerId: number; startX: number; startScrollLeft: number; moved: boolean; } | null>(null); const hasMultiple = slides.length > 1; const goTo = useCallback((index: number, behavior: ScrollBehavior) => { const scroller = scrollerRef.current; if (!scroller) return; const target = scroller.children[index] as HTMLElement | undefined; if (!target) return; scroller.scrollTo({ left: target.offsetLeft, behavior }); }, []); const pauseAutoplay = useCallback(() => { setAutoplayPaused(true); if (resumeTimerRef.current !== null) { window.clearTimeout(resumeTimerRef.current); } resumeTimerRef.current = window.setTimeout(() => { setAutoplayPaused(false); resumeTimerRef.current = null; }, RESUME_AFTER_INTERACTION_MS); }, []); useEffect( () => () => { if (resumeTimerRef.current !== null) { window.clearTimeout(resumeTimerRef.current); } }, [], ); useEffect(() => { const scroller = scrollerRef.current; if (!scroller) return; const handleScroll = () => { const width = scroller.clientWidth; if (width === 0) return; const next = Math.round(scroller.scrollLeft / width); setActiveIndex((prev) => (prev === next ? prev : next)); }; handleScroll(); scroller.addEventListener("scroll", handleScroll, { passive: true }); return () => scroller.removeEventListener("scroll", handleScroll); }, [slides.length]); useEffect(() => { if (!hasMultiple || autoplayPaused) return; const timer = window.setInterval(() => { setActiveIndex((prev) => { const next = (prev + 1) % slides.length; goTo(next, "smooth"); return next; }); }, AUTOPLAY_MS); return () => window.clearInterval(timer); }, [hasMultiple, autoplayPaused, slides.length, goTo]); const handlePointerDown = (event: ReactPointerEvent) => { if (event.pointerType !== "mouse") return; const scroller = scrollerRef.current; if (!scroller) return; dragStateRef.current = { pointerId: event.pointerId, startX: event.clientX, startScrollLeft: scroller.scrollLeft, moved: false, }; scroller.setPointerCapture(event.pointerId); pauseAutoplay(); }; const handlePointerMove = (event: ReactPointerEvent) => { const drag = dragStateRef.current; if (!drag || drag.pointerId !== event.pointerId) return; const scroller = scrollerRef.current; if (!scroller) return; const dx = event.clientX - drag.startX; if (!drag.moved && Math.abs(dx) > 4) { drag.moved = true; scroller.style.scrollSnapType = "none"; } if (drag.moved) { scroller.scrollLeft = drag.startScrollLeft - dx; } }; const endDrag = (event: ReactPointerEvent) => { const drag = dragStateRef.current; if (!drag || drag.pointerId !== event.pointerId) return; const scroller = scrollerRef.current; dragStateRef.current = null; if (!scroller) return; if (scroller.hasPointerCapture(event.pointerId)) { scroller.releasePointerCapture(event.pointerId); } if (drag.moved) { const width = scroller.clientWidth || 1; const nearest = Math.round(scroller.scrollLeft / width); const clamped = Math.max(0, Math.min(slides.length - 1, nearest)); scroller.style.scrollSnapType = ""; goTo(clamped, "smooth"); } }; const pagination = hasMultiple ? (
{slides.map((slide, index) => { const active = index === activeIndex; return (
) : null; return (
{slides.map((slide, index) => (
{slide.alt}
))}
{hasMultiple ? ( <>
{pagination}
{pagination}
) : null}
); }