@@ -25,6 +25,8 @@ import {
2525 ANIMATION_MODULE_TYPE ,
2626 inject ,
2727 HostAttributeToken ,
28+ signal ,
29+ computed ,
2830} from '@angular/core' ;
2931import {
3032 MAT_RIPPLE_GLOBAL_OPTIONS ,
@@ -44,7 +46,7 @@ import {BehaviorSubject, Subject} from 'rxjs';
4446import { startWith , takeUntil } from 'rxjs/operators' ;
4547import { ENTER , SPACE } from '@angular/cdk/keycodes' ;
4648import { MAT_TABS_CONFIG , MatTabsConfig } from '../tab-config' ;
47- import { MatPaginatedTabHeader } from '../paginated-tab-header' ;
49+ import { MatPaginatedTabHeader , MatPaginatedTabHeaderItem } from '../paginated-tab-header' ;
4850import { CdkObserveContent } from '@angular/cdk/observers' ;
4951import { _CdkPrivateStyleLoader } from '@angular/cdk/private' ;
5052
@@ -75,6 +77,8 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
7577 imports : [ MatRipple , CdkObserveContent ] ,
7678} )
7779export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit , AfterViewInit {
80+ _focusedItem = signal < MatPaginatedTabHeaderItem | null > ( null ) ;
81+
7882 /** Whether the ink bar should fit its width to the size of the tab label content. */
7983 @Input ( { transform : booleanAttribute } )
8084 get fitInkBarToContent ( ) : boolean {
@@ -195,6 +199,11 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
195199 . subscribe ( ( ) => this . updateActiveLink ( ) ) ;
196200
197201 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+ ) ;
198207 }
199208
200209 override ngAfterViewInit ( ) {
@@ -215,12 +224,13 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
215224 for ( let i = 0 ; i < items . length ; i ++ ) {
216225 if ( items [ i ] . active ) {
217226 this . selectedIndex = i ;
218- this . _changeDetectorRef . markForCheck ( ) ;
219-
220227 if ( this . tabPanel ) {
221228 this . tabPanel . _activeTabId = items [ i ] . id ;
222229 }
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 ( ) ;
224234 return ;
225235 }
226236 }
@@ -231,6 +241,10 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
231241 _getRole ( ) : string | null {
232242 return this . tabPanel ? 'tablist' : this . _elementRef . nativeElement . getAttribute ( 'role' ) ;
233243 }
244+
245+ _hasFocus ( link : MatTabLink ) : boolean {
246+ return this . _keyManager ?. activeItem === link ;
247+ }
234248}
235249
236250/**
@@ -250,7 +264,7 @@ export class MatTabNav extends MatPaginatedTabHeader implements AfterContentInit
250264 '[attr.aria-disabled]' : 'disabled' ,
251265 '[attr.aria-selected]' : '_getAriaSelected()' ,
252266 '[attr.id]' : 'id' ,
253- '[attr.tabIndex]' : '_getTabIndex ()' ,
267+ '[attr.tabIndex]' : '_tabIndex ()' ,
254268 '[attr.role]' : '_getRole()' ,
255269 '[class.mat-mdc-tab-disabled]' : 'disabled' ,
256270 '[class.mdc-tab--active]' : 'active' ,
@@ -272,6 +286,10 @@ export class MatTabLink
272286 /** Whether the tab link is active or not. */
273287 protected _isActive : boolean = false ;
274288
289+ protected _tabIndex = computed ( ( ) =>
290+ this . _tabNavBar . _focusedItem ( ) === this ? this . tabIndex : - 1 ,
291+ ) ;
292+
275293 /** Whether the link is active. */
276294 @Input ( { transform : booleanAttribute } )
277295 get active ( ) : boolean {
@@ -407,14 +425,6 @@ export class MatTabLink
407425 _getRole ( ) : string | null {
408426 return this . _tabNavBar . tabPanel ? 'tab' : this . elementRef . nativeElement . getAttribute ( 'role' ) ;
409427 }
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- }
418428}
419429
420430/**
0 commit comments