@@ -33,6 +33,8 @@ type DropdownMenuContextValue = {
33
33
onOpenChange ( open : boolean ) : void ;
34
34
onOpenToggle ( ) : void ;
35
35
modal : boolean ;
36
+ wasOpenedWithArrowUp : boolean ;
37
+ setWasOpenedWithArrowUp ( value : boolean ) : void ;
36
38
} ;
37
39
38
40
const [ DropdownMenuProvider , useDropdownMenuContext ] =
@@ -65,6 +67,14 @@ const DropdownMenu: React.FC<DropdownMenuProps> = (props: ScopedProps<DropdownMe
65
67
onChange : onOpenChange ,
66
68
caller : DROPDOWN_MENU_NAME ,
67
69
} ) ;
70
+ const [ wasOpenedWithArrowUp , setWasOpenedWithArrowUp ] = React . useState ( false ) ;
71
+
72
+ // Reset arrow up state when menu closes
73
+ React . useEffect ( ( ) => {
74
+ if ( ! open ) {
75
+ setWasOpenedWithArrowUp ( false ) ;
76
+ }
77
+ } , [ open ] ) ;
68
78
69
79
return (
70
80
< DropdownMenuProvider
@@ -76,6 +86,8 @@ const DropdownMenu: React.FC<DropdownMenuProps> = (props: ScopedProps<DropdownMe
76
86
onOpenChange = { setOpen }
77
87
onOpenToggle = { React . useCallback ( ( ) => setOpen ( ( prevOpen ) => ! prevOpen ) , [ setOpen ] ) }
78
88
modal = { modal }
89
+ wasOpenedWithArrowUp = { wasOpenedWithArrowUp }
90
+ setWasOpenedWithArrowUp = { setWasOpenedWithArrowUp }
79
91
>
80
92
< MenuPrimitive . Root { ...menuScope } open = { open } onOpenChange = { setOpen } dir = { dir } modal = { modal } >
81
93
{ children }
@@ -130,7 +142,11 @@ const DropdownMenuTrigger = React.forwardRef<DropdownMenuTriggerElement, Dropdow
130
142
if ( event . key === 'ArrowDown' ) context . onOpenChange ( true ) ;
131
143
// prevent keydown from scrolling window / first focused item to execute
132
144
// that keydown (inadvertently closing the menu)
133
- if ( [ 'Enter' , ' ' , 'ArrowDown' ] . includes ( event . key ) ) event . preventDefault ( ) ;
145
+ if ( event . key === 'ArrowUp' ) {
146
+ context . setWasOpenedWithArrowUp ( true ) ;
147
+ context . onOpenChange ( true ) ;
148
+ }
149
+ if ( [ 'Enter' , ' ' , 'ArrowDown' , 'ArrowUp' ] . includes ( event . key ) ) event . preventDefault ( ) ;
134
150
} ) }
135
151
/>
136
152
</ MenuPrimitive . Anchor >
@@ -167,7 +183,7 @@ const CONTENT_NAME = 'DropdownMenuContent';
167
183
168
184
type DropdownMenuContentElement = React . ComponentRef < typeof MenuPrimitive . Content > ;
169
185
type MenuContentProps = React . ComponentPropsWithoutRef < typeof MenuPrimitive . Content > ;
170
- interface DropdownMenuContentProps extends Omit < MenuContentProps , 'onEntryFocus' > { }
186
+ interface DropdownMenuContentProps extends MenuContentProps { }
171
187
172
188
const DropdownMenuContent = React . forwardRef < DropdownMenuContentElement , DropdownMenuContentProps > (
173
189
( props : ScopedProps < DropdownMenuContentProps > , forwardedRef ) => {
@@ -183,6 +199,21 @@ const DropdownMenuContent = React.forwardRef<DropdownMenuContentElement, Dropdow
183
199
{ ...menuScope }
184
200
{ ...contentProps }
185
201
ref = { forwardedRef }
202
+ onEntryFocus = { ( event : Event ) => {
203
+ // If opened with ArrowUp, focus last item instead of first
204
+ if ( context . wasOpenedWithArrowUp ) {
205
+ event . preventDefault ( ) ;
206
+ // Focus the last item
207
+ const target = event . target as HTMLElement ;
208
+ const items = target . querySelectorAll ( '[role="menuitem"]:not([data-disabled])' ) ;
209
+ const lastItem = items [ items . length - 1 ] as HTMLElement ;
210
+ if ( lastItem ) {
211
+ setTimeout ( ( ) => lastItem . focus ( ) , 0 ) ;
212
+ }
213
+ }
214
+ // Call the original onEntryFocus if provided
215
+ contentProps . onEntryFocus ?.( event ) ;
216
+ } }
186
217
onCloseAutoFocus = { composeEventHandlers ( props . onCloseAutoFocus , ( event ) => {
187
218
if ( ! hasInteractedOutsideRef . current ) context . triggerRef . current ?. focus ( ) ;
188
219
hasInteractedOutsideRef . current = false ;
0 commit comments