@@ -55,31 +55,12 @@ export class ListNavigation<T extends ListNavigationItem> {
5555
5656 /** Navigates to the next item in the list. */
5757 next ( ) {
58- const items = this . inputs . items ( ) ;
59- const itemCount = items . length ;
60- const startIndex = this . inputs . activeIndex ( ) ;
61- const step = ( i : number ) => this . _stepIndex ( i , 1 ) ;
62-
63- for ( let i = step ( startIndex ) ; i !== startIndex && i < itemCount ; i = step ( i ) ) {
64- if ( this . isFocusable ( items [ i ] ) ) {
65- this . goto ( items [ i ] ) ;
66- return ;
67- }
68- }
58+ this . advance ( 1 ) ;
6959 }
7060
7161 /** Navigates to the previous item in the list. */
7262 prev ( ) {
73- const items = this . inputs . items ( ) ;
74- const startIndex = this . inputs . activeIndex ( ) ;
75- const step = ( i : number ) => this . _stepIndex ( i , - 1 ) ;
76-
77- for ( let i = step ( startIndex ) ; i !== startIndex && i >= 0 ; i = step ( i ) ) {
78- if ( this . isFocusable ( items [ i ] ) ) {
79- this . goto ( items [ i ] ) ;
80- return ;
81- }
82- }
63+ this . advance ( - 1 ) ;
8364 }
8465
8566 /** Navigates to the first item in the list. */
@@ -107,8 +88,22 @@ export class ListNavigation<T extends ListNavigationItem> {
10788 return ! item . disabled ( ) || ! this . inputs . skipDisabled ( ) ;
10889 }
10990
110- private _stepIndex ( index : number , step : - 1 | 1 ) {
111- const itemCount = this . inputs . items ( ) . length ;
112- return this . inputs . wrap ( ) ? ( index + step + itemCount ) % itemCount : index + step ;
91+ /** Advances to the next or previous focusable item in the list based on the given delta. */
92+ private advance ( delta : 1 | - 1 ) {
93+ const items = this . inputs . items ( ) ;
94+ const itemCount = items . length ;
95+ const startIndex = this . inputs . activeIndex ( ) ;
96+ const step = ( i : number ) =>
97+ this . inputs . wrap ( ) ? ( i + delta + itemCount ) % itemCount : i + delta ;
98+
99+ // If wrapping is enabled, this loop ultimately terminates when `i` gets back to `startIndex`
100+ // in the case that all options are disabled. If wrapping is disabled, the loop terminates
101+ // when the index goes out of bounds.
102+ for ( let i = step ( startIndex ) ; i !== startIndex && i < itemCount && i >= 0 ; i = step ( i ) ) {
103+ if ( this . isFocusable ( items [ i ] ) ) {
104+ this . goto ( items [ i ] ) ;
105+ return ;
106+ }
107+ }
113108 }
114109}
0 commit comments