Skip to content

Commit 09e8561

Browse files
committed
feat(): add progress-fab component.
1 parent 4f9051f commit 09e8561

12 files changed

+345
-1
lines changed

src/components/progress-circle/progress-circle.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class MdProgressCircle {
5656
// total circumference.
5757

5858
// The total circumference is calculated based on the radius we use, 45.
59-
// PI * 2 * 45
59+
// PI * 2 * 40
6060
return 251.3274 * (100 - this._value) / 100;
6161
}
6262

src/components/progress-fab/README.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# MdProgressFab
2+
`MdProgressFab` is a normal FAB button surrounded with a `progress-circle`.
3+
4+
### Screenshots
5+
![Preview](https://cloud.githubusercontent.com/assets/4987015/14406410/eaf20a54-fea6-11e5-8a24-4f751df7e80a.png)
6+
7+
## `[md-progress-fab]`
8+
### Bound Properties
9+
10+
| Name | Type | Description |
11+
| --- | --- | --- |
12+
| `color` | `"primary" | "accent" | "warn"` | The color palette for the FAB button |
13+
| `value` | `number` | Value for the `progress-circle`.<br/> Necessary when using the `determinate` mode. |
14+
| `mode` | `"determinate" | "indeterminate"` | Mode for the `progress-circle`.<br/> |
15+
| `progressColor` | `"primary" | "accent" | "warn"` | Color for the `progress-circle`.<br/> |
16+
17+
18+
### Examples
19+
A basic progress-fab will have the markup:
20+
```html
21+
<button md-progress-fab color="accent">
22+
<i class="material-icons md-24">favorite</i>
23+
</button>
24+
```
25+
It will use by default a `indeterminate` progress circle.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<span class="md-button-wrapper">
2+
<ng-content></ng-content>
3+
4+
<div class="md-progress-wrap">
5+
<md-progress-circle
6+
[mode]="progressMode"
7+
[value]="progressValue"
8+
[attr.color]="progressColor">
9+
</md-progress-circle>
10+
</div>
11+
</span>
12+
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@import "../button/button-base";
2+
3+
$md-fab-progress-stroke: 6px;
4+
5+
// Subtract 1px to avoid the ugly space between FAB button and progress circle.
6+
$md-fab-progress-rectangle: ($md-fab-size + $md-fab-padding + $md-fab-progress-stroke / 2) - 1px;
7+
8+
:host {
9+
@include md-fab($md-fab-size, $md-fab-padding);
10+
11+
margin: $md-fab-progress-stroke;
12+
13+
.md-progress-wrap {
14+
position: absolute;
15+
top: 50%;
16+
left: 50%;
17+
18+
transform: translate3d(-50%, -50%, 0);
19+
20+
md-progress-circle {
21+
width: $md-fab-progress-rectangle;
22+
height: $md-fab-progress-rectangle;
23+
24+
svg {
25+
overflow: visible;
26+
27+
circle {
28+
stroke-width: $md-fab-progress-stroke;
29+
}
30+
}
31+
}
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import {
2+
it,
3+
describe,
4+
expect,
5+
beforeEach,
6+
inject,
7+
} from '@angular/core/testing';
8+
import {TestComponentBuilder} from '@angular/compiler/testing';
9+
import {Component} from '@angular/core';
10+
import {By} from '@angular/platform-browser';
11+
import {MdProgressFab} from './progress-fab';
12+
import {MdProgressCircle} from '../progress-circle/progress-circle';
13+
14+
export function main() {
15+
describe('MdProgressFab', () => {
16+
let builder: TestComponentBuilder;
17+
18+
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
19+
builder = tcb;
20+
}));
21+
22+
it('should correctly apply the color attribute on the progress circle', (done: () => void) => {
23+
return builder
24+
.createAsync(TestApp)
25+
.then((fixture) => {
26+
let testComponent = fixture.debugElement.componentInstance;
27+
let progressDebugElement = fixture.debugElement.query(By.css('md-progress-circle'));
28+
29+
testComponent.progressColor = 'primary';
30+
fixture.detectChanges();
31+
32+
expect(progressDebugElement.nativeElement.getAttribute('color')).toBe('primary');
33+
34+
testComponent.progressColor = 'accent';
35+
fixture.detectChanges();
36+
37+
expect(progressDebugElement.nativeElement.getAttribute('color')).toBe('accent');
38+
39+
done();
40+
});
41+
});
42+
43+
it('should correctly apply the mode on the progress circle', (done: () => void) => {
44+
return builder
45+
.createAsync(TestApp)
46+
.then((fixture) => {
47+
let testComponent = fixture.debugElement.componentInstance;
48+
let progressComponent: MdProgressCircle = fixture.debugElement
49+
.query(By.css('md-progress-circle')).componentInstance;
50+
51+
testComponent.progressMode = 'determinate';
52+
fixture.detectChanges();
53+
54+
expect(progressComponent.mode).toBe('determinate');
55+
56+
testComponent.progressColor = 'indeterminate';
57+
fixture.detectChanges();
58+
59+
expect(progressComponent.mode).toBe('determinate');
60+
61+
done();
62+
});
63+
});
64+
65+
it('should correctly apply the value on the progress circle', (done: () => void) => {
66+
return builder
67+
.createAsync(TestApp)
68+
.then((fixture) => {
69+
let testComponent = fixture.debugElement.componentInstance;
70+
let progressComponent: MdProgressCircle = fixture.debugElement
71+
.query(By.css('md-progress-circle')).componentInstance;
72+
73+
testComponent.progressValue = 50;
74+
fixture.detectChanges();
75+
76+
expect(progressComponent._value).toBe(50);
77+
78+
testComponent.progressValue = 70;
79+
fixture.detectChanges();
80+
81+
expect(progressComponent._value).toBe(70);
82+
83+
done();
84+
});
85+
});
86+
87+
it('should correctly apply the color on the button', (done: () => void) => {
88+
return builder
89+
.createAsync(TestApp)
90+
.then((fixture) => {
91+
let testComponent = fixture.debugElement.componentInstance;
92+
let buttonDebugElement = fixture.debugElement.query(By.css('button'));
93+
94+
testComponent.buttonColor = 'primary';
95+
fixture.detectChanges();
96+
97+
expect(buttonDebugElement.nativeElement.classList.contains('md-primary')).toBe(true);
98+
99+
testComponent.buttonColor = 'accent';
100+
fixture.detectChanges();
101+
expect(buttonDebugElement.nativeElement.classList.contains('md-accent')).toBe(true);
102+
103+
done();
104+
});
105+
});
106+
107+
});
108+
}
109+
110+
@Component({
111+
selector: 'test-app',
112+
template: `
113+
<button md-progress-fab [color]="buttonColor" [progressColor]="progressColor"
114+
[mode]="progressMode" [value]="progressValue">
115+
</button>`,
116+
directives: [MdProgressFab]
117+
})
118+
class TestApp {
119+
buttonColor: string = 'primary';
120+
progressColor: string = 'accent';
121+
progressMode: string = 'indeterminate';
122+
progressValue: number = 0;
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {
2+
Component,
3+
ChangeDetectionStrategy,
4+
Renderer,
5+
ElementRef,
6+
Input
7+
} from '@angular/core';
8+
import {MdProgressCircle} from '../progress-circle/progress-circle';
9+
import {MdButton} from '../button/button';
10+
11+
@Component({
12+
selector: '[md-progress-fab]:not(a)',
13+
templateUrl: './components/progress-fab/progress-fab.html',
14+
styleUrls: ['./components/progress-fab/progress-fab.css'],
15+
directives: [MdProgressCircle],
16+
inputs: ['color'],
17+
host: {
18+
'[class.md-button-focus]': 'isKeyboardFocused',
19+
'(mousedown)': 'setMousedown()',
20+
'(focus)': 'setKeyboardFocus()',
21+
'(blur)': 'removeKeyboardFocus()'
22+
},
23+
changeDetection: ChangeDetectionStrategy.OnPush,
24+
})
25+
export class MdProgressFab extends MdButton {
26+
27+
@Input('mode') progressMode: string = 'indeterminate';
28+
@Input('value') progressValue: number;
29+
@Input('progressColor') progressColor: string;
30+
31+
constructor(elementRef: ElementRef, renderer: Renderer) {
32+
super(elementRef, renderer);
33+
}
34+
35+
}

src/demo-app/button/button-demo.ts

+10
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,14 @@ import {MdIcon} from '../../components/icon/icon';
1111
export class ButtonDemo {
1212
isDisabled: boolean = false;
1313
clickCounter: number = 0;
14+
fabProgressValue: number = 0;
15+
16+
constructor() {
17+
18+
setInterval(() => this.increaseFabProgress(), 200);
19+
}
20+
21+
increaseFabProgress() {
22+
this.fabProgressValue += 7;
23+
}
1424
}

src/demo-app/demo-app.html

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<a md-list-item [routerLink]="['overlay']">Overlay</a>
1414
<a md-list-item [routerLink]="['portal']">Portal</a>
1515
<a md-list-item [routerLink]="['progress-circle']">Progress Circle</a>
16+
<a md-list-item [routerLink]="['progress-fab']">Progress Fab</a>
1617
<a md-list-item [routerLink]="['progress-bar']">Progress Bar</a>
1718
<a md-list-item [routerLink]="['radio']">Radio</a>
1819
<a md-list-item [routerLink]="['sidenav']">Sidenav</a>

src/demo-app/demo-app.ts

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {InputDemo} from './input/input-demo';
2222
import {LiveAnnouncerDemo} from './live-announcer/live-announcer-demo';
2323
import {GesturesDemo} from './gestures/gestures-demo';
2424
import {GridListDemo} from './grid-list/grid-list-demo';
25+
import {ProgressFabDemo} from './progress-fab/progress-fab-demo';
2526

2627
@Component({
2728
selector: 'home',
@@ -56,6 +57,7 @@ export class Home {}
5657
new Route({path: '/sidenav', component: SidenavDemo}),
5758
new Route({path: '/progress-circle', component: ProgressCircleDemo}),
5859
new Route({path: '/progress-bar', component: ProgressBarDemo}),
60+
new Route({path: '/progress-fab', component: ProgressFabDemo}),
5961
new Route({path: '/portal', component: PortalDemo}),
6062
new Route({path: '/overlay', component: OverlayDemo}),
6163
new Route({path: '/checkbox', component: CheckboxDemo}),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<div class="demo-progress-fab">
2+
3+
<md-card class="demo-progress-card">
4+
<md-toolbar color="primary">Basic</md-toolbar>
5+
<md-card-content>
6+
<div class="demo-content">
7+
<button md-progress-fab color="accent">
8+
<i class="material-icons md-24">favorite</i>
9+
</button>
10+
11+
<button md-progress-fab
12+
color="primary"
13+
progressColor="warn"
14+
mode="determinate"
15+
[value]="fabProgressValue">
16+
17+
<i class="material-icons md-24">feedback</i>
18+
</button>
19+
</div>
20+
</md-card-content>
21+
</md-card>
22+
23+
<md-card class="demo-progress-card">
24+
<md-toolbar color="primary">Determinate</md-toolbar>
25+
<md-card-content>
26+
<div class="demo-content">
27+
28+
<button md-progress-fab
29+
class="demo-determinate-progress"
30+
[color]="determinateColor"
31+
progressColor="warn"
32+
mode="determinate"
33+
[value]="fabProgressValue"
34+
[class.hide-progress]="determinateHidden">
35+
36+
<i class="material-icons md-24">feedback</i>
37+
</button>
38+
</div>
39+
</md-card-content>
40+
</md-card>
41+
42+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.demo-progress-fab {
2+
3+
.demo-progress-card {
4+
padding: 0;
5+
margin: 16px;
6+
7+
.demo-content {
8+
padding: 7px;
9+
10+
.demo-determinate-progress.hide-progress {
11+
.md-progress-wrap {
12+
md-progress-circle {
13+
transition-duration: 0.7s;
14+
transition-property: transform, opacity;
15+
transition-timing-function: ease-out;
16+
17+
transform: scale(0.8);
18+
opacity: 0;
19+
}
20+
}
21+
}
22+
23+
}
24+
}
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {Component, ViewEncapsulation} from '@angular/core';
2+
import {MdProgressFab} from '../../components/progress-fab/progress-fab';
3+
import {MdToolbar} from '../../components/toolbar/toolbar';
4+
import {MdCard} from '../../components/card/card';
5+
6+
@Component({
7+
selector: 'progress-fab-demo',
8+
templateUrl: 'demo-app/progress-fab/progress-fab-demo.html',
9+
styleUrls: ['demo-app/progress-fab/progress-fab-demo.css'],
10+
directives: [MdProgressFab, MdToolbar, MdCard],
11+
encapsulation: ViewEncapsulation.None
12+
})
13+
export class ProgressFabDemo {
14+
15+
fabProgressValue: number = 0;
16+
17+
determinateHidden: boolean = false;
18+
determinateColor: string = 'primary';
19+
20+
constructor() {
21+
22+
setInterval(() => this.increaseFabProgress(), 200);
23+
}
24+
25+
increaseFabProgress() {
26+
this.fabProgressValue += 7;
27+
28+
if (this.fabProgressValue >= 100) {
29+
this.determinateColor = 'warn';
30+
this.determinateHidden = true;
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)