55 AutocompleteSource ,
66 GetSources ,
77 AutocompleteSuggestion ,
8+ AutocompleteOptions ,
89} from './types' ;
910
1011export const noop = ( ) => { } ;
@@ -70,30 +71,34 @@ export function normalizeGetSources<TItem>(
7071 } ;
7172}
7273
73- export function getNextHighlightedIndex (
74+ export function getNextHighlightedIndex < TItem > (
7475 moveAmount : number ,
75- baseIndex : number ,
76- itemCount : number
77- ) {
78- const itemsLastIndex = itemCount - 1 ;
79-
80- if (
81- typeof baseIndex !== 'number' ||
82- baseIndex < 0 ||
83- baseIndex >= itemCount
84- ) {
85- baseIndex = moveAmount > 0 ? - 1 : itemsLastIndex + 1 ;
76+ baseIndex : number | null ,
77+ itemCount : number ,
78+ defaultHighlightedIndex : AutocompleteOptions < TItem > [ 'defaultHighlightedIndex' ]
79+ ) : number | null {
80+ // We allow circular keyboard navigation from the base index.
81+ // The base index can either be `null` (nothing is highlighted) or `0`
82+ // (the first item is highlighted).
83+ // The base index is allowed to get assigned `null` only if
84+ // `props.defaultHighlightedIndex` is `null`. This pattern allows to "stop"
85+ // by the actual query before navigating to other suggestions as seen on
86+ // Google or Amazon.
87+ if ( baseIndex === null && moveAmount < 0 ) {
88+ return itemCount - 1 ;
8689 }
8790
88- let newIndex = baseIndex + moveAmount ;
91+ if ( defaultHighlightedIndex !== null && baseIndex === 0 && moveAmount < 0 ) {
92+ return itemCount - 1 ;
93+ }
94+
95+ const numericIndex = ( baseIndex === null ? - 1 : baseIndex ) + moveAmount ;
8996
90- if ( newIndex < 0 ) {
91- newIndex = itemsLastIndex ;
92- } else if ( newIndex > itemsLastIndex ) {
93- newIndex = 0 ;
97+ if ( numericIndex <= - 1 || numericIndex >= itemCount ) {
98+ return defaultHighlightedIndex === null ? null : 0 ;
9499 }
95100
96- return newIndex ;
101+ return numericIndex ;
97102}
98103
99104// We don't have access to the autocomplete source when we call `onKeyDown`
@@ -120,7 +125,7 @@ function getSuggestionFromHighlightedIndex<TItem>({
120125
121126 // Based on the accumulated counts, we can infer the index of the suggestion.
122127 const suggestionIndex = accumulatedSuggestionsCount . reduce ( ( acc , current ) => {
123- if ( current <= state . highlightedIndex ) {
128+ if ( current <= state . highlightedIndex ! ) {
124129 return acc + 1 ;
125130 }
126131
@@ -164,7 +169,7 @@ function getRelativeHighlightedIndex<TItem>({
164169 counter ++ ;
165170 }
166171
167- return state . highlightedIndex - previousItemsOffset ;
172+ return state . highlightedIndex ! - previousItemsOffset ;
168173}
169174
170175export function getHighlightedItem < TItem > ( {
0 commit comments