@@ -43,6 +43,10 @@ const SearchBar = (): JSX.Element => {
4343
4444 const isEmpty = ! results || results . length === 0 ;
4545
46+ const listRef = useRef < HTMLUListElement | null > ( null ) ;
47+
48+ const activeIndexRef = useRef < number > ( - 1 ) ;
49+
4650 const changeHandler = ( e : React . ChangeEvent < HTMLInputElement > ) => {
4751 e . preventDefault ( ) ;
4852
@@ -56,6 +60,7 @@ const SearchBar = (): JSX.Element => {
5660 const collapseContainer = ( ) => {
5761 setExpanded ( false ) ;
5862 setQuery ( '' ) ;
63+ activeIndexRef . current = - 1 ;
5964 } ;
6065
6166 useEffect ( ( ) => {
@@ -94,6 +99,14 @@ const SearchBar = (): JSX.Element => {
9499 const toggleContainer = ( ) =>
95100 isExpanded ? collapseContainer ( ) : expandContainer ( ) ;
96101
102+ const focusActiveSearchItem = ( ) => {
103+ if ( listRef . current ) {
104+ const el = listRef . current . children [ activeIndexRef . current ] ;
105+ el ?. querySelector ( 'a' ) ?. focus ( ) ;
106+ el ?. scrollIntoView ( { behavior : 'smooth' , block : 'nearest' } ) ;
107+ }
108+ } ;
109+
97110 useKeyPress ( {
98111 targetKey : 'ctrl+k' ,
99112 callback : toggleContainer ,
@@ -115,6 +128,31 @@ const SearchBar = (): JSX.Element => {
115128 } ,
116129 } ) ;
117130
131+ const handleSearchResultFocus = ( e : React . KeyboardEvent ) => {
132+ if ( ! isExpanded || isEmpty || ! listRef . current ) return ;
133+
134+ if ( e . key === 'ArrowDown' ) {
135+ e . preventDefault ( ) ;
136+ activeIndexRef . current =
137+ activeIndexRef . current + 1 > listRef . current . children . length - 1
138+ ? 0
139+ : activeIndexRef . current + 1 ;
140+
141+ focusActiveSearchItem ( ) ;
142+ }
143+
144+ if ( e . key === 'ArrowUp' ) {
145+ e . preventDefault ( ) ;
146+
147+ activeIndexRef . current =
148+ activeIndexRef . current - 1 < 0
149+ ? listRef . current . children . length - 1
150+ : activeIndexRef . current - 1 ;
151+
152+ focusActiveSearchItem ( ) ;
153+ }
154+ } ;
155+
118156 return (
119157 < motion . div
120158 className = { containerClassNames }
@@ -124,6 +162,7 @@ const SearchBar = (): JSX.Element => {
124162 transition = { containerTransition }
125163 ref = { parentRef }
126164 onBlur = { onBlurHandler }
165+ onKeyDown = { handleSearchResultFocus }
127166 >
128167 < div
129168 className = { styles . searchInputContainer }
@@ -181,7 +220,7 @@ const SearchBar = (): JSX.Element => {
181220 </ div >
182221 ) }
183222 { ! isEmpty && (
184- < ul >
223+ < ul ref = { listRef } >
185224 { results . map ( ( result : SearchResult ) => {
186225 const sectionPath =
187226 result . category === 'api'
0 commit comments