# Angular2 Fab Buttons
Angular2 version of the FAB Buttons in [Angular 1 Material](https://material.angularjs.org/latest/demo/fabSpeedDial).
/**
* PLUNKER VERSION (based on systemjs.config.js in angular.io)
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
* Override at the last minute with global.filterSystemConfig (as plunkers do)
*/
(function(global) {
var ngVer = '@2.0.0-rc.1'; // lock in the angular package version; do not let it float to current!
//map tells the System loader where to look for things
var map = {
'core-js': 'https://npmcdn.com/core-js@2.4.0/client/core.min.js',
'app': 'src', // 'dist',
'boostrap': 'src', // 'dist',
'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6',
'angular2-data-table': 'https://raw.githubusercontent.com/swimlane/angular2-data-table/master/release/angular2-data-table.js',
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api' // get latest
};
//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'bootstrap': { main: 'bootstrap.ts', defaultExtension: 'ts' },
'app': { main: 'app.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' }
};
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
// add map entries for angular packages in the form '@angular/common': 'https://npmcdn.com/@angular/common@0.0.0-3'
packageNames.forEach(function(pkgName) {
map[pkgName] = 'https://npmcdn.com/' + pkgName + ngVer;
});
// add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
transpiler: 'typescript',
typescriptOptions: {
emitDecoratorMetadata: true
},
map: map,
packages: packages
}
// filterSystemConfig - index.html's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
<!DOCTYPE html>
<html>
<head>
<title>angular2-data-table</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/swimlane/angular2-data-table/master/release/datatable.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/swimlane/angular2-data-table/master/release/material.css" />
<script src="https://npmcdn.com/zone.js@0.6.12"></script>
<script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.27/system.js"></script>
<script src="https://npmcdn.com/typescript@1.8.10/lib/typescript.js"></script>
<script src="config.js"></script>
<script>
System.import('./src/bootstrap')
.catch(console.error.bind(console));
</script>
</head>
<body>
<app>
loading...
</app>
</body>
</html>
import {
Component,
Input,
ContentChildren,
ContentChild,
ElementRef,
HostListener
} from '@angular/core';
import { FabToggle } from './FabToggle';
import { FabButton } from './FabButton';
@Component({
selector: 'fab',
directives: [ FabToggle, FabButton ],
template: `
<nav
class="fab-menu"
[class.active]="active">
<ng-content></ng-content>
</nav>
`
})
export class Fab {
@Input() dir = 'right';
@ContentChild(FabToggle) toggle;
@ContentChildren(FabButton) buttons;
get active() {
return this._active;
}
set active(val) {
this.updateButtons(val);
this._active = val;
}
constructor(element: ElementRef) {
this.element = element.nativeElement;
}
ngAfterContentInit() {
this.toggle.onClick.subscribe(() => {
this.active = !this.active;
});
}
getTranslate(idx) {
if(this.dir === 'right') {
return `translate3d(${ 60 * idx }px,0,0)`;
} else if(this.dir === 'down') {
return `translate3d(0,${ 60 * idx }px,0)`;
} else {
console.error(`Unsupported direction for Fab; ${this.dir}`);
}
}
updateButtons(active) {
let idx = 1;
for(let btn of this.buttons.toArray()) {
let style = btn.element.nativeElement.style;
style['transition-duration'] = active ? `${ 90 + (100 * idx) }ms` : '';
style['transform'] = active ? this.getTranslate(idx) : '';
idx++;
}
}
@HostListener('document:click', ['$event.target'])
onDocumentClick(target) {
if(this.active && !this.element.contains(target)) {
this.active = false;
}
}
}
export const FAB_COMPONENTS = [
FabToggle,
FabButton,
Fab
];
import {
Component,
Input,
Output,
EventEmitter,
ViewChild
} from '@angular/core';
@Component({
selector: 'fab-button',
template: `
<a
#anchor
href="#"
class="fab-item"
(click)="onClick.emit($event)">
<span
[class]="'icon-' + icon">
</span>
<ng-content></ng-content>
</a>
`
})
export class FabButton {
@Input() icon;
@Output() onClick = new EventEmitter();
@ViewChild('anchor') element;
}
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'fab-toggle',
template: `
<a
href="#"
class="fab-toggle"
(click)="onClick.emit($event)">
<span
[class]="'icon-' + icon">
</span>
<ng-content></ng-content>
</a>
`
})
export class FabToggle {
@Input() icon;
@Output() onClick = new EventEmitter();
}
import { Component } from '@angular/core';
import { FAB_COMPONENTS } from './Fab';
@Component({
selector: 'app',
template: `
<div>
<fab dir="down">
<fab-toggle>+</fab-toggle>
<fab-button>A</fab-button>
<fab-button>B</fab-button>
<fab-button>C</fab-button>
<fab-button>D</fab-button>
</fab>
</div>
`,
directives: [ FAB_COMPONENTS ]
})
export class App {
}
import 'core-js';
import {bootstrap} from '@angular/platform-browser-dynamic';
import { App } from './app.ts';
bootstrap(App, [])
.catch(err => console.error(err));
/* Styles go here */
body{
font-family: 'RobotoDraft', 'Roboto', 'Helvetica Neue, Helvetica, Arial', sans-serif;
text-align: center;
font-style: normal;
font-weight: 300;
font-size: 1.4rem;
line-height: 2rem;
letter-spacing: 0.01rem;
color: #212121;
background-color: #f5f5f5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
margin:20px
}
a {
text-decoration: none;
}
.fab-menu {
position: absolute;
top: 10%;
left: 50%;
box-sizing: border-box;
font-size: 20px;
text-align: left;
}
.fab-menu .fab-item {
background: #22cf7d;
border-radius: 100%;
width: 40px;
height: 40px;
margin-left: -40px;
position: absolute;
top: 20px;
color: white;
text-align: center;
line-height: 40px;
transform: translate3d(0, 0, 0);
transition: transform ease-out 200ms;
}
.fab-menu .fab-item {
transition-duration: 180ms;
}
.fab-menu .fab-item:hover {
background: white;
color: #22cf7d;
}
.fab-menu .fab-toggle {
background: #106fe4;
border-radius: 100%;
width: 40px;
height: 40px;
margin-left: -40px;
position: absolute;
top: 20px;
color: white;
text-align: center;
line-height: 40px;
transform: translate3d(0, 0, 0);
transition: transform ease-out 200ms;
z-index: 2;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition-duration: 400ms;
transform: scale(1.1, 1.1) translate3d(0, 0, 0);
cursor: pointer;
}
.fab-menu .fab-toggle:hover {
transform: scale(1.2, 1.2) translate3d(0, 0, 0);
}
.fab-menu .fab-item {
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
.fab-menu.active .fab-toggle {
transition-timing-function: linear;
transition-duration: 200ms;
transform: scale(0.8, 0.8) translate3d(0, 0, 0);
}