@@ -19,6 +19,7 @@ import {
19
19
EventEmitter ,
20
20
AfterViewInit ,
21
21
OnDestroy ,
22
+ ChangeDetectorRef ,
22
23
} from '@angular/core' ;
23
24
import { CanColor , mixinColor } from '@angular/material-experimental/mdc-core' ;
24
25
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ;
@@ -27,20 +28,12 @@ import {
27
28
MAT_PROGRESS_BAR_DEFAULT_OPTIONS ,
28
29
ProgressAnimationEnd ,
29
30
} 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' ;
38
31
39
32
// Boilerplate for applying mixins to MatProgressBar.
40
33
/** @docs -private */
41
34
const _MatProgressBarBase = mixinColor (
42
35
class {
43
- constructor ( public _elementRef : ElementRef ) { }
36
+ constructor ( public _elementRef : ElementRef < HTMLElement > ) { }
44
37
} ,
45
38
'primary' ,
46
39
) ;
@@ -57,10 +50,12 @@ export type ProgressBarMode = 'determinate' | 'indeterminate' | 'buffer' | 'quer
57
50
// set tab index to -1 so screen readers will read the aria-label
58
51
// Note: there is a known issue with JAWS that does not read progressbar aria labels on FireFox
59
52
'tabindex' : '-1' ,
60
- '[attr.aria-valuenow]' : '(mode === "indeterminate" || mode === "query" ) ? null : value' ,
53
+ '[attr.aria-valuenow]' : '_isIndeterminate( ) ? null : value' ,
61
54
'[attr.mode]' : 'mode' ,
62
55
'class' : 'mat-mdc-progress-bar mdc-linear-progress' ,
63
56
'[class._mat-animation-noopable]' : '_isNoopAnimation' ,
57
+ '[class.mdc-linear-progress--animation-ready]' : '!_isNoopAnimation' ,
58
+ '[class.mdc-linear-progress--indeterminate]' : '_isIndeterminate()' ,
64
59
} ,
65
60
inputs : [ 'color' ] ,
66
61
templateUrl : 'progress-bar.html' ,
@@ -75,20 +70,14 @@ export class MatProgressBar
75
70
constructor (
76
71
elementRef : ElementRef < HTMLElement > ,
77
72
private _ngZone : NgZone ,
78
- @ Optional ( ) dir ?: Directionality ,
73
+ private _changeDetectorRef : ChangeDetectorRef ,
79
74
@Optional ( ) @Inject ( ANIMATION_MODULE_TYPE ) public _animationMode ?: string ,
80
75
@Optional ( )
81
76
@Inject ( MAT_PROGRESS_BAR_DEFAULT_OPTIONS )
82
77
defaults ?: MatProgressBarDefaultOptions ,
83
78
) {
84
79
super ( elementRef ) ;
85
80
this . _isNoopAnimation = _animationMode === 'NoopAnimations' ;
86
- if ( dir ) {
87
- this . _dirChangeSubscription = dir . change . subscribe ( ( ) => {
88
- this . _syncFoundation ( ) ;
89
- this . _foundation ?. restartAnimation ( ) ;
90
- } ) ;
91
- }
92
81
93
82
if ( defaults ) {
94
83
if ( defaults . color ) {
@@ -99,56 +88,6 @@ export class MatProgressBar
99
88
}
100
89
}
101
90
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
-
152
91
/** Flag that indicates whether NoopAnimations mode is set to true. */
153
92
_isNoopAnimation = false ;
154
93
@@ -159,7 +98,7 @@ export class MatProgressBar
159
98
}
160
99
set value ( v : number ) {
161
100
this . _value = clamp ( v || 0 ) ;
162
- this . _syncFoundation ( ) ;
101
+ this . _changeDetectorRef . markForCheck ( ) ;
163
102
}
164
103
private _value = 0 ;
165
104
@@ -170,26 +109,17 @@ export class MatProgressBar
170
109
}
171
110
set bufferValue ( v : number ) {
172
111
this . _bufferValue = clamp ( v || 0 ) ;
173
- this . _syncFoundation ( ) ;
112
+ this . _changeDetectorRef . markForCheck ( ) ;
174
113
}
175
114
private _bufferValue = 0 ;
176
115
177
- private _primaryBar : HTMLElement ;
178
- private _bufferBar : HTMLElement ;
179
-
180
116
/**
181
117
* Event emitted when animation of the primary progress bar completes. This event will not
182
118
* be emitted when animations are disabled, nor will it be emitted for modes with continuous
183
119
* animations (indeterminate and query).
184
120
*/
185
121
@Output ( ) readonly animationEnd = new EventEmitter < ProgressAnimationEnd > ( ) ;
186
122
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
-
193
123
/**
194
124
* Mode of the progress bar.
195
125
*
@@ -205,63 +135,51 @@ export class MatProgressBar
205
135
// Note that we don't technically need a getter and a setter here,
206
136
// but we use it to match the behavior of the existing mat-progress-bar.
207
137
this . _mode = value ;
208
- this . _syncFoundation ( ) ;
138
+ this . _changeDetectorRef . markForCheck ( ) ;
209
139
}
210
140
private _mode : ProgressBarMode = 'determinate' ;
211
141
212
142
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
-
222
143
// Run outside angular so change detection didn't get triggered on every transition end
223
144
// instead only on the animation that we care about (primary value bar's transitionend)
224
145
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 ) ;
238
147
} ) ;
239
148
}
240
149
241
150
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 ) ;
247
152
}
248
153
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
+ }
252
158
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
+ }
256
163
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
+ }
259
168
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 ;
263
177
}
264
- }
178
+
179
+ if ( this . mode === 'determinate' || this . mode === 'buffer' ) {
180
+ this . _ngZone . run ( ( ) => this . animationEnd . next ( { value : this . value } ) ) ;
181
+ }
182
+ } ;
265
183
}
266
184
267
185
/** Clamps a value to be between two numbers, by default 0 and 100. */
0 commit comments