From 3fb2ad11d4a4d00d210b83e27b87664887257d2f Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 4 May 2016 20:32:41 +0200 Subject: [PATCH] feat(): add progress-fab component. --- .../progress-circle/progress-circle.ts | 2 +- src/components/progress-fab/README.md | 25 ++++ src/components/progress-fab/progress-fab.html | 13 ++ src/components/progress-fab/progress-fab.scss | 34 +++++ .../progress-fab/progress-fab.spec.ts | 123 ++++++++++++++++++ src/components/progress-fab/progress-fab.ts | 35 +++++ src/demo-app/button/button-demo.ts | 10 ++ src/demo-app/demo-app.html | 1 + src/demo-app/demo-app.ts | 2 + .../progress-fab/progress-fab-demo.html | 42 ++++++ .../progress-fab/progress-fab-demo.scss | 26 ++++ .../progress-fab/progress-fab-demo.ts | 33 +++++ 12 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 src/components/progress-fab/README.md create mode 100644 src/components/progress-fab/progress-fab.html create mode 100644 src/components/progress-fab/progress-fab.scss create mode 100644 src/components/progress-fab/progress-fab.spec.ts create mode 100644 src/components/progress-fab/progress-fab.ts create mode 100644 src/demo-app/progress-fab/progress-fab-demo.html create mode 100644 src/demo-app/progress-fab/progress-fab-demo.scss create mode 100644 src/demo-app/progress-fab/progress-fab-demo.ts 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 +![Preview](https://cloud.githubusercontent.com/assets/4987015/14406410/eaf20a54-fea6-11e5-8a24-4f751df7e80a.png) + +## `[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 @@ +
+ + + Basic + +
+ + + +
+
+
+ + + Determinate + +
+ + +
+
+
+ +
diff --git a/src/demo-app/progress-fab/progress-fab-demo.scss b/src/demo-app/progress-fab/progress-fab-demo.scss new file mode 100644 index 000000000000..8c6d4fb6b159 --- /dev/null +++ b/src/demo-app/progress-fab/progress-fab-demo.scss @@ -0,0 +1,26 @@ +.demo-progress-fab { + + .demo-progress-card { + padding: 0; + margin: 16px; + + .demo-content { + padding: 7px; + + .demo-determinate-progress.hide-progress { + .md-progress-wrap { + md-progress-circle { + transition-duration: 0.7s; + transition-property: transform, opacity; + transition-timing-function: ease-out; + + transform: scale(0.8); + opacity: 0; + } + } + } + + } + } + +} \ No newline at end of file diff --git a/src/demo-app/progress-fab/progress-fab-demo.ts b/src/demo-app/progress-fab/progress-fab-demo.ts new file mode 100644 index 000000000000..a08d2091de2b --- /dev/null +++ b/src/demo-app/progress-fab/progress-fab-demo.ts @@ -0,0 +1,33 @@ +import {Component, ViewEncapsulation} from '@angular/core'; +import {MdProgressFab} from '../../components/progress-fab/progress-fab'; +import {MdToolbar} from '../../components/toolbar/toolbar'; +import {MdCard} from '../../components/card/card'; + +@Component({ + selector: 'progress-fab-demo', + templateUrl: 'demo-app/progress-fab/progress-fab-demo.html', + styleUrls: ['demo-app/progress-fab/progress-fab-demo.css'], + directives: [MdProgressFab, MdToolbar, MdCard], + encapsulation: ViewEncapsulation.None +}) +export class ProgressFabDemo { + + fabProgressValue: number = 0; + + determinateHidden: boolean = false; + determinateColor: string = 'primary'; + + constructor() { + + setInterval(() => this.increaseFabProgress(), 200); + } + + increaseFabProgress() { + this.fabProgressValue += 7; + + if (this.fabProgressValue >= 100) { + this.determinateColor = 'warn'; + this.determinateHidden = true; + } + } +}