8
8
9
9
import { Direction , Directionality } from '@angular/cdk/bidi' ;
10
10
import { coerceNumberProperty } from '@angular/cdk/coercion' ;
11
- import { END , ENTER , HOME , LEFT_ARROW , RIGHT_ARROW , SPACE } from '@angular/cdk/keycodes' ;
11
+ import { END , ENTER , HOME , SPACE } from '@angular/cdk/keycodes' ;
12
12
import { ViewportRuler } from '@angular/cdk/scrolling' ;
13
13
import {
14
14
AfterContentChecked ,
@@ -31,6 +31,7 @@ import {CanDisableRipple, mixinDisableRipple} from '@angular/material/core';
31
31
import { merge , of as observableOf , Subscription } from 'rxjs' ;
32
32
import { MatInkBar } from './ink-bar' ;
33
33
import { MatTabLabelWrapper } from './tab-label-wrapper' ;
34
+ import { FocusKeyManager } from '@angular/cdk/a11y' ;
34
35
35
36
36
37
/**
@@ -80,9 +81,6 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
80
81
@ViewChild ( 'tabListContainer' ) _tabListContainer : ElementRef ;
81
82
@ViewChild ( 'tabList' ) _tabList : ElementRef ;
82
83
83
- /** The tab index that is focused. */
84
- private _focusIndex : number = 0 ;
85
-
86
84
/** The distance in pixels that the tab labels should be translated to the left. */
87
85
private _scrollDistance = 0 ;
88
86
@@ -110,6 +108,9 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
110
108
/** Whether the scroll distance has changed and should be applied after the view is checked. */
111
109
private _scrollDistanceChanged : boolean ;
112
110
111
+ /** Used to manage focus between the tabs. */
112
+ private _keyManager : FocusKeyManager < MatTabLabelWrapper > ;
113
+
113
114
private _selectedIndex : number = 0 ;
114
115
115
116
/** The index of the active tab. */
@@ -119,7 +120,10 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
119
120
value = coerceNumberProperty ( value ) ;
120
121
this . _selectedIndexChanged = this . _selectedIndex != value ;
121
122
this . _selectedIndex = value ;
122
- this . _focusIndex = value ;
123
+
124
+ if ( this . _keyManager ) {
125
+ this . _keyManager . updateActiveItemIndex ( value ) ;
126
+ }
123
127
}
124
128
125
129
/** Event emitted when the option is selected. */
@@ -164,25 +168,21 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
164
168
165
169
_handleKeydown ( event : KeyboardEvent ) {
166
170
switch ( event . keyCode ) {
167
- case RIGHT_ARROW :
168
- this . _focusNextTab ( ) ;
169
- break ;
170
- case LEFT_ARROW :
171
- this . _focusPreviousTab ( ) ;
172
- break ;
173
171
case HOME :
174
- this . _focusFirstTab ( ) ;
172
+ this . _keyManager . setFirstItemActive ( ) ;
175
173
event . preventDefault ( ) ;
176
174
break ;
177
175
case END :
178
- this . _focusLastTab ( ) ;
176
+ this . _keyManager . setLastItemActive ( ) ;
179
177
event . preventDefault ( ) ;
180
178
break ;
181
179
case ENTER :
182
180
case SPACE :
183
181
this . selectFocusedIndex . emit ( this . focusIndex ) ;
184
182
event . preventDefault ( ) ;
185
183
break ;
184
+ default :
185
+ this . _keyManager . onKeydown ( event ) ;
186
186
}
187
187
}
188
188
@@ -197,10 +197,19 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
197
197
this . _alignInkBarToSelectedTab ( ) ;
198
198
} ;
199
199
200
+ this . _keyManager = new FocusKeyManager ( this . _labelWrappers )
201
+ . withHorizontalOrientation ( this . _getLayoutDirection ( ) ) ;
202
+
203
+ this . _keyManager . updateActiveItemIndex ( 0 ) ;
204
+
200
205
// Defer the first call in order to allow for slower browsers to lay out the elements.
201
206
// This helps in cases where the user lands directly on a page with paginated tabs.
202
207
typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame ( realign ) : realign ( ) ;
203
- this . _realignInkBar = merge ( dirChange , resize ) . subscribe ( realign ) ;
208
+
209
+ this . _realignInkBar = merge ( dirChange , resize ) . subscribe ( ( ) => {
210
+ realign ( ) ;
211
+ this . _keyManager . withHorizontalOrientation ( this . _getLayoutDirection ( ) ) ;
212
+ } ) ;
204
213
}
205
214
206
215
ngOnDestroy ( ) {
@@ -227,14 +236,14 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
227
236
228
237
/** Tracks which element has focus; used for keyboard navigation */
229
238
get focusIndex ( ) : number {
230
- return this . _focusIndex ;
239
+ return this . _keyManager ? this . _keyManager . activeItemIndex ! : 0 ;
231
240
}
232
241
233
242
/** When the focus index is set, we must manually send focus to the correct label */
234
243
set focusIndex ( value : number ) {
235
- if ( ! this . _isValidIndex ( value ) || this . _focusIndex == value ) { return ; }
244
+ if ( ! this . _isValidIndex ( value ) || this . focusIndex == value || ! this . _keyManager ) { return ; }
236
245
237
- this . _focusIndex = value ;
246
+ this . _keyManager . setActiveItem ( value ) ;
238
247
this . indexFocused . emit ( value ) ;
239
248
this . _setTabFocus ( value ) ;
240
249
}
@@ -276,53 +285,6 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
276
285
}
277
286
}
278
287
279
- /**
280
- * Moves the focus towards the beginning or the end of the list depending on the offset provided.
281
- * Valid offsets are 1 and -1.
282
- */
283
- _moveFocus ( offset : number ) {
284
- if ( this . _labelWrappers ) {
285
- const tabs : MatTabLabelWrapper [ ] = this . _labelWrappers . toArray ( ) ;
286
-
287
- for ( let i = this . focusIndex + offset ; i < tabs . length && i >= 0 ; i += offset ) {
288
- if ( this . _isValidIndex ( i ) ) {
289
- this . focusIndex = i ;
290
- return ;
291
- }
292
- }
293
- }
294
- }
295
-
296
- /** Increment the focus index by 1 until a valid tab is found. */
297
- _focusNextTab ( ) : void {
298
- this . _moveFocus ( this . _getLayoutDirection ( ) == 'ltr' ? 1 : - 1 ) ;
299
- }
300
-
301
- /** Decrement the focus index by 1 until a valid tab is found. */
302
- _focusPreviousTab ( ) : void {
303
- this . _moveFocus ( this . _getLayoutDirection ( ) == 'ltr' ? - 1 : 1 ) ;
304
- }
305
-
306
- /** Focuses the first tab. */
307
- private _focusFirstTab ( ) : void {
308
- for ( let i = 0 ; i < this . _labelWrappers . length ; i ++ ) {
309
- if ( this . _isValidIndex ( i ) ) {
310
- this . focusIndex = i ;
311
- break ;
312
- }
313
- }
314
- }
315
-
316
- /** Focuses the last tab. */
317
- private _focusLastTab ( ) : void {
318
- for ( let i = this . _labelWrappers . length - 1 ; i > - 1 ; i -- ) {
319
- if ( this . _isValidIndex ( i ) ) {
320
- this . focusIndex = i ;
321
- break ;
322
- }
323
- }
324
- }
325
-
326
288
/** The layout direction of the containing app. */
327
289
_getLayoutDirection ( ) : Direction {
328
290
return this . _dir && this . _dir . value === 'rtl' ? 'rtl' : 'ltr' ;
0 commit comments