"use client" import * as React from "react" import { cn } from "@/lib/utils" import useEmblaCarousel, { type UseEmblaCarouselType, } from "embla-carousel-react" import { CaretLeft, CaretRight } from "@phosphor-icons/react" import { Button } from "@/components/ui/button" type CarouselApi = UseEmblaCarouselType[1] type UseCarouselParameters = Parameters type CarouselOptions = UseCarouselParameters[0] type CarouselPlugin = UseCarouselParameters[1] type CarouselProps = { opts?: CarouselOptions plugins?: CarouselPlugin orientation?: "horizontal" | "vertical" setApi?: (api: CarouselApi) => void } type CarouselContextProps = { carouselRef: UseEmblaCarouselType[0] api: UseEmblaCarouselType[1] scrollPrev: () => void scrollNext: () => void canScrollPrev: boolean canScrollNext: boolean } & CarouselProps const CarouselContext = React.createContext(null) function useCarousel() { const context = React.useContext(CarouselContext) if (!context) { throw new Error("useCarousel must be used within a ") } return context } function Carousel({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }: React.ComponentProps<"div"> & CarouselProps) { const [carouselRef, api] = useEmblaCarousel( { ...opts, axis: orientation === "horizontal" ? "x" : "y", }, plugins ) const [, setScrollTick] = React.useState(0) /* Nilai tombol dibaca saat render; peristiwa embla hanya memicu render ulang. Memanggil setState langsung di badan effect melanggar react-hooks/set-state-in-effect. */ const canScrollPrev = api?.canScrollPrev() ?? false const canScrollNext = api?.canScrollNext() ?? false const scrollPrev = React.useCallback(() => { api?.scrollPrev() }, [api]) const scrollNext = React.useCallback(() => { api?.scrollNext() }, [api]) const handleKeyDown = React.useCallback( (event: React.KeyboardEvent) => { if (event.key === "ArrowLeft") { event.preventDefault() scrollPrev() } else if (event.key === "ArrowRight") { event.preventDefault() scrollNext() } }, [scrollPrev, scrollNext] ) React.useEffect(() => { if (!api || !setApi) return setApi(api) }, [api, setApi]) React.useEffect(() => { if (!api) return const rerender = () => setScrollTick((tick) => tick + 1) api.on("reInit", rerender) api.on("select", rerender) return () => { api.off("reInit", rerender) api.off("select", rerender) } }, [api]) return (
{children}
) } function CarouselContent({ className, ...props }: React.ComponentProps<"div">) { const { carouselRef, orientation } = useCarousel() return (
) } function CarouselItem({ className, ...props }: React.ComponentProps<"div">) { const { orientation } = useCarousel() return (
) } function CarouselPrevious({ className, variant = "neutral", size = "icon", ...props }: React.ComponentProps) { const { orientation, scrollPrev, canScrollPrev } = useCarousel() return ( ) } function CarouselNext({ className, variant = "neutral", size = "icon", ...props }: React.ComponentProps) { const { orientation, scrollNext, canScrollNext } = useCarousel() return ( ) } export { type CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, useCarousel, }