@@ -22,6 +22,8 @@ import {
22
22
ViewEncapsulation ,
23
23
inject ,
24
24
HostAttributeToken ,
25
+ signal ,
26
+ computed ,
25
27
} from '@angular/core' ;
26
28
import {
27
29
MAT_RIPPLE_GLOBAL_OPTIONS ,
@@ -39,7 +41,7 @@ import {BehaviorSubject, Subject} from 'rxjs';
39
41
import { startWith , takeUntil } from 'rxjs/operators' ;
40
42
import { ENTER , SPACE } from '@angular/cdk/keycodes' ;
41
43
import { MAT_TABS_CONFIG , MatTabsConfig } from '../tab-config' ;
42
- import { MatPaginatedTabHeader } from '../paginated-tab-header' ;
44
+ import { MatPaginatedTabHeader , MatPaginatedTabHeaderItem } from '../paginated-tab-header' ;
43
45
import { CdkObserveContent } from '@angular/cdk/observers' ;
44
46
import { _CdkPrivateStyleLoader } from '@angular/cdk/private' ;
45
47
@@ -70,6 +72,8 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
70
72
imports : [ MatRipple , CdkObserveContent ] ,
71
73
} )
72
74
export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit , AfterViewInit {
75
+ _focusedItem = signal < MatPaginatedTabHeaderItem | null > ( null ) ;
76
+
73
77
/** Whether the ink bar should fit its width to the size of the tab label content. */
74
78
@Input ( { transform : booleanAttribute } )
75
79
get fitInkBarToContent ( ) : boolean {
@@ -183,6 +187,11 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
183
187
. subscribe ( ( ) => this . updateActiveLink ( ) ) ;
184
188
185
189
super . ngAfterContentInit ( ) ;
190
+
191
+ // Turn the `change` stream into a signal to try and avoid "changed after checked" errors.
192
+ this . _keyManager ! . change . pipe ( startWith ( null ) , takeUntil ( this . _destroyed ) ) . subscribe ( ( ) =>
193
+ this . _focusedItem . set ( this . _keyManager ?. activeItem || null ) ,
194
+ ) ;
186
195
}
187
196
188
197
override ngAfterViewInit ( ) {
@@ -203,12 +212,13 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
203
212
for ( let i = 0 ; i < items . length ; i ++ ) {
204
213
if ( items [ i ] . active ) {
205
214
this . selectedIndex = i ;
206
- this . _changeDetectorRef . markForCheck ( ) ;
207
-
208
215
if ( this . tabPanel ) {
209
216
this . tabPanel . _activeTabId = items [ i ] . id ;
210
217
}
211
-
218
+ // Updating the `selectedIndex` won't trigger the `change` event on
219
+ // the key manager so we need to set the signal from here.
220
+ this . _focusedItem . set ( items [ i ] ) ;
221
+ this . _changeDetectorRef . markForCheck ( ) ;
212
222
return ;
213
223
}
214
224
}
@@ -219,6 +229,10 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
219
229
_getRole ( ) : string | null {
220
230
return this . tabPanel ? 'tablist' : this . _elementRef . nativeElement . getAttribute ( 'role' ) ;
221
231
}
232
+
233
+ _hasFocus ( link : MatTabLink ) : boolean {
234
+ return this . _keyManager ?. activeItem === link ;
235
+ }
222
236
}
223
237
224
238
/**
@@ -238,7 +252,7 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
238
252
'[attr.aria-disabled]' : 'disabled' ,
239
253
'[attr.aria-selected]' : '_getAriaSelected()' ,
240
254
'[attr.id]' : 'id' ,
241
- '[attr.tabIndex]' : '_getTabIndex ()' ,
255
+ '[attr.tabIndex]' : '_tabIndex ()' ,
242
256
'[attr.role]' : '_getRole()' ,
243
257
'[class.mat-mdc-tab-disabled]' : 'disabled' ,
244
258
'[class.mdc-tab--active]' : 'active' ,
@@ -260,6 +274,10 @@ export class MatTabLink
260
274
/** Whether the tab link is active or not. */
261
275
protected _isActive : boolean = false ;
262
276
277
+ protected _tabIndex = computed ( ( ) =>
278
+ this . _tabNavBar . _focusedItem ( ) === this ? this . tabIndex : - 1 ,
279
+ ) ;
280
+
263
281
/** Whether the link is active. */
264
282
@Input ( { transform : booleanAttribute } )
265
283
get active ( ) : boolean {
@@ -393,14 +411,6 @@ export class MatTabLink
393
411
_getRole ( ) : string | null {
394
412
return this . _tabNavBar . tabPanel ? 'tab' : this . elementRef . nativeElement . getAttribute ( 'role' ) ;
395
413
}
396
-
397
- _getTabIndex ( ) : number {
398
- if ( this . _tabNavBar . tabPanel ) {
399
- return this . _isActive && ! this . disabled ? 0 : - 1 ;
400
- } else {
401
- return this . disabled ? - 1 : this . tabIndex ;
402
- }
403
- }
404
414
}
405
415
406
416
/**
0 commit comments