@@ -71,6 +71,11 @@ export interface ListControllerConfig<Item extends ListItem> {
71
71
* disabled.
72
72
*/
73
73
isActivatable ?: ( item : Item ) => boolean ;
74
+ /**
75
+ * Whether or not navigating past the end of the list wraps to the beginning
76
+ * and vice versa. Defaults to true.
77
+ */
78
+ wrapNavigation ?: ( ) => boolean ;
74
79
}
75
80
76
81
/**
@@ -84,6 +89,7 @@ export class ListController<Item extends ListItem> {
84
89
private readonly activateItem : ( item : Item ) => void ;
85
90
private readonly isNavigableKey : ( key : string ) => boolean ;
86
91
private readonly isActivatable ?: ( item : Item ) => boolean ;
92
+ private readonly wrapNavigation : ( ) => boolean ;
87
93
88
94
constructor ( config : ListControllerConfig < Item > ) {
89
95
const {
@@ -94,6 +100,7 @@ export class ListController<Item extends ListItem> {
94
100
activateItem,
95
101
isNavigableKey,
96
102
isActivatable,
103
+ wrapNavigation,
97
104
} = config ;
98
105
this . isItem = isItem ;
99
106
this . getPossibleItems = getPossibleItems ;
@@ -102,6 +109,7 @@ export class ListController<Item extends ListItem> {
102
109
this . activateItem = activateItem ;
103
110
this . isNavigableKey = isNavigableKey ;
104
111
this . isActivatable = isActivatable ;
112
+ this . wrapNavigation = wrapNavigation ?? ( ( ) => true ) ;
105
113
}
106
114
107
115
/**
@@ -149,10 +157,6 @@ export class ListController<Item extends ListItem> {
149
157
150
158
const activeItemRecord = getActiveItem ( items , this . isActivatable ) ;
151
159
152
- if ( activeItemRecord ) {
153
- activeItemRecord . item . tabIndex = - 1 ;
154
- }
155
-
156
160
event . preventDefault ( ) ;
157
161
158
162
const isRtl = this . isRtl ( ) ;
@@ -163,32 +167,53 @@ export class ListController<Item extends ListItem> {
163
167
? NavigableKeys . ArrowLeft
164
168
: NavigableKeys . ArrowRight ;
165
169
170
+ let nextActiveItem : Item | null = null ;
166
171
switch ( key ) {
167
172
// Activate the next item
168
173
case NavigableKeys . ArrowDown :
169
174
case inlineNext :
170
- activateNextItem ( items , activeItemRecord , this . isActivatable ) ;
175
+ nextActiveItem = activateNextItem (
176
+ items ,
177
+ activeItemRecord ,
178
+ this . isActivatable ,
179
+ this . wrapNavigation ( ) ,
180
+ ) ;
171
181
break ;
172
182
173
183
// Activate the previous item
174
184
case NavigableKeys . ArrowUp :
175
185
case inlinePrevious :
176
- activatePreviousItem ( items , activeItemRecord , this . isActivatable ) ;
186
+ nextActiveItem = activatePreviousItem (
187
+ items ,
188
+ activeItemRecord ,
189
+ this . isActivatable ,
190
+ this . wrapNavigation ( ) ,
191
+ ) ;
177
192
break ;
178
193
179
194
// Activate the first item
180
195
case NavigableKeys . Home :
181
- activateFirstItem ( items , this . isActivatable ) ;
196
+ nextActiveItem = activateFirstItem ( items , this . isActivatable ) ;
182
197
break ;
183
198
184
199
// Activate the last item
185
200
case NavigableKeys . End :
186
- activateLastItem ( items , this . isActivatable ) ;
201
+ nextActiveItem = activateLastItem ( items , this . isActivatable ) ;
187
202
break ;
188
203
189
204
default :
190
205
break ;
191
206
}
207
+
208
+ if (
209
+ nextActiveItem &&
210
+ activeItemRecord &&
211
+ activeItemRecord . item !== nextActiveItem
212
+ ) {
213
+ // If a new item was activated, remove the tabindex of the previous
214
+ // activated item.
215
+ activeItemRecord . item . tabIndex = - 1 ;
216
+ }
192
217
} ;
193
218
194
219
/**
@@ -203,7 +228,12 @@ export class ListController<Item extends ListItem> {
203
228
if ( activeItemRecord ) {
204
229
activeItemRecord . item . tabIndex = - 1 ;
205
230
}
206
- return activateNextItem ( items , activeItemRecord , this . isActivatable ) ;
231
+ return activateNextItem (
232
+ items ,
233
+ activeItemRecord ,
234
+ this . isActivatable ,
235
+ this . wrapNavigation ( ) ,
236
+ ) ;
207
237
}
208
238
209
239
/**
@@ -218,7 +248,12 @@ export class ListController<Item extends ListItem> {
218
248
if ( activeItemRecord ) {
219
249
activeItemRecord . item . tabIndex = - 1 ;
220
250
}
221
- return activatePreviousItem ( items , activeItemRecord , this . isActivatable ) ;
251
+ return activatePreviousItem (
252
+ items ,
253
+ activeItemRecord ,
254
+ this . isActivatable ,
255
+ this . wrapNavigation ( ) ,
256
+ ) ;
222
257
}
223
258
224
259
/**
0 commit comments