درخت ناوبری (NavTree)
لایهی ناوبری تودرتو با گروههای قابلجمعشدن، کاسکید حالت فعال، و indent خودکار — لایهی v2 روی Sidebar primitive
معرفی
NavTree لایهی composition ناوبری است که روی primitive های Sidebar مینشیند. سه ویژگی کلیدی:
- تودرتویی چندسطحی —
NavItemکهNavItemهای دیگر را بهعنوان children دارد، خودکار به parent قابلجمعشدن تبدیل میشود؛ depth بدون محدودیت. - کاسکید حالت فعال — وقتی
activePathبا یک leaf تودرتو match میکند، همهی parentها بهطور خودکارexpandمیشوند وdata-active-withinمیگیرند (highlight ملایمتر از leaf فعال). - گروههای قابلجمعشدن —
NavGroupباheadingقابلکلیک؛ کل گروه را باز/بسته میکند (بر خلاف سایدبار primitive که گروههایش static بودند).
چه زمانی استفاده کنیم:
- داشبوردهای enterprise با ناوبری ۳+ سطح (رصد → منابع → تلویزیون)
- اپهایی که کاربر باید یک نقطهی عمیق در درخت ناوبری را bookmark کند و هنگام بازگشت بهطور خودکار ببیند
- سایدبارهای طولانی که نیاز به group collapse دارند
چه زمانی استفاده نکنیم:
- ناوبری تخت با کمتر از ۸ آیتم — از
SidebarMenuprimitive استفاده کنید، سبکتر است - ناوبری افقی top-nav — از
NavigationMenuاستفاده کنید - ناوبری bottom-tab موبایل — از الگوی اختصاصی استفاده کنید (NavTree عمودی است)
زمین بازی
با تغییر تنظیمات زیر، پیشنمایش زنده را مشاهده کنید.
زمین بازی
تنظیمات
حالت
ظاهر
محتوا
کد این نمونه بهصورت خودکار قابل تولید نیست — برای کد آمادهی copy/paste به بخش «استفاده» در بالای صفحه مراجعه کنید.
استفاده
'use client'
import { usePathname } from 'next/navigation'
import {
Sidebar,
SidebarContent,
SidebarProvider,
NavTree,
NavTreeProvider,
NavGroup,
NavItem,
} from '@parto-system-design/ui'
import { LayoutDashboard, BarChart3, Search, Settings } from 'lucide-react'
export function AppSidebar() {
const pathname = usePathname()
return (
<SidebarProvider>
<Sidebar>
<SidebarContent className="p-2">
<NavTreeProvider activePath={pathname}>
<NavTree>
<NavGroup heading="رصد و پایش">
<NavItem href="/" icon={LayoutDashboard}>
داشبورد
</NavItem>
<NavItem href="/clusters" icon={BarChart3}>
خوشهها
</NavItem>
<NavItem icon={Search}>
جستجو
<NavItem href="/search/posts">پستها</NavItem>
<NavItem href="/search/pages">صفحات</NavItem>
</NavItem>
</NavGroup>
<NavGroup heading="تنظیمات" defaultOpen={false}>
<NavItem href="/settings" icon={Settings}>
پیکربندی
</NavItem>
</NavGroup>
</NavTree>
</NavTreeProvider>
</SidebarContent>
</Sidebar>
</SidebarProvider>
)
}کاسکید حالت فعال (خودکار)
وقتی activePath مطابق یک leaf تودرتو باشد، همهی parent های بالای آن بهطور خودکار data-active-within میگیرند و expand میشوند. دکمههای بالا را در دموی زیر تغییر دهید تا رفتار را ببینید:
activePath فعلی را تغییر دهید:
توجه کنید وقتی `/analysis/comments/labels` انتخاب است، همهی والدین بهطور خودکار باز و data-active-within میشوند.
جزئیات الگوریتم:
matchStrategy="prefix"(پیشفرض):/clustersهم برای/clustersو هم برای/clusters/42فعال میشود. فقط مرز segment رعایت میشود (یعنی/clبا/clustersmatch نمیشود).matchStrategy="exact": فقط تطابق دقیق.
گروههای قابلجمعشدن
headingنمایش یک ردیف سرگروه و بهطور پیشفرض قابلکلیک (collapse/expand)collapsible={false}heading را static میکند (مثل primitive SidebarGroupLabel)defaultOpen={false}برای بسته بودن در رندر اولopen+onOpenChangeبرای کنترل controlled
Props
NavTreeProvider
NavGroup
NavItem
ادغام با Sidebar primitive
NavTree داخل SidebarContent (از primitive Sidebar) قرار میگیرد. SidebarProvider رفتار mobile/keyboard-shortcut را مدیریت میکند و NavTree ساختار ناوبری را.
<SidebarProvider>
<Sidebar variant="inset">
<SidebarHeader>...</SidebarHeader>
<SidebarContent className="p-2">
<NavTreeProvider activePath={pathname}>
<NavTree>...</NavTree>
</NavTreeProvider>
</SidebarContent>
<SidebarFooter>...</SidebarFooter>
</Sidebar>
</SidebarProvider>راهنمای استفاده
بکنید
- برای هر سطح nesting، یک آیکون بصری متمایز در NavItem بگذارید تا سلسلهمراتب بصری هم واضح باشد -
badgeرا برای شمارندهی notification یا new indicator استفاده کنید — از componentBadgeیا یک span سفارشی - در بخش تنظیمات (که معمولاً به ندرت باز میشود) ازNavGroup defaultOpen={false}استفاده کنید -matchStrategy="prefix"را حفظ کنید مگر اینکه routeهای overlap داشته باشید (مثلاً/clusterو/clusters)
نکنید
- بیش از ۴ سطح nesting نکنید — کاربر جنگل میبیند
- badge را برای متن طولانی (بیش از ۳ کاراکتر) استفاده نکنید — sidebar باریک میشود
- از
<NavItem>های داخل یک<div>ساده استفاده نکنید — split-children logic بر اساس React.Children مستقیم کار میکند - حالت
activeرا روی چند آیتم همزمان تنظیم نکنید — ممکن است highlight چندگانه گیجکننده شود
دسترسیپذیری
<nav aria-label="ناوبری اصلی">landmark برای screen reader- parent NavItem دارای
aria-expanded+ button semantics - کلیک، Space، و Enter همگی parent را toggle میکنند
- active leaf دارای
data-active="true"— بصری و برای automated testing - chevron بصری در RTL به شکل صحیح میچرخد (left → down در باز، rotate-90 در بسته)
محدودیتهای فعلی
- جستجو در درخت هنوز ارائه نشده. برنامهی بعدی:
NavSearchورودی که لیست را filter میکند و parent های مرتبط را باز نگه میدارد. (فاز ۱.۱ روادمپ — مرحلهی بعد) - Drag-reorder آیتمها در نظر نیست — این یک UI ناوبری است نه یک tree builder.
کامپوننتهای مرتبط
- Sidebar — container primitive که NavTree درون
SidebarContentقرار میگیرد - NavigationMenu — برای ناوبری افقی top-nav
- Breadcrumb — برای نمایش مسیر فعلی درون SiteHeader
- SiteHeader — هدر اپ که با NavTree جفت میشود