diff --git a/src/components/progress-circle/progress-circle.ts b/src/components/progress-circle/progress-circle.ts
index 34655b893733..d91dcb30c223 100644
--- a/src/components/progress-circle/progress-circle.ts
+++ b/src/components/progress-circle/progress-circle.ts
@@ -56,7 +56,7 @@ export class MdProgressCircle {
// total circumference.
// The total circumference is calculated based on the radius we use, 45.
- // PI * 2 * 45
+ // PI * 2 * 40
return 251.3274 * (100 - this._value) / 100;
}
diff --git a/src/components/progress-fab/README.md b/src/components/progress-fab/README.md
new file mode 100644
index 000000000000..b53eb7cb9b0d
--- /dev/null
+++ b/src/components/progress-fab/README.md
@@ -0,0 +1,25 @@
+# MdProgressFab
+`MdProgressFab` is a normal FAB button surrounded with a `progress-circle`.
+
+### Screenshots
+
+
+## `[md-progress-fab]`
+### Bound Properties
+
+| Name | Type | Description |
+| --- | --- | --- |
+| `color` | `"primary" | "accent" | "warn"` | The color palette for the FAB button |
+| `value` | `number` | Value for the `progress-circle`.
Necessary when using the `determinate` mode. |
+| `mode` | `"determinate" | "indeterminate"` | Mode for the `progress-circle`.
|
+| `progressColor` | `"primary" | "accent" | "warn"` | Color for the `progress-circle`.
|
+
+
+### Examples
+A basic progress-fab will have the markup:
+```html
+
+```
+It will use by default a `indeterminate` progress circle.
diff --git a/src/components/progress-fab/progress-fab.html b/src/components/progress-fab/progress-fab.html
new file mode 100644
index 000000000000..17707125e068
--- /dev/null
+++ b/src/components/progress-fab/progress-fab.html
@@ -0,0 +1,13 @@
+
+
+
diff --git a/src/components/progress-fab/progress-fab.scss b/src/components/progress-fab/progress-fab.scss
new file mode 100644
index 000000000000..d9ed68f104c5
--- /dev/null
+++ b/src/components/progress-fab/progress-fab.scss
@@ -0,0 +1,34 @@
+@import "../button/button-base";
+
+$md-fab-progress-stroke: 6px;
+
+// Subtract 1px to avoid the ugly space between FAB button and progress circle.
+$md-fab-progress-rectangle: ($md-fab-size + $md-fab-padding + $md-fab-progress-stroke / 2) - 1px;
+
+:host {
+ @include md-fab($md-fab-size, $md-fab-padding);
+
+ margin: $md-fab-progress-stroke;
+
+ .md-progress-wrap {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+
+ transform: translate3d(-50%, -50%, 0);
+
+ md-progress-circle {
+ width: $md-fab-progress-rectangle;
+ height: $md-fab-progress-rectangle;
+
+ svg {
+ overflow: visible;
+
+ circle {
+ stroke-width: $md-fab-progress-stroke;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/progress-fab/progress-fab.spec.ts b/src/components/progress-fab/progress-fab.spec.ts
new file mode 100644
index 000000000000..53f251d6880e
--- /dev/null
+++ b/src/components/progress-fab/progress-fab.spec.ts
@@ -0,0 +1,123 @@
+import {
+ it,
+ describe,
+ expect,
+ beforeEach,
+ inject,
+} from '@angular/core/testing';
+import {TestComponentBuilder} from '@angular/compiler/testing';
+import {Component} from '@angular/core';
+import {By} from '@angular/platform-browser';
+import {MdProgressFab} from './progress-fab';
+import {MdProgressCircle} from '../progress-circle/progress-circle';
+
+export function main() {
+ describe('MdProgressFab', () => {
+ let builder: TestComponentBuilder;
+
+ beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ builder = tcb;
+ }));
+
+ it('should correctly apply the color attribute on the progress circle', (done: () => void) => {
+ return builder
+ .createAsync(TestApp)
+ .then((fixture) => {
+ let testComponent = fixture.debugElement.componentInstance;
+ let progressDebugElement = fixture.debugElement.query(By.css('md-progress-circle'));
+
+ testComponent.progressColor = 'primary';
+ fixture.detectChanges();
+
+ expect(progressDebugElement.nativeElement.getAttribute('color')).toBe('primary');
+
+ testComponent.progressColor = 'accent';
+ fixture.detectChanges();
+
+ expect(progressDebugElement.nativeElement.getAttribute('color')).toBe('accent');
+
+ done();
+ });
+ });
+
+ it('should correctly apply the mode on the progress circle', (done: () => void) => {
+ return builder
+ .createAsync(TestApp)
+ .then((fixture) => {
+ let testComponent = fixture.debugElement.componentInstance;
+ let progressComponent: MdProgressCircle = fixture.debugElement
+ .query(By.css('md-progress-circle')).componentInstance;
+
+ testComponent.progressMode = 'determinate';
+ fixture.detectChanges();
+
+ expect(progressComponent.mode).toBe('determinate');
+
+ testComponent.progressColor = 'indeterminate';
+ fixture.detectChanges();
+
+ expect(progressComponent.mode).toBe('determinate');
+
+ done();
+ });
+ });
+
+ it('should correctly apply the value on the progress circle', (done: () => void) => {
+ return builder
+ .createAsync(TestApp)
+ .then((fixture) => {
+ let testComponent = fixture.debugElement.componentInstance;
+ let progressComponent: MdProgressCircle = fixture.debugElement
+ .query(By.css('md-progress-circle')).componentInstance;
+
+ testComponent.progressValue = 50;
+ fixture.detectChanges();
+
+ expect(progressComponent._value).toBe(50);
+
+ testComponent.progressValue = 70;
+ fixture.detectChanges();
+
+ expect(progressComponent._value).toBe(70);
+
+ done();
+ });
+ });
+
+ it('should correctly apply the color on the button', (done: () => void) => {
+ return builder
+ .createAsync(TestApp)
+ .then((fixture) => {
+ let testComponent = fixture.debugElement.componentInstance;
+ let buttonDebugElement = fixture.debugElement.query(By.css('button'));
+
+ testComponent.buttonColor = 'primary';
+ fixture.detectChanges();
+
+ expect(buttonDebugElement.nativeElement.classList.contains('md-primary')).toBe(true);
+
+ testComponent.buttonColor = 'accent';
+ fixture.detectChanges();
+ expect(buttonDebugElement.nativeElement.classList.contains('md-accent')).toBe(true);
+
+ done();
+ });
+ });
+
+ });
+}
+
+@Component({
+ selector: 'test-app',
+ template: `
+ `,
+ directives: [MdProgressFab]
+})
+class TestApp {
+ buttonColor: string = 'primary';
+ progressColor: string = 'accent';
+ progressMode: string = 'indeterminate';
+ progressValue: number = 0;
+}
diff --git a/src/components/progress-fab/progress-fab.ts b/src/components/progress-fab/progress-fab.ts
new file mode 100644
index 000000000000..c1c688013841
--- /dev/null
+++ b/src/components/progress-fab/progress-fab.ts
@@ -0,0 +1,35 @@
+import {
+ Component,
+ ChangeDetectionStrategy,
+ Renderer,
+ ElementRef,
+ Input
+} from '@angular/core';
+import {MdProgressCircle} from '../progress-circle/progress-circle';
+import {MdButton} from '../button/button';
+
+@Component({
+ selector: '[md-progress-fab]:not(a)',
+ templateUrl: './components/progress-fab/progress-fab.html',
+ styleUrls: ['./components/progress-fab/progress-fab.css'],
+ directives: [MdProgressCircle],
+ inputs: ['color'],
+ host: {
+ '[class.md-button-focus]': 'isKeyboardFocused',
+ '(mousedown)': 'setMousedown()',
+ '(focus)': 'setKeyboardFocus()',
+ '(blur)': 'removeKeyboardFocus()'
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MdProgressFab extends MdButton {
+
+ @Input('mode') progressMode: string = 'indeterminate';
+ @Input('value') progressValue: number;
+ @Input('progressColor') progressColor: string;
+
+ constructor(elementRef: ElementRef, renderer: Renderer) {
+ super(elementRef, renderer);
+ }
+
+}
diff --git a/src/demo-app/button/button-demo.ts b/src/demo-app/button/button-demo.ts
index 9f591b02144b..50db8067a7f3 100644
--- a/src/demo-app/button/button-demo.ts
+++ b/src/demo-app/button/button-demo.ts
@@ -11,4 +11,14 @@ import {MdIcon} from '../../components/icon/icon';
export class ButtonDemo {
isDisabled: boolean = false;
clickCounter: number = 0;
+ fabProgressValue: number = 0;
+
+ constructor() {
+
+ setInterval(() => this.increaseFabProgress(), 200);
+ }
+
+ increaseFabProgress() {
+ this.fabProgressValue += 7;
+ }
}
diff --git a/src/demo-app/demo-app.html b/src/demo-app/demo-app.html
index a4b60622bc16..1d24b62e4120 100644
--- a/src/demo-app/demo-app.html
+++ b/src/demo-app/demo-app.html
@@ -13,6 +13,7 @@
Overlay
Portal
Progress Circle
+ Progress Fab
Progress Bar
Radio
Sidenav
diff --git a/src/demo-app/demo-app.ts b/src/demo-app/demo-app.ts
index 4e63e2206f48..ea4502435394 100644
--- a/src/demo-app/demo-app.ts
+++ b/src/demo-app/demo-app.ts
@@ -22,6 +22,7 @@ import {InputDemo} from './input/input-demo';
import {LiveAnnouncerDemo} from './live-announcer/live-announcer-demo';
import {GesturesDemo} from './gestures/gestures-demo';
import {GridListDemo} from './grid-list/grid-list-demo';
+import {ProgressFabDemo} from './progress-fab/progress-fab-demo';
@Component({
selector: 'home',
@@ -56,6 +57,7 @@ export class Home {}
new Route({path: '/sidenav', component: SidenavDemo}),
new Route({path: '/progress-circle', component: ProgressCircleDemo}),
new Route({path: '/progress-bar', component: ProgressBarDemo}),
+ new Route({path: '/progress-fab', component: ProgressFabDemo}),
new Route({path: '/portal', component: PortalDemo}),
new Route({path: '/overlay', component: OverlayDemo}),
new Route({path: '/checkbox', component: CheckboxDemo}),
diff --git a/src/demo-app/progress-fab/progress-fab-demo.html b/src/demo-app/progress-fab/progress-fab-demo.html
new file mode 100644
index 000000000000..4ab9bce1e69b
--- /dev/null
+++ b/src/demo-app/progress-fab/progress-fab-demo.html
@@ -0,0 +1,42 @@
+