@@ -4,34 +4,119 @@ import {
44 SidebarMenu ,
55 SidebarMenuButton ,
66 SidebarMenuItem ,
7+ SidebarMenuSub ,
8+ SidebarMenuSubButton ,
9+ SidebarMenuSubItem ,
10+ useSidebar ,
711} from '@/components/ui/sidebar' ;
812import { resolveUrl } from '@/lib/utils' ;
913import { type NavItem } from '@/types' ;
1014import { Link , usePage } from '@inertiajs/react' ;
15+ import { ChevronRight } from 'lucide-react' ;
16+ import { Collapsible , CollapsibleContent , CollapsibleTrigger } from '@/components/ui/collapsible' ;
17+ import { useState } from 'react' ;
18+ import { DropdownMenu , DropdownMenuContent , DropdownMenuTrigger } from '@/components/ui/dropdown-menu' ;
1119
1220export function NavMain ( { items = [ ] } : { items : NavItem [ ] } ) {
1321 const page = usePage ( ) ;
22+ const [ activeMenu , setActiveMenu ] = useState < string | null > ( page . url ) ;
23+ const { state } = useSidebar ( ) ;
1424 return (
1525 < SidebarGroup className = "px-2 py-0" >
1626 < SidebarGroupLabel > Platform</ SidebarGroupLabel >
1727 < SidebarMenu >
18- { items . map ( ( item ) => (
19- < SidebarMenuItem key = { item . title } >
20- < SidebarMenuButton
21- asChild
22- isActive = { page . url . startsWith (
23- resolveUrl ( item . href ) ,
28+ { items . map ( ( item , index ) => {
29+ const hasChildren = item . items && item . items . length > 0 ;
30+ const menuKey = item . href && item . href !== "#" ? String ( item . href ) : `#menu-${ index } ` ;
31+ const childMatch = hasChildren && item . items ! . some ( ( c ) => page . url . startsWith ( resolveUrl ( c . href ) ) ) ;
32+ const isOpen = hasChildren
33+ ? activeMenu === menuKey || childMatch // active parent menu if child menu in active state
34+ : activeMenu === menuKey || page . url . startsWith ( resolveUrl ( item . href ) ) ;
35+ const isDropdownOpen = activeMenu === menuKey ;
36+ const MenuLabel = (
37+ < >
38+ { item . icon && < item . icon /> }
39+ < span > { item . title } </ span >
40+ { hasChildren && state === "expanded" && (
41+ < ChevronRight className = "ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
2442 ) }
25- tooltip = { { children : item . title } }
26- >
27- < Link href = { item . href } prefetch >
28- { item . icon && < item . icon /> }
29- < span > { item . title } </ span >
30- </ Link >
31- </ SidebarMenuButton >
32- </ SidebarMenuItem >
33- ) ) }
43+ </ >
44+ ) ;
45+ return (
46+ < SidebarMenuItem key = { item . title } >
47+ { state === "expanded" && (
48+ < Collapsible open = { isOpen } onOpenChange = { ( ) => { setActiveMenu ( isOpen ? null : menuKey ) ; } } className = "group/collapsible" >
49+ < CollapsibleTrigger asChild disabled = { ! hasChildren } >
50+ < SidebarMenuButton asChild isActive = { isOpen } tooltip = { { children : item . title } } >
51+ < Link href = { item . href } onClick = { ( ) => setActiveMenu ( menuKey ) } preserveState >
52+ { item . icon && < item . icon /> }
53+ < span > { item . title } </ span >
54+ { hasChildren && (
55+ < ChevronRight className = "ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
56+ ) }
57+ </ Link >
58+ </ SidebarMenuButton >
59+ </ CollapsibleTrigger >
60+ { hasChildren && (
61+ < CollapsibleContent >
62+ < SidebarMenuSub >
63+ { item . items ! . map ( ( sub ) => {
64+ const subActive = page . url . startsWith ( resolveUrl ( sub . href ) ) ;
65+ return (
66+ < SidebarMenuSubItem key = { sub . title } >
67+ < SidebarMenuSubButton asChild isActive = { subActive } >
68+ < Link href = { sub . href } preserveState >
69+ { sub . icon && < sub . icon /> }
70+ < span > { sub . title } </ span >
71+ </ Link >
72+ </ SidebarMenuSubButton >
73+ </ SidebarMenuSubItem >
74+ ) ;
75+ } ) }
76+ </ SidebarMenuSub >
77+ </ CollapsibleContent >
78+ ) }
79+ </ Collapsible >
80+ ) }
81+ { state === "collapsed" && (
82+ < DropdownMenu
83+ open = { isDropdownOpen }
84+ onOpenChange = { ( o ) => setActiveMenu ( o ? menuKey : null ) }
85+ >
86+ < DropdownMenuTrigger asChild >
87+ < SidebarMenuButton
88+ asChild
89+ onMouseEnter = { ( ) => hasChildren && setActiveMenu ( menuKey ) }
90+ tooltip = { ! hasChildren ? { children : item . title } : undefined }
91+ >
92+ < Link href = { item . href !== "#" ? item . href : "#" } preserveState >
93+ { MenuLabel }
94+ </ Link >
95+ </ SidebarMenuButton >
96+ </ DropdownMenuTrigger >
97+ { hasChildren && (
98+ < DropdownMenuContent align = "end" side = "right" onMouseLeave = { ( ) => setActiveMenu ( null ) } >
99+ { item . items ! . map ( ( sub ) => {
100+ const subActive = page . url . startsWith ( resolveUrl ( sub . href ) ) ;
101+ return (
102+ < SidebarMenuSubItem key = { sub . title } >
103+ < SidebarMenuSubButton asChild isActive = { subActive } >
104+ < Link href = { sub . href } >
105+ { sub . icon && < sub . icon /> }
106+ < span > { sub . title } </ span >
107+ </ Link >
108+ </ SidebarMenuSubButton >
109+ </ SidebarMenuSubItem >
110+ ) ;
111+ } ) }
112+ </ DropdownMenuContent >
113+ ) }
114+ </ DropdownMenu >
115+ ) }
116+ </ SidebarMenuItem >
117+ ) ;
118+ } ) }
34119 </ SidebarMenu >
35120 </ SidebarGroup >
36121 ) ;
37- }
122+ }
0 commit comments