feat : buzz feedback
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function ScrollableNav({
|
||||
ariaLabel,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
ariaLabel: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
const navRef = useRef<HTMLElement>(null);
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(false);
|
||||
|
||||
const updateScrollState = useCallback(() => {
|
||||
const nav = navRef.current;
|
||||
if (!nav) return;
|
||||
setCanScrollLeft(nav.scrollLeft > 0);
|
||||
setCanScrollRight(nav.scrollLeft + nav.clientWidth < nav.scrollWidth - 1);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const nav = navRef.current;
|
||||
if (!nav) return;
|
||||
updateScrollState();
|
||||
nav.addEventListener("scroll", updateScrollState, { passive: true });
|
||||
window.addEventListener("resize", updateScrollState);
|
||||
return () => {
|
||||
nav.removeEventListener("scroll", updateScrollState);
|
||||
window.removeEventListener("resize", updateScrollState);
|
||||
};
|
||||
}, [updateScrollState]);
|
||||
|
||||
const scroll = (direction: -1 | 1) => {
|
||||
navRef.current?.scrollBy({ left: direction * navRef.current.clientWidth * 0.75, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center gap-1", className)}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0 sm:hidden"
|
||||
disabled={!canScrollLeft}
|
||||
onClick={() => scroll(-1)}
|
||||
aria-label="เลื่อนเมนูไปทางซ้าย"
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
</Button>
|
||||
<nav
|
||||
ref={navRef}
|
||||
aria-label={ariaLabel}
|
||||
className="flex min-w-0 flex-1 gap-1 overflow-x-auto [scrollbar-width:none]"
|
||||
>
|
||||
{children}
|
||||
</nav>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0 sm:hidden"
|
||||
disabled={!canScrollRight}
|
||||
onClick={() => scroll(1)}
|
||||
aria-label="เลื่อนเมนูไปทางขวา"
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user