"use client"; import { cn } from "@/app/lib/cn"; import useEmblaCarousel from "embla-carousel-react"; import { ArrowLeft, ArrowRight } from "iconsax-reactjs"; import { createContext, useContext, useEffect, useState, type FC, type HTMLAttributes, type ReactNode, } from "react"; type CarouselApi = { scrollPrev: () => void; scrollNext: () => void; canScrollPrev: boolean; canScrollNext: boolean; slidesPerView: number; gap: number; }; type ViewportRef = (node: HTMLElement | null) => void; const CarouselContext = createContext(null); const CarouselViewportContext = createContext(null); function useCarousel() { const context = useContext(CarouselContext); if (!context) { throw new Error("Carousel components must be used within "); } return context; } type CarouselProps = { children: ReactNode; slidesPerView?: number; slidesToScroll?: number; gap?: number; direction?: "rtl" | "ltr"; className?: string; }; const Carousel: FC = ({ children, slidesPerView = 5, slidesToScroll, gap = 40, direction = "rtl", className, }) => { const [emblaRef, emblaApi] = useEmblaCarousel({ align: "start", direction, slidesToScroll: slidesToScroll ?? slidesPerView, containScroll: "trimSnaps", }); const [canScrollPrev, setCanScrollPrev] = useState(false); const [canScrollNext, setCanScrollNext] = useState(false); useEffect(() => { if (!emblaApi) return; const updateButtons = () => { setCanScrollPrev(emblaApi.canScrollPrev()); setCanScrollNext(emblaApi.canScrollNext()); }; updateButtons(); emblaApi.on("select", updateButtons); emblaApi.on("reInit", updateButtons); return () => { emblaApi.off("select", updateButtons); emblaApi.off("reInit", updateButtons); }; }, [emblaApi]); return ( emblaApi?.scrollPrev(), scrollNext: () => emblaApi?.scrollNext(), canScrollPrev, canScrollNext, slidesPerView, gap, }} >
{children}
); }; type CarouselControlsProps = { className?: string; prevLabel?: string; nextLabel?: string; }; const CarouselControls: FC = ({ className, prevLabel = "قبلی", nextLabel = "بعدی", }) => { const { scrollPrev, scrollNext, canScrollPrev, canScrollNext } = useCarousel(); return (
); }; type CarouselTrackProps = { children: ReactNode; className?: string; }; const CarouselTrack: FC = ({ children, className }) => { const emblaRef = useContext(CarouselViewportContext); const { gap } = useCarousel(); if (!emblaRef) { throw new Error("CarouselTrack must be used within "); } return (
{children}
); }; type CarouselSlideProps = HTMLAttributes & { children: ReactNode; }; const CarouselSlide: FC = ({ children, className, style, ...rest }) => { const { slidesPerView, gap } = useCarousel(); const totalGap = (slidesPerView - 1) * gap; return (
{children}
); }; export { Carousel, CarouselControls, CarouselSlide, CarouselTrack }; export default Carousel;