mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
committed by
Jeremy Elbourn
parent
15fb40e2a5
commit
5ccbfb1718
@@ -0,0 +1,110 @@
|
||||
@import "variables";
|
||||
|
||||
@import "default-theme";
|
||||
|
||||
/* Animation Durations */
|
||||
$md-progress-circle-duration : 5.25s !default;
|
||||
$md-progress-circle-value-change-duration : $md-progress-circle-duration * 0.25 !default;
|
||||
$md-progress-circle-constant-rotate-duration : $md-progress-circle-duration * 0.55 !default;
|
||||
$md-progress-circle-sporadic-rotate-duration : $md-progress-circle-duration !default;
|
||||
|
||||
/** Component sizing */
|
||||
$md-progress-circle-stroke-width: 10px !default;
|
||||
$md-progress-circle-radius: 40px !default;
|
||||
$md-progress-circle-circumference: $pi * $md-progress-circle-radius * 2 !default;
|
||||
$md-progress-circle-center-point: 50px !default;
|
||||
// Height and weight of the viewport for md-progress-circle.
|
||||
$md-progress-circle-viewport-size : 100px !default;
|
||||
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
/** Height and width are provided for md-progress-circle to act as a default.
|
||||
The height and width are expected to be overwritten by application css. */
|
||||
height: $md-progress-circle-viewport-size;
|
||||
width: $md-progress-circle-viewport-size;
|
||||
|
||||
/** SVG's viewBox is defined as 0 0 100 100, this means that all SVG children will placed
|
||||
based on a 100px by 100px box.
|
||||
|
||||
The SVG and Circle dimensions/location:
|
||||
SVG
|
||||
Height: 100px
|
||||
Width: 100px
|
||||
Circle
|
||||
Radius: 40px
|
||||
Circumference: 251.3274px
|
||||
Center x: 50px
|
||||
Center y: 50px
|
||||
*/
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
circle {
|
||||
cx: $md-progress-circle-center-point;
|
||||
cy: $md-progress-circle-center-point;
|
||||
fill: transparent;
|
||||
r: $md-progress-circle-radius;
|
||||
stroke: md-color($md-primary, 600);
|
||||
/** Stroke width of 10px defines stroke as 10% of the viewBox */
|
||||
stroke-width: $md-progress-circle-stroke-width;
|
||||
/** SVG circle rotations begin rotated 90deg clockwise from the circle's center top */
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: center;
|
||||
transition: stroke-dashoffset 0.225s linear;
|
||||
/** The dash array of the circle is defined as the circumference of the circle. */
|
||||
stroke-dasharray: $md-progress-circle-circumference;
|
||||
/** The stroke dashoffset is used to "fill" the circle, 0px represents an full circle,
|
||||
while the circles full circumference represents an empty circle. */
|
||||
stroke-dashoffset: 0px;
|
||||
}
|
||||
|
||||
|
||||
&.md-accent circle {
|
||||
stroke: md-color($md-accent, 600);
|
||||
}
|
||||
|
||||
|
||||
&.md-warn circle {
|
||||
stroke: md-color($md-warn, 600);
|
||||
}
|
||||
|
||||
|
||||
&[mode="indeterminate"] circle {
|
||||
animation-duration: $md-progress-circle-sporadic-rotate-duration,
|
||||
$md-progress-circle-constant-rotate-duration,
|
||||
$md-progress-circle-value-change-duration;
|
||||
animation-name: md-progress-circle-sporadic-rotate,
|
||||
md-progress-circle-linear-rotate,
|
||||
md-progress-circle-value-change;
|
||||
animation-timing-function: $ease-in-out-curve-function,
|
||||
linear,
|
||||
$ease-in-out-curve-function;
|
||||
animation-iteration-count: infinite;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Animations for indeterminate mode */
|
||||
@keyframes md-progress-circle-linear-rotate {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes md-progress-circle-sporadic-rotate {
|
||||
12.5% { transform: rotate( 135deg); }
|
||||
25% { transform: rotate( 270deg); }
|
||||
37.5% { transform: rotate( 405deg); }
|
||||
50% { transform: rotate( 540deg); }
|
||||
62.5% { transform: rotate( 675deg); }
|
||||
75% { transform: rotate( 810deg); }
|
||||
87.5% { transform: rotate( 945deg); }
|
||||
100% { transform: rotate(1080deg); }
|
||||
}
|
||||
@keyframes md-progress-circle-value-change {
|
||||
0% { stroke-dashoffset: 261.3274px; }
|
||||
100% { stroke-dashoffset: -241.3274px; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!--
|
||||
preserveAspectRatio of xMidYMid meet as the center of the viewport is the circle's
|
||||
center. The center of the circle with remain at the center of the md-progress-circle
|
||||
element containing the SVG.
|
||||
-->
|
||||
<svg viewBox="0 0 100 100"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<circle [style.strokeDashoffset]="strokeDashOffset()"></circle>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
@@ -0,0 +1,99 @@
|
||||
import {beforeEach, ddescribe, expect, inject, it, TestComponentBuilder} from 'angular2/testing';
|
||||
import {Component, DebugElement} from 'angular2/core';
|
||||
import {By} from 'angular2/platform/browser'
|
||||
import {MdProgressCircle} from './progress_circle';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('MdProgressCircular', () => {
|
||||
let builder:TestComponentBuilder;
|
||||
|
||||
beforeEach(inject([TestComponentBuilder], (tcb:TestComponentBuilder) => {
|
||||
builder = tcb;
|
||||
}));
|
||||
|
||||
it('should apply a mode of "determinate" if no mode is provided.', (done:() => void) => {
|
||||
builder
|
||||
.overrideTemplate(TestApp, '<md-progress-circle></md-progress-circle>')
|
||||
.createAsync(TestApp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
let progressElement = getChildDebugElement(fixture.debugElement, 'md-progress-circle');
|
||||
expect(progressElement.componentInstance.mode).toBe('determinate');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply a mode of "determinate" if an invalid mode is provided.', (done:() => void) => {
|
||||
builder
|
||||
.overrideTemplate(TestApp, '<md-progress-circle mode="spinny"></md-progress-circle>')
|
||||
.createAsync(TestApp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
let progressElement = getChildDebugElement(fixture.debugElement, 'md-progress-circle');
|
||||
expect(progressElement.componentInstance.mode).toBe('determinate');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not modify the mode if a valid mode is provided.', (done:() => void) => {
|
||||
builder
|
||||
.overrideTemplate(TestApp, '<md-progress-circle mode="indeterminate"></md-progress-circle>')
|
||||
.createAsync(TestApp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
let progressElement = getChildDebugElement(fixture.debugElement, 'md-progress-circle');
|
||||
expect(progressElement.componentInstance.mode).toBe('indeterminate');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should define a default value for the value attribute', (done:() => void) => {
|
||||
builder
|
||||
.overrideTemplate(TestApp, '<md-progress-circle></md-progress-circle>')
|
||||
.createAsync(TestApp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
let progressElement = getChildDebugElement(fixture.debugElement, 'md-progress-circle');
|
||||
expect(progressElement.componentInstance.value).toBe(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should clamp the value of the progress between 0 and 100', (done:() => void) => {
|
||||
builder
|
||||
.overrideTemplate(TestApp, '<md-progress-circle></md-progress-circle>')
|
||||
.createAsync(TestApp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
let progressElement = getChildDebugElement(fixture.debugElement, 'md-progress-circle');
|
||||
let progressComponent = progressElement.componentInstance;
|
||||
|
||||
progressComponent.value = 50;
|
||||
expect(progressComponent.value).toBe(50);
|
||||
|
||||
progressComponent.value = 999;
|
||||
expect(progressComponent.value).toBe(100);
|
||||
|
||||
progressComponent.value = -10;
|
||||
expect(progressComponent.value).toBe(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/** Gets a child DebugElement by tag name. */
|
||||
function getChildDebugElement(parent: DebugElement, selector: string): DebugElement {
|
||||
return parent.query(By.css(selector));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Test component that contains an MdButton. */
|
||||
@Component({
|
||||
directives: [MdProgressCircle],
|
||||
template: '',
|
||||
})
|
||||
class TestApp {}
|
||||
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
Attribute,
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
ElementRef,
|
||||
HostBinding,
|
||||
Input,
|
||||
ViewEncapsulation,
|
||||
} from 'angular2/core';
|
||||
import {isPresent, CONST} from 'angular2/src/facade/lang';
|
||||
import {OneOf} from '../../core/annotations/one-of';
|
||||
|
||||
|
||||
// TODO(josephperrott): Benchpress tests.
|
||||
|
||||
/** Display modes of Progress Circle */
|
||||
@CONST()
|
||||
class ProgressMode {
|
||||
@CONST() static DETERMINATE = 'determinate';
|
||||
@CONST() static INDETERMINATE = 'indeterminate';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <md-progress-circle> component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'md-progress-circle',
|
||||
host: {
|
||||
'role': 'progressbar',
|
||||
'aria-valuemin': '0',
|
||||
'aria-valuemax': '100',
|
||||
},
|
||||
templateUrl: './components/progress-circle/progress_circle.html',
|
||||
styleUrls: ['./components/progress-circle/progress-circle.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MdProgressCircle {
|
||||
/**
|
||||
* Value of the progress circle.
|
||||
*
|
||||
* Input:number, defaults to 0.
|
||||
* value_ is bound to the host as the attribute aria-valuenow.
|
||||
*/
|
||||
@Input('value')
|
||||
value_: number = 0;
|
||||
@HostBinding('attr.aria-valuenow')
|
||||
get _value() {
|
||||
return this.value_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode of the progress circle
|
||||
*
|
||||
* Input must be one of the values from ProgressMode, defaults to 'determinate'.
|
||||
* mode is bound to the host as the attribute host.
|
||||
*/
|
||||
@Input()
|
||||
@OneOf([ProgressMode.DETERMINATE, ProgressMode.INDETERMINATE])
|
||||
mode: string;
|
||||
@HostBinding('attr.mode')
|
||||
get _mode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the current stroke dash offset to represent the progress circle.
|
||||
*
|
||||
* The stroke dash offset specifies the distance between dashes in the circle's stroke.
|
||||
* Setting the offset to a percentage of the total circumference of the circle, fills this
|
||||
* percentage of the overall circumference of the circle.
|
||||
*/
|
||||
strokeDashOffset() {
|
||||
// To determine how far the offset should be, we multiple the current percentage by the
|
||||
// total circumference.
|
||||
|
||||
// The total circumference is calculated based on the radius we use, 45.
|
||||
// PI * 2 * 45
|
||||
return 251.3274 * (100 - this.value_) / 100;
|
||||
}
|
||||
|
||||
|
||||
/** Gets the progress value, returning the clamped value. */
|
||||
get value() {
|
||||
return this.value_;
|
||||
}
|
||||
|
||||
|
||||
/** Sets the progress value, clamping before setting the internal value. */
|
||||
set value(v: number) {
|
||||
if (isPresent(v)) {
|
||||
this.value_ = MdProgressCircle.clamp(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Clamps a value to be between 0 and 100. */
|
||||
static clamp(v: number) {
|
||||
return Math.max(0, Math.min(100, v));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <md-spinner> component.
|
||||
*
|
||||
* This is a component definition to be used as a convenience reference to create an
|
||||
* indeterminate <md-progress-circle> instance.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'md-spinner',
|
||||
host: {
|
||||
'role': 'progressbar',
|
||||
},
|
||||
templateUrl: './components/progress-circle/progress_circle.html',
|
||||
styleUrls: ['./components/progress-circle/progress-circle.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MdSpinner extends MdProgressCircle {
|
||||
mode: string = ProgressMode.INDETERMINATE;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
$md-is-dark-theme: false;
|
||||
|
||||
|
||||
$md-primary: md-palette($md-indigo, 500, 100, 700, $md-contrast-palettes);
|
||||
$md-primary: md-palette($md-blue, 500, 100, 700, $md-contrast-palettes);
|
||||
$md-accent: md-palette($md-red, A200, A100, A400, $md-contrast-palettes);
|
||||
$md-warn: md-palette($md-orange, 500, 300, 800, $md-contrast-palettes);
|
||||
$md-foreground: if($md-is-dark-theme, $md-dark-theme-foreground, $md-light-theme-foreground);
|
||||
|
||||
@@ -12,8 +12,14 @@ $md-xsmall: "max-width: 600px";
|
||||
$z-index-fab: 20 !default;
|
||||
$z-index-drawer: 100 !default;
|
||||
|
||||
// Global constants
|
||||
$pi: 3.14159265;
|
||||
|
||||
// Easing Curves
|
||||
// TODO(jelbourn): all of these need to be revisited
|
||||
|
||||
$ease-in-out-curve-function: cubic-bezier(0.35, 0, 0.25, 1) !default;
|
||||
|
||||
$swift-ease-out-duration: 0.4s !default;
|
||||
$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
|
||||
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default;
|
||||
@@ -23,5 +29,5 @@ $swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2) !default;
|
||||
$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function !default;
|
||||
|
||||
$swift-ease-in-out-duration: 0.5s !default;
|
||||
$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1) !default;
|
||||
$swift-ease-in-out-timing-function: $ease-in-out-curve-function !default;
|
||||
$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function !default;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<li><a [routerLink]="['ButtonDemo']">Button demo</a></li>
|
||||
<li><a [routerLink]="['CardDemo']">Card demo</a></li>
|
||||
<li><a [routerLink]="['SidenavDemo']">Sidenav demo</a></li>
|
||||
<li><a [routerLink]="['ProgressCircleDemo']">Progress Circle demo</a></li>
|
||||
</ul>
|
||||
<button md-raised-button (click)="root.dir = (root.dir == 'rtl' ? 'ltr' : 'rtl')">
|
||||
{{root.dir.toUpperCase()}}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {Component} from 'angular2/core';
|
||||
import {CardDemo} from './card/card-demo';
|
||||
import {ButtonDemo} from './button/button-demo';
|
||||
import {SidenavDemo} from './sidenav/sidenav-demo';
|
||||
import {ProgressCircleDemo} from './progress-circle/progress-circle-demo';
|
||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||
import {Dir} from '../directives/dir/dir';
|
||||
import {MdButton} from '../components/button/button';
|
||||
@@ -24,6 +25,7 @@ export class Home {}
|
||||
{path: '/', name: 'Home', component: Home, useAsDefault: true},
|
||||
{path: '/button', name: 'ButtonDemo', component: ButtonDemo},
|
||||
{path: '/card', name: 'CardDemo', component: CardDemo},
|
||||
{path: '/sidenav', name: 'SidenavDemo', component: SidenavDemo}
|
||||
{path: '/sidenav', name: 'SidenavDemo', component: SidenavDemo},
|
||||
{path: '/progress-circle', name: 'ProgressCircleDemo', component: ProgressCircleDemo},
|
||||
])
|
||||
export class DemoApp { }
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<h1>Determinate</h1>
|
||||
<div class="demo-progress-circle-container">
|
||||
<md-progress-circle mode="determinate"
|
||||
[value]="progressValue"
|
||||
class="md-primary"></md-progress-circle>
|
||||
<md-progress-circle [value]="progressValue"
|
||||
class="md-accent"></md-progress-circle>
|
||||
</div>
|
||||
<span>Value: {{progressValue}}</span>
|
||||
<button md-raised-button (click)="step(10)">Increase</button>
|
||||
<button md-raised-button (click)="step(-10)">Decrease</button>
|
||||
|
||||
|
||||
|
||||
<h1>Indeterminate</h1>
|
||||
<div class="demo-progress-circle-container">
|
||||
<md-progress-circle mode="indeterminate"></md-progress-circle>
|
||||
<md-progress-circle mode="indeterminate"
|
||||
class="md-accent"></md-progress-circle>
|
||||
<md-spinner></md-spinner>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
.demo-progress-circle-container {
|
||||
width: 100%;
|
||||
|
||||
md-progress-circle,
|
||||
md-spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {Component} from 'angular2/core';
|
||||
import {MdButton} from '../../components/button/button';
|
||||
import {MdProgressCircle, MdSpinner} from '../../components/progress-circle/progress_circle';
|
||||
|
||||
@Component({
|
||||
selector: 'progress-circle-demo',
|
||||
templateUrl: 'demo-app/progress-circle/progress-circle-demo.html',
|
||||
styleUrls: ['demo-app/progress-circle/progress-circle-demo.css'],
|
||||
directives: [MdProgressCircle, MdSpinner, MdButton]
|
||||
})
|
||||
export class ProgressCircleDemo {
|
||||
progressValue: number = 40;
|
||||
|
||||
step(val: number) {
|
||||
this.progressValue += val;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user