You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

72 lines
2.3 KiB

"use client";
import React, { useEffect, useState } from "react";
import type { Banner } from "../types";
export interface BannerCarouselProps {
items: Banner[];
intervalMs?: number;
basePath?: string;
aspectClass?: string; // e.g. aspect-[16/6], aspect-[16/9]
}
export function BannerCarousel({ items, intervalMs = 5000, basePath = "", aspectClass = "aspect-[16/6]" }: BannerCarouselProps) {
const [index, setIndex] = useState(0);
const total = items.length;
const go = (i: number) => {
if (total === 0) return;
const n = (i + total) % total;
setIndex(n);
};
useEffect(() => {
if (total <= 1) return;
const t = setInterval(() => setIndex((i) => (i + 1) % total), intervalMs);
return () => clearInterval(t);
}, [total, intervalMs]);
if (items.length === 0) return null;
const current = items[index];
return (
<div className={`relative w-full overflow-hidden rounded-lg group ${aspectClass}`}>
<a href={current.href ? `${basePath}${current.href}` : "#"} className="block w-full h-full">
<img
src={current.image}
alt={current.title}
className="absolute inset-0 w-full h-full object-cover"
/>
</a>
{total > 1 && (
<>
<button
type="button"
aria-label="Previous"
onClick={() => go(index - 1)}
className="absolute left-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 text-white opacity-0 group-hover:opacity-100 transition flex items-center justify-center"
>
</button>
<button
type="button"
aria-label="Next"
onClick={() => go(index + 1)}
className="absolute right-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 text-white opacity-0 group-hover:opacity-100 transition flex items-center justify-center"
>
</button>
</>
)}
<div className="absolute inset-x-0 bottom-3 flex justify-center gap-2">
{items.map((_, i) => (
<button
key={i}
aria-label={`Go to slide ${i + 1}`}
onClick={() => go(i)}
className={`h-2 w-2 rounded-full ${i === index ? "bg-white" : "bg-white/50"}`}
/>
))}
</div>
</div>
);
}