Skip to content

Commit

Permalink
add slide selector component CarouselDots
Browse files Browse the repository at this point in the history
  • Loading branch information
Lordfirespeed committed Sep 12, 2024
1 parent 4a8dbda commit a5bae45
Showing 1 changed file with 75 additions and 2 deletions.
77 changes: 75 additions & 2 deletions src/components/ui/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ type CarouselContextProps = {
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
scrollTo: (scrollSnap: number) => void
canScrollPrev: boolean
canScrollNext: boolean
selectedIndex: number
scrollSnaps: number[]
} & CarouselProps

const CarouselContext = React.createContext<CarouselContextProps | null>(null)
Expand Down Expand Up @@ -67,6 +70,8 @@ const Carousel = React.forwardRef<
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const [selectedIndex, setSelectedIndex] = React.useState(0)
const [scrollSnaps, setScrollSnaps] = React.useState<number[]>([])

const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
Expand All @@ -75,6 +80,8 @@ const Carousel = React.forwardRef<

setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
setSelectedIndex(api.selectedScrollSnap())
setScrollSnaps(api.scrollSnapList())
}, [])

const scrollPrev = React.useCallback(() => {
Expand All @@ -85,6 +92,10 @@ const Carousel = React.forwardRef<
api?.scrollNext()
}, [api])

const scrollTo = React.useCallback((scrollSnap: number) => {
api?.scrollTo(scrollSnap)
}, [api])

const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
Expand Down Expand Up @@ -130,8 +141,11 @@ const Carousel = React.forwardRef<
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
scrollTo,
canScrollPrev,
canScrollNext,
selectedIndex,
scrollSnaps,
}}
>
<div
Expand Down Expand Up @@ -206,7 +220,7 @@ const CarouselPrevious = React.forwardRef<
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
Expand Down Expand Up @@ -252,11 +266,70 @@ const CarouselNext = React.forwardRef<
})
CarouselNext.displayName = "CarouselNext"

const CarouselDot = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button> & { carouselDotIndex: number }
>(({ className, variant = "outline", size = "icon", carouselDotIndex, ...props}, ref) => {
const { scrollTo, selectedIndex: selectedSlideIndex } = useCarousel();

return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"h-4 w-4 rounded-full",
selectedSlideIndex == carouselDotIndex
? ""
: "bg-transparent",
className
)}
onClick={() => scrollTo(carouselDotIndex)}
>
<span className="sr-only">Slide {carouselDotIndex}</span>
</Button>
)
})
CarouselDot.displayName = "CarouselDot"

const CarouselDots = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { orientation, scrollSnaps } = useCarousel();

function makeDot(_: number, index: number) {
return (
<CarouselDot
key={index}
carouselDotIndex={index}
/>
)
}

return (
<nav
ref={ref}
className={cn(
"absolute flex h-8 w-1/2 items-center justify-center gap-2",
orientation === "horizontal"
? "bottom-4 left-1/2 -translate-x-1/2"
: "left-4 top-1/2 -translate-y-1/2 rotate-90",
className
)}
>
{scrollSnaps.map(makeDot)}
</nav>
)
})
CarouselDots.displayName = "CarouselDots"

export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
}
CarouselDots,
}

0 comments on commit a5bae45

Please sign in to comment.