import React from "react"; import { Pin } from "lucide-react"; import { NavigationLink } from "#/components/shared/navigation-link"; import { StyledTooltip } from "#/components/shared/buttons/styled-tooltip"; import { useNavigation } from "#/context/navigation-context"; import { hoverRevealActionClassName } from "#/utils/hover-reveal-classes"; import { cn } from "#/utils/utils"; import { SidebarCollapsedIconSlot } from "./sidebar-collapsed-icon-slot"; import { SIDEBAR_ICON_SLOT_CLASS, SIDEBAR_ROW_INTERACTIVE_CLASS, sidebarNavLabelClassName, sidebarNavRowClassName, } from "./sidebar-layout"; function isPathActive(currentPath: string, to: string, end: boolean) { if (to === "/") { return currentPath === to; } if (end) { return currentPath === to; } return currentPath === to || currentPath.startsWith(`${to}/`); } export interface SidebarNavLinkPinAction { pinned: boolean; onToggle: () => void; /** Localized aria-label; pin vs unpin variants resolved by the caller. */ label: string; testId: string; } interface SidebarNavLinkProps { to: string; label: string; end?: boolean; indent?: boolean; testId?: string; disabled?: boolean; icon?: React.ReactElement; collapsed?: boolean; hoverContent?: React.ReactNode; /** * When true, forces the active style regardless of the current path. * Useful for links that should appear active for multiple related routes * (e.g. the Extensions link being active on /mcp and /plugins too). */ forceActive?: boolean; /** Pin-as-home toggle; rendered only when the sidebar is expanded. */ pinAction?: SidebarNavLinkPinAction; } export function SidebarNavLink({ to, label, end = false, indent = false, testId, disabled = false, icon, collapsed = false, hoverContent, forceActive = false, pinAction, }: SidebarNavLinkProps) { const { currentPath } = useNavigation(); const active = forceActive || isPathActive(currentPath, to, end); const showPinAction = !collapsed && pinAction != null; const link = ( { if (disabled) { e.preventDefault(); } }} className={cn( sidebarNavRowClassName({ indent, collapsed }), !collapsed && (active ? SIDEBAR_ROW_INTERACTIVE_CLASS.active : SIDEBAR_ROW_INTERACTIVE_CLASS.idle), disabled && "opacity-50", disabled && "pointer-events-none", // Constant right reserve so label truncation doesn't reflow on hover. showPinAction && "pr-9", )} > {icon ? ( collapsed ? ( {icon} ) : ( {icon} ) ) : null} {label} ); if (!collapsed) { if (!pinAction) return link; // NavigationLink renders an , so the pin toggle is an absolutely // positioned sibling rather than a nested button. Bare `group` is safe // here: only collapsed rows use it, and this branch is expanded-only. return (
{link}
); } return ( {link} ); }