कैरसेल
उत्पाद गैलरियों, प्रशंसापत्रों और हीरो सेक्शन के लिए स्वाइप करने योग्य स्लाइडशो — नियोब्रूटलिस्ट बॉर्डर और कठोर छायाओं में सजी हुई।
import * as React from "react"
import { Card, CardContent } from "@/components/ui/card"कैरॉसेल स्लाइडों की पट्टी पर स्नैप करता है — हॉरिज़ॉन्टल या वर्टिकल — ड्रैग और स्वाइप, previous/next बटन, और imperative API के साथ। यह Embla Carousel लाइब्रेरी पर बना है और नियोब्रूटलिस्ट रेसिपी के अनुसार स्टाइल किया गया है: मोटे बॉर्डर, कठोर छायाएँ और बोल्ड टाइपोग्राफ़ी।
इसे इन स्थितियों में चुनें:
- प्रोडक्ट गैलरी — लंबी स्क्रॉल की जगह तय स्लॉट में स्क्रीनशॉट या फ़ोटो घुमाएँ।
- टेस्टिमोनियल और लोगो वॉल — सोशल प्रूफ़ घुमाएँ; Embla प्लगिन से autoplay।
- ऑनबोर्डिंग और फ़ीचर टूर — छोटी श्रृंखला को एक-एक कार्ड पर दिखाएँ।
परिचय#
carousel कंपोनेंट Embla Carousel लाइब्रेरी का उपयोग करके बनाया गया है।
इंस्टॉलेशन#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/carousel.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/carousel.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/carousel.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/carousel.json
निम्नलिखित डिपेंडेंसी इंस्टॉल करें:
pnpm add embla-carousel-reactnpm install embla-carousel-reactyarn add embla-carousel-reactbun add embla-carousel-react
निम्नलिखित कोड को कॉपी करके अपने प्रोजेक्ट में पेस्ट करें।
"use client"
import * as React from "react"
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react"
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = {
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
function useCarousel() {
const context = React.useContext(CarouselContext)
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />")
}
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 [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) return
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
const scrollPrev = React.useCallback(() => {
api?.scrollPrev()
}, [api])
const scrollNext = React.useCallback(() => {
api?.scrollNext()
}, [api])
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
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
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
return () => {
api?.off("reInit", onSelect)
api?.off("select", onSelect)
}
}, [api, onSelect])
return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
onKeyDownCapture={handleKeyDown}
className={cn("relative", className)}
role="region"
aria-roledescription="carousel"
data-slot="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
)
}
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
const { carouselRef, orientation } = useCarousel()
return (
<div
ref={carouselRef}
className="overflow-hidden"
data-slot="carousel-content"
>
<div
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className
)}
{...props}
/>
</div>
)
}
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
const { orientation } = useCarousel()
return (
<div
role="group"
aria-roledescription="slide"
data-slot="carousel-item"
className={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
className
)}
{...props}
/>
)
}
function CarouselPrevious({
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
data-slot="carousel-previous"
variant={variant}
size={size}
className={cn(
"absolute size-8 touch-manipulation rounded",
orientation === "horizontal"
? "top-1/2 -left-12 -translate-y-1/2 hover:-translate-y-[calc(50%-2px)] active:-translate-y-[calc(50%-4px)]"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90 hover:-translate-x-[calc(50%-2px)] active:-translate-x-[calc(50%-4px)]",
className
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeftIcon className="cn-rtl-flip" />
<span className="sr-only">Previous slide</span>
</Button>
)
}
function CarouselNext({
className,
variant = "outline",
size = "icon-sm",
...props
}: React.ComponentProps<typeof Button>) {
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
data-slot="carousel-next"
variant={variant}
size={size}
className={cn(
"absolute size-8 touch-manipulation rounded",
orientation === "horizontal"
? "top-1/2 -right-12 -translate-y-1/2 hover:-translate-y-[calc(50%-2px)] active:-translate-y-[calc(50%-4px)]"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90 hover:-translate-x-[calc(50%-2px)] active:-translate-x-[calc(50%-4px)]",
className
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRightIcon className="cn-rtl-flip" />
<span className="sr-only">Next slide</span>
</Button>
)
}
export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
useCarousel,
}
अपने प्रोजेक्ट सेटअप से मेल खाने के लिए import पाथ अपडेट करें।
उपयोग#
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>कंपोज़िशन#
Carousel बनाने के लिए निम्नलिखित कंपोज़िशन का उपयोग करें:
Carousel
├── CarouselContent
│ ├── CarouselItem
│ └── CarouselItem
├── CarouselPrevious
└── CarouselNextउदाहरण#
साइज़#
आइटम का साइज़ सेट करने के लिए, आप <CarouselItem /> पर basis यूटिलिटी क्लास का उपयोग कर सकते हैं।
import * as React from "react"
import { Card, CardContent } from "@/components/ui/card"// 33% of the carousel width.
<Carousel>
<CarouselContent>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>// 50% on small screens and 33% on larger screens.
<Carousel>
<CarouselContent>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>स्पेसिंग#
आइटम के बीच स्पेसिंग सेट करने के लिए, हम <CarouselItem /> पर एक pl-[VALUE] यूटिलिटी और <CarouselContent /> पर एक नेगेटिव -ml-[VALUE] का उपयोग करते हैं।
import * as React from "react"
import { Card, CardContent } from "@/components/ui/card"<Carousel>
<CarouselContent className="-ml-4">
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
</CarouselContent>
</Carousel><Carousel>
<CarouselContent className="-ml-2 md:-ml-4">
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
</CarouselContent>
</Carousel>ओरिएंटेशन#
carousel का ओरिएंटेशन सेट करने के लिए orientation prop का उपयोग करें।
import * as React from "react"
import { Card, CardContent } from "@/components/ui/card"<Carousel orientation="vertical | horizontal">
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>विकल्प#
आप opts prop का उपयोग करके carousel को विकल्प पास कर सकते हैं। अधिक जानकारी के लिए Embla Carousel docs देखें।
<Carousel
opts={{
align: "start",
loop: true,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>API#
carousel API का इंस्टेंस पाने के लिए एक state और setApi prop का उपयोग करें।
"use client"
import * as React from "react"import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
const [current, setCurrent] = React.useState(0)
const [count, setCount] = React.useState(0)
React.useEffect(() => {
if (!api) {
return
}
setCount(api.scrollSnapList().length)
setCurrent(api.selectedScrollSnap() + 1)
api.on("select", () => {
setCurrent(api.selectedScrollSnap() + 1)
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
)
}इवेंट्स#
आप setApi से मिली api इंस्टेंस का उपयोग करके इवेंट्स सुन सकते हैं।
import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
React.useEffect(() => {
if (!api) {
return
}
api.on("select", () => {
// Do something on select.
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
)
}इवेंट्स के उपयोग के बारे में अधिक जानकारी के लिए Embla Carousel docs देखें।
Plugins#
आप carousel में plugins जोड़ने के लिए plugins prop का उपयोग कर सकते हैं।
import Autoplay from "embla-carousel-autoplay"
export function Example() {
return (
<Carousel
plugins={[
Autoplay({
delay: 2000,
}),
]}
>
// ...
</Carousel>
)
}"use client"
import * as React from "react"RTL#
Neobrutalism में RTL सपोर्ट सक्षम करने के लिए, RTL कॉन्फ़िगरेशन गाइड देखें।
"use client"
import * as React from "react"RTL भाषाओं के लिए carousel को लोकलाइज़ करते समय, आपको टेक्स्ट की दिशा से मेल खाने के लिए opts prop में direction विकल्प सेट करना होगा। इससे यह सुनिश्चित होता है कि carousel सही दिशा में स्क्रॉल करे।
<Carousel
dir={dir}
opts={{
direction: dir,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious className="rtl:rotate-180" />
<CarouselNext className="rtl:rotate-180" />
</Carousel>direction विकल्प "ltr" या "rtl" स्वीकार करता है और उसे dir prop की वैल्यू से मेल खाना चाहिए। आप navigation बटन को सही दिशा में इंगित करने के लिए rtl:rotate-180 क्लास का उपयोग करके घुमाना भी चाह सकते हैं।
एक्सेसिबिलिटी#
कैरॉसेल WAI-ARIA Carousel पैटर्न का पालन करता है: रूट role="region" और aria-roledescription="carousel" रेंडर करता है, हर स्लाइड role="group" और aria-roledescription="slide" होती है, और लूप न होने पर सिरों पर previous/next बटन अक्षम हो जाते हैं। Embla पॉइंटर और टच संभालता है; रैपर कीबोर्ड लेयर जोड़ता है।
कीबोर्ड इंटरैक्शन:
| कुंजी | क्रिया |
|---|---|
Tab / Shift + Tab | previous / next नेविगेशन बटन पर फ़ोकस ले जाएँ |
Space / Enter | फ़ोकस किए गए नेविगेशन बटन को सक्रिय करें |
ArrowLeft / ArrowRight | कैरॉसेल के अंदर फ़ोकस होने पर पिछली / अगली स्लाइड पर स्क्रॉल करें |
API रेफ़रेंस#
props और plugins के बारे में अधिक जानकारी के लिए Embla Carousel docs देखें।