huggingface/ai-deadlines
781
1import * as React from "react"2import useEmblaCarousel, {3 type UseEmblaCarouselType,4} from "embla-carousel-react"5import { ArrowLeft, ArrowRight } from "lucide-react"6 7import { cn } from "@/lib/utils"8import { Button } from "@/components/ui/button"9 10type CarouselApi = UseEmblaCarouselType[1]11type UseCarouselParameters = Parameters<typeof useEmblaCarousel>12type CarouselOptions = UseCarouselParameters[0]13type CarouselPlugin = UseCarouselParameters[1]14 15type CarouselProps = {16 opts?: CarouselOptions17 plugins?: CarouselPlugin18 orientation?: "horizontal" | "vertical"19 setApi?: (api: CarouselApi) => void20}21 22type CarouselContextProps = {23 carouselRef: ReturnType<typeof useEmblaCarousel>[0]24 api: ReturnType<typeof useEmblaCarousel>[1]25 scrollPrev: () => void26 scrollNext: () => void27 canScrollPrev: boolean28 canScrollNext: boolean29} & CarouselProps30 31const CarouselContext = React.createContext<CarouselContextProps | null>(null)32 33function useCarousel() {34 const context = React.useContext(CarouselContext)35 36 if (!context) {37 throw new Error("useCarousel must be used within a <Carousel />")38 }39 40 return context41}42 43const Carousel = React.forwardRef<44 HTMLDivElement,45 React.HTMLAttributes<HTMLDivElement> & CarouselProps46>(47 (48 {49 orientation = "horizontal",50 opts,51 setApi,52 plugins,53 className,54 children,55 ...props56 },57 ref58 ) => {59 const [carouselRef, api] = useEmblaCarousel(60 {61 ...opts,62 axis: orientation === "horizontal" ? "x" : "y",63 },64 plugins65 )66 const [canScrollPrev, setCanScrollPrev] = React.useState(false)67 const [canScrollNext, setCanScrollNext] = React.useState(false)68 69 const onSelect = React.useCallback((api: CarouselApi) => {70 if (!api) {71 return72 }73 74 setCanScrollPrev(api.canScrollPrev())75 setCanScrollNext(api.canScrollNext())76 }, [])77 78 const scrollPrev = React.useCallback(() => {79 api?.scrollPrev()80 }, [api])81 82 const scrollNext = React.useCallback(() => {83 api?.scrollNext()84 }, [api])85 86 const handleKeyDown = React.useCallback(87 (event: React.KeyboardEvent<HTMLDivElement>) => {88 if (event.key === "ArrowLeft") {89 event.preventDefault()90 scrollPrev()91 } else if (event.key === "ArrowRight") {92 event.preventDefault()93 scrollNext()94 }95 },96 [scrollPrev, scrollNext]97 )98 99 React.useEffect(() => {100 if (!api || !setApi) {101 return102 }103 104 setApi(api)105 }, [api, setApi])106 107 React.useEffect(() => {108 if (!api) {109 return110 }111 112 onSelect(api)113 api.on("reInit", onSelect)114 api.on("select", onSelect)115 116 return () => {117 api?.off("select", onSelect)118 }119 }, [api, onSelect])120 121 return (122 <CarouselContext.Provider123 value={{124 carouselRef,125 api: api,126 opts,127 orientation:128 orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),129 scrollPrev,130 scrollNext,131 canScrollPrev,132 canScrollNext,133 }}134 >135 <div136 ref={ref}137 onKeyDownCapture={handleKeyDown}138 className={cn("relative", className)}139 role="region"140 aria-roledescription="carousel"141 {...props}142 >143 {children}144 </div>145 </CarouselContext.Provider>146 )147 }148)149Carousel.displayName = "Carousel"150 151const CarouselContent = React.forwardRef<152 HTMLDivElement,153 React.HTMLAttributes<HTMLDivElement>154>(({ className, ...props }, ref) => {155 const { carouselRef, orientation } = useCarousel()156 157 return (158 <div ref={carouselRef} className="overflow-hidden">159 <div160 ref={ref}161 className={cn(162 "flex",163 orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",164 className165 )}166 {...props}167 />168 </div>169 )170})171CarouselContent.displayName = "CarouselContent"172 173const CarouselItem = React.forwardRef<174 HTMLDivElement,175 React.HTMLAttributes<HTMLDivElement>176>(({ className, ...props }, ref) => {177 const { orientation } = useCarousel()178 179 return (180 <div181 ref={ref}182 role="group"183 aria-roledescription="slide"184 className={cn(185 "min-w-0 shrink-0 grow-0 basis-full",186 orientation === "horizontal" ? "pl-4" : "pt-4",187 className188 )}189 {...props}190 />191 )192})193CarouselItem.displayName = "CarouselItem"194 195const CarouselPrevious = React.forwardRef<196 HTMLButtonElement,197 React.ComponentProps<typeof Button>198>(({ className, variant = "outline", size = "icon", ...props }, ref) => {199 const { orientation, scrollPrev, canScrollPrev } = useCarousel()200 201 return (202 <Button203 ref={ref}204 variant={variant}205 size={size}206 className={cn(207 "absolute h-8 w-8 rounded-full",208 orientation === "horizontal"209 ? "-left-12 top-1/2 -translate-y-1/2"210 : "-top-12 left-1/2 -translate-x-1/2 rotate-90",211 className212 )}213 disabled={!canScrollPrev}214 onClick={scrollPrev}215 {...props}216 >217 <ArrowLeft className="h-4 w-4" />218 <span className="sr-only">Previous slide</span>219 </Button>220 )221})222CarouselPrevious.displayName = "CarouselPrevious"223 224const CarouselNext = React.forwardRef<225 HTMLButtonElement,226 React.ComponentProps<typeof Button>227>(({ className, variant = "outline", size = "icon", ...props }, ref) => {228 const { orientation, scrollNext, canScrollNext } = useCarousel()229 230 return (231 <Button232 ref={ref}233 variant={variant}234 size={size}235 className={cn(236 "absolute h-8 w-8 rounded-full",237 orientation === "horizontal"238 ? "-right-12 top-1/2 -translate-y-1/2"239 : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",240 className241 )}242 disabled={!canScrollNext}243 onClick={scrollNext}244 {...props}245 >246 <ArrowRight className="h-4 w-4" />247 <span className="sr-only">Next slide</span>248 </Button>249 )250})251CarouselNext.displayName = "CarouselNext"252 253export {254 type CarouselApi,255 Carousel,256 CarouselContent,257 CarouselItem,258 CarouselPrevious,259 CarouselNext,260}261 