Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions goldens/material/tabs/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,17 @@ export class MatTabGroup implements AfterViewInit, AfterContentInit, AfterConten
alignTabs: string | null;
_allTabs: QueryList<MatTab>;
readonly animationDone: EventEmitter<void>;
get animationDuration(): string;
set animationDuration(value: string | number);
// (undocumented)
protected _animationsDisabled(): boolean;
get animationDuration(): MatTabGroupAnimationDuration;
set animationDuration(value: MatTabGroupAnimationDuration);
ariaLabel: string;
ariaLabelledby: string;
// @deprecated
get backgroundColor(): ThemePalette;
set backgroundColor(value: ThemePalette);
// (undocumented)
protected _bodyAnimationDuration: string;
// (undocumented)
protected _bodyAnimationsDisabled(): boolean;
protected _bodyCentered(isCenter: boolean): void;
color: ThemePalette;
get contentTabIndex(): number | null;
Expand All @@ -271,6 +273,8 @@ export class MatTabGroup implements AfterViewInit, AfterContentInit, AfterConten
_getTabIndex(index: number): number;
_getTabLabelId(tab: MatTab, index: number): string;
_handleClick(tab: MatTab, tabHeader: MatTabGroupBaseHeader, index: number): void;
// (undocumented)
protected _headerAnimationDuration: string;
headerPosition: MatTabHeaderPosition;
protected _isServer: boolean;
// (undocumented)
Expand Down
2 changes: 1 addition & 1 deletion src/material/tabs/_tabs-common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ $mat-tab-animation-duration: 500ms !default;
}

.mdc-tab-indicator .mdc-tab-indicator__content {
transition-duration: var(--mat-tab-animation-duration, 250ms);
transition-duration: var(--mat-tab-header-animation-duration, 250ms);
}

.mat-mdc-tab-header-pagination {
Expand Down
2 changes: 1 addition & 1 deletion src/material/tabs/tab-body.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
.mat-tab-body-content-can-animate {
// Note: there's a 1ms delay so that transition events
// still fire even if the duration is set to zero.
transition: transform var(--mat-tab-animation-duration) 1ms cubic-bezier(0.35, 0, 0.25, 1);
transition: transform var(--mat-tab-body-animation-duration) 1ms cubic-bezier(0.35, 0, 0.25, 1);

.mat-mdc-tab-body-wrapper._mat-animation-noopable & {
transition: none;
Expand Down
4 changes: 2 additions & 2 deletions src/material/tabs/tab-group.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

<div
class="mat-mdc-tab-body-wrapper"
[class._mat-animation-noopable]="_animationsDisabled()"
[class._mat-animation-noopable]="_bodyAnimationsDisabled()"
#tabBodyWrapper>
@for (tab of _tabs; track tab;) {
<mat-tab-body role="tabpanel"
Expand All @@ -76,7 +76,7 @@
[class]="tab.bodyClass"
[content]="tab.content!"
[position]="tab.position!"
[animationDuration]="animationDuration"
[animationDuration]="_bodyAnimationDuration"
[preserveContent]="preserveContent"
(_onCentered)="_removeTabBodyWrapperHeight()"
(_onCentering)="_setTabBodyWrapperHeight($event)"
Expand Down
34 changes: 33 additions & 1 deletion src/material/tabs/tab-group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,26 @@ describe('MatTabGroup', () => {
});
});

describe('animation duration', () => {
it('should set the body and header animation duration when value is a string', () => {
const fixture = TestBed.createComponent(TabsWithCustomAnimationDuration);
fixture.detectChanges();

const tabGroup = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
expect(tabGroup.style.getPropertyValue('--mat-tab-body-animation-duration')).toBe('500ms');
expect(tabGroup.style.getPropertyValue('--mat-tab-header-animation-duration')).toBe('500ms');
});

it('should set the body and header animation duration when value is an object', () => {
const fixture = TestBed.createComponent(TabsWithObjectAnimationDuration);
fixture.detectChanges();

const tabGroup = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
expect(tabGroup.style.getPropertyValue('--mat-tab-body-animation-duration')).toBe('100ms');
expect(tabGroup.style.getPropertyValue('--mat-tab-header-animation-duration')).toBe('200ms');
});
});

describe('aria labelling', () => {
let fixture: ComponentFixture<TabGroupWithAriaInputs>;
let tab: HTMLElement;
Expand Down Expand Up @@ -1062,7 +1082,8 @@ describe('nested MatTabGroup with enabled animations', () => {
tick();

const tabGroup = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
expect(tabGroup.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms');
expect(tabGroup.style.getPropertyValue('--mat-tab-body-animation-duration')).toBe('500ms');
expect(tabGroup.style.getPropertyValue('--mat-tab-header-animation-duration')).toBe('500ms');
}));
});

Expand Down Expand Up @@ -1473,6 +1494,17 @@ class TabGroupWithIsActiveBinding {}
})
class TabsWithCustomAnimationDuration {}

@Component({
template: `
<mat-tab-group [animationDuration]="{body: '100ms', header: '200ms'}">
<mat-tab label="One">Tab one content</mat-tab>
<mat-tab label="Two">Tab two content</mat-tab>
</mat-tab-group>
`,
imports: [MatTabsModule],
})
class TabsWithObjectAnimationDuration {}

@Component({
template: `
<mat-tab-group>
Expand Down
39 changes: 30 additions & 9 deletions src/material/tabs/tab-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export interface MatTabGroupBaseHeader {
/** Possible positions for the tab header. */
export type MatTabHeaderPosition = 'above' | 'below';

/** Possible values for the animation duration of a tab group. */
export type MatTabGroupAnimationDuration =
| string
| number
| {body: string | number; header: string | number};

/** Boolean constant that determines whether the tab group supports the `backgroundColor` input */
const ENABLE_BACKGROUND_INPUT = true;

Expand Down Expand Up @@ -79,7 +85,8 @@ const ENABLE_BACKGROUND_INPUT = true;
'[class.mat-mdc-tab-group-inverted-header]': 'headerPosition === "below"',
'[class.mat-mdc-tab-group-stretch-tabs]': 'stretchTabs',
'[attr.mat-align-tabs]': 'alignTabs',
'[style.--mat-tab-animation-duration]': 'animationDuration',
'[style.--mat-tab-body-animation-duration]': '_bodyAnimationDuration',
'[style.--mat-tab-header-animation-duration]': '_headerAnimationDuration',
},
imports: [
MatTabHeader,
Expand All @@ -100,6 +107,8 @@ export class MatTabGroup
private _tabLabelSubscription = Subscription.EMPTY;
private _tabBodySubscription = Subscription.EMPTY;
private _diAnimationsDisabled = _animationsDisabled();
protected _bodyAnimationDuration!: string;
protected _headerAnimationDuration!: string;

/**
* All tabs inside the tab group. This includes tabs that belong to groups that are nested
Expand Down Expand Up @@ -170,14 +179,20 @@ export class MatTabGroup

/** Duration for the tab animation. Will be normalized to milliseconds if no units are set. */
@Input()
get animationDuration(): string {
get animationDuration(): MatTabGroupAnimationDuration {
return this._animationDuration;
}
set animationDuration(value: string | number) {
const stringValue = value + '';
this._animationDuration = /^\d+$/.test(stringValue) ? value + 'ms' : stringValue;
set animationDuration(value: MatTabGroupAnimationDuration) {
this._animationDuration = value;

if (value && typeof value === 'object') {
this._bodyAnimationDuration = normalizeDuration(value.body);
this._headerAnimationDuration = normalizeDuration(value.header);
} else {
this._headerAnimationDuration = this._bodyAnimationDuration = normalizeDuration(value);
}
}
private _animationDuration!: string;
private _animationDuration!: MatTabGroupAnimationDuration;

/**
* `tabindex` to be set on the inner element that wraps the tab content. Can be used for improved
Expand Down Expand Up @@ -577,11 +592,11 @@ export class MatTabGroup
}
}

protected _animationsDisabled(): boolean {
protected _bodyAnimationsDisabled(): boolean {
return (
this._diAnimationsDisabled ||
this.animationDuration === '0' ||
this.animationDuration === '0ms'
this._bodyAnimationDuration === '0' ||
this._bodyAnimationDuration === '0ms'
);
}
}
Expand All @@ -593,3 +608,9 @@ export class MatTabChangeEvent {
/** Reference to the currently-selected tab. */
tab!: MatTab;
}

/** Normalizes an animation duration value. */
function normalizeDuration(value: string | number): string {
const stringValue = value + '';
return /^\d+$/.test(stringValue) ? value + 'ms' : stringValue;
}
2 changes: 1 addition & 1 deletion src/material/tabs/tab-nav-bar/tab-nav-bar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ describe('MatTabNavBar with enabled animations', () => {
tick();

const tabNavBar = fixture.nativeElement.querySelector('.mat-mdc-tab-nav-bar');
expect(tabNavBar.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms');
expect(tabNavBar.style.getPropertyValue('--mat-tab-header-animation-duration')).toBe('500ms');
}));
});

Expand Down
2 changes: 1 addition & 1 deletion src/material/tabs/tab-nav-bar/tab-nav-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import {_CdkPrivateStyleLoader} from '@angular/cdk/private';
'[class.mat-accent]': 'color === "accent"',
'[class.mat-warn]': 'color === "warn"',
'[class._mat-animation-noopable]': '_animationsDisabled',
'[style.--mat-tab-animation-duration]': 'animationDuration',
'[style.--mat-tab-header-animation-duration]': 'animationDuration',
},
encapsulation: ViewEncapsulation.None,
// tslint:disable-next-line:validate-decorators
Expand Down
Loading