Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit

Permalink
feat(loading-fab): add loading fab component
Browse files Browse the repository at this point in the history
  • Loading branch information
marcjulian committed Oct 11, 2018
1 parent d25160f commit 8a493cd
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 1 deletion.
5 changes: 4 additions & 1 deletion projects/core/src/lib/fivethree.core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ToolbarSearchComponent } from './toolbar-search/toolbar-search.componen
import { LoadingButtonComponent } from './loading-button/loading-button.component';
import { LoadingProgressBarComponent } from './loading-progress-bar/loading-progress-bar.component';
import { LoadingContentComponent } from './loading-content/loading-content.component';
import { LoadingFabComponent } from './loading-fab/loading-fab.component';

@NgModule({
imports: [
Expand All @@ -38,6 +39,7 @@ import { LoadingContentComponent } from './loading-content/loading-content.compo
LoadingButtonComponent,
LoadingProgressBarComponent,
LoadingContentComponent,
LoadingFabComponent,
],
exports: [
ExpandableComponent,
Expand All @@ -53,7 +55,8 @@ import { LoadingContentComponent } from './loading-content/loading-content.compo
ToolbarSearchComponent,
LoadingButtonComponent,
LoadingProgressBarComponent,
LoadingContentComponent
LoadingContentComponent,
LoadingFabComponent,
]
})
export class FivethreeCoreModule { }
10 changes: 10 additions & 0 deletions projects/core/src/lib/loading-fab/loading-fab.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<ion-fab [@fabAnim] [vertical]="vertical" [horizontal]="horizontal" [slot]="slot" [edge]="edge">
<svg *ngIf="loading" #spinner class="spinner rotate" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
<circle [@fillAnim]="isComplete ? 'fill' : 'spinning'" (@fillAnim.done)="fillAnimationDone($event)" [ngClass]="{'path': !isComplete}"
fill="none" stroke-width="4" stroke-linecap="round" cx="36" cy="36" r="32"></circle>
</svg>
<ion-fab-button [color]="fabColor" [disabled]="disabled">
<ion-icon [color]="iconColor" [@rotateAnim]="iconState" (@rotateAnim.done)="changeIconAndReveal($event,'md-checkmark')"
[name]="icon"></ion-icon>
</ion-fab-button>
</ion-fab>
125 changes: 125 additions & 0 deletions projects/core/src/lib/loading-fab/loading-fab.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
$fab-size: 72px !default;

:host{
position: absolute;
width: $fab-size;
height: $fab-size;
}

// FAB Vertical Positioning
// --------------------------------------------------

:host(.fab-vertical-top) {
top: 0;
}

:host(.fab-vertical-top.fab-edge) {
top: -$fab-size / 2;
}

:host(.fab-vertical-bottom.fab-edge) {
bottom: -$fab-size / 2;
}

:host(.fab-vertical-bottom) {
bottom: 0;
}

:host(.fab-vertical-center) {

top: 50%;
}

// FAB Horizontal Positioning
// --------------------------------------------------

:host(.fab-horizontal-center) {

}

:host(.fab-horizontal-start) {
left: 0;
}

:host(.fab-horizontal-end) {
right: 0;
}

ion-spinner {
position: absolute;
top: -8px;
left: -8px;
width: 72px;
height: 72px;
}

$offset: 187;
$duration: 1.4s;

.spinner {
position: absolute;
width:72px;
height:72px;
left:-8px;
top:-8px;
}

.rotate{
animation: rotator $duration linear infinite;
}

@keyframes rotator {
0% { transform: rotate(0deg); }
100% { transform: rotate(270deg); }
}

.path {
stroke-dasharray: $offset;
stroke-dashoffset: 0;
transform-origin: center;
animation:
dash $duration ease-in-out infinite,
colors ($duration*4) ease-in-out infinite;
}

.fill{
stroke-dasharray: 360;
stroke-dashoffset: 0;
transform-origin: center;
animation:
fill $duration ease-out;
stroke: #1B9A59;

}

@keyframes colors {
0% { stroke: #4285F4; }
25% { stroke: #DE3E35; }
50% { stroke: #F7C223; }
75% { stroke: #1B9A59; }
100% { stroke: #4285F4; }
}

@keyframes dash {
0% { stroke-dashoffset: $offset; }
50% {
stroke-dashoffset: $offset/4;
transform:rotate(135deg);
}
100% {
stroke-dashoffset: $offset;
transform:rotate(450deg);
}
}

@keyframes fill {
0% { stroke-dashoffset: 360; }
80% {
stroke-dashoffset: 180;
transform:rotate(135deg);
}
100% {
stroke-dashoffset: 0;
transform:rotate(450deg);
}
}
25 changes: 25 additions & 0 deletions projects/core/src/lib/loading-fab/loading-fab.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LoadingFabComponent } from './loading-fab.component';

describe('LoadingFabComponent', () => {
let component: LoadingFabComponent;
let fixture: ComponentFixture<LoadingFabComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoadingFabComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(LoadingFabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
144 changes: 144 additions & 0 deletions projects/core/src/lib/loading-fab/loading-fab.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Component, OnInit, Input, HostBinding, ViewChild, ElementRef, Output, EventEmitter } from '@angular/core';
import { animate, style, transition, trigger, state } from '@angular/animations';

@Component({
selector: 'fiv-loading-fab',
templateUrl: './loading-fab.component.html',
styleUrls: ['./loading-fab.component.scss'],
animations: [trigger('fabAnim', [
transition('void => *', [
style({ transform: 'scale(0)' }),
animate('250ms ease-out')
]),
transition('* => void', [
animate('250ms ease-in', style({ transform: 'scale(0)' }))
])
]),
trigger('spinnerAnim', [
transition('void => *', [
style({ opacity: '0' }),
animate('250ms ease-out')
]),
transition('* => void', [
animate('250ms ease-in', style({ opacity: '0' }))
])
]),
trigger('rotateAnim', [
transition('normal => rotate', [
animate('125ms ease-out')
]),
transition('rotate => normal', [
animate('125ms ease-in')
]),
state('rotate', style({ opacity: '0', transform: 'rotateZ(45deg)' })),
state('normal', style({ opacity: '1', transform: 'rotateZ(0deg)' }))
]),
trigger('fillAnim', [
transition('* => fill', [
style({
'stroke-dasharray': 180,
'stroke-dashoffset': 90,
'transformOrigin': 'center',
'stroke': '#DE3E35'
}),
animate('1400ms ease-out')
]),
state('fill', style({
'stroke-dasharray': 315,
'stroke-dashoffset': 0,
'transformOrigin': 'center',
'stroke': '#1B9A59',
'opacity': 0
})
)]
)]
})
export class LoadingFabComponent implements OnInit {

@Input() vertical?: 'top' | 'center' | 'bottom';
@Input() horizontal?: 'center' | 'start' | 'end';
@Input() edge: boolean;
@Input() slot: string;
@Input() icon: string;
@Input() spinColor: string;
@Input() fabColor: string;
@Input() iconColor = '#000';
@Input() checkmark = false;
@Input() disabled = false;

@Output() fivComplete: EventEmitter<LoadingFabComponent> = new EventEmitter<LoadingFabComponent>();
@Output() fivRefresh: EventEmitter<LoadingFabComponent> = new EventEmitter<LoadingFabComponent>();

@ViewChild('spinner') spinner: ElementRef;

loading = false;
isComplete = false;
iconState = 'normal';

@HostBinding('class') get classes(): string {
const verticalClass = !!this.vertical ? `fab-vertical-${this.vertical}` : '';
const horizontalClass = !!this.horizontal ? `fab-horizontal-${this.horizontal}` : '';
const edgeClass = this.edge ? `fab-edge-` : '';

return `${verticalClass} ${horizontalClass} ${edgeClass}`;
}

constructor() {
}

ngOnInit() {
}

toggleSpinner() {
if (this.icon !== 'md-checkmark') {
this.loading = !this.loading;
}
}

load() {
this.loading = true;
this.fivRefresh.emit(this);
}

unload() {
this.loading = false;
}

complete() {
if (this.loading) {
this.isComplete = true;

}
}

fillAnimationDone(event) {
if (event.fromState === 'spinning') {
if (this.checkmark) {
console.log('fill animation done', event);
this.iconState = 'rotate';
} else {
this.postComplete();
}
}
}

changeIconAndReveal(event, icon: string) {
console.log(event, icon);
if (event.fromState === 'normal') {
this.icon = icon;
this.iconState = 'normal';
} else {
if (event.fromState === 'rotate') {
this.postComplete();
}

}
}

postComplete() {
this.unload();
this.isComplete = false;
this.fivComplete.emit(this);
}

}
3 changes: 3 additions & 0 deletions src/app/loading/loading.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
<fiv-loading-button #bb (click)="bb.loading ? bb.complete() : bb.toggleSpinner()">Formular absenden</fiv-loading-button>

</fiv-loading-content>

<fiv-loading-fab checkmark="true" slot="fixed" (fivComplete)="fabComplete()" loading="true" #fab (click)="fab.loading ? fab.complete() : fab.toggleSpinner()"
horizontal="end" vertical="bottom" icon="md-add" spinColor="dark" fabColor="light" iconColor="primary"></fiv-loading-fab>

0 comments on commit 8a493cd

Please sign in to comment.