"use client"; import { useState, useEffect, useCallback, ReactNode } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import Image from "next/image"; // ============================================================================= // CAROUSEL // Sliding content carousel // ============================================================================= interface CarouselProps { children: ReactNode[]; autoPlay?: boolean; autoPlayInterval?: number; showArrows?: boolean; showDots?: boolean; className?: string; } export function Carousel({ children, autoPlay = false, autoPlayInterval = 5000, showArrows = true, showDots = true, className, }: CarouselProps) { const [current, setCurrent] = useState(0); const [direction, setDirection] = useState(0); const slideCount = children.length; const next = useCallback(() => { setDirection(1); setCurrent((prev) => (prev + 1) % slideCount); }, [slideCount]); const prev = useCallback(() => { setDirection(-1); setCurrent((prev) => (prev - 1 + slideCount) % slideCount); }, [slideCount]); const goTo = (index: number) => { setDirection(index > current ? 1 : -1); setCurrent(index); }; useEffect(() => { if (!autoPlay) return; const timer = setInterval(next, autoPlayInterval); return () => clearInterval(timer); }, [autoPlay, autoPlayInterval, next]); const variants = { enter: (direction: number) => ({ x: direction > 0 ? 1000 : -1000, opacity: 0, }), center: { x: 0, opacity: 1, }, exit: (direction: number) => ({ x: direction < 0 ? 1000 : -1000, opacity: 0, }), }; return (
{children[current]}
{/* Arrows */} {showArrows && slideCount > 1 && ( <> )} {/* Dots */} {showDots && slideCount > 1 && (
{children.map((_, index) => (
)}
); } // ============================================================================= // IMAGE CAROUSEL // Carousel with images // ============================================================================= interface ImageCarouselProps { images: { src: string; alt: string; caption?: string }[]; autoPlay?: boolean; autoPlayInterval?: number; showArrows?: boolean; showDots?: boolean; showCaptions?: boolean; className?: string; } export function ImageCarousel({ images, autoPlay = true, autoPlayInterval = 5000, showArrows = true, showDots = true, showCaptions = true, className, }: ImageCarouselProps) { return ( {images.map((image, index) => (
{image.alt} {showCaptions && image.caption && (

{image.caption}

)}
))}
); } // ============================================================================= // TESTIMONIAL CAROUSEL // Carousel for testimonials // ============================================================================= interface Testimonial { content: string; author: string; role?: string; avatar?: string; rating?: number; } interface TestimonialCarouselProps { testimonials: Testimonial[]; autoPlay?: boolean; className?: string; } export function TestimonialCarousel({ testimonials, autoPlay = true, className, }: TestimonialCarouselProps) { const [current, setCurrent] = useState(0); useEffect(() => { if (!autoPlay) return; const timer = setInterval(() => { setCurrent((prev) => (prev + 1) % testimonials.length); }, 6000); return () => clearInterval(timer); }, [autoPlay, testimonials.length]); return (
{/* Rating */} {testimonials[current].rating && (
{Array.from({ length: 5 }).map((_, i) => ( ))}
)} {/* Quote */}
“{testimonials[current].content}”
{/* Author */}
{testimonials[current].avatar && (
{testimonials[current].author}
)}
{testimonials[current].author}
{testimonials[current].role && (
{testimonials[current].role}
)}
{/* Dots */} {testimonials.length > 1 && (
{testimonials.map((_, index) => (
)}
); }