54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useRef, type ReactNode } from "react";
|
|
import { IconArrow } from "./Icons";
|
|
|
|
type Props = {
|
|
children: ReactNode;
|
|
className?: string;
|
|
ariaLabel?: string;
|
|
flush?: boolean;
|
|
tight?: boolean;
|
|
};
|
|
|
|
export function Carousel({ children, className = "", ariaLabel, flush = false, tight = false }: Props) {
|
|
const scroller = useRef<HTMLDivElement>(null);
|
|
|
|
const move = (direction: number) => {
|
|
const el = scroller.current;
|
|
if (!el) return;
|
|
const delta = Math.max(el.clientWidth * 0.55, 240) * direction;
|
|
el.scrollBy({ left: delta, behavior: "smooth" });
|
|
};
|
|
|
|
return (
|
|
<div className={`relative ${className}`}>
|
|
<div
|
|
ref={scroller}
|
|
aria-label={ariaLabel}
|
|
className={`flex overflow-x-auto scroll-smooth snap-x snap-mandatory [scrollbar-width:none] [&::-webkit-scrollbar]:hidden ${
|
|
flush ? "divide-x divide-zinc-200" : tight ? "gap-2" : "gap-4 pb-2"
|
|
}`}
|
|
>
|
|
{children}
|
|
</div>
|
|
<button
|
|
type="button"
|
|
aria-label="قبلی"
|
|
onClick={() => move(1)}
|
|
className="absolute top-1/2 right-2 z-10 -translate-y-1/2 hidden md:grid place-items-center size-8 rounded-md bg-white text-ink shadow-sm border border-black/10 hover:bg-brand hover:text-white"
|
|
>
|
|
<IconArrow dir="right" className="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
aria-label="بعدی"
|
|
onClick={() => move(-1)}
|
|
className="absolute top-1/2 left-2 z-10 -translate-y-1/2 hidden md:grid place-items-center size-8 rounded-md bg-white text-ink shadow-sm border border-black/10 hover:bg-brand hover:text-white"
|
|
>
|
|
<IconArrow dir="left" className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|