mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(progress-bar): add transitionend output for value animation (#12409)
This commit is contained in:
committed by
Jeremy Elbourn
parent
f82a94bd51
commit
6a1a70753e
@@ -8,12 +8,15 @@
|
||||
|
||||
<div class="demo-progress-bar-controls">
|
||||
<span>Value: {{determinateProgressValue}}</span>
|
||||
<br/>
|
||||
<span>Last animation complete value: {{determinateAnimationEndValue}}</span>
|
||||
<button mat-raised-button (click)="stepDeterminateProgressVal(10)">Increase</button>
|
||||
<button mat-raised-button (click)="stepDeterminateProgressVal(-10)">Decrease</button>
|
||||
</div>
|
||||
|
||||
<div class="demo-progress-bar-container">
|
||||
<mat-progress-bar mode="determinate" [value]="determinateProgressValue" [color]="color">
|
||||
<mat-progress-bar mode="determinate" [value]="determinateProgressValue" [color]="color"
|
||||
(animationEnd)="determinateAnimationEndValue = $event.value">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +24,8 @@
|
||||
|
||||
<div class="demo-progress-bar-controls">
|
||||
<span>Value: {{bufferProgressValue}}</span>
|
||||
<br/>
|
||||
<span>Last animation complete value: {{bufferAnimationEndValue}}</span>
|
||||
<button mat-raised-button (click)="stepBufferProgressVal(10)">Increase</button>
|
||||
<button mat-raised-button (click)="stepBufferProgressVal(-10)">Decrease</button>
|
||||
<span class="demo-progress-bar-spacer"></span>
|
||||
@@ -31,7 +36,8 @@
|
||||
|
||||
<div class="demo-progress-bar-container">
|
||||
<mat-progress-bar [value]="bufferProgressValue" [bufferValue]="bufferBufferValue" mode="buffer"
|
||||
[color]="color"></mat-progress-bar>
|
||||
[color]="color" (animationEnd)="bufferAnimationEndValue = $event.value">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
|
||||
<h1>Indeterminate</h1>
|
||||
|
||||
@@ -20,6 +20,8 @@ import {Component} from '@angular/core';
|
||||
export class ProgressBarDemo {
|
||||
color: string = 'primary';
|
||||
determinateProgressValue: number = 30;
|
||||
determinateAnimationEndValue: number;
|
||||
bufferAnimationEndValue: number;
|
||||
bufferProgressValue: number = 30;
|
||||
bufferBufferValue: number = 40;
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
<rect [attr.fill]="_rectangleFillValue" width="100%" height="100%"/>
|
||||
</svg>
|
||||
<div class="mat-progress-bar-buffer mat-progress-bar-element" [ngStyle]="_bufferTransform()"></div>
|
||||
<div class="mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element" [ngStyle]="_primaryTransform()"></div>
|
||||
<div class="mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element" [ngStyle]="_primaryTransform()" #primaryValueBar></div>
|
||||
<div class="mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element"></div>
|
||||
|
||||
|
Before Width: | Height: | Size: 796 B After Width: | Height: | Size: 813 B |
@@ -1,15 +1,19 @@
|
||||
import {TestBed, ComponentFixture} from '@angular/core/testing';
|
||||
import {Component, Type} from '@angular/core';
|
||||
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
|
||||
import {Component, DebugElement, Type} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {dispatchFakeEvent} from '@angular/cdk/testing';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MatProgressBarModule, MAT_PROGRESS_BAR_LOCATION} from './index';
|
||||
import {MatProgressBar} from './progress-bar';
|
||||
|
||||
|
||||
describe('MatProgressBar', () => {
|
||||
let fakePath = '/fake-path';
|
||||
|
||||
function createComponent<T>(componentType: Type<T>): ComponentFixture<T> {
|
||||
function createComponent<T>(componentType: Type<T>,
|
||||
imports?: Array<Type<{}>>): ComponentFixture<T> {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatProgressBarModule],
|
||||
imports: imports || [MatProgressBarModule],
|
||||
declarations: [componentType],
|
||||
providers: [{
|
||||
provide: MAT_PROGRESS_BAR_LOCATION,
|
||||
@@ -20,119 +24,210 @@ describe('MatProgressBar', () => {
|
||||
return TestBed.createComponent<T>(componentType);
|
||||
}
|
||||
|
||||
describe('basic progress-bar', () => {
|
||||
it('should apply a mode of "determinate" if no mode is provided.', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
describe('with animation', () => {
|
||||
describe('basic progress-bar', () => {
|
||||
it('should apply a mode of "determinate" if no mode is provided.', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
expect(progressElement.componentInstance.mode).toBe('determinate');
|
||||
});
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
expect(progressElement.componentInstance.mode).toBe('determinate');
|
||||
it('should define default values for value and bufferValue attributes', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
expect(progressElement.componentInstance.value).toBe(0);
|
||||
expect(progressElement.componentInstance.bufferValue).toBe(0);
|
||||
});
|
||||
|
||||
it('should clamp value and bufferValue between 0 and 100', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
const progressComponent = progressElement.componentInstance;
|
||||
|
||||
progressComponent.value = 50;
|
||||
expect(progressComponent.value).toBe(50);
|
||||
|
||||
progressComponent.value = 999;
|
||||
expect(progressComponent.value).toBe(100);
|
||||
|
||||
progressComponent.value = -10;
|
||||
expect(progressComponent.value).toBe(0);
|
||||
|
||||
progressComponent.bufferValue = -29;
|
||||
expect(progressComponent.bufferValue).toBe(0);
|
||||
|
||||
progressComponent.bufferValue = 9;
|
||||
expect(progressComponent.bufferValue).toBe(9);
|
||||
|
||||
progressComponent.bufferValue = 1320;
|
||||
expect(progressComponent.bufferValue).toBe(100);
|
||||
});
|
||||
|
||||
it('should return the transform attribute for bufferValue and mode', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
const progressComponent = progressElement.componentInstance;
|
||||
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.value = 40;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.4)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.value = 35;
|
||||
progressComponent.bufferValue = 55;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.35)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.mode = 'buffer';
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.35)'});
|
||||
expect(progressComponent._bufferTransform()).toEqual({transform: 'scaleX(0.55)'});
|
||||
|
||||
|
||||
progressComponent.value = 60;
|
||||
progressComponent.bufferValue = 60;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.6)'});
|
||||
expect(progressComponent._bufferTransform()).toEqual({transform: 'scaleX(0.6)'});
|
||||
});
|
||||
|
||||
it('should prefix SVG references with the current path', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const rect = fixture.debugElement.query(By.css('rect')).nativeElement;
|
||||
expect(rect.getAttribute('fill')).toMatch(/^url\(['"]?\/fake-path#.*['"]?\)$/);
|
||||
});
|
||||
|
||||
it('should account for location hash when prefixing the SVG references', () => {
|
||||
fakePath = '/fake-path#anchor';
|
||||
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const rect = fixture.debugElement.query(By.css('rect')).nativeElement;
|
||||
expect(rect.getAttribute('fill')).not.toContain('#anchor#');
|
||||
});
|
||||
|
||||
it('should not be able to tab into the underlying SVG element', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const svg = fixture.debugElement.query(By.css('svg')).nativeElement;
|
||||
expect(svg.getAttribute('focusable')).toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
it('should define default values for value and bufferValue attributes', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
describe('animation trigger on determinate setting', () => {
|
||||
let fixture: ComponentFixture<BasicProgressBar>;
|
||||
let progressComponent: MatProgressBar;
|
||||
let primaryValueBar: DebugElement;
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
expect(progressElement.componentInstance.value).toBe(0);
|
||||
expect(progressElement.componentInstance.bufferValue).toBe(0);
|
||||
beforeEach(() => {
|
||||
fixture = createComponent(BasicProgressBar);
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
progressComponent = progressElement.componentInstance;
|
||||
primaryValueBar = progressElement.query(By.css('.mat-progress-bar-primary'));
|
||||
});
|
||||
|
||||
it('should trigger output event on primary value bar animation end', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(progressComponent.animationEnd, 'next');
|
||||
|
||||
progressComponent.value = 40;
|
||||
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
|
||||
|
||||
// On animation end, output should be emitted.
|
||||
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
|
||||
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({ value: 40 });
|
||||
});
|
||||
});
|
||||
|
||||
it('should clamp value and bufferValue between 0 and 100', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
describe('animation trigger on buffer setting', () => {
|
||||
let fixture: ComponentFixture<BufferProgressBar>;
|
||||
let progressComponent: MatProgressBar;
|
||||
let primaryValueBar: DebugElement;
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
const progressComponent = progressElement.componentInstance;
|
||||
beforeEach(() => {
|
||||
fixture = createComponent(BufferProgressBar);
|
||||
|
||||
progressComponent.value = 50;
|
||||
expect(progressComponent.value).toBe(50);
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
progressComponent = progressElement.componentInstance;
|
||||
primaryValueBar = progressElement.query(By.css('.mat-progress-bar-primary'));
|
||||
});
|
||||
|
||||
progressComponent.value = 999;
|
||||
expect(progressComponent.value).toBe(100);
|
||||
it('should bind on transitionend eventListener on primaryBarValue', () => {
|
||||
spyOn(primaryValueBar.nativeElement, 'addEventListener');
|
||||
fixture.detectChanges();
|
||||
|
||||
progressComponent.value = -10;
|
||||
expect(progressComponent.value).toBe(0);
|
||||
expect(primaryValueBar.nativeElement.addEventListener).toHaveBeenCalled();
|
||||
expect(primaryValueBar.nativeElement.addEventListener
|
||||
.calls.mostRecent().args[0]).toBe('transitionend');
|
||||
});
|
||||
|
||||
progressComponent.bufferValue = -29;
|
||||
expect(progressComponent.bufferValue).toBe(0);
|
||||
it('should trigger output event on primary value bar animation end', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(progressComponent.animationEnd, 'next');
|
||||
|
||||
progressComponent.bufferValue = 9;
|
||||
expect(progressComponent.bufferValue).toBe(9);
|
||||
progressComponent.value = 40;
|
||||
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
|
||||
|
||||
progressComponent.bufferValue = 1320;
|
||||
expect(progressComponent.bufferValue).toBe(100);
|
||||
});
|
||||
// On animation end, output should be emitted.
|
||||
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
|
||||
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({ value: 40 });
|
||||
});
|
||||
|
||||
it('should return the transform attribute for bufferValue and mode', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
it('should trigger output event with value not bufferValue', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(progressComponent.animationEnd, 'next');
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
const progressComponent = progressElement.componentInstance;
|
||||
progressComponent.value = 40;
|
||||
progressComponent.bufferValue = 70;
|
||||
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
|
||||
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.value = 40;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.4)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.value = 35;
|
||||
progressComponent.bufferValue = 55;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.35)'});
|
||||
expect(progressComponent._bufferTransform()).toBe(undefined);
|
||||
|
||||
progressComponent.mode = 'buffer';
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.35)'});
|
||||
expect(progressComponent._bufferTransform()).toEqual({transform: 'scaleX(0.55)'});
|
||||
|
||||
|
||||
progressComponent.value = 60;
|
||||
progressComponent.bufferValue = 60;
|
||||
expect(progressComponent._primaryTransform()).toEqual({transform: 'scaleX(0.6)'});
|
||||
expect(progressComponent._bufferTransform()).toEqual({transform: 'scaleX(0.6)'});
|
||||
});
|
||||
|
||||
it('should prefix SVG references with the current path', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const rect = fixture.debugElement.query(By.css('rect')).nativeElement;
|
||||
expect(rect.getAttribute('fill')).toMatch(/^url\(['"]?\/fake-path#.*['"]?\)$/);
|
||||
});
|
||||
|
||||
it('should account for location hash when prefixing the SVG references', () => {
|
||||
fakePath = '/fake-path#anchor';
|
||||
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const rect = fixture.debugElement.query(By.css('rect')).nativeElement;
|
||||
expect(rect.getAttribute('fill')).not.toContain('#anchor#');
|
||||
});
|
||||
|
||||
it('should not be able to tab into the underlying SVG element', () => {
|
||||
const fixture = createComponent(BasicProgressBar);
|
||||
fixture.detectChanges();
|
||||
|
||||
const svg = fixture.debugElement.query(By.css('svg')).nativeElement;
|
||||
expect(svg.getAttribute('focusable')).toBe('false');
|
||||
// On animation end, output should be emitted.
|
||||
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
|
||||
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({ value: 40 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buffer progress-bar', () => {
|
||||
it('should not modify the mode if a valid mode is provided.', () => {
|
||||
const fixture = createComponent(BufferProgressBar);
|
||||
describe('With NoopAnimations', () => {
|
||||
let progressComponent: MatProgressBar;
|
||||
let primaryValueBar: DebugElement;
|
||||
let fixture: ComponentFixture<BasicProgressBar>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = createComponent(BasicProgressBar, [MatProgressBarModule, NoopAnimationsModule]);
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
progressComponent = progressElement.componentInstance;
|
||||
primaryValueBar = progressElement.query(By.css('.mat-progress-bar-primary'));
|
||||
}));
|
||||
|
||||
it('should not bind transition end listener', () => {
|
||||
spyOn(primaryValueBar.nativeElement, 'addEventListener');
|
||||
fixture.detectChanges();
|
||||
|
||||
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'));
|
||||
expect(progressElement.componentInstance.mode).toBe('buffer');
|
||||
expect(primaryValueBar.nativeElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger the animationEnd output on value set', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(progressComponent.animationEnd, 'next');
|
||||
|
||||
progressComponent.value = 40;
|
||||
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({ value: 40 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@Component({template: '<mat-progress-bar></mat-progress-bar>'})
|
||||
class BasicProgressBar { }
|
||||
|
||||
|
||||
@@ -11,11 +11,19 @@ import {
|
||||
ElementRef,
|
||||
Inject,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
Optional,
|
||||
NgZone,
|
||||
ViewEncapsulation,
|
||||
AfterViewInit,
|
||||
ViewChild,
|
||||
OnDestroy,
|
||||
InjectionToken,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import {fromEvent, Subscription} from 'rxjs';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
|
||||
import {CanColor, mixinColor} from '@angular/material/core';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
@@ -29,6 +37,11 @@ export class MatProgressBarBase {
|
||||
constructor(public _elementRef: ElementRef) { }
|
||||
}
|
||||
|
||||
/** Last animation end data. */
|
||||
export interface ProgressAnimationEnd {
|
||||
value: number;
|
||||
}
|
||||
|
||||
export const _MatProgressBarMixinBase = mixinColor(MatProgressBarBase, 'primary');
|
||||
|
||||
/**
|
||||
@@ -74,7 +87,7 @@ let progressbarId = 0;
|
||||
'[attr.aria-valuenow]': 'value',
|
||||
'[attr.mode]': 'mode',
|
||||
'class': 'mat-progress-bar',
|
||||
'[class._mat-animation-noopable]': `_animationMode === 'NoopAnimations'`,
|
||||
'[class._mat-animation-noopable]': `_isNoopAnimation`,
|
||||
},
|
||||
inputs: ['color'],
|
||||
templateUrl: 'progress-bar.html',
|
||||
@@ -82,8 +95,9 @@ let progressbarId = 0;
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class MatProgressBar extends _MatProgressBarMixinBase implements CanColor {
|
||||
constructor(public _elementRef: ElementRef,
|
||||
export class MatProgressBar extends _MatProgressBarMixinBase implements CanColor,
|
||||
AfterViewInit, OnDestroy {
|
||||
constructor(public _elementRef: ElementRef, private _ngZone: NgZone,
|
||||
@Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string,
|
||||
/**
|
||||
* @deprecated `location` parameter to be made required.
|
||||
@@ -94,18 +108,30 @@ export class MatProgressBar extends _MatProgressBarMixinBase implements CanColor
|
||||
|
||||
// We need to prefix the SVG reference with the current path, otherwise they won't work
|
||||
// in Safari if the page has a `<base>` tag. Note that we need quotes inside the `url()`,
|
||||
// because named route URLs can contain parentheses (see #12338). Also we don't use
|
||||
// `Location` from `@angular/common` since we can't tell the difference between whether
|
||||
|
||||
// because named route URLs can contain parentheses (see #12338). Also we don't use since
|
||||
// we can't tell the difference between whether
|
||||
// the consumer is using the hash location strategy or not, because `Location` normalizes
|
||||
// both `/#/foo/bar` and `/foo/bar` to the same thing.
|
||||
const path = location && location.pathname ? location.pathname.split('#')[0] : '';
|
||||
this._rectangleFillValue = `url('${path}#${this.progressbarId}')`;
|
||||
this._isNoopAnimation = _animationMode === 'NoopAnimations';
|
||||
}
|
||||
|
||||
/** Flag that indicates whether NoopAnimations mode is set to true. */
|
||||
_isNoopAnimation = false;
|
||||
|
||||
/** Value of the progress bar. Defaults to zero. Mirrored to aria-valuenow. */
|
||||
@Input()
|
||||
get value(): number { return this._value; }
|
||||
set value(v: number) { this._value = clamp(v || 0); }
|
||||
set value(v: number) {
|
||||
this._value = clamp(v || 0);
|
||||
|
||||
// When noop animation is set to true, trigger animationEnd directly.
|
||||
if (this._isNoopAnimation) {
|
||||
this.emitAnimationEnd();
|
||||
}
|
||||
}
|
||||
private _value: number = 0;
|
||||
|
||||
/** Buffer value of the progress bar. Defaults to zero. */
|
||||
@@ -114,6 +140,18 @@ export class MatProgressBar extends _MatProgressBarMixinBase implements CanColor
|
||||
set bufferValue(v: number) { this._bufferValue = clamp(v || 0); }
|
||||
private _bufferValue: number = 0;
|
||||
|
||||
@ViewChild('primaryValueBar') _primaryValueBar: ElementRef;
|
||||
|
||||
/**
|
||||
* Event emitted when animation of the primary progress bar completes. This event will not
|
||||
* be emitted when animations are disabled, nor will it be emitted for modes with continuous
|
||||
* animations (indeterminate and query).
|
||||
*/
|
||||
@Output() animationEnd = new EventEmitter<ProgressAnimationEnd>();
|
||||
|
||||
/** Reference to animation end subscription to be unsubscribed on destroy. */
|
||||
private _animationEndSubscription: Subscription = Subscription.EMPTY;
|
||||
|
||||
/**
|
||||
* Mode of the progress bar.
|
||||
*
|
||||
@@ -145,6 +183,31 @@ export class MatProgressBar extends _MatProgressBarMixinBase implements CanColor
|
||||
return {transform: `scaleX(${scale})`};
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (!this._isNoopAnimation) {
|
||||
// Run outside angular so change detection didn't get triggered on every transition end
|
||||
// instead only on the animation that we care about (primary value bar's transitionend)
|
||||
this._ngZone.runOutsideAngular((() => {
|
||||
this._animationEndSubscription =
|
||||
fromEvent(this._primaryValueBar.nativeElement, 'transitionend')
|
||||
.pipe(filter(((e: TransitionEvent) =>
|
||||
e.target === this._primaryValueBar.nativeElement)))
|
||||
.subscribe(_ => this._ngZone.run(() => this.emitAnimationEnd()));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._animationEndSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
/** Emit an animationEnd event if in determinate or buffer mode. */
|
||||
private emitAnimationEnd(): void {
|
||||
if (this.mode === 'determinate' || this.mode === 'buffer') {
|
||||
this.animationEnd.next({value: this.value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Clamps a value to be between two numbers, by default 0 and 100. */
|
||||
|
||||
Reference in New Issue
Block a user