@@ -9,7 +9,6 @@ import { toast } from 'sonner';
99import useSWR from 'swr' ;
1010import { cn , fetcher } from '@/lib/utils' ;
1111import {
12- FileIcon ,
1312 MoreHorizontalIcon ,
1413 PlusIcon ,
1514 TrashIcon ,
@@ -24,12 +23,6 @@ import {
2423 AlertDialogHeader ,
2524 AlertDialogTitle ,
2625} from '@/components/ui/alert-dialog' ;
27- import {
28- DropdownMenu ,
29- DropdownMenuContent ,
30- DropdownMenuItem ,
31- DropdownMenuTrigger ,
32- } from '@/components/ui/dropdown-menu' ;
3326import {
3427 SidebarGroup ,
3528 SidebarGroupContent ,
@@ -45,7 +38,6 @@ import { Input } from '@/components/ui/input';
4538import { Button } from '@/components/ui/button' ;
4639import { Checkbox } from '@/components/ui/checkbox' ;
4740import useSWRInfinite from 'swr/infinite' ;
48- import { motion } from 'framer-motion' ;
4941
5042type GroupedDocuments = {
5143 today : Document [ ] ;
@@ -105,7 +97,17 @@ const PureDocumentItem = ({
10597
10698 const router = useRouter ( ) ;
10799 const buttonRef = useRef < HTMLButtonElement > ( null ) ;
100+ const menuRef = useRef < HTMLDivElement > ( null ) ;
101+ const closeTimerRef = useRef < number | null > ( null ) ;
108102 const [ showCustomMenu , setShowCustomMenu ] = useState ( false ) ;
103+
104+ useEffect ( ( ) => {
105+ return ( ) => {
106+ if ( closeTimerRef . current ) {
107+ clearTimeout ( closeTimerRef . current ) ;
108+ }
109+ } ;
110+ } , [ ] ) ;
109111
110112 return (
111113 < SidebarMenuItem className = "relative" >
@@ -138,33 +140,65 @@ const PureDocumentItem = ({
138140 ref = { buttonRef }
139141 className = "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground mr-0.5"
140142 showOnHover = { ! isActive }
141- onBlur = { ( ) => setTimeout ( ( ) => setShowCustomMenu ( false ) , 100 ) }
142- onClick = { ( ) => setShowCustomMenu ( ! showCustomMenu ) }
143+ data-state = { showCustomMenu ? 'open' : 'closed' }
144+ aria-haspopup = "menu"
145+ aria-expanded = { showCustomMenu }
146+ aria-controls = { `doc-menu-${ document . id } ` }
147+ onBlur = { ( e ) => {
148+ if ( closeTimerRef . current ) {
149+ clearTimeout ( closeTimerRef . current ) ;
150+ closeTimerRef . current = null ;
151+ }
152+ const next = e . relatedTarget as Node | null ;
153+ if ( menuRef . current && next && menuRef . current . contains ( next ) ) return ;
154+ closeTimerRef . current = window . setTimeout ( ( ) => setShowCustomMenu ( false ) , 100 ) ;
155+ } }
156+ onClick = { ( ) => setShowCustomMenu ( ( v ) => ! v ) }
157+ onKeyDown = { ( e ) => {
158+ if ( e . key === 'Escape' ) {
159+ e . stopPropagation ( ) ;
160+ setShowCustomMenu ( false ) ;
161+ }
162+ if ( e . key === 'Enter' || e . key === ' ' ) {
163+ e . preventDefault ( ) ;
164+ setShowCustomMenu ( ( v ) => ! v ) ;
165+ }
166+ } }
143167 >
144168 < MoreHorizontalIcon />
145169 < span className = "sr-only" > More</ span >
146170 </ SidebarMenuAction >
147171 ) }
148172
149- { /* Dropdown positioned just below the three dots */ }
150173 { ! isSelectionMode && showCustomMenu && (
151174 < div
152- className = "absolute right-0 top-full z-[9999] min-w-[8rem] overflow-visible rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md"
175+ ref = { menuRef }
176+ id = { `doc-menu-${ document . id } ` }
177+ role = "menu"
178+ aria-labelledby = { `doc-menu-${ document . id } ` }
179+ className = "absolute right-0 top-full z-50 min-w-[8rem] overflow-visible rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md"
153180 style = { {
154- marginTop : '2px' // Small gap below the button
181+ marginTop : '2px'
155182 } }
156183 onMouseDown = { ( e ) => e . preventDefault ( ) }
157- >
158- < div
159- className = "cursor-pointer text-destructive hover:bg-destructive/15 hover:text-destructive relative flex select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors"
160- onClick = { ( ) => {
184+ onKeyDown = { ( e ) => {
185+ if ( e . key === 'Escape' ) {
186+ e . stopPropagation ( ) ;
161187 setShowCustomMenu ( false ) ;
162- onDelete ( document . id ) ;
163- } }
188+ buttonRef . current ?. focus ( ) ;
189+ }
190+ } }
164191 >
192+ < Button
193+ variant = "destructive"
194+ size = "sm"
195+ role = "menuitem"
196+ className = "h-7 text-sm w-full"
197+ onClick = { ( ) => { setShowCustomMenu ( false ) ; onDelete ( document . id ) ; } }
198+ >
199+ Delete
165200 < TrashIcon />
166- < span > Delete</ span >
167- </ div >
201+ </ Button >
168202 </ div >
169203 ) }
170204 </ SidebarMenuItem >
0 commit comments