@@ -124,19 +124,24 @@ const INDETERMINATE_ANIMATION_TEMPLATE = `
124
124
encapsulation : ViewEncapsulation . None ,
125
125
} )
126
126
export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements CanColor {
127
-
128
127
private _value = 0 ;
129
128
private _strokeWidth : number ;
130
129
private _fallbackAnimation = false ;
131
130
132
- /** Tracks diameters of existing instances to de-dupe generated styles (default d = 100) */
133
- private static _diameters = new Set < number > ( [ BASE_SIZE ] ) ;
131
+ /**
132
+ * Element to which we should add the generated style tags for the indeterminate animation.
133
+ * For most elements this is the document, but for the ones in the Shadow DOM we need to
134
+ * use the shadow root.
135
+ */
136
+ private _styleRoot : Node ;
134
137
135
138
/**
136
- * Used for storing all of the generated keyframe animations.
137
- * @dynamic
139
+ * Tracks diameters of existing instances to de-dupe generated styles (default d = 100).
140
+ * We need to keep track of which elements the diameters were attached to, because for
141
+ * elements in the Shadow DOM the style tags are attached to the shadow root, rather
142
+ * than the document head.
138
143
*/
139
- private static _styleTag : HTMLStyleElement | null = null ;
144
+ private static _diameters = new WeakMap < Node , Set < number > > ( ) ;
140
145
141
146
/** Whether the _mat-animation-noopable class should be applied, disabling animations. */
142
147
_noopAnimations : boolean ;
@@ -147,8 +152,13 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
147
152
set diameter ( size : number ) {
148
153
this . _diameter = coerceNumberProperty ( size ) ;
149
154
150
- if ( ! this . _fallbackAnimation && ! MatProgressSpinner . _diameters . has ( this . _diameter ) ) {
151
- this . _attachStyleNode ( ) ;
155
+ if ( ! this . _fallbackAnimation ) {
156
+ const trackedDiameters = MatProgressSpinner . _diameters ;
157
+ const diametersForElement = trackedDiameters . get ( this . _styleRoot ) ;
158
+
159
+ if ( ! diametersForElement || ! diametersForElement . has ( this . _diameter ) ) {
160
+ this . _attachStyleNode ( ) ;
161
+ }
152
162
}
153
163
}
154
164
private _diameter = BASE_SIZE ;
@@ -182,6 +192,16 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
182
192
defaults ?: MatProgressSpinnerDefaultOptions ) {
183
193
184
194
super ( _elementRef ) ;
195
+
196
+ const trackedDiameters = MatProgressSpinner . _diameters ;
197
+
198
+ // The base size is already inserted via the component's structural styles. We still
199
+ // need to track it so we don't end up adding the same styles again.
200
+ if ( ! trackedDiameters . has ( _document . head ) ) {
201
+ trackedDiameters . set ( _document . head , new Set < number > ( [ BASE_SIZE ] ) ) ;
202
+ }
203
+
204
+ this . _styleRoot = getShadowRoot ( _elementRef . nativeElement , _document ) || _document . head ;
185
205
this . _fallbackAnimation = platform . EDGE || platform . TRIDENT ;
186
206
this . _noopAnimations = animationMode === 'NoopAnimations' &&
187
207
( ! ! defaults && ! defaults . _forceAnimations ) ;
@@ -241,19 +261,22 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
241
261
242
262
/** Dynamically generates a style tag containing the correct animation for this diameter. */
243
263
private _attachStyleNode ( ) : void {
244
- let styleTag = MatProgressSpinner . _styleTag ;
245
-
246
- if ( ! styleTag ) {
247
- styleTag = this . _document . createElement ( 'style' ) ;
248
- this . _document . head . appendChild ( styleTag ) ;
249
- MatProgressSpinner . _styleTag = styleTag ;
264
+ const styleTag : HTMLStyleElement = this . _document . createElement ( 'style' ) ;
265
+ const styleRoot = this . _styleRoot ;
266
+ const currentDiameter = this . _diameter ;
267
+ const diameters = MatProgressSpinner . _diameters ;
268
+ let diametersForElement = diameters . get ( styleRoot ) ;
269
+
270
+ styleTag . setAttribute ( 'mat-spinner-animation' , currentDiameter + '' ) ;
271
+ styleTag . textContent = this . _getAnimationText ( ) ;
272
+ styleRoot . appendChild ( styleTag ) ;
273
+
274
+ if ( ! diametersForElement ) {
275
+ diametersForElement = new Set < number > ( ) ;
276
+ diameters . set ( styleRoot , diametersForElement ) ;
250
277
}
251
278
252
- if ( styleTag && styleTag . sheet ) {
253
- ( styleTag . sheet as CSSStyleSheet ) . insertRule ( this . _getAnimationText ( ) , 0 ) ;
254
- }
255
-
256
- MatProgressSpinner . _diameters . add ( this . diameter ) ;
279
+ diametersForElement . add ( currentDiameter ) ;
257
280
}
258
281
259
282
/** Generates animation styles adjusted for the spinner's diameter. */
@@ -300,3 +323,26 @@ export class MatSpinner extends MatProgressSpinner {
300
323
this . mode = 'indeterminate' ;
301
324
}
302
325
}
326
+
327
+
328
+ /** Gets the shadow root of an element, if supported and the element is inside the Shadow DOM. */
329
+ function getShadowRoot ( element : HTMLElement , _document : Document ) : Node | null {
330
+ // TODO(crisbeto): see whether we should move this into the CDK
331
+ // feature detection utilities once #15616 gets merged in.
332
+ if ( typeof window !== 'undefined' ) {
333
+ const head = _document . head ;
334
+
335
+ // Check whether the browser supports Shadow DOM.
336
+ if ( head && ( ( head as any ) . createShadowRoot || head . attachShadow ) ) {
337
+ const rootNode = element . getRootNode ? element . getRootNode ( ) : null ;
338
+
339
+ // We need to take the `ShadowRoot` off of `window`, because the built-in types are
340
+ // incorrect. See https://github.com/Microsoft/TypeScript/issues/27929.
341
+ if ( rootNode instanceof ( window as any ) . ShadowRoot ) {
342
+ return rootNode ;
343
+ }
344
+ }
345
+ }
346
+
347
+ return null ;
348
+ }
0 commit comments