@@ -18,75 +18,35 @@ import {
1818 useSidebar ,
1919} from '@/components/ui/sidebar'
2020import { Badge } from '../ui/badge'
21- import { NavItem , type NavGroup } from './types'
21+ import {
22+ DropdownMenu ,
23+ DropdownMenuContent ,
24+ DropdownMenuItem ,
25+ DropdownMenuLabel ,
26+ DropdownMenuSeparator ,
27+ DropdownMenuTrigger ,
28+ } from '../ui/dropdown-menu'
29+ import { NavCollapsible , NavItem , NavLink , type NavGroup } from './types'
2230
2331export function NavGroup ( { title, items } : NavGroup ) {
24- const { setOpenMobile } = useSidebar ( )
32+ const { state } = useSidebar ( )
2533 const href = useLocation ( { select : ( location ) => location . href } )
2634 return (
2735 < SidebarGroup >
2836 < SidebarGroupLabel > { title } </ SidebarGroupLabel >
2937 < SidebarMenu >
3038 { items . map ( ( item ) => {
31- if ( ! item . items ) {
39+ const key = `${ item . title } -${ item . url } `
40+
41+ if ( ! item . items )
42+ return < SidebarMenuLink key = { key } item = { item } href = { href } />
43+
44+ if ( state === 'collapsed' )
3245 return (
33- < SidebarMenuItem key = { item . title } >
34- < SidebarMenuButton
35- asChild
36- isActive = { checkIsActive ( href , item ) }
37- tooltip = { item . title }
38- >
39- < Link to = { item . url } onClick = { ( ) => setOpenMobile ( false ) } >
40- { item . icon && < item . icon /> }
41- < span > { item . title } </ span >
42- { item . badge && < NavBadge > { item . badge } </ NavBadge > }
43- </ Link >
44- </ SidebarMenuButton >
45- </ SidebarMenuItem >
46+ < SidebarMenuCollapsedDropdown key = { key } item = { item } href = { href } />
4647 )
47- }
48- return (
49- < Collapsible
50- key = { item . title }
51- asChild
52- defaultOpen = { checkIsActive ( href , item , true ) }
53- className = 'group/collapsible'
54- >
55- < SidebarMenuItem >
56- < CollapsibleTrigger asChild >
57- < SidebarMenuButton tooltip = { item . title } >
58- { item . icon && < item . icon /> }
59- < span > { item . title } </ span >
60- { item . badge && < NavBadge > { item . badge } </ NavBadge > }
61- < ChevronRight className = 'ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90' />
62- </ SidebarMenuButton >
63- </ CollapsibleTrigger >
64- < CollapsibleContent className = 'CollapsibleContent' >
65- < SidebarMenuSub >
66- { item . items . map ( ( subItem ) => (
67- < SidebarMenuSubItem key = { subItem . title } >
68- < SidebarMenuSubButton
69- asChild
70- isActive = { checkIsActive ( href , subItem ) }
71- >
72- < Link
73- to = { subItem . url }
74- onClick = { ( ) => setOpenMobile ( false ) }
75- >
76- { subItem . icon && < subItem . icon /> }
77- < span > { subItem . title } </ span >
78- { subItem . badge && (
79- < NavBadge > { subItem . badge } </ NavBadge >
80- ) }
81- </ Link >
82- </ SidebarMenuSubButton >
83- </ SidebarMenuSubItem >
84- ) ) }
85- </ SidebarMenuSub >
86- </ CollapsibleContent >
87- </ SidebarMenuItem >
88- </ Collapsible >
89- )
48+
49+ return < SidebarMenuCollapsible key = { key } item = { item } href = { href } />
9050 } ) }
9151 </ SidebarMenu >
9252 </ SidebarGroup >
@@ -97,6 +57,117 @@ const NavBadge = ({ children }: { children: ReactNode }) => (
9757 < Badge className = 'text-xs rounded-full px-1 py-0' > { children } </ Badge >
9858)
9959
60+ const SidebarMenuLink = ( { item, href } : { item : NavLink ; href : string } ) => {
61+ const { setOpenMobile } = useSidebar ( )
62+ return (
63+ < SidebarMenuItem >
64+ < SidebarMenuButton
65+ asChild
66+ isActive = { checkIsActive ( href , item ) }
67+ tooltip = { item . title }
68+ >
69+ < Link to = { item . url } onClick = { ( ) => setOpenMobile ( false ) } >
70+ { item . icon && < item . icon /> }
71+ < span > { item . title } </ span >
72+ { item . badge && < NavBadge > { item . badge } </ NavBadge > }
73+ </ Link >
74+ </ SidebarMenuButton >
75+ </ SidebarMenuItem >
76+ )
77+ }
78+
79+ const SidebarMenuCollapsible = ( {
80+ item,
81+ href,
82+ } : {
83+ item : NavCollapsible
84+ href : string
85+ } ) => {
86+ const { setOpenMobile } = useSidebar ( )
87+ return (
88+ < Collapsible
89+ asChild
90+ defaultOpen = { checkIsActive ( href , item , true ) }
91+ className = 'group/collapsible'
92+ >
93+ < SidebarMenuItem >
94+ < CollapsibleTrigger asChild >
95+ < SidebarMenuButton tooltip = { item . title } >
96+ { item . icon && < item . icon /> }
97+ < span > { item . title } </ span >
98+ { item . badge && < NavBadge > { item . badge } </ NavBadge > }
99+ < ChevronRight className = 'ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90' />
100+ </ SidebarMenuButton >
101+ </ CollapsibleTrigger >
102+ < CollapsibleContent className = 'CollapsibleContent' >
103+ < SidebarMenuSub >
104+ { item . items . map ( ( subItem ) => (
105+ < SidebarMenuSubItem key = { subItem . title } >
106+ < SidebarMenuSubButton
107+ asChild
108+ isActive = { checkIsActive ( href , subItem ) }
109+ >
110+ < Link to = { subItem . url } onClick = { ( ) => setOpenMobile ( false ) } >
111+ { subItem . icon && < subItem . icon /> }
112+ < span > { subItem . title } </ span >
113+ { subItem . badge && < NavBadge > { subItem . badge } </ NavBadge > }
114+ </ Link >
115+ </ SidebarMenuSubButton >
116+ </ SidebarMenuSubItem >
117+ ) ) }
118+ </ SidebarMenuSub >
119+ </ CollapsibleContent >
120+ </ SidebarMenuItem >
121+ </ Collapsible >
122+ )
123+ }
124+
125+ const SidebarMenuCollapsedDropdown = ( {
126+ item,
127+ href,
128+ } : {
129+ item : NavCollapsible
130+ href : string
131+ } ) => {
132+ return (
133+ < SidebarMenuItem >
134+ < DropdownMenu >
135+ < DropdownMenuTrigger asChild >
136+ < SidebarMenuButton
137+ tooltip = { item . title }
138+ isActive = { checkIsActive ( href , item ) }
139+ >
140+ { item . icon && < item . icon /> }
141+ < span > { item . title } </ span >
142+ { item . badge && < NavBadge > { item . badge } </ NavBadge > }
143+ < ChevronRight className = 'ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90' />
144+ </ SidebarMenuButton >
145+ </ DropdownMenuTrigger >
146+ < DropdownMenuContent side = 'right' align = 'start' sideOffset = { 4 } >
147+ < DropdownMenuLabel >
148+ { item . title } { item . badge ? `(${ item . badge } )` : '' }
149+ </ DropdownMenuLabel >
150+ < DropdownMenuSeparator />
151+ { item . items . map ( ( sub ) => (
152+ < DropdownMenuItem key = { `${ sub . title } -${ sub . url } ` } asChild >
153+ < Link
154+ to = { sub . url }
155+ className = { `${ checkIsActive ( href , sub ) ? 'bg-secondary' : '' } ` }
156+ >
157+ { sub . icon && < sub . icon /> }
158+ < span className = 'max-w-52 text-wrap' > { sub . title } </ span >
159+ { sub . badge && (
160+ < span className = 'ml-auto text-xs' > { sub . badge } </ span >
161+ ) }
162+ </ Link >
163+ </ DropdownMenuItem >
164+ ) ) }
165+ </ DropdownMenuContent >
166+ </ DropdownMenu >
167+ </ SidebarMenuItem >
168+ )
169+ }
170+
100171function checkIsActive ( href : string , item : NavItem , mainNav = false ) {
101172 return (
102173 href === item . url || // /endpint?search=param
0 commit comments