const { useState, useEffect, useCallback } = React; const { motion, AnimatePresence, useReducedMotion } = window.Motion; function cn(...classes) { return classes.filter(Boolean).join(" "); } function wrapIndex(n, len) { if (len <= 0) return 0; return ((n % len) + len) % len; } function signedOffset(i, active, len, loop) { const raw = i - active; if (!loop || len <= 1) return raw; const alt = raw > 0 ? raw - len : raw + len; return Math.abs(alt) < Math.abs(raw) ? alt : raw; } const SquareArrowOutUpRight = ({ className }) => ( ); function CardStack({ items, initialIndex = 0, maxVisible = 7, cardWidth = 520, cardHeight = 320, overlap = 0.48, spreadDeg = 48, perspectivePx = 1100, depthPx = 140, tiltXDeg = 12, activeLiftPx = 22, activeScale = 1.03, inactiveScale = 0.94, springStiffness = 280, springDamping = 28, loop = true, autoAdvance = false, intervalMs = 2800, pauseOnHover = true, showDots = true, className, onChangeIndex, renderCard, }) { const reduceMotion = useReducedMotion ? useReducedMotion() : false; const len = items.length; const [active, setActive] = useState(() => wrapIndex(initialIndex, len)); const [hovering, setHovering] = useState(false); useEffect(() => { setActive((a) => wrapIndex(a, len)); }, [len]); useEffect(() => { if (!len) return; onChangeIndex?.(active, items[active]); }, [active]); const maxOffset = Math.max(0, Math.floor(maxVisible / 2)); const cardSpacing = Math.max(10, Math.round(cardWidth * (1 - overlap))); const stepDeg = maxOffset > 0 ? spreadDeg / maxOffset : 0; const canGoPrev = loop || active > 0; const canGoNext = loop || active < len - 1; const prev = useCallback(() => { if (!len || !canGoPrev) return; setActive((a) => wrapIndex(a - 1, len)); }, [canGoPrev, len]); const next = useCallback(() => { if (!len || !canGoNext) return; setActive((a) => wrapIndex(a + 1, len)); }, [canGoNext, len]); const onKeyDown = (e) => { if (e.key === "ArrowLeft") prev(); if (e.key === "ArrowRight") next(); }; useEffect(() => { if (!autoAdvance) return; if (reduceMotion) return; if (!len) return; if (pauseOnHover && hovering) return; const id = window.setInterval( () => { if (loop || active < len - 1) next(); }, Math.max(700, intervalMs), ); return () => window.clearInterval(id); }, [ autoAdvance, intervalMs, hovering, pauseOnHover, reduceMotion, len, loop, active, next, ]); if (!len) return null; const activeItem = items[active]; return (
setHovering(true)} onMouseLeave={() => setHovering(false)} >
); } function DefaultFanCard({ item, active }) { return (
{item.imageSrc ? ( {item.title} ) : (
No image
)}
{item.tag && ( {item.tag} )}
{item.title}
{item.description ? (
{item.description}
) : null}
); } function CardStackDemo() { const items = (window.prestasiData || []).map(p => ({ id: p.id, title: p.title, description: p.desc, imageSrc: p.photo, tag: p.tag, href: null })); if (items.length === 0) { items.push( { id: 1, title: "Juara Harapan 1 Turnamen Futsal", description: "Tim futsal sekolah sukses menembus babak semifinal dan meraih juara harapan 1.", imageSrc: "img/futsal.jpg", tag: "Olahraga" } ); } const isMobile = window.innerWidth < 640; const cardWidth = isMobile ? 300 : 420; const cardHeight = isMobile ? 400 : 480; const overlap = isMobile ? 0.35 : 0.45; return (
); } const rootElement = document.getElementById("react-prestasi-root"); if (rootElement) { const root = ReactDOM.createRoot(rootElement); root.render(); }