UNPKG

lightswind

Version:

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

175 lines (174 loc) 10.7 kB
"use client"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useCallback, useEffect } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { cn } from '../lib/utils'; export const TeamCarousel = ({ members, title = "OUR TEAM", titleSize = "2xl", titleColor = "rgb(8, 42, 123)", background = "#000", cardWidth = 280, cardHeight = 380, cardRadius = 20, showArrows = true, showDots = true, keyboardNavigation = true, touchNavigation = true, animationDuration = 800, autoPlay = 0, pauseOnHover = true, visibleCards = 2, sideCardScale = 0.9, sideCardOpacity = 0.8, grayscaleEffect = true, className, cardClassName, titleClassName, infoPosition = "bottom", infoTextColor = "rgb(8, 42, 123)", infoBackground = "transparent", onMemberChange, onCardClick, initialIndex = 0, }) => { const [currentIndex, setCurrentIndex] = useState(initialIndex); const [isAnimating, setIsAnimating] = useState(false); const [touchStart, setTouchStart] = useState(0); const [touchEnd, setTouchEnd] = useState(0); const updateCarousel = useCallback((newIndex) => { if (isAnimating || members.length === 0) return; setIsAnimating(true); const nextIndex = (newIndex + members.length) % members.length; setCurrentIndex(nextIndex); onMemberChange?.(members[nextIndex], nextIndex); setTimeout(() => { setIsAnimating(false); }, animationDuration); }, [isAnimating, members, animationDuration, onMemberChange]); const getCardPosition = (index) => { const offset = (index - currentIndex + members.length) % members.length; if (offset === 0) return 'center'; if (offset === 1) return 'right-1'; if (offset === 2) return 'right-2'; if (offset === members.length - 1) return 'left-1'; if (offset === members.length - 2) return 'left-2'; return 'hidden'; }; const getCardStyles = (position) => { const baseTransform = 'translateY(-50%)'; switch (position) { case 'center': return { transform: `${baseTransform} scale(1.1) translateZ(0)`, zIndex: 10, opacity: 1, }; case 'left-1': return { transform: `${baseTransform} translateX(-${cardWidth * 0.7}px) scale(${sideCardScale}) translateZ(-100px)`, zIndex: 5, opacity: sideCardOpacity, }; case 'left-2': return { transform: `${baseTransform} translateX(-${cardWidth * 1.4}px) scale(${sideCardScale * 0.9}) translateZ(-300px)`, zIndex: 1, opacity: sideCardOpacity * 0.7, }; case 'right-1': return { transform: `${baseTransform} translateX(${cardWidth * 0.7}px) scale(${sideCardScale}) translateZ(-100px)`, zIndex: 5, opacity: sideCardOpacity, }; case 'right-2': return { transform: `${baseTransform} translateX(${cardWidth * 1.4}px) scale(${sideCardScale * 0.9}) translateZ(-300px)`, zIndex: 1, opacity: sideCardOpacity * 0.7, }; default: return { transform: `${baseTransform} scale(0.8)`, opacity: 0, pointerEvents: 'none', }; } }; // Auto-play functionality useEffect(() => { if (autoPlay > 0) { const interval = setInterval(() => { updateCarousel(currentIndex + 1); }, autoPlay); return () => clearInterval(interval); } }, [autoPlay, currentIndex, updateCarousel]); // Keyboard navigation useEffect(() => { if (!keyboardNavigation) return; const handleKeyDown = (e) => { if (e.key === 'ArrowLeft') { updateCarousel(currentIndex - 1); } else if (e.key === 'ArrowRight') { updateCarousel(currentIndex + 1); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [keyboardNavigation, currentIndex, updateCarousel]); // Touch navigation const handleTouchStart = (e) => { if (!touchNavigation) return; setTouchStart(e.targetTouches[0].clientX); }; const handleTouchMove = (e) => { if (!touchNavigation) return; setTouchEnd(e.targetTouches[0].clientX); }; const handleTouchEnd = () => { if (!touchNavigation) return; const swipeThreshold = 50; const diff = touchStart - touchEnd; if (Math.abs(diff) > swipeThreshold) { if (diff > 0) { updateCarousel(currentIndex + 1); } else { updateCarousel(currentIndex - 1); } } }; const titleSizeClasses = { sm: 'text-4xl', md: 'text-5xl', lg: 'text-6xl', xl: 'text-7xl', '2xl': 'text-8xl', }; return (_jsxs("div", { className: cn(`min-h-screen flex flex-col items-center justify-center overflow-hidden`, className), style: { background }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [title && (_jsx("h1", { className: cn(`font-black uppercase  tracking-tight absolute top-12 left-1/2 transform -translate-x-1/2 pointer-events-none whitespace-nowrap`, titleSizeClasses[titleSize], titleClassName), style: { color: 'transparent', // Keep only one color declaration here background: `linear-gradient(to bottom, ${titleColor}35 30%, transparent 76%)`, WebkitBackgroundClip: 'text', backgroundClip: 'text', }, children: title })), _jsxs("div", { className: "w-full max-w-6xl relative mt-20", style: { height: cardHeight + 100, perspective: '1000px', }, children: [showArrows && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => updateCarousel(currentIndex - 1), className: "absolute left-5 top-1/2 transform -translate-y-1/2 bg-black/60 hover:bg-black/80 text-white w-10 h-10 rounded-full flex items-center justify-center z-20 transition-all duration-300 hover:scale-110", children: _jsx(ChevronLeft, { className: "w-6 h-6" }) }), _jsx("button", { onClick: () => updateCarousel(currentIndex + 1), className: "absolute right-5 top-1/2 transform -translate-y-1/2 bg-black/60 hover:bg-black/80 text-white w-10 h-10 rounded-full flex items-center justify-center z-20 transition-all duration-300 hover:scale-110", children: _jsx(ChevronRight, { className: "w-6 h-6" }) })] })), _jsx("div", { className: "w-full h-full flex justify-center items-center relative", style: { transformStyle: 'preserve-3d' }, children: members.map((member, index) => { const position = getCardPosition(index); const styles = getCardStyles(position); return (_jsxs("div", { className: cn("absolute  bg-white overflow-hidden shadow-2xl cursor-pointer transition-all ", cardClassName), style: { width: cardWidth, height: cardHeight, borderRadius: cardRadius, top: '90%', left: '50%', marginLeft: -cardWidth / 2, marginTop: -cardHeight / 2, transitionDuration: `${animationDuration}ms`, transitionTimingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', ...styles, }, onClick: () => { updateCarousel(index); onCardClick?.(member, index); }, children: [_jsx("img", { src: member.image, alt: member.name, className: "w-full h-full object-cover transition-all duration-800", style: { filter: position !== 'center' && grayscaleEffect ? 'grayscale(100%)' : 'none', } }), infoPosition === 'overlay' && (_jsxs("div", { className: "absolute bottom-0 left-0 right-0 p-4 text-center", style: { background: infoBackground || 'linear-gradient(transparent, rgba(0,0,0,0.8))', color: infoTextColor, }, children: [_jsx("h3", { className: "text-lg font-bold", children: member.name }), _jsx("p", { className: "text-sm opacity-90", children: member.role })] }))] }, member.id)); }) })] }), infoPosition === 'bottom' && members[currentIndex] && (_jsxs("div", { className: "text-center mt-10 transition-all duration-500", children: [_jsxs("h2", { className: "text-4xl font-bold mb-3 relative inline-block", style: { color: infoTextColor }, children: [members[currentIndex].name, _jsx("span", { className: "absolute top-full left-0 w-full h-0.5 mt-2", style: { background: infoTextColor } })] }), _jsx("p", { className: "text-xl font-medium opacity-80 uppercase tracking-wider", style: { color: infoTextColor }, children: members[currentIndex].role }), members[currentIndex].bio && (_jsx("p", { className: "text-base mt-4 max-w-lg mx-auto opacity-70", children: members[currentIndex].bio }))] })), showDots && (_jsx("div", { className: "flex justify-center gap-3 mt-15 ", children: members.map((_, index) => (_jsx("button", { onClick: () => updateCarousel(index), className: cn("w-3 h-3 rounded-full transition-all duration-300", index === currentIndex ? "scale-125" : "hover:scale-110"), style: { background: index === currentIndex ? infoTextColor : `${infoTextColor}40`, } }, index))) }))] })); }; export default TeamCarousel;