UNPKG

lightswind

Version:

A professionally designed animate react component library & templates market that brings together functionality, accessibility, and beautiful aesthetics for modern applications.

94 lines (93 loc) 4.8 kB
"use client"; import { jsx as _jsx } from "react/jsx-runtime"; import { useEffect, useRef } from "react"; import { cn } from "../lib/utils"; // Optional utility for className merging const SlidingCards = ({ cards, className = "", cardSize = "w-24 h-24", onCardClick, }) => { const cardStackRef = useRef(null); const cardsRef = useRef([]); useEffect(() => { const cardStack = cardStackRef.current; if (!cardStack) return; cardsRef.current = Array.from(cardStack.querySelectorAll(".card")); let isSwiping = false; let startX = 0; let currentX = 0; let animationFrameId = null; const getDuration = () => 300; const getActiveCard = () => cardsRef.current[0]; const updatePositions = () => { cardsRef.current.forEach((card, i) => { const offset = i + 1; card.style.zIndex = `${100 - offset}`; card.style.transform = `perspective(700px) translateZ(${-12 * offset}px) translateY(${7 * offset}px) translateX(0px) rotateY(0deg)`; card.style.opacity = `1`; }); }; const applySwipeStyles = (deltaX) => { const card = getActiveCard(); if (!card) return; const rotate = deltaX * 0.2; const opacity = 1 - Math.min(Math.abs(deltaX) / 100, 1) * 0.75; card.style.transform = `perspective(700px) translateZ(-12px) translateY(7px) translateX(${deltaX}px) rotateY(${rotate}deg)`; card.style.opacity = `${opacity}`; }; const handleStart = (clientX) => { if (isSwiping) return; isSwiping = true; startX = currentX = clientX; const card = getActiveCard(); card && (card.style.transition = "none"); }; const handleMove = (clientX) => { if (!isSwiping) return; if (animationFrameId) cancelAnimationFrame(animationFrameId); animationFrameId = requestAnimationFrame(() => { currentX = clientX; const deltaX = currentX - startX; applySwipeStyles(deltaX); if (Math.abs(deltaX) > 50) handleEnd(); }); }; const handleEnd = () => { if (!isSwiping) return; if (animationFrameId) cancelAnimationFrame(animationFrameId); const deltaX = currentX - startX; const threshold = 50; const duration = getDuration(); const card = getActiveCard(); if (card) { card.style.transition = `transform ${duration}ms ease, opacity ${duration}ms ease`; if (Math.abs(deltaX) > threshold) { const direction = Math.sign(deltaX); card.style.transform = `perspective(700px) translateZ(-12px) translateY(7px) translateX(${direction * 300}px) rotateY(${direction * 20}deg)`; setTimeout(() => { card.style.transform = `perspective(700px) translateZ(-12px) translateY(7px) translateX(${direction * 300}px) rotateY(${-direction * 20}deg)`; }, duration / 2); setTimeout(() => { cardsRef.current = [...cardsRef.current.slice(1), card]; updatePositions(); }, duration); } else { applySwipeStyles(0); } } isSwiping = false; startX = currentX = 0; }; cardStack.addEventListener("pointerdown", (e) => handleStart(e.clientX)); cardStack.addEventListener("pointermove", (e) => handleMove(e.clientX)); cardStack.addEventListener("pointerup", handleEnd); updatePositions(); }, []); return (_jsx("section", { ref: cardStackRef, className: cn("relative w-64 h-[22rem] grid place-content-center touch-none select-none", className), children: cards.map(({ id, icon, bgClass = "bg-gradient-to-br from-pink-300 to-orange-200" }, index) => (_jsx("article", { onClick: () => onCardClick?.(index), className: cn("card absolute inset-4 grid place-content-center rounded-xl border border-gray-400 shadow-md cursor-grab transition-transform ease-in-out", bgClass), children: _jsx("span", { className: cn("aspect-square grid place-content-center", cardSize), children: icon || (_jsx("svg", { className: "w-full h-full fill-white drop-shadow-md", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", children: _jsx("circle", { cx: "8", cy: "8", r: "6" }) })) }) }, id))) })); }; export default SlidingCards;