Skip to content

Commit 9aee2ee

Browse files
feat(material/tabs): synchronize ink bar animation with tab (#27056)
The animation of the ink bar of the mat tab group and the mat tab nav bar is now synchronized with the animationDuration of the tab body. When the animation duration of the tab body was set to e.g. 0ms the ink bar still had an animation of 500ms. The following feat extends the animationDuration property to not only affect the tab body but also the ink bar of the tab group and the tab nav bar.
1 parent 0028c68 commit 9aee2ee

File tree

6 files changed

+71
-2
lines changed

6 files changed

+71
-2
lines changed

src/material/tabs/_tabs-common.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ $mat-tab-animation-duration: 500ms !default;
217217
tokens-mat-tab-header.$prefix, tokens-mat-tab-header.get-unthemable-tokens());
218218
}
219219

220+
.mdc-tab-indicator .mdc-tab-indicator__content {
221+
transition-duration: var(--mat-tab-animation-duration, 250ms);
222+
}
223+
220224
.mat-mdc-tab-header-pagination {
221225
@include vendor-prefixes.user-select(none);
222226
position: relative;

src/material/tabs/tab-group.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,15 @@ describe('nested MatTabGroup with enabled animations', () => {
999999
tick();
10001000
}).not.toThrow();
10011001
}));
1002+
1003+
it('should set appropiate css variable given a specified animationDuration', fakeAsync(() => {
1004+
let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration);
1005+
fixture.detectChanges();
1006+
tick();
1007+
1008+
const tabGroup = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
1009+
expect(tabGroup.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms');
1010+
}));
10021011
});
10031012

10041013
describe('MatTabGroup with ink bar fit to content', () => {

src/material/tabs/tab-group.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ export abstract class _MatTabGroupBase
528528
'[class.mat-mdc-tab-group-dynamic-height]': 'dynamicHeight',
529529
'[class.mat-mdc-tab-group-inverted-header]': 'headerPosition === "below"',
530530
'[class.mat-mdc-tab-group-stretch-tabs]': 'stretchTabs',
531+
'[style.--mat-tab-animation-duration]': 'animationDuration',
531532
},
532533
})
533534
export class MatTabGroup extends _MatTabGroupBase {

src/material/tabs/tab-nav-bar/tab-nav-bar.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,34 @@ describe('MatTabNavBar with a default config', () => {
489489
});
490490
});
491491

492+
describe('MatTabNavBar with enabled animations', () => {
493+
beforeEach(fakeAsync(() => {
494+
TestBed.configureTestingModule({
495+
imports: [MatTabsModule, BrowserAnimationsModule],
496+
declarations: [TabsWithCustomAnimationDuration],
497+
});
498+
499+
TestBed.compileComponents();
500+
}));
501+
502+
it('should not throw when setting an animationDuration without units', fakeAsync(() => {
503+
expect(() => {
504+
let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration);
505+
fixture.detectChanges();
506+
tick();
507+
}).not.toThrow();
508+
}));
509+
510+
it('should set appropiate css variable given a specified animationDuration', fakeAsync(() => {
511+
let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration);
512+
fixture.detectChanges();
513+
tick();
514+
515+
const tabNavBar = fixture.nativeElement.querySelector('.mat-mdc-tab-nav-bar');
516+
expect(tabNavBar.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms');
517+
}));
518+
});
519+
492520
@Component({
493521
selector: 'test-app',
494522
template: `
@@ -545,3 +573,15 @@ class TabLinkWithNgIf {
545573
class TabBarWithInactiveTabsOnInit {
546574
tabs = [0, 1, 2];
547575
}
576+
577+
@Component({
578+
template: `
579+
<nav [animationDuration]="500" mat-tab-nav-bar [tabPanel]="tabPanel">
580+
<a mat-tab-link *ngFor="let link of links">{{link}}</a>
581+
</nav>
582+
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>,
583+
`,
584+
})
585+
class TabsWithCustomAnimationDuration {
586+
links = ['First', 'Second', 'Third'];
587+
}

src/material/tabs/tab-nav-bar/tab-nav-bar.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {Directionality} from '@angular/cdk/bidi';
4545
import {ViewportRuler} from '@angular/cdk/scrolling';
4646
import {Platform} from '@angular/cdk/platform';
4747
import {MatInkBar, MatInkBarItem, mixinInkBarItem} from '../ink-bar';
48-
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
48+
import {BooleanInput, coerceBooleanProperty, NumberInput} from '@angular/cdk/coercion';
4949
import {BehaviorSubject, Subject} from 'rxjs';
5050
import {startWith, takeUntil} from 'rxjs/operators';
5151
import {SPACE} from '@angular/cdk/keycodes';
@@ -319,6 +319,7 @@ const _MatTabLinkBaseWithInkBarItem = mixinInkBarItem(_MatTabLinkBase);
319319
'[class.mat-accent]': 'color === "accent"',
320320
'[class.mat-warn]': 'color === "warn"',
321321
'[class._mat-animation-noopable]': '_animationMode === "NoopAnimations"',
322+
'[style.--mat-tab-animation-duration]': 'animationDuration',
322323
},
323324
encapsulation: ViewEncapsulation.None,
324325
// tslint:disable-next-line:validate-decorators
@@ -346,6 +347,17 @@ export class MatTabNav extends _MatTabNavBase implements AfterContentInit, After
346347
}
347348
private _stretchTabs = true;
348349

350+
@Input()
351+
get animationDuration(): string {
352+
return this._animationDuration;
353+
}
354+
355+
set animationDuration(value: NumberInput) {
356+
this._animationDuration = /^\d+$/.test(value + '') ? value + 'ms' : (value as string);
357+
}
358+
359+
private _animationDuration: string;
360+
349361
@ContentChildren(forwardRef(() => MatTabLink), {descendants: true}) _items: QueryList<MatTabLink>;
350362
@ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef;
351363
@ViewChild('tabList', {static: true}) _tabList: ElementRef;

tools/public_api_guard/material/tabs.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,9 @@ export class _MatTabLinkBase extends _MatTabLinkMixinBase implements AfterViewIn
489489
// @public
490490
export class MatTabNav extends _MatTabNavBase implements AfterContentInit, AfterViewInit {
491491
constructor(elementRef: ElementRef, dir: Directionality, ngZone: NgZone, changeDetectorRef: ChangeDetectorRef, viewportRuler: ViewportRuler, platform: Platform, animationMode?: string, defaultConfig?: MatTabsConfig);
492+
// (undocumented)
493+
get animationDuration(): string;
494+
set animationDuration(value: NumberInput);
492495
get fitInkBarToContent(): boolean;
493496
set fitInkBarToContent(v: BooleanInput);
494497
// (undocumented)
@@ -514,7 +517,7 @@ export class MatTabNav extends _MatTabNavBase implements AfterContentInit, After
514517
// (undocumented)
515518
_tabListInner: ElementRef;
516519
// (undocumented)
517-
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabNav, "[mat-tab-nav-bar]", ["matTabNavBar", "matTabNav"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; }, {}, ["_items"], ["*"], false, never>;
520+
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabNav, "[mat-tab-nav-bar]", ["matTabNavBar", "matTabNav"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; }, {}, ["_items"], ["*"], false, never>;
518521
// (undocumented)
519522
static ɵfac: i0.ɵɵFactoryDeclaration<MatTabNav, [null, { optional: true; }, null, null, null, null, { optional: true; }, { optional: true; }]>;
520523
}

0 commit comments

Comments
 (0)