@@ -140,49 +140,63 @@ export default function useDropdownMenu(itemCount: number): DropdownMenuResponse
140140 // Destructure the key property from the event object
141141 const { key } = e ;
142142
143- // Ignore keys that we shouldn't handle
144- if ( ! [ 'Tab' , 'Shift' , 'Enter' , 'Escape' , 'ArrowUp' , 'ArrowDown' , ' ' ] . includes ( key ) ) {
145- return ;
146- }
143+ // Handle keyboard controls
144+ if ( [ 'Tab' , 'Shift' , 'Enter' , 'Escape' , 'ArrowUp' , 'ArrowDown' , ' ' ] . includes ( key ) ) {
145+ // Create mutable value that initializes as the currentFocusIndex value
146+ let newFocusIndex = currentFocusIndex . current ;
147147
148- // Create mutable value that initializes as the currentFocusIndex value
149- let newFocusIndex = currentFocusIndex . current ;
148+ // Controls whether the menu is open or closed, if the button should regain focus on close, and if a handler function should be called
149+ if ( key === 'Escape' ) {
150+ setIsOpen ( false ) ;
151+ buttonRef . current ?. focus ( ) ;
152+ return ;
153+ } else if ( key === 'Tab' ) {
154+ setIsOpen ( false ) ;
155+ return ;
156+ } else if ( key === 'Enter' || key === ' ' ) {
157+ if ( ! e . currentTarget . href ) {
158+ e . currentTarget . click ( ) ;
159+ }
150160
151- // Controls whether the menu is open or closed, if the button should regain focus on close, and if a handler function should be called
152- if ( key === 'Escape' ) {
153- setIsOpen ( false ) ;
154- buttonRef . current ?. focus ( ) ;
155- return ;
156- } else if ( key === 'Tab' ) {
157- setIsOpen ( false ) ;
158- return ;
159- } else if ( key === 'Enter' || key === ' ' ) {
160- if ( ! e . currentTarget . href ) {
161- e . currentTarget . click ( ) ;
161+ setIsOpen ( false ) ;
162+ return ;
162163 }
163164
164- setIsOpen ( false ) ;
165- return ;
166- }
165+ // Controls the current index to focus
166+ if ( newFocusIndex !== null ) {
167+ if ( key === 'ArrowUp' ) {
168+ newFocusIndex -= 1 ;
169+ } else if ( key === 'ArrowDown' ) {
170+ newFocusIndex += 1 ;
171+ }
167172
168- // Controls the current index to focus
169- if ( newFocusIndex !== null ) {
170- if ( key === 'ArrowUp' ) {
171- newFocusIndex -= 1 ;
172- } else if ( key === 'ArrowDown' ) {
173- newFocusIndex += 1 ;
173+ if ( newFocusIndex > itemRefs . length - 1 ) {
174+ newFocusIndex = 0 ;
175+ } else if ( newFocusIndex < 0 ) {
176+ newFocusIndex = itemRefs . length - 1 ;
177+ }
174178 }
175179
176- if ( newFocusIndex > itemRefs . length - 1 ) {
177- newFocusIndex = 0 ;
178- } else if ( newFocusIndex < 0 ) {
179- newFocusIndex = itemRefs . length - 1 ;
180+ // After any modification set state to the modified value
181+ if ( newFocusIndex !== null ) {
182+ moveFocus ( newFocusIndex ) ;
180183 }
184+
185+ return ;
181186 }
182187
183- // After any modification set state to the modified value
184- if ( newFocusIndex !== null ) {
185- moveFocus ( newFocusIndex ) ;
188+ // Handle printable keys
189+ if ( / [ a - z A - Z 0 - 9 . / < > ? ; : " ' ` ! @ # $ % ^ & * ( ) \\ [ \] { } _ + = | \\ - ~ , ] / . test ( key ) ) {
190+ const index = itemRefs . findIndex (
191+ ( ref ) =>
192+ ref . current ?. innerText ?. toLowerCase ( ) . startsWith ( key . toLowerCase ( ) ) ||
193+ ref . current ?. textContent ?. toLowerCase ( ) . startsWith ( key . toLowerCase ( ) ) ||
194+ ref . current ?. getAttribute ( 'aria-label' ) ?. toLowerCase ( ) . startsWith ( key . toLowerCase ( ) )
195+ ) ;
196+
197+ if ( index !== - 1 ) {
198+ moveFocus ( index ) ;
199+ }
186200 }
187201 } ;
188202
0 commit comments