@@ -25,6 +25,8 @@ import {
25
25
ANIMATION_MODULE_TYPE ,
26
26
inject ,
27
27
HostAttributeToken ,
28
+ signal ,
29
+ computed ,
28
30
} from '@angular/core' ;
29
31
import {
30
32
MAT_RIPPLE_GLOBAL_OPTIONS ,
@@ -44,7 +46,7 @@ import {BehaviorSubject, Subject} from 'rxjs';
44
46
import { startWith , takeUntil } from 'rxjs/operators' ;
45
47
import { ENTER , SPACE } from '@angular/cdk/keycodes' ;
46
48
import { MAT_TABS_CONFIG , MatTabsConfig } from '../tab-config' ;
47
- import { MatPaginatedTabHeader } from '../paginated-tab-header' ;
49
+ import { MatPaginatedTabHeader , MatPaginatedTabHeaderItem } from '../paginated-tab-header' ;
48
50
import { CdkObserveContent } from '@angular/cdk/observers' ;
49
51
import { _CdkPrivateStyleLoader } from '@angular/cdk/private' ;
50
52
@@ -75,6 +77,8 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
75
77
imports : [ MatRipple , CdkObserveContent ] ,
76
78
} )
77
79
export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit , AfterViewInit {
80
+ _focusedItem = signal < MatPaginatedTabHeaderItem | null > ( null ) ;
81
+
78
82
/** Whether the ink bar should fit its width to the size of the tab label content. */
79
83
@Input ( { transform : booleanAttribute } )
80
84
get fitInkBarToContent ( ) : boolean {
@@ -195,6 +199,11 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
195
199
. subscribe ( ( ) => this . updateActiveLink ( ) ) ;
196
200
197
201
super . ngAfterContentInit ( ) ;
202
+
203
+ // Turn the `change` stream into a signal to try and avoid "changed after checked" errors.
204
+ this . _keyManager ! . change . pipe ( startWith ( null ) , takeUntil ( this . _destroyed ) ) . subscribe ( ( ) =>
205
+ this . _focusedItem . set ( this . _keyManager ?. activeItem || null ) ,
206
+ ) ;
198
207
}
199
208
200
209
override ngAfterViewInit ( ) {
@@ -215,12 +224,13 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
215
224
for ( let i = 0 ; i < items . length ; i ++ ) {
216
225
if ( items [ i ] . active ) {
217
226
this . selectedIndex = i ;
218
- this . _changeDetectorRef . markForCheck ( ) ;
219
-
220
227
if ( this . tabPanel ) {
221
228
this . tabPanel . _activeTabId = items [ i ] . id ;
222
229
}
223
-
230
+ // Updating the `selectedIndex` won't trigger the `change` event on
231
+ // the key manager so we need to set the signal from here.
232
+ this . _focusedItem . set ( items [ i ] ) ;
233
+ this . _changeDetectorRef . markForCheck ( ) ;
224
234
return ;
225
235
}
226
236
}
@@ -231,6 +241,10 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
231
241
_getRole ( ) : string | null {
232
242
return this . tabPanel ? 'tablist' : this . _elementRef . nativeElement . getAttribute ( 'role' ) ;
233
243
}
244
+
245
+ _hasFocus ( link : MatTabLink ) : boolean {
246
+ return this . _keyManager ?. activeItem === link ;
247
+ }
234
248
}
235
249
236
250
/**
@@ -250,7 +264,7 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
250
264
'[attr.aria-disabled]' : 'disabled' ,
251
265
'[attr.aria-selected]' : '_getAriaSelected()' ,
252
266
'[attr.id]' : 'id' ,
253
- '[attr.tabIndex]' : '_getTabIndex ()' ,
267
+ '[attr.tabIndex]' : '_tabIndex ()' ,
254
268
'[attr.role]' : '_getRole()' ,
255
269
'[class.mat-mdc-tab-disabled]' : 'disabled' ,
256
270
'[class.mdc-tab--active]' : 'active' ,
@@ -272,6 +286,10 @@ export class MatTabLink
272
286
/** Whether the tab link is active or not. */
273
287
protected _isActive : boolean = false ;
274
288
289
+ protected _tabIndex = computed ( ( ) =>
290
+ this . _tabNavBar . _focusedItem ( ) === this ? this . tabIndex : - 1 ,
291
+ ) ;
292
+
275
293
/** Whether the link is active. */
276
294
@Input ( { transform : booleanAttribute } )
277
295
get active ( ) : boolean {
@@ -407,14 +425,6 @@ export class MatTabLink
407
425
_getRole ( ) : string | null {
408
426
return this . _tabNavBar . tabPanel ? 'tab' : this . elementRef . nativeElement . getAttribute ( 'role' ) ;
409
427
}
410
-
411
- _getTabIndex ( ) : number {
412
- if ( this . _tabNavBar . tabPanel ) {
413
- return this . _isActive && ! this . disabled ? 0 : - 1 ;
414
- } else {
415
- return this . disabled ? - 1 : this . tabIndex ;
416
- }
417
- }
418
428
}
419
429
420
430
/**
0 commit comments