import { Link, useLocation, useNavigate } from 'react-router'; import { ConfiguredIcon } from '@/components/ConfiguredIcon'; import { ChevronRight } from '@/components/icons'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { isExternalHref } from '@/lib/paths'; import { getStandalonePage } from '@/lib/site-config'; import type { DocsTab, LinkTarget, NavNode, NormalizedDocsConfig } from '@/lib/types'; import { cn } from '@/lib/utils'; interface MenuLink { label: string; href: string; target: LinkTarget; routed: boolean; } function collectMenuLinks(nodes: NavNode[]): MenuLink[] { const links: MenuLink[] = []; for (const node of nodes) { if (node.kind === 'page') { if (!node.page.hidden) { links.push({ label: node.page.label, href: node.page.url, target: '_self', routed: true }); } } else if (node.kind === 'link') { if (!node.hidden) { links.push({ label: node.label, href: node.href, target: node.target, routed: false }); } } else if (!node.hidden) { if (node.root && !node.root.page.hidden) { links.push({ label: node.root.page.label, href: node.root.page.url, target: '_self', routed: true, }); } links.push(...collectMenuLinks(node.children)); } } return links; } export function TopNav({ docs, label }: { docs: NormalizedDocsConfig; label: string }) { const { pathname } = useLocation(); const navigate = useNavigate(); const tabs = docs.tabs.filter(tab => !tab.hidden); if (!tabs.length) { return null; } const page = docs.pages.find(item => item.url === pathname); const matchedTabId = [...tabs] .sort((a, b) => b.url.length - a.url.length) .find(tab => pathname === tab.url || pathname.startsWith(`${tab.url}/`))?.id; // Standalone pages live outside the docs tree, so no tab owns them. Only // unmatched *docs* routes fall back to highlighting the first tab. const fallbackTabId = getStandalonePage(pathname) ? undefined : tabs[0]?.id; const selected = page?.tabId || matchedTabId || fallbackTabId; const tabClass = (tab: DocsTab) => cn( 'flex h-full items-center gap-1 whitespace-nowrap border-transparent border-b-2 font-medium', { 'border-b-primary text-foreground': tab.id === selected, 'text-muted-foreground hover:text-foreground': tab.id !== selected, }, ); return ( ); }