Files
tgex-frontend/components/nav-main.tsx
2026-01-07 14:17:43 +03:00

179 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { ChevronRight, type LucideIcon } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
SidebarGroup,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
useSidebar,
} from "@/components/ui/sidebar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { usePathname } from "next/navigation";
import Link from "next/link";
import * as React from "react";
import { cn } from "@/lib/utils";
export type NavGuideState = {
highlightedItem?: string;
expandedItems?: string[];
};
export function NavMain({
items,
guideState,
}: {
items: {
title: string;
url: string;
icon?: LucideIcon;
isActive?: boolean;
items?: {
title: string;
url: string;
}[];
}[];
guideState?: NavGuideState;
}) {
const pathname = usePathname();
const { state } = useSidebar();
const isCollapsed = state === "collapsed";
const highlightedItem = guideState?.highlightedItem;
return (
<SidebarGroup>
<SidebarMenu>
{items.map((item) => {
const hasSubmenu = item.items && item.items.length > 0;
const isActive = pathname === item.url;
const hasActiveSubmenu =
hasSubmenu && item.items!.some((sub) => pathname === sub.url);
const shouldForceOpen = guideState?.expandedItems?.includes(item.title);
const isGuideHighlight = highlightedItem === item.title;
const highlightedButtonClass = isGuideHighlight
? "ring-2 ring-primary/70"
: undefined;
// В свернутом состоянии показываем DropdownMenu для элементов с подменю
if (isCollapsed && hasSubmenu) {
return (
<SidebarMenuItem key={item.title}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={isActive || hasActiveSubmenu}
className={cn(highlightedButtonClass)}
>
{item.icon && <item.icon />}
<span>{item.title}</span>
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
side="right"
align="start"
className="min-w-[200px]"
>
{item.items!.map((subItem) => {
const isSubActive = pathname === subItem.url;
return (
<DropdownMenuItem key={subItem.title} asChild>
<Link
href={subItem.url}
className={
isSubActive
? "bg-accent text-accent-foreground"
: ""
}
>
{subItem.title}
</Link>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
);
}
// Обычное отображение для развернутого состояния
return (
<Collapsible
key={`${item.title}-${shouldForceOpen ? "guide" : "default"}`}
asChild
defaultOpen={
shouldForceOpen || isActive || hasActiveSubmenu
}
className="group/collapsible"
>
<SidebarMenuItem>
{hasSubmenu ? (
<>
<CollapsibleTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={isActive || hasActiveSubmenu}
className={cn(highlightedButtonClass)}
>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items!.map((subItem) => {
const isSubActive = pathname === subItem.url;
return (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
isActive={isSubActive}
>
<Link href={subItem.url}>
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
);
})}
</SidebarMenuSub>
</CollapsibleContent>
</>
) : (
<SidebarMenuButton
asChild
tooltip={item.title}
isActive={isActive}
className={cn(highlightedButtonClass)}
>
<Link href={item.url}>
{item.icon && <item.icon />}
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
)}
</SidebarMenuItem>
</Collapsible>
);
})}
</SidebarMenu>
</SidebarGroup>
);
}