|
9 | 9 | import {
|
10 | 10 | AfterViewInit,
|
11 | 11 | ChangeDetectionStrategy,
|
12 |
| - Component, |
13 |
| - OnDestroy, |
| 12 | + Component, ElementRef, Inject, InjectionToken, |
| 13 | + Input, NgZone, |
| 14 | + OnDestroy, Optional, |
14 | 15 | ViewEncapsulation
|
15 | 16 | } from '@angular/core';
|
16 |
| -import {MDCCircularProgressFoundation} from '@material/circular-progress' |
| 17 | +import { |
| 18 | + MDCCircularProgressAdapter, |
| 19 | + MDCCircularProgressFoundation |
| 20 | +} from '@material/circular-progress'; |
| 21 | +import {CanColor, CanColorCtor, mixinColor} from '@angular/material/core'; |
| 22 | +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; |
| 23 | +import { |
| 24 | + MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS, |
| 25 | + MatProgressSpinnerDefaultOptions |
| 26 | +} from '@angular/material/progress-spinner'; |
| 27 | +import {coerceNumberProperty} from '@angular/cdk/coercion'; |
| 28 | + |
| 29 | +// Boilerplate for applying mixins to MatProgressBar. |
| 30 | +/** @docs-private */ |
| 31 | +class MatProgressSpinnerBase { |
| 32 | + constructor(public _elementRef: ElementRef) { } |
| 33 | +} |
| 34 | + |
| 35 | +const _MatProgressSpinnerMixinBase: CanColorCtor & typeof MatProgressSpinnerBase = |
| 36 | + mixinColor(MatProgressSpinnerBase, 'primary'); |
| 37 | + |
| 38 | +/** |
| 39 | + * Base reference size of the spinner. |
| 40 | + * @docs-private |
| 41 | + */ |
| 42 | + |
| 43 | +export type ProgressSpinnerMode = 'determinate' | 'indeterminate'; |
| 44 | + |
| 45 | +const BASE_SIZE = 100; |
17 | 46 |
|
18 | 47 | @Component({
|
19 | 48 | selector: 'mat-progress-spinner',
|
20 | 49 | exportAs: 'matProgressSpinner',
|
| 50 | + host: { |
| 51 | + 'role': 'progressbar', |
| 52 | + 'class': 'mat-progress-spinner', |
| 53 | + '[class._mat-animation-noopable]': `_noopAnimations`, |
| 54 | + '[style.width.px]': 'diameter', |
| 55 | + '[style.height.px]': 'diameter', |
| 56 | + '[attr.aria-valuemin]': 'mode === "determinate" ? 0 : null', |
| 57 | + '[attr.aria-valuemax]': 'mode === "determinate" ? 100 : null', |
| 58 | + '[attr.aria-valuenow]': 'mode === "determinate" ? value : null', |
| 59 | + '[attr.mode]': 'mode', |
| 60 | + }, |
| 61 | + inputs: ['color'], |
21 | 62 | templateUrl: 'progress-spinner.html',
|
22 | 63 | styleUrls: ['progress-spinner.css'],
|
23 | 64 | changeDetection: ChangeDetectionStrategy.OnPush,
|
24 | 65 | encapsulation: ViewEncapsulation.None,
|
25 | 66 | })
|
26 |
| -export class MatProgressSpinner implements AfterViewInit, OnDestroy{ |
27 |
| - private _circularProgressFoundation: MDCCircularProgressFoundation; |
| 67 | +export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements AfterViewInit, OnDestroy, CanColor { |
| 68 | + /** Implements all of the logic of the MDC circular progress. */ |
| 69 | + private _foundation: MDCCircularProgressFoundation | undefined; |
| 70 | + |
| 71 | + /** Adapter used by MDC to interact with the DOM. */ |
| 72 | + private _adapter: MDCCircularProgressAdapter = { |
| 73 | + addClass: (className: string) => this._rootElement.classList.add(className), |
| 74 | + hasClass: (className: string) => this._rootElement.classList.contains(className), |
| 75 | + removeClass: (className: string) => this._rootElement.classList.remove(className), |
| 76 | + removeAttribute: (name: string) => this._rootElement.removeAttribute(name), |
| 77 | + setAttribute: (name: string, value: string) => this._rootElement.setAttribute(name, value), |
| 78 | + getDeterminateCircleAttribute: (attributeName: string) => this._determinateCircle.getAttribute( |
| 79 | + attributeName), |
| 80 | + setDeterminateCircleAttribute: (attributeName: string, |
| 81 | + value: string) => this._determinateCircle.setAttribute( |
| 82 | + attributeName, |
| 83 | + value), |
| 84 | + }; |
28 | 85 |
|
29 |
| - constructor() { |
30 |
| - this._circularProgressFoundation = new MDCCircularProgressFoundation(); |
| 86 | + /** Whether the _mat-animation-noopable class should be applied, disabling animations. */ |
| 87 | + _noopAnimations: boolean; |
| 88 | + |
| 89 | + private _rootElement: HTMLElement; |
| 90 | + private _determinateCircle: HTMLElement; |
| 91 | + |
| 92 | + constructor(public _elementRef: ElementRef<HTMLElement>, private _ngZone: NgZone, |
| 93 | + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode: string, |
| 94 | + @Inject(MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS) |
| 95 | + defaults?: MatProgressSpinnerDefaultOptions) { |
| 96 | + super(_elementRef); |
| 97 | + this._noopAnimations = animationMode === 'NoopAnimations' && |
| 98 | + (!!defaults && !defaults._forceAnimations); |
31 | 99 | }
|
32 | 100 |
|
| 101 | + /** |
| 102 | + * Mode of the progress bar. |
| 103 | + * |
| 104 | + * Input must be one of these values: determinate, indeterminate, buffer, query, defaults to |
| 105 | + * 'determinate'. |
| 106 | + * Mirrored to mode attribute. |
| 107 | + */ |
| 108 | + @Input() |
| 109 | + get mode(): ProgressSpinnerMode { |
| 110 | + return this._mode; |
| 111 | + } |
| 112 | + |
| 113 | + set mode(value: ProgressSpinnerMode) { |
| 114 | + // Note that we don't technically need a getter and a setter here, |
| 115 | + // but we use it to match the behavior of the existing mat-progress-bar. |
| 116 | + this._mode = value; |
| 117 | + this._syncFoundation(); |
| 118 | + } |
| 119 | + private _mode: ProgressSpinnerMode = 'determinate'; |
| 120 | + |
| 121 | + |
| 122 | + /** Value of the progress bar. Defaults to zero. Mirrored to aria-valuenow. */ |
| 123 | + @Input() |
| 124 | + get value(): number { |
| 125 | + return this.mode === 'determinate' ? this._value : 0; |
| 126 | + } |
| 127 | + |
| 128 | + set value(v: number) { |
| 129 | + this._value = Math.max(0, Math.min(100, coerceNumberProperty(v))); |
| 130 | + this._syncFoundation(); |
| 131 | + } |
| 132 | + private _value = 0; |
| 133 | + |
| 134 | + /** The diameter of the progress spinner (will set width and height of svg). */ |
| 135 | + @Input() |
| 136 | + get diameter(): number { return this._diameter; } |
| 137 | + set diameter(size: number) { |
| 138 | + this._diameter = size; |
| 139 | + this._syncFoundation(); |
| 140 | + } |
| 141 | + private _diameter = BASE_SIZE; |
| 142 | + |
33 | 143 | ngAfterViewInit() {
|
34 |
| - this._circularProgressFoundation.init(); |
| 144 | + this._foundation = new MDCCircularProgressFoundation(this._adapter); |
| 145 | + this._foundation.init(); |
| 146 | + this._syncFoundation(); |
35 | 147 | }
|
36 | 148 |
|
37 | 149 | ngOnDestroy() {
|
38 |
| - this._circularProgressFoundation.destroy(); |
| 150 | + if (this._foundation) { |
| 151 | + this._foundation.destroy(); |
| 152 | + } |
| 153 | + } |
| 154 | + |
| 155 | + private _syncFoundation() { |
| 156 | + const foundation = this._foundation; |
| 157 | + |
| 158 | + if (foundation) { |
| 159 | + const mode = this.mode; |
| 160 | + foundation.setDeterminate(mode === 'determinate'); |
| 161 | + foundation.setProgress(this.value / 100); |
| 162 | + } |
39 | 163 | }
|
40 | 164 | }
|
0 commit comments