news/app/components/untils/CarouselPreset.tsx

96 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as React from "react"
import Autoplay from "embla-carousel-autoplay"
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
type CarouselApi,
} from "@/ui/carousel"
import { CardContent } from "@/ui/card"
import { cn } from "@/lib/utils"
type Slide = {
key?: React.Key
content: React.ReactNode
}
type CarouselPresetProps = {
slides: Slide[] //传入的轮播项
autoplayDelay?: number //自动轮播的间隔默认为3000
showControls?: boolean //是否显示左右切换按钮
showIndicators?: boolean //是否显示底部的分页指示器
indicatorVariant?: "dot" | "pill" //分页指示器的样式,默认为"dot"dot圆点pill小胶囊
className?: string // 作用在最外层容器,用来定制整体尺寸/布局
contentClassName?: string //作用在内部的 CarouselContent 容器,可自定义内层排列方式(例如圆角、阴影等)
itemClassName?: string //作用在每个 CarouselItem 上,调整单个 slide 的额外样式(比如内边距、宽高)
}
export function CarouselPreset({
slides,
autoplayDelay = 3000,
showControls = true,
showIndicators = true,
indicatorVariant = "dot",
className,
contentClassName,
itemClassName,
}: CarouselPresetProps) {
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())
api.on("select", () => setCurrent(api.selectedScrollSnap()))
}, [api])
return (
<div className={cn("relative w-full h-full", className)}>
<Carousel
opts={{ loop: true }}
plugins={[Autoplay({ delay: autoplayDelay, stopOnInteraction: false })]}
setApi={setApi}
className="w-full h-full"
>
<CarouselContent className={cn("h-full w-full -ml-0", contentClassName)}>
{slides.map((slide, index) => (
<CarouselItem
key={slide.key ?? index}
className={cn("w-full h-full pl-0", itemClassName)}
>
<CardContent className="p-0 w-full h-full m-0">{slide.content}</CardContent>
</CarouselItem>
))}
</CarouselContent>
{showControls && (
<>
<CarouselPrevious className="absolute left-2 top-1/2 -translate-y-1/2 z-10" />
<CarouselNext className="absolute right-2 top-1/2 -translate-y-1/2 z-10" />
</>
)}
</Carousel>
{showIndicators && (
<div className="absolute bottom-4 right-4 flex gap-2 z-10">
{Array.from({ length: count }).map((_, index) => (
<button
key={index}
onClick={() => api?.scrollTo(index)}
className={cn(
"transition-colors bg-white/50 hover:bg-white",
indicatorVariant === "dot" ? "h-2 w-2 rounded-full" : "h-2 w-8 rounded",
index === current && "bg-white"
)}
aria-label={`${index + 1}`}
/>
))}
</div>
)}
</div>
)
}