"use client"; import React, { useEffect, useMemo, useState } from "react"; function hasIdentifier( value: unknown ): value is { id: string | number; } { if (typeof value !== "object" || value === null || !("id" in value)) { return false; } const candidate = (value as { id: unknown }).id; return typeof candidate === "string" || typeof candidate === "number"; } interface SimpleCarouselProps { items: T[]; renderItem: (item: T, index: number, isActive: boolean) => React.ReactNode; keyExtractor?: (item: T, index: number) => string | number; className?: string; autoPlay?: boolean; interval?: number; showControls?: boolean; showIndicators?: boolean; } export function SimpleCarousel({ items, renderItem, keyExtractor, className = "", autoPlay = true, interval = 5000, showControls = true, showIndicators = true, }: SimpleCarouselProps) { const slides = useMemo(() => items.filter(Boolean), [items]); const [activeIndex, setActiveIndex] = useState(0); useEffect(() => { if (!autoPlay || slides.length <= 1) return; const timer = window.setInterval(() => { setActiveIndex((prev) => (prev + 1) % slides.length); }, interval); return () => window.clearInterval(timer); }, [autoPlay, interval, slides.length]); useEffect(() => { if (activeIndex >= slides.length) { setActiveIndex(Math.max(slides.length - 1, 0)); } }, [activeIndex, slides.length]); if (slides.length === 0) { return null; } const goTo = (index: number) => { setActiveIndex((prev) => { if (index < 0) { return slides.length - 1; } if (index >= slides.length) { return 0; } return index; }); }; return (
{slides.map((item, index) => { const key = keyExtractor?.(item, index) ?? (hasIdentifier(item) ? item.id : index); return (
{renderItem(item, index, index === activeIndex)}
); })}
{showControls && slides.length > 1 && ( <> )} {showIndicators && slides.length > 1 && (
{slides.map((_, index) => (
)}
); }