منوی نقطهایBounce Sidebar
یه ستونِ ناوبری که نشانگرِ انتخابه یه نقطهی کوچیکه؛ موقعِ عوضشدنِ ردیف نقطه روی یه کمانِ کمعمق به بیرونِ متن میپره، نه با یه لغزشِ ساده.
وابستگیها
نوع تعامل
روی هر ردیف بزن تا انتخاب شه؛ نقطه از مرکزِ ردیفِ فعلی روی یه کمانِ کوچیکِ روبهبیرون به مرکزِ ردیفِ تازه میپره. هرچی فاصله بیشتر باشه کمان کمعمقتره تا پرشِ بلند قوسِ غول نسازه. ردیفها میتونن سرفصلِ گروه داشته باشن (`{ label, heading: true }`): سرفصل انتخابشدنی نیست ولی توی شمارشِ ایندکسهاست. اگه `href` بدی ردیف پیوند میشه، وگرنه دکمه؛ در هر دو حالت `onChange` صدا زده میشه. با `prefers-reduced-motion` نقطه بیکمان و بیدرنگ جابهجا میشه.
پراپها
گزینههایی که میتونی به کامپوننت بدی؛ ستارهدارها اجباریاند.
items*BounceSidebarItem[]پیشفرض: [{ label: "عمومی", heading: true }, { label: "داشبورد", href: "/" }, "امنیت"]ردیفها. هر آیتم یا رشتهست، یا `{ label, href }`، یا `{ label, heading: true }` برای سرفصلِ گروه. سرفصلها در ایندکس شمرده میشن ولی انتخابشدنی نیستن.
valuenumberایندکسِ انتخابشده در حالتِ کنترلشده. برای همگامکردن با مسیر همینه.
defaultValuenumberپیشفرض: 0ایندکسِ اولیه در حالتِ نامکنترلشده.
onChange(index: number) => voidبا هر انتخاب، ایندکسِ تازه رو برمیگردونه.
dotColorstringپیشفرض: var(--brand, currentColor)رنگِ نقطه. هر مقدارِ CSS قبوله؛ اگه پروژهات `--brand` نداره، رنگِ متن میشه.
classNamestringکلاسِ بیرونی روی `ul`؛ عرضِ ستون رو از همینجا بده.
نصب
۱. این آدرس رو یکبار توی components.json بذار تا shadcn مخزن فارسیفای رو بشناسه.
{
"registries": {
"@farsify": "https://farsify.ir/r/{name}.json"
}
}۲. بعد با دستورِ زیر کامپوننت رو نصب کن.
npx shadcn@latest add @farsify/bounce-sidebarپیشنیاز: Tailwind CSS v4 و صفحهٔ راستبهچپ dir="rtl".
نمونه
"use client";
import { useState } from "react";
import { BounceSidebar } from "@/components/farsify/bounce-sidebar";
const items = [
{ label: "عمومی", heading: true },
{ label: "داشبورد", href: "/dashboard" },
{ label: "گزارشها", href: "/reports" },
{ label: "حساب", heading: true },
{ label: "پروفایل", href: "/profile" },
{ label: "امنیت", href: "/security" },
];
export function SettingsMenu() {
const [active, setActive] = useState(1);
return (
<BounceSidebar
aria-label="حساب کاربری"
className="w-52"
items={items}
value={active}
onChange={setActive}
/>
);
}
// فهرستِ تخت، بدونِ سرفصل و با رنگِ دلخواه
<BounceSidebar
className="w-44"
items={["خانه", "محصولات", "درباره"]}
dotColor="#007bff"
/>
// همگام با مسیر — خودِ کامپوننت به Next وابسته نیست، پس مسیر را شما میدهید
const pathname = usePathname();
const index = items.findIndex(
(item) => typeof item !== "string" && "href" in item && item.href === pathname,
);
<BounceSidebar items={items} value={index} />نکتهها
- رنگِ برند فقط روی نقطه مینشیند؛ سرفصلها سفید و هماندازهی ردیفها هستن تا سلسلهمراتب از وزن ساخته شه، نه از رنگِ تأکید.
- `next/link` عمداً import نشده — نسخهی تحویلی باید در پروژهی بدونِ Next هم کامپایل شه. اگه ناوبریِ نرم میخوای، `value` رو از مسیر بده.
- جهتِ کمان از `dir`ِ خودِ المان خونده میشه: در راستبهچپ نقطه به بیرونِ راست میپره و در چپبهراست به بیرونِ چپ — چیزی رو دستی آینه نکن.
- موقعیت با اندازهگیریِ DOM حساب میشه و روی مرزِ پیکسلِ دستگاه گرد میشه، پس روی نمایشگرِ ۱٫۲۵ برابر نقطه مات و لرزان نمیشه.
- برای فهرستهای بلند ارتفاعِ ثابت با `overflow-y-auto` بهتره تا ستونِ بیپایان؛ نقطه داخلِ همون قاب اندازهگیری میشه.
مجوز
کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژهی خودت نگهش دار. برای پروژههای شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسیفای رو یه جا توی پروژه نگه دار.