@@ -19,6 +19,7 @@ import {
1919 EventEmitter ,
2020 AfterViewInit ,
2121 OnDestroy ,
22+ ChangeDetectorRef ,
2223} from '@angular/core' ;
2324import { CanColor , mixinColor } from '@angular/material-experimental/mdc-core' ;
2425import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ;
@@ -27,20 +28,12 @@ import {
2728 MAT_PROGRESS_BAR_DEFAULT_OPTIONS ,
2829 ProgressAnimationEnd ,
2930} from '@angular/material/progress-bar' ;
30- import {
31- MDCLinearProgressAdapter ,
32- MDCLinearProgressFoundation ,
33- WithMDCResizeObserver ,
34- } from '@material/linear-progress' ;
35- import { Subscription , fromEvent , Observable } from 'rxjs' ;
36- import { filter } from 'rxjs/operators' ;
37- import { Directionality } from '@angular/cdk/bidi' ;
3831
3932// Boilerplate for applying mixins to MatProgressBar.
4033/** @docs -private */
4134const _MatProgressBarBase = mixinColor (
4235 class {
43- constructor ( public _elementRef : ElementRef ) { }
36+ constructor ( public _elementRef : ElementRef < HTMLElement > ) { }
4437 } ,
4538 'primary' ,
4639) ;
@@ -57,10 +50,12 @@ export type ProgressBarMode = 'determinate' | 'indeterminate' | 'buffer' | 'quer
5750 // set tab index to -1 so screen readers will read the aria-label
5851 // Note: there is a known issue with JAWS that does not read progressbar aria labels on FireFox
5952 'tabindex' : '-1' ,
60- '[attr.aria-valuenow]' : '(mode === "indeterminate" || mode === "query" ) ? null : value' ,
53+ '[attr.aria-valuenow]' : '_isIndeterminate( ) ? null : value' ,
6154 '[attr.mode]' : 'mode' ,
6255 'class' : 'mat-mdc-progress-bar mdc-linear-progress' ,
6356 '[class._mat-animation-noopable]' : '_isNoopAnimation' ,
57+ '[class.mdc-linear-progress--animation-ready]' : '!_isNoopAnimation' ,
58+ '[class.mdc-linear-progress--indeterminate]' : '_isIndeterminate()' ,
6459 } ,
6560 inputs : [ 'color' ] ,
6661 templateUrl : 'progress-bar.html' ,
@@ -75,20 +70,14 @@ export class MatProgressBar
7570 constructor (
7671 elementRef : ElementRef < HTMLElement > ,
7772 private _ngZone : NgZone ,
78- @ Optional ( ) dir ?: Directionality ,
73+ private _changeDetectorRef : ChangeDetectorRef ,
7974 @Optional ( ) @Inject ( ANIMATION_MODULE_TYPE ) public _animationMode ?: string ,
8075 @Optional ( )
8176 @Inject ( MAT_PROGRESS_BAR_DEFAULT_OPTIONS )
8277 defaults ?: MatProgressBarDefaultOptions ,
8378 ) {
8479 super ( elementRef ) ;
8580 this . _isNoopAnimation = _animationMode === 'NoopAnimations' ;
86- if ( dir ) {
87- this . _dirChangeSubscription = dir . change . subscribe ( ( ) => {
88- this . _syncFoundation ( ) ;
89- this . _foundation ?. restartAnimation ( ) ;
90- } ) ;
91- }
9281
9382 if ( defaults ) {
9483 if ( defaults . color ) {
@@ -99,56 +88,6 @@ export class MatProgressBar
9988 }
10089 }
10190
102- /** Implements all of the logic of the MDC progress bar. */
103- private _foundation : MDCLinearProgressFoundation | undefined ;
104-
105- /** Adapter used by MDC to interact with the DOM. */
106- private _adapter : MDCLinearProgressAdapter = {
107- addClass : ( className : string ) => this . _elementRef . nativeElement . classList . add ( className ) ,
108- forceLayout : ( ) => this . _elementRef . nativeElement . offsetWidth ,
109- removeAttribute : ( name : string ) => this . _elementRef . nativeElement . removeAttribute ( name ) ,
110- setAttribute : ( name : string , value : string ) => {
111- if ( name !== 'aria-valuenow' ) {
112- this . _elementRef . nativeElement . setAttribute ( name , value ) ;
113- }
114- } ,
115- hasClass : ( className : string ) => this . _elementRef . nativeElement . classList . contains ( className ) ,
116- removeClass : ( className : string ) => this . _elementRef . nativeElement . classList . remove ( className ) ,
117- setPrimaryBarStyle : ( styleProperty : string , value : string ) => {
118- ( this . _primaryBar . style as any ) [ styleProperty ] = value ;
119- } ,
120- setBufferBarStyle : ( styleProperty : string , value : string ) => {
121- ( this . _bufferBar . style as any ) [ styleProperty ] = value ;
122- } ,
123- setStyle : ( styleProperty : string , value : string ) => {
124- ( this . _elementRef . nativeElement . style as any ) [ styleProperty ] = value ;
125- } ,
126- getWidth : ( ) => this . _elementRef . nativeElement . offsetWidth ,
127- attachResizeObserver : callback => {
128- const resizeObserverConstructor =
129- typeof window !== 'undefined' &&
130- ( window as unknown as WithMDCResizeObserver ) . ResizeObserver ;
131-
132- if ( resizeObserverConstructor ) {
133- return this . _ngZone . runOutsideAngular ( ( ) => {
134- const observer = new resizeObserverConstructor ( callback ) ;
135-
136- // Internal client users found production errors where `observe` was not a function
137- // on the constructed `observer`. This should not happen, but adding this check for this
138- // edge case.
139- if ( typeof observer . observe === 'function' ) {
140- observer . observe ( this . _elementRef . nativeElement ) ;
141- return observer ;
142- }
143-
144- return null ;
145- } ) ;
146- }
147-
148- return null ;
149- } ,
150- } ;
151-
15291 /** Flag that indicates whether NoopAnimations mode is set to true. */
15392 _isNoopAnimation = false ;
15493
@@ -159,7 +98,7 @@ export class MatProgressBar
15998 }
16099 set value ( v : number ) {
161100 this . _value = clamp ( v || 0 ) ;
162- this . _syncFoundation ( ) ;
101+ this . _changeDetectorRef . markForCheck ( ) ;
163102 }
164103 private _value = 0 ;
165104
@@ -170,26 +109,17 @@ export class MatProgressBar
170109 }
171110 set bufferValue ( v : number ) {
172111 this . _bufferValue = clamp ( v || 0 ) ;
173- this . _syncFoundation ( ) ;
112+ this . _changeDetectorRef . markForCheck ( ) ;
174113 }
175114 private _bufferValue = 0 ;
176115
177- private _primaryBar : HTMLElement ;
178- private _bufferBar : HTMLElement ;
179-
180116 /**
181117 * Event emitted when animation of the primary progress bar completes. This event will not
182118 * be emitted when animations are disabled, nor will it be emitted for modes with continuous
183119 * animations (indeterminate and query).
184120 */
185121 @Output ( ) readonly animationEnd = new EventEmitter < ProgressAnimationEnd > ( ) ;
186122
187- /** Reference to animation end subscription to be unsubscribed on destroy. */
188- private _animationEndSubscription = Subscription . EMPTY ;
189-
190- /** Subscription to when the layout direction changes. */
191- private _dirChangeSubscription = Subscription . EMPTY ;
192-
193123 /**
194124 * Mode of the progress bar.
195125 *
@@ -205,63 +135,51 @@ export class MatProgressBar
205135 // Note that we don't technically need a getter and a setter here,
206136 // but we use it to match the behavior of the existing mat-progress-bar.
207137 this . _mode = value ;
208- this . _syncFoundation ( ) ;
138+ this . _changeDetectorRef . markForCheck ( ) ;
209139 }
210140 private _mode : ProgressBarMode = 'determinate' ;
211141
212142 ngAfterViewInit ( ) {
213- const element = this . _elementRef . nativeElement ;
214-
215- this . _primaryBar = element . querySelector ( '.mdc-linear-progress__primary-bar' ) as HTMLElement ;
216- this . _bufferBar = element . querySelector ( '.mdc-linear-progress__buffer-bar' ) as HTMLElement ;
217-
218- this . _foundation = new MDCLinearProgressFoundation ( this . _adapter ) ;
219- this . _foundation . init ( ) ;
220- this . _syncFoundation ( ) ;
221-
222143 // Run outside angular so change detection didn't get triggered on every transition end
223144 // instead only on the animation that we care about (primary value bar's transitionend)
224145 this . _ngZone . runOutsideAngular ( ( ) => {
225- this . _animationEndSubscription = (
226- fromEvent ( this . _primaryBar , 'transitionend' ) as Observable < TransitionEvent >
227- )
228- . pipe ( filter ( ( e : TransitionEvent ) => e . target === this . _primaryBar ) )
229- . subscribe ( ( ) => {
230- if ( this . animationEnd . observers . length === 0 ) {
231- return ;
232- }
233-
234- if ( this . mode === 'determinate' || this . mode === 'buffer' ) {
235- this . _ngZone . run ( ( ) => this . animationEnd . next ( { value : this . value } ) ) ;
236- }
237- } ) ;
146+ this . _elementRef . nativeElement . addEventListener ( 'transitionend' , this . _transitionendHandler ) ;
238147 } ) ;
239148 }
240149
241150 ngOnDestroy ( ) {
242- if ( this . _foundation ) {
243- this . _foundation . destroy ( ) ;
244- }
245- this . _animationEndSubscription . unsubscribe ( ) ;
246- this . _dirChangeSubscription . unsubscribe ( ) ;
151+ this . _elementRef . nativeElement . removeEventListener ( 'transitionend' , this . _transitionendHandler ) ;
247152 }
248153
249- /** Syncs the state of the progress bar with the MDC foundation. */
250- private _syncFoundation ( ) {
251- const foundation = this . _foundation ;
154+ /** Gets the transform style that should be applied to the primary bar. */
155+ _getPrimaryBarTransform ( ) : string {
156+ return `scaleX(${ this . _isIndeterminate ( ) ? 1 : this . value / 100 } )` ;
157+ }
252158
253- if ( foundation ) {
254- const mode = this . mode ;
255- foundation . setDeterminate ( mode !== 'indeterminate' && mode !== 'query' ) ;
159+ /** Gets the `flex-basis` value that should be applied to the buffer bar. */
160+ _getBufferBarFlexBasis ( ) : string {
161+ return `${ this . mode === 'buffer' ? this . bufferValue : 100 } %` ;
162+ }
256163
257- // Divide by 100 because MDC deals with values between 0 and 1.
258- foundation . setProgress ( this . value / 100 ) ;
164+ /** Returns whether the progress bar is indeterminate. */
165+ _isIndeterminate ( ) : boolean {
166+ return this . mode === 'indeterminate' || this . mode === 'query' ;
167+ }
259168
260- if ( mode === 'buffer' ) {
261- foundation . setBuffer ( this . bufferValue / 100 ) ;
262- }
169+ /** Event handler for `transitionend` events. */
170+ private _transitionendHandler = ( event : TransitionEvent ) => {
171+ if (
172+ this . animationEnd . observers . length === 0 ||
173+ ! event . target ||
174+ ! ( event . target as HTMLElement ) . classList . contains ( 'mdc-linear-progress__primary-bar' )
175+ ) {
176+ return ;
263177 }
264- }
178+
179+ if ( this . mode === 'determinate' || this . mode === 'buffer' ) {
180+ this . _ngZone . run ( ( ) => this . animationEnd . next ( { value : this . value } ) ) ;
181+ }
182+ } ;
265183}
266184
267185/** Clamps a value to be between two numbers, by default 0 and 100. */
0 commit comments