feat : buzz feedback
CI / Verify (push) Successful in 1m5s
CI / Build immutable images and deploy (push) Successful in 1m41s

This commit is contained in:
2026-08-30 15:11:35 +00:00 Unverified
parent b2e9b69619
commit 14a98c55f1
13 changed files with 418 additions and 96 deletions
+78
View File
@@ -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>
);
}