blob: 086863b5bf0bfd2a036a313d37774a27b71a7f5c [file] [log] [blame]
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { coerceNumberProperty } from '@angular/cdk/coercion';
import { Platform } from '@angular/cdk/platform';
import { DOCUMENT, CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, ElementRef, Inject, InjectionToken, Input, Optional, ViewEncapsulation, NgModule } from '@angular/core';
import { mixinColor, MatCommonModule } from '@angular/material/core';
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Base reference size of the spinner.
* \@docs-private
* @type {?}
*/
const BASE_SIZE = 100;
/**
* Base reference stroke width of the spinner.
* \@docs-private
* @type {?}
*/
const BASE_STROKE_WIDTH = 10;
// Boilerplate for applying mixins to MatProgressSpinner.
/**
* \@docs-private
*/
class MatProgressSpinnerBase {
/**
* @param {?} _elementRef
*/
constructor(_elementRef) {
this._elementRef = _elementRef;
}
}
/** @type {?} */
const _MatProgressSpinnerMixinBase = mixinColor(MatProgressSpinnerBase, 'primary');
/**
* Injection token to be used to override the default options for `mat-progress-spinner`.
* @type {?}
*/
const MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS = new InjectionToken('mat-progress-spinner-default-options', {
providedIn: 'root',
factory: MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY,
});
/**
* \@docs-private
* @return {?}
*/
function MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY() {
return { diameter: BASE_SIZE };
}
// .0001 percentage difference is necessary in order to avoid unwanted animation frames
// for example because the animation duration is 4 seconds, .1% accounts to 4ms
// which are enough to see the flicker described in
// https://github.com/angular/components/issues/8984
/** @type {?} */
const INDETERMINATE_ANIMATION_TEMPLATE = `
@keyframes mat-progress-spinner-stroke-rotate-DIAMETER {
0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }
12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }
12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }
25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }
25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }
37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }
37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }
50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }
50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }
62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }
62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }
75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }
75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }
87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }
87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }
100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }
}
`;
/**
* `<mat-progress-spinner>` component.
*/
class MatProgressSpinner extends _MatProgressSpinnerMixinBase {
/**
* @param {?} _elementRef
* @param {?} platform
* @param {?} _document
* @param {?} animationMode
* @param {?=} defaults
*/
constructor(_elementRef, platform, _document, animationMode, defaults) {
super(_elementRef);
this._elementRef = _elementRef;
this._document = _document;
this._diameter = BASE_SIZE;
this._value = 0;
this._fallbackAnimation = false;
/**
* Mode of the progress circle
*/
this.mode = 'determinate';
/** @type {?} */
const trackedDiameters = MatProgressSpinner._diameters;
// The base size is already inserted via the component's structural styles. We still
// need to track it so we don't end up adding the same styles again.
if (!trackedDiameters.has(_document.head)) {
trackedDiameters.set(_document.head, new Set([BASE_SIZE]));
}
this._styleRoot = _getShadowRoot(_elementRef.nativeElement, _document) || _document.head;
this._fallbackAnimation = platform.EDGE || platform.TRIDENT;
this._noopAnimations = animationMode === 'NoopAnimations' &&
(!!defaults && !defaults._forceAnimations);
if (defaults) {
if (defaults.diameter) {
this.diameter = defaults.diameter;
}
if (defaults.strokeWidth) {
this.strokeWidth = defaults.strokeWidth;
}
}
// On IE and Edge, we can't animate the `stroke-dashoffset`
// reliably so we fall back to a non-spec animation.
/** @type {?} */
const animationClass = `mat-progress-spinner-indeterminate${this._fallbackAnimation ? '-fallback' : ''}-animation`;
_elementRef.nativeElement.classList.add(animationClass);
}
/**
* The diameter of the progress spinner (will set width and height of svg).
* @return {?}
*/
get diameter() { return this._diameter; }
/**
* @param {?} size
* @return {?}
*/
set diameter(size) {
this._diameter = coerceNumberProperty(size);
if (!this._fallbackAnimation) {
/** @type {?} */
const trackedDiameters = MatProgressSpinner._diameters;
/** @type {?} */
const diametersForElement = trackedDiameters.get(this._styleRoot);
if (!diametersForElement || !diametersForElement.has(this._diameter)) {
this._attachStyleNode();
}
}
}
/**
* Stroke width of the progress spinner.
* @return {?}
*/
get strokeWidth() {
return this._strokeWidth || this.diameter / 10;
}
/**
* @param {?} value
* @return {?}
*/
set strokeWidth(value) {
this._strokeWidth = coerceNumberProperty(value);
}
/**
* Value of the progress circle.
* @return {?}
*/
get value() {
return this.mode === 'determinate' ? this._value : 0;
}
/**
* @param {?} newValue
* @return {?}
*/
set value(newValue) {
this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));
}
/**
* The radius of the spinner, adjusted for stroke width.
* @return {?}
*/
get _circleRadius() {
return (this.diameter - BASE_STROKE_WIDTH) / 2;
}
/**
* The view box of the spinner's svg element.
* @return {?}
*/
get _viewBox() {
/** @type {?} */
const viewBox = this._circleRadius * 2 + this.strokeWidth;
return `0 0 ${viewBox} ${viewBox}`;
}
/**
* The stroke circumference of the svg circle.
* @return {?}
*/
get _strokeCircumference() {
return 2 * Math.PI * this._circleRadius;
}
/**
* The dash offset of the svg circle.
* @return {?}
*/
get _strokeDashOffset() {
if (this.mode === 'determinate') {
return this._strokeCircumference * (100 - this._value) / 100;
}
// In fallback mode set the circle to 80% and rotate it with CSS.
if (this._fallbackAnimation && this.mode === 'indeterminate') {
return this._strokeCircumference * 0.2;
}
return null;
}
/**
* Stroke width of the circle in percent.
* @return {?}
*/
get _circleStrokeWidth() {
return this.strokeWidth / this.diameter * 100;
}
/**
* Dynamically generates a style tag containing the correct animation for this diameter.
* @private
* @return {?}
*/
_attachStyleNode() {
/** @type {?} */
const styleTag = this._document.createElement('style');
/** @type {?} */
const styleRoot = this._styleRoot;
/** @type {?} */
const currentDiameter = this._diameter;
/** @type {?} */
const diameters = MatProgressSpinner._diameters;
/** @type {?} */
let diametersForElement = diameters.get(styleRoot);
styleTag.setAttribute('mat-spinner-animation', currentDiameter + '');
styleTag.textContent = this._getAnimationText();
styleRoot.appendChild(styleTag);
if (!diametersForElement) {
diametersForElement = new Set();
diameters.set(styleRoot, diametersForElement);
}
diametersForElement.add(currentDiameter);
}
/**
* Generates animation styles adjusted for the spinner's diameter.
* @private
* @return {?}
*/
_getAnimationText() {
return INDETERMINATE_ANIMATION_TEMPLATE
// Animation should begin at 5% and end at 80%
.replace(/START_VALUE/g, `${0.95 * this._strokeCircumference}`)
.replace(/END_VALUE/g, `${0.2 * this._strokeCircumference}`)
.replace(/DIAMETER/g, `${this.diameter}`);
}
}
/**
* Tracks diameters of existing instances to de-dupe generated styles (default d = 100).
* We need to keep track of which elements the diameters were attached to, because for
* elements in the Shadow DOM the style tags are attached to the shadow root, rather
* than the document head.
*/
MatProgressSpinner._diameters = new WeakMap();
MatProgressSpinner.decorators = [
{ type: Component, args: [{selector: 'mat-progress-spinner',
exportAs: 'matProgressSpinner',
host: {
'role': 'progressbar',
'class': 'mat-progress-spinner',
'[class._mat-animation-noopable]': `_noopAnimations`,
'[style.width.px]': 'diameter',
'[style.height.px]': 'diameter',
'[attr.aria-valuemin]': 'mode === "determinate" ? 0 : null',
'[attr.aria-valuemax]': 'mode === "determinate" ? 100 : null',
'[attr.aria-valuenow]': 'mode === "determinate" ? value : null',
'[attr.mode]': 'mode',
},
inputs: ['color'],
template: "<svg [style.width.px]=\"diameter\" [style.height.px]=\"diameter\" [attr.viewBox]=\"_viewBox\" preserveAspectRatio=\"xMidYMid meet\" focusable=\"false\" [ngSwitch]=\"mode === 'indeterminate'\"><circle *ngSwitchCase=\"true\" cx=\"50%\" cy=\"50%\" [attr.r]=\"_circleRadius\" [style.animation-name]=\"'mat-progress-spinner-stroke-rotate-' + diameter\" [style.stroke-dashoffset.px]=\"_strokeDashOffset\" [style.stroke-dasharray.px]=\"_strokeCircumference\" [style.stroke-width.%]=\"_circleStrokeWidth\"></circle><circle *ngSwitchCase=\"false\" cx=\"50%\" cy=\"50%\" [attr.r]=\"_circleRadius\" [style.stroke-dashoffset.px]=\"_strokeDashOffset\" [style.stroke-dasharray.px]=\"_strokeCircumference\" [style.stroke-width.%]=\"_circleStrokeWidth\"></circle></svg>",
styles: [".mat-progress-spinner{display:block;position:relative}.mat-progress-spinner svg{position:absolute;transform:rotate(-90deg);top:0;left:0;transform-origin:center;overflow:visible}.mat-progress-spinner circle{fill:transparent;transform-origin:center;transition:stroke-dashoffset 225ms linear}._mat-animation-noopable.mat-progress-spinner circle{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate]{animation:mat-progress-spinner-linear-rotate 2s linear infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate]{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate] circle{transition-property:stroke;animation-duration:4s;animation-timing-function:cubic-bezier(.35,0,.25,1);animation-iteration-count:infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate] circle{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate]{animation:mat-progress-spinner-stroke-rotate-fallback 10s cubic-bezier(.87,.03,.33,1) infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate]{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate] circle{transition-property:stroke}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate] circle{transition:none;animation:none}@keyframes mat-progress-spinner-linear-rotate{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes mat-progress-spinner-stroke-rotate-100{0%{stroke-dashoffset:268.60617px;transform:rotate(0)}12.5%{stroke-dashoffset:56.54867px;transform:rotate(0)}12.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(72.5deg)}25%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(72.5deg)}25.0001%{stroke-dashoffset:268.60617px;transform:rotate(270deg)}37.5%{stroke-dashoffset:56.54867px;transform:rotate(270deg)}37.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(161.5deg)}50%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(161.5deg)}50.0001%{stroke-dashoffset:268.60617px;transform:rotate(180deg)}62.5%{stroke-dashoffset:56.54867px;transform:rotate(180deg)}62.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(251.5deg)}75%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(251.5deg)}75.0001%{stroke-dashoffset:268.60617px;transform:rotate(90deg)}87.5%{stroke-dashoffset:56.54867px;transform:rotate(90deg)}87.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(341.5deg)}100%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(341.5deg)}}@keyframes mat-progress-spinner-stroke-rotate-fallback{0%{transform:rotate(0)}25%{transform:rotate(1170deg)}50%{transform:rotate(2340deg)}75%{transform:rotate(3510deg)}100%{transform:rotate(4680deg)}}"],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
},] },
];
/** @nocollapse */
MatProgressSpinner.ctorParameters = () => [
{ type: ElementRef },
{ type: Platform },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] },
{ type: String, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATION_MODULE_TYPE,] }] },
{ type: undefined, decorators: [{ type: Inject, args: [MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS,] }] }
];
MatProgressSpinner.propDecorators = {
diameter: [{ type: Input }],
strokeWidth: [{ type: Input }],
mode: [{ type: Input }],
value: [{ type: Input }]
};
/**
* `<mat-spinner>` component.
*
* This is a component definition to be used as a convenience reference to create an
* indeterminate `<mat-progress-spinner>` instance.
*/
class MatSpinner extends MatProgressSpinner {
/**
* @param {?} elementRef
* @param {?} platform
* @param {?} document
* @param {?} animationMode
* @param {?=} defaults
*/
constructor(elementRef, platform, document, animationMode, defaults) {
super(elementRef, platform, document, animationMode, defaults);
this.mode = 'indeterminate';
}
}
MatSpinner.decorators = [
{ type: Component, args: [{selector: 'mat-spinner',
host: {
'role': 'progressbar',
'mode': 'indeterminate',
'class': 'mat-spinner mat-progress-spinner',
'[class._mat-animation-noopable]': `_noopAnimations`,
'[style.width.px]': 'diameter',
'[style.height.px]': 'diameter',
},
inputs: ['color'],
template: "<svg [style.width.px]=\"diameter\" [style.height.px]=\"diameter\" [attr.viewBox]=\"_viewBox\" preserveAspectRatio=\"xMidYMid meet\" focusable=\"false\" [ngSwitch]=\"mode === 'indeterminate'\"><circle *ngSwitchCase=\"true\" cx=\"50%\" cy=\"50%\" [attr.r]=\"_circleRadius\" [style.animation-name]=\"'mat-progress-spinner-stroke-rotate-' + diameter\" [style.stroke-dashoffset.px]=\"_strokeDashOffset\" [style.stroke-dasharray.px]=\"_strokeCircumference\" [style.stroke-width.%]=\"_circleStrokeWidth\"></circle><circle *ngSwitchCase=\"false\" cx=\"50%\" cy=\"50%\" [attr.r]=\"_circleRadius\" [style.stroke-dashoffset.px]=\"_strokeDashOffset\" [style.stroke-dasharray.px]=\"_strokeCircumference\" [style.stroke-width.%]=\"_circleStrokeWidth\"></circle></svg>",
styles: [".mat-progress-spinner{display:block;position:relative}.mat-progress-spinner svg{position:absolute;transform:rotate(-90deg);top:0;left:0;transform-origin:center;overflow:visible}.mat-progress-spinner circle{fill:transparent;transform-origin:center;transition:stroke-dashoffset 225ms linear}._mat-animation-noopable.mat-progress-spinner circle{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate]{animation:mat-progress-spinner-linear-rotate 2s linear infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate]{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate] circle{transition-property:stroke;animation-duration:4s;animation-timing-function:cubic-bezier(.35,0,.25,1);animation-iteration-count:infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-animation[mode=indeterminate] circle{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate]{animation:mat-progress-spinner-stroke-rotate-fallback 10s cubic-bezier(.87,.03,.33,1) infinite}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate]{transition:none;animation:none}.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate] circle{transition-property:stroke}._mat-animation-noopable.mat-progress-spinner.mat-progress-spinner-indeterminate-fallback-animation[mode=indeterminate] circle{transition:none;animation:none}@keyframes mat-progress-spinner-linear-rotate{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes mat-progress-spinner-stroke-rotate-100{0%{stroke-dashoffset:268.60617px;transform:rotate(0)}12.5%{stroke-dashoffset:56.54867px;transform:rotate(0)}12.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(72.5deg)}25%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(72.5deg)}25.0001%{stroke-dashoffset:268.60617px;transform:rotate(270deg)}37.5%{stroke-dashoffset:56.54867px;transform:rotate(270deg)}37.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(161.5deg)}50%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(161.5deg)}50.0001%{stroke-dashoffset:268.60617px;transform:rotate(180deg)}62.5%{stroke-dashoffset:56.54867px;transform:rotate(180deg)}62.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(251.5deg)}75%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(251.5deg)}75.0001%{stroke-dashoffset:268.60617px;transform:rotate(90deg)}87.5%{stroke-dashoffset:56.54867px;transform:rotate(90deg)}87.5001%{stroke-dashoffset:56.54867px;transform:rotateX(180deg) rotate(341.5deg)}100%{stroke-dashoffset:268.60617px;transform:rotateX(180deg) rotate(341.5deg)}}@keyframes mat-progress-spinner-stroke-rotate-fallback{0%{transform:rotate(0)}25%{transform:rotate(1170deg)}50%{transform:rotate(2340deg)}75%{transform:rotate(3510deg)}100%{transform:rotate(4680deg)}}"],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
},] },
];
/** @nocollapse */
MatSpinner.ctorParameters = () => [
{ type: ElementRef },
{ type: Platform },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] },
{ type: String, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATION_MODULE_TYPE,] }] },
{ type: undefined, decorators: [{ type: Inject, args: [MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS,] }] }
];
/**
* Gets the shadow root of an element, if supported and the element is inside the Shadow DOM.
* @param {?} element
* @param {?} _document
* @return {?}
*/
function _getShadowRoot(element, _document) {
// TODO(crisbeto): see whether we should move this into the CDK
// feature detection utilities once #15616 gets merged in.
if (typeof window !== 'undefined') {
/** @type {?} */
const head = _document.head;
// Check whether the browser supports Shadow DOM.
if (head && (((/** @type {?} */ (head))).createShadowRoot || head.attachShadow)) {
/** @type {?} */
const rootNode = element.getRootNode ? element.getRootNode() : null;
// We need to take the `ShadowRoot` off of `window`, because the built-in types are
// incorrect. See https://github.com/Microsoft/TypeScript/issues/27929.
if (rootNode instanceof ((/** @type {?} */ (window))).ShadowRoot) {
return rootNode;
}
}
}
return null;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class MatProgressSpinnerModule {
}
MatProgressSpinnerModule.decorators = [
{ type: NgModule, args: [{
imports: [MatCommonModule, CommonModule],
exports: [
MatProgressSpinner,
MatSpinner,
MatCommonModule
],
declarations: [
MatProgressSpinner,
MatSpinner
],
},] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { MatProgressSpinner, MatSpinner, MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS, MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY, MatProgressSpinnerModule };
//# sourceMappingURL=progress-spinner.js.map