@@ -18,75 +18,35 @@ import {
18
18
useSidebar ,
19
19
} from '@/components/ui/sidebar'
20
20
import { 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'
22
30
23
31
export function NavGroup ( { title, items } : NavGroup ) {
24
- const { setOpenMobile } = useSidebar ( )
32
+ const { state } = useSidebar ( )
25
33
const href = useLocation ( { select : ( location ) => location . href } )
26
34
return (
27
35
< SidebarGroup >
28
36
< SidebarGroupLabel > { title } </ SidebarGroupLabel >
29
37
< SidebarMenu >
30
38
{ 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' )
32
45
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 } />
46
47
)
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 } />
90
50
} ) }
91
51
</ SidebarMenu >
92
52
</ SidebarGroup >
@@ -97,6 +57,117 @@ const NavBadge = ({ children }: { children: ReactNode }) => (
97
57
< Badge className = 'text-xs rounded-full px-1 py-0' > { children } </ Badge >
98
58
)
99
59
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
+
100
171
function checkIsActive ( href : string , item : NavItem , mainNav = false ) {
101
172
return (
102
173
href === item . url || // /endpint?search=param
0 commit comments