import React, { useState, Children, isValidElement, cloneElement, useContext } from 'react'; import { useStyles, useTheme } from '../../core'; import { Flex } from '../Flex/Flex'; import { IconButton } from '../IconButton/IconButton'; import { ChevronLeftIcon, ChevronRightIcon } from '../../icons'; interface CarouselContextType { activeIndex: number; setActiveIndex: (index: number) => void; totalSlides: number; } const CarouselContext = React.createContext(null); const useCarousel = () => { const context = useContext(CarouselContext); if (!context) throw new Error('Carousel components must be used within a Carousel provider.'); return context; }; // Main Wrapper export const Carousel: React.FC<{ children: React.ReactNode, className?: string }> & { Container?: React.FC<{ children: React.ReactNode }>; Previous?: React.FC; Next?: React.FC; Dots?: React.FC; } = ({ children, className }) => { const validChildren = Children.toArray(children).filter(isValidElement); const [activeIndex, setActiveIndex] = useState(0); const containerChild = validChildren.find(c => (c.type as any).displayName === 'Carousel.Container'); const totalSlides = containerChild ? Children.count((containerChild as React.ReactElement).props.children) : 0; return (
{children}
); }; // Slides Container const CarouselContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { activeIndex } = useCarousel(); const createStyle = useStyles('carousel-container'); const containerClass = createStyle({ overflow: 'hidden', borderRadius: '8px', }); const innerClass = createStyle({ display: 'flex', transition: 'transform 0.5s ease-in-out', transform: `translateX(-${activeIndex * 100}%)`, }); return (
{Children.map(children, child => (
{child}
))}
); }; CarouselContainer.displayName = 'Carousel.Container'; Carousel.Container = CarouselContainer; // Prev Button const CarouselPrevious: React.FC = () => { const { activeIndex, setActiveIndex, totalSlides } = useCarousel(); return ( setActiveIndex((activeIndex - 1 + totalSlides) % totalSlides)} isRound style={{ position: 'absolute', top: '50%', left: '1rem', transform: 'translateY(-50%)', zIndex: 10 }} /> ); }; CarouselPrevious.displayName = 'Carousel.Previous'; Carousel.Previous = CarouselPrevious; // Next Button const CarouselNext: React.FC = () => { const { activeIndex, setActiveIndex, totalSlides } = useCarousel(); return ( setActiveIndex((activeIndex + 1) % totalSlides)} isRound style={{ position: 'absolute', top: '50%', right: '1rem', transform: 'translateY(-50%)', zIndex: 10 }} /> ); }; CarouselNext.displayName = 'Carousel.Next'; Carousel.Next = CarouselNext; // Dots const CarouselDots: React.FC = () => { const { activeIndex, setActiveIndex, totalSlides } = useCarousel(); const createStyle = useStyles('carousel-dots'); const dotClass = createStyle({ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: 'rgba(255, 255, 255, 0.4)', transition: 'all 0.3s', cursor: 'pointer', border: 'none', padding: 0, '&[data-active="true"]': { backgroundColor: 'rgba(255, 255, 255, 1)', transform: 'scale(1.2)' } }); return ( {Array.from({ length: totalSlides }).map((_, index) => (