@@ -22,6 +22,8 @@ import {
2222 ViewEncapsulation ,
2323 inject ,
2424 HostAttributeToken ,
25+ signal ,
26+ computed ,
2527} from '@angular/core' ;
2628import {
2729 MAT_RIPPLE_GLOBAL_OPTIONS ,
@@ -39,7 +41,7 @@ import {BehaviorSubject, Subject} from 'rxjs';
3941import { startWith , takeUntil } from 'rxjs/operators' ;
4042import { ENTER , SPACE } from '@angular/cdk/keycodes' ;
4143import { MAT_TABS_CONFIG , MatTabsConfig } from '../tab-config' ;
42- import { MatPaginatedTabHeader } from '../paginated-tab-header' ;
44+ import { MatPaginatedTabHeader , MatPaginatedTabHeaderItem } from '../paginated-tab-header' ;
4345import { CdkObserveContent } from '@angular/cdk/observers' ;
4446import { _CdkPrivateStyleLoader } from '@angular/cdk/private' ;
4547
@@ -70,6 +72,8 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
7072 imports : [ MatRipple , CdkObserveContent ] ,
7173} )
7274export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit , AfterViewInit {
75+ _focusedItem = signal < MatPaginatedTabHeaderItem | null > ( null ) ;
76+
7377 /** Whether the ink bar should fit its width to the size of the tab label content. */
7478 @Input ( { transform : booleanAttribute } )
7579 get fitInkBarToContent ( ) : boolean {
@@ -183,6 +187,11 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
183187 . subscribe ( ( ) => this . updateActiveLink ( ) ) ;
184188
185189 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+ ) ;
186195 }
187196
188197 override ngAfterViewInit ( ) {
@@ -203,12 +212,13 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
203212 for ( let i = 0 ; i < items . length ; i ++ ) {
204213 if ( items [ i ] . active ) {
205214 this . selectedIndex = i ;
206- this . _changeDetectorRef . markForCheck ( ) ;
207-
208215 if ( this . tabPanel ) {
209216 this . tabPanel . _activeTabId = items [ i ] . id ;
210217 }
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 ( ) ;
212222 return ;
213223 }
214224 }
@@ -219,6 +229,10 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
219229 _getRole ( ) : string | null {
220230 return this . tabPanel ? 'tablist' : this . _elementRef . nativeElement . getAttribute ( 'role' ) ;
221231 }
232+
233+ _hasFocus ( link : MatTabLink ) : boolean {
234+ return this . _keyManager ?. activeItem === link ;
235+ }
222236}
223237
224238/**
@@ -238,7 +252,7 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
238252 '[attr.aria-disabled]' : 'disabled' ,
239253 '[attr.aria-selected]' : '_getAriaSelected()' ,
240254 '[attr.id]' : 'id' ,
241- '[attr.tabIndex]' : '_getTabIndex ()' ,
255+ '[attr.tabIndex]' : '_tabIndex ()' ,
242256 '[attr.role]' : '_getRole()' ,
243257 '[class.mat-mdc-tab-disabled]' : 'disabled' ,
244258 '[class.mdc-tab--active]' : 'active' ,
@@ -260,6 +274,10 @@ export class MatTabLink
260274 /** Whether the tab link is active or not. */
261275 protected _isActive : boolean = false ;
262276
277+ protected _tabIndex = computed ( ( ) =>
278+ this . _tabNavBar . _focusedItem ( ) === this ? this . tabIndex : - 1 ,
279+ ) ;
280+
263281 /** Whether the link is active. */
264282 @Input ( { transform : booleanAttribute } )
265283 get active ( ) : boolean {
@@ -393,14 +411,6 @@ export class MatTabLink
393411 _getRole ( ) : string | null {
394412 return this . _tabNavBar . tabPanel ? 'tab' : this . elementRef . nativeElement . getAttribute ( 'role' ) ;
395413 }
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- }
404414}
405415
406416/**
0 commit comments