mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
feat(fab): update floating action buttons
This commit is contained in:

committed by
Adam Bradley

parent
83d973b1a8
commit
490a06dd3e
@ -1,58 +0,0 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Floating Action Buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Width and height of the FAB button
|
||||
$button-fab-size: 56px !default;
|
||||
|
||||
|
||||
.button-fab {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
line-height: $button-fab-size;
|
||||
vertical-align: middle;
|
||||
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.button-fab.button {
|
||||
width: $button-fab-size;
|
||||
min-width: 0;
|
||||
height: $button-fab-size;
|
||||
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button-fab ion-icon {
|
||||
flex: 1;
|
||||
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.button-fab[fab-center] {
|
||||
left: 50%;
|
||||
|
||||
margin-left: -$button-fab-size / 2;
|
||||
}
|
||||
|
||||
.button-fab[fab-top] {
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
.button-fab[fab-right] {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.button-fab[fab-bottom] {
|
||||
bottom: 16px;
|
||||
}
|
||||
|
||||
.button-fab[fab-left] {
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
.button-fab[fab-fixed] {
|
||||
position: fixed;
|
||||
}
|
@ -311,14 +311,6 @@ $button-ios-fab-border-radius: 50% !default;
|
||||
}
|
||||
|
||||
|
||||
// iOS FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
.button-fab-ios {
|
||||
border-radius: $button-ios-fab-border-radius;
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS Button Colors
|
||||
// --------------------------------------------------
|
||||
|
||||
|
@ -394,23 +394,6 @@ $button-md-fab-box-shadow-activated: 0 5px 15px 0 rgba(0, 0, 0, .4),
|
||||
border-radius: $button-md-round-border-radius;
|
||||
}
|
||||
|
||||
|
||||
// Material Design FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
.button-fab-md {
|
||||
border-radius: $button-md-fab-border-radius;
|
||||
box-shadow: $button-md-fab-box-shadow;
|
||||
|
||||
transition: box-shadow $button-md-transition-duration $button-md-transition-timing-function,
|
||||
background-color $button-md-transition-duration $button-md-transition-timing-function,
|
||||
color $button-md-transition-duration $button-md-transition-timing-function;
|
||||
}
|
||||
|
||||
.button-fab-md.activated {
|
||||
box-shadow: $button-md-fab-box-shadow-activated;
|
||||
}
|
||||
|
||||
.button-md [icon-only] {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -309,14 +309,6 @@ $button-wp-fab-border-radius: 50% !default;
|
||||
border-radius: $button-wp-round-border-radius;
|
||||
}
|
||||
|
||||
|
||||
// Windows FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
.button-fab-wp {
|
||||
border-radius: $button-wp-fab-border-radius;
|
||||
}
|
||||
|
||||
.button-wp [icon-only] {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule } from '../../../..';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EPage {}
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="rootPage"></ion-nav>'
|
||||
})
|
||||
export class E2EApp {
|
||||
rootPage = E2EPage;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp)
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EPage
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
@ -1 +0,0 @@
|
||||
|
@ -1,25 +0,0 @@
|
||||
<ion-content>
|
||||
<button ion-button fab fab-left fab-top>
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button ion-button fab fab-center fab-top color="secondary">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button ion-button fab fab-right fab-top color="danger">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button ion-button fab fab-left fab-bottom color="light">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button ion-button fab fab-center fab-bottom color="primary">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button ion-button fab fab-right fab-bottom color="dark">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
</button>
|
||||
</ion-content>
|
@ -57,6 +57,27 @@ ion-content.js-scroll > .scroll-content {
|
||||
}
|
||||
|
||||
|
||||
// Fixed Content (ion-fixed and ion-fab)
|
||||
// --------------------------------------------------
|
||||
|
||||
.fixed-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
[ion-fixed] {
|
||||
position: absolute;
|
||||
|
||||
z-index: $z-index-fixed-content;
|
||||
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
|
||||
// Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@ -151,15 +172,3 @@ ion-content.js-scroll > .scroll-content {
|
||||
margin-left: $content-margin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Content Fixed
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fixed {
|
||||
position: absolute;
|
||||
|
||||
z-index: $z-index-fixed-content;
|
||||
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
@ -103,10 +103,12 @@ import { isTrueProperty } from '../../util/util';
|
||||
@Component({
|
||||
selector: 'ion-content',
|
||||
template:
|
||||
'<div class="fixed-content">' +
|
||||
'<ng-content select="[ion-fixed],ion-fab"></ng-content>' +
|
||||
'</div>' +
|
||||
'<div class="scroll-content">' +
|
||||
'<ng-content></ng-content>' +
|
||||
'</div>' +
|
||||
'<ng-content select="ion-fixed"></ng-content>' +
|
||||
'<ng-content select="ion-refresher"></ng-content>',
|
||||
host: {
|
||||
'[class.statusbar-padding]': '_sbPadding'
|
||||
@ -136,6 +138,11 @@ export class Content extends Ion {
|
||||
*/
|
||||
_scrollEle: HTMLElement;
|
||||
|
||||
/*
|
||||
* @private
|
||||
*/
|
||||
_fixedEle: HTMLElement;
|
||||
|
||||
/**
|
||||
* A number representing how many pixels the top of the content has been
|
||||
* adjusted, which could be by either padding or margin.
|
||||
@ -175,7 +182,8 @@ export class Content extends Ion {
|
||||
* @private
|
||||
*/
|
||||
ngOnInit() {
|
||||
this._scrollEle = this._elementRef.nativeElement.children[0];
|
||||
this._fixedEle = this._elementRef.nativeElement.children[0];
|
||||
this._scrollEle = this._elementRef.nativeElement.children[1];
|
||||
|
||||
this._zone.runOutsideAngular(() => {
|
||||
this._scroll = new ScrollView(this._scrollEle);
|
||||
@ -530,63 +538,61 @@ export class Content extends Ion {
|
||||
* DOM WRITE
|
||||
*/
|
||||
writeDimensions() {
|
||||
let newVal: number;
|
||||
let scrollEle = this._scrollEle;
|
||||
let scrollEle = this._scrollEle as any;
|
||||
if (!scrollEle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scrollEle) return;
|
||||
let fixedEle = this._fixedEle;
|
||||
if (!fixedEle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only write when it has changed
|
||||
// Toolbar height
|
||||
let contentTop = this._headerHeight;
|
||||
let contentBottom = this._footerHeight;
|
||||
|
||||
// Tabs height
|
||||
if (this._tabsPlacement === 'top') {
|
||||
contentTop += this._tabbarHeight;
|
||||
|
||||
} else if (this._tabsPlacement === 'bottom') {
|
||||
contentBottom += this._tabbarHeight;
|
||||
|
||||
// Update footer position
|
||||
if (contentBottom > 0 && this._footerEle) {
|
||||
this._footerEle.style.bottom = cssFormat(contentBottom - this._footerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle fullscreen viewport (padding vs margin)
|
||||
let topProperty = 'marginTop';
|
||||
let bottomProperty = 'marginBottom';
|
||||
let fixedTop: number = contentTop;
|
||||
let fixedBottom: number = contentBottom;
|
||||
if (this._fullscreen) {
|
||||
// adjust the content with padding, allowing content to scroll under headers/footers
|
||||
// however, on iOS you cannot control the margins of the scrollbar (last tested iOS9.2)
|
||||
// only add inline padding styles if the computed padding value, which would
|
||||
// have come from the app's css, is different than the new padding value
|
||||
contentTop += this._paddingTop;
|
||||
contentBottom += this._paddingBottom;
|
||||
topProperty = 'paddingTop';
|
||||
bottomProperty = 'paddingBottom';
|
||||
}
|
||||
|
||||
newVal = this._headerHeight + this._paddingTop;
|
||||
if (this._tabsPlacement === 'top') {
|
||||
newVal += this._tabbarHeight;
|
||||
}
|
||||
if (newVal !== this.contentTop) {
|
||||
scrollEle.style.paddingTop = (newVal > 0 ? newVal + 'px' : '');
|
||||
this.contentTop = newVal;
|
||||
}
|
||||
// Only update top margin if value changed
|
||||
if (contentTop !== this.contentTop) {
|
||||
scrollEle.style[topProperty] = cssFormat(contentTop);
|
||||
fixedEle.style.marginTop = cssFormat(fixedTop);
|
||||
this.contentTop = contentTop;
|
||||
}
|
||||
|
||||
newVal = this._footerHeight + this._paddingBottom;
|
||||
if (this._tabsPlacement === 'bottom') {
|
||||
newVal += this._tabbarHeight;
|
||||
|
||||
if (newVal > 0 && this._footerEle) {
|
||||
this._footerEle.style.bottom = (newVal - this._footerHeight - this._paddingBottom) + 'px';
|
||||
}
|
||||
}
|
||||
if (newVal !== this.contentBottom) {
|
||||
scrollEle.style.paddingBottom = (newVal > 0 ? newVal + 'px' : '');
|
||||
this.contentBottom = newVal;
|
||||
}
|
||||
|
||||
} else {
|
||||
// adjust the content with margins
|
||||
newVal = this._headerHeight;
|
||||
if (this._tabsPlacement === 'top') {
|
||||
newVal += this._tabbarHeight;
|
||||
}
|
||||
if (newVal !== this.contentTop) {
|
||||
scrollEle.style.marginTop = (newVal > 0 ? newVal + 'px' : '');
|
||||
this.contentTop = newVal;
|
||||
}
|
||||
|
||||
newVal = this._footerHeight;
|
||||
if (this._tabsPlacement === 'bottom') {
|
||||
newVal += this._tabbarHeight;
|
||||
}
|
||||
if (newVal !== this.contentBottom) {
|
||||
scrollEle.style.marginBottom = (newVal > 0 ? newVal + 'px' : '');
|
||||
this.contentBottom = newVal;
|
||||
|
||||
if (newVal > 0 && this._footerEle) {
|
||||
this._footerEle.style.bottom = (newVal - this._footerHeight) + 'px';
|
||||
}
|
||||
}
|
||||
// Only update bottom margin if value changed
|
||||
if (contentBottom !== this.contentBottom) {
|
||||
scrollEle.style[bottomProperty] = cssFormat(contentBottom);
|
||||
fixedEle.style.marginBottom = cssFormat(fixedBottom);
|
||||
this.contentBottom = contentBottom;
|
||||
}
|
||||
|
||||
|
||||
@ -606,3 +612,7 @@ export class Content extends Ion {
|
||||
function parsePxUnit(val: string): number {
|
||||
return (val.indexOf('px') > 0) ? parseInt(val, 10) : 0;
|
||||
}
|
||||
|
||||
function cssFormat(val: number): string {
|
||||
return (val > 0 ? val + 'px' : '');
|
||||
}
|
||||
|
32
src/components/fab/fab.ios.scss
Executable file
32
src/components/fab/fab.ios.scss
Executable file
@ -0,0 +1,32 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
|
||||
// iOS FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Border radius of the FAB button
|
||||
$button-ios-fab-border-radius: 50% !default;
|
||||
|
||||
.fab-button {
|
||||
border-radius: $button-ios-fab-border-radius;
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
|
||||
|
||||
$background-color: $color-base;
|
||||
$background-color-activated: color-shade($background-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-ios-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
.fab-ios-#{$color-name}.activated {
|
||||
background-color: $background-color-activated;
|
||||
}
|
||||
}
|
||||
|
48
src/components/fab/fab.md.scss
Executable file
48
src/components/fab/fab.md.scss
Executable file
@ -0,0 +1,48 @@
|
||||
@import "../../themes/ionic.globals.md";
|
||||
|
||||
// Material Design FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Border radius of the FAB button
|
||||
$button-md-fab-border-radius: 50% !default;
|
||||
|
||||
/// @prop - Box shadow of the FAB button
|
||||
$button-md-fab-box-shadow: 0 4px 6px 0 rgba(0, 0, 0, .14), 0 4px 5px rgba(0, 0, 0, .1) !default;
|
||||
|
||||
/// @prop - Box shadow of the activated FAB button
|
||||
$button-md-fab-box-shadow-activated: 0 5px 15px 0 rgba(0, 0, 0, .4), 0 4px 7px 0 rgba(0, 0, 0, .1) !default;
|
||||
|
||||
|
||||
.fab-button {
|
||||
border-radius: $button-md-fab-border-radius;
|
||||
box-shadow: $button-md-fab-box-shadow;
|
||||
|
||||
transition: box-shadow $button-md-transition-duration $button-md-transition-timing-function,
|
||||
background-color $button-md-transition-duration $button-md-transition-timing-function,
|
||||
color $button-md-transition-duration $button-md-transition-timing-function;
|
||||
|
||||
&.activated {
|
||||
box-shadow: $button-md-fab-box-shadow-activated;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
|
||||
|
||||
$background-color: $color-base;
|
||||
$background-color-activated: color-shade($background-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-md-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
.fab-md-#{$color-name}.activated {
|
||||
background-color: $background-color-activated;
|
||||
}
|
||||
}
|
||||
|
202
src/components/fab/fab.scss
Executable file
202
src/components/fab/fab.scss
Executable file
@ -0,0 +1,202 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Floating Action Buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Width and height of the FAB button
|
||||
$button-fab-size: 56px !default;
|
||||
$button-fab-mini-size: 40px !default;
|
||||
$button-fab-content-margin: 10px !default;
|
||||
$button-fab-list-margin: 10px !default;
|
||||
|
||||
|
||||
.fab-button {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
width: $button-fab-size;
|
||||
height: $button-fab-size;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: $button-fab-size;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
color: #fff;
|
||||
background-color: #327eff;
|
||||
cursor: pointer;
|
||||
transition: background-color, opacity 100ms linear;
|
||||
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background-clip: padding-box;
|
||||
font-kerning: none;
|
||||
user-select: none;
|
||||
|
||||
ion-icon {
|
||||
flex: 1;
|
||||
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
// FAB mini
|
||||
// --------------------------------------------------
|
||||
|
||||
.fab-button[mini] {
|
||||
margin: ($button-fab-size - $button-fab-mini-size) / 2;
|
||||
|
||||
width: $button-fab-mini-size;
|
||||
height: $button-fab-mini-size;
|
||||
|
||||
line-height: $button-fab-mini-size;
|
||||
|
||||
.fab-close-icon {
|
||||
line-height: $button-fab-mini-size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FAB container
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fab {
|
||||
position: absolute;
|
||||
z-index: $z-index-fixed-content;
|
||||
|
||||
&[center] {
|
||||
left: 50%;
|
||||
|
||||
margin-left: -$button-fab-size / 2;
|
||||
}
|
||||
|
||||
&[middle] {
|
||||
top: 50%;
|
||||
|
||||
margin-top: -$button-fab-size / 2;
|
||||
}
|
||||
|
||||
&[top] {
|
||||
top: $button-fab-content-margin;
|
||||
}
|
||||
|
||||
&[right] {
|
||||
right: $button-fab-content-margin;
|
||||
}
|
||||
|
||||
&[bottom] {
|
||||
bottom: $button-fab-content-margin;
|
||||
}
|
||||
|
||||
&[left] {
|
||||
left: $button-fab-content-margin;
|
||||
}
|
||||
|
||||
&[top][edge] {
|
||||
top: -$button-fab-size / 2;
|
||||
}
|
||||
|
||||
&[bottom][edge] {
|
||||
bottom: -$button-fab-size / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FAB list (speed dial)
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fab-list {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: none;
|
||||
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
margin: $button-fab-size + $button-fab-list-margin 0;
|
||||
|
||||
min-width: $button-fab-size;
|
||||
min-height: $button-fab-size;
|
||||
|
||||
.fab-button {
|
||||
margin: 8px;
|
||||
|
||||
width: $button-fab-mini-size;
|
||||
height: $button-fab-mini-size;
|
||||
|
||||
color: #797979;
|
||||
background: #fff;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: scale(0);
|
||||
transition: all 200ms ease;
|
||||
transition-delay: 10ms;
|
||||
transition-property: transform, opacity;
|
||||
|
||||
&.fab-dial-button-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-fab-list[side=top] {
|
||||
top: initial;
|
||||
bottom: 0;
|
||||
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
ion-fab-list[side=left] {
|
||||
right: 0;
|
||||
|
||||
flex-direction: row-reverse;
|
||||
|
||||
margin: 0 $button-fab-size + $button-fab-list-margin;
|
||||
}
|
||||
|
||||
ion-fab-list[side=right] {
|
||||
left: 0;
|
||||
|
||||
flex-direction: row;
|
||||
|
||||
margin: 0 $button-fab-size + $button-fab-list-margin;
|
||||
}
|
||||
|
||||
|
||||
// FAB animation
|
||||
// --------------------------------------------------
|
||||
|
||||
.fab-list-active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fab-close-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
|
||||
line-height: $button-fab-size;
|
||||
opacity: 0;
|
||||
transform: scale(.4) rotateZ(-45deg);
|
||||
transition: all ease-in-out 300ms;
|
||||
}
|
||||
|
||||
.fab-button .button-inner {
|
||||
transition: all ease-in-out 300ms;
|
||||
}
|
||||
|
||||
.fab-close-active .fab-close-icon {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotateZ(0deg);
|
||||
}
|
||||
|
||||
.fab-close-active .button-inner {
|
||||
opacity: 0;
|
||||
transform: scale(.4) rotateZ(45deg);
|
||||
}
|
168
src/components/fab/fab.ts
Executable file
168
src/components/fab/fab.ts
Executable file
@ -0,0 +1,168 @@
|
||||
import { Component, ContentChild, Input, ContentChildren, QueryList, ChangeDetectionStrategy, Directive, ElementRef, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
|
||||
import { UIEventManager } from '../../util/ui-event-manager';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
|
||||
@Component({
|
||||
selector: '[ion-fab]',
|
||||
template:
|
||||
'<ion-icon name="close" class="fab-close-icon"></ion-icon>' +
|
||||
'<span class="button-inner">' +
|
||||
'<ng-content></ng-content>' +
|
||||
'</span>' +
|
||||
'<div class="button-effect"></div>',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class FabButton extends Ion {
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.setElementClass('fab-button', true); // set role
|
||||
}
|
||||
/**
|
||||
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
|
||||
*/
|
||||
@Input()
|
||||
set color(val: string) {
|
||||
this._setColor('fab', val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {string} The mode to apply to this component.
|
||||
*/
|
||||
@Input()
|
||||
set mode(val: string) {
|
||||
this._setMode('fab', val);
|
||||
}
|
||||
|
||||
constructor(
|
||||
config: Config,
|
||||
elementRef: ElementRef,
|
||||
renderer: Renderer,
|
||||
) {
|
||||
super(config, elementRef, renderer);
|
||||
this.mode = config.get('mode');
|
||||
}
|
||||
|
||||
|
||||
setActiveClose(closeVisible: boolean) {
|
||||
this.setElementClass('fab-close-active', closeVisible);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Fab
|
||||
* @module ionic
|
||||
*
|
||||
* @demo /docs/v2/demos/fab/
|
||||
* @see {@link /docs/v2/components#fab Fab Component Docs}
|
||||
*/
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-fab-list',
|
||||
host: {
|
||||
'[class.fab-list-active]': '_visible'
|
||||
}
|
||||
})
|
||||
export class FabList {
|
||||
_visible: boolean = false;
|
||||
|
||||
@ContentChildren(FabButton) _buttons: QueryList<FabButton>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setVisible(val: boolean) {
|
||||
let visible = isTrueProperty(val);
|
||||
if (visible === this._visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
let buttons = this._buttons.toArray();
|
||||
let i = 1;
|
||||
if (visible) {
|
||||
buttons.forEach(fab => {
|
||||
setTimeout(() => fab.setElementClass('fab-dial-button-visible', true), i * 30);
|
||||
i++;
|
||||
});
|
||||
} else {
|
||||
buttons.forEach(fab => fab.setElementClass('fab-dial-button-visible', false));
|
||||
}
|
||||
this._visible = visible;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ion-fab',
|
||||
template: '<ng-content></ng-content>'
|
||||
})
|
||||
export class Fab {
|
||||
_events: UIEventManager = new UIEventManager();
|
||||
_listsActive: boolean = false;
|
||||
|
||||
@ContentChild(FabButton) _mainButton: FabButton;
|
||||
@ContentChildren(FabList) _fabLists: QueryList<FabList>;
|
||||
|
||||
constructor(private _elementRef: ElementRef) { }
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngAfterContentInit() {
|
||||
this._events.listen(this._mainButton.getNativeElement(), 'click', this.pointerUp.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
pointerUp(ev: any) {
|
||||
if (this.canActivateList(ev)) {
|
||||
this.toggleList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
canActivateList(ev: any): boolean {
|
||||
if (this._fabLists.length > 0 && this._mainButton && ev.target) {
|
||||
let ele = ev.target.closest('ion-fab>button');
|
||||
return (ele && ele === this._mainButton.getNativeElement());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
toggleList() {
|
||||
this.setActiveLists(!this._listsActive);
|
||||
}
|
||||
|
||||
setActiveLists(isActive: boolean) {
|
||||
if (isActive === this._listsActive) {
|
||||
return;
|
||||
}
|
||||
let lists = this._fabLists.toArray();
|
||||
for (let list of lists) {
|
||||
list.setVisible(isActive);
|
||||
}
|
||||
this._mainButton.setActiveClose(isActive);
|
||||
this._listsActive = isActive;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setActiveLists(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this._events.unlistenAll();
|
||||
}
|
||||
}
|
32
src/components/fab/fab.wp.scss
Executable file
32
src/components/fab/fab.wp.scss
Executable file
@ -0,0 +1,32 @@
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
|
||||
// Windows FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Border radius of the FAB button
|
||||
$button-wp-fab-border-radius: 50% !default;
|
||||
|
||||
|
||||
.fab-button {
|
||||
border-radius: $button-wp-fab-border-radius;
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
|
||||
|
||||
$background-color: $color-base;
|
||||
$background-color-activated: color-shade($background-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-wp-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $background-color;
|
||||
}
|
||||
|
||||
.fab-wp-#{$color-name}.activated {
|
||||
background-color: $background-color-activated;
|
||||
}
|
||||
}
|
45
src/components/fab/test/basic/app-module.ts
Executable file
45
src/components/fab/test/basic/app-module.ts
Executable file
@ -0,0 +1,45 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule, Fab } from '../../../..';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EPage {
|
||||
array: number[] = [];
|
||||
|
||||
add() {
|
||||
this.array.push(1);
|
||||
}
|
||||
|
||||
clickMainFAB() {
|
||||
console.log('Clicked open social menu');
|
||||
}
|
||||
|
||||
openSocial(network: string, fab: Fab) {
|
||||
console.log('Share in ' + network);
|
||||
fab.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
export class E2EApp {
|
||||
root: any = E2EPage;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp)
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EPage
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
73
src/components/fab/test/basic/main.html
Executable file
73
src/components/fab/test/basic/main.html
Executable file
@ -0,0 +1,73 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Floating Action Buttons</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content fullscreen>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<button ion-button>Test</button>
|
||||
<div f *ngFor="let a of array"></div>
|
||||
|
||||
<ion-fab top right edge #fab1>
|
||||
<button ion-fab mini (click)="clickMainFAB()"><ion-icon name="add"></ion-icon></button>
|
||||
<ion-fab-list>
|
||||
<button ion-fab (click)="openSocial('facebook', fab1)"><ion-icon name="logo-facebook"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('twitter', fab1)"><ion-icon name="logo-twitter"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('vimeo', fab1)"><ion-icon name="logo-vimeo"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('googleplus', fab1)"><ion-icon name="logo-googleplus"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab right bottom #fab2>
|
||||
<button ion-fab color="light"><ion-icon name="arrow-dropleft"></ion-icon></button>
|
||||
<ion-fab-list side="left">
|
||||
<button ion-fab (click)="openSocial('facebook', fab2)"><ion-icon name="logo-facebook"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('twitter', fab2)"><ion-icon name="logo-twitter"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('vimeo', fab2)"><ion-icon name="logo-vimeo"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('googleplus', fab2)"><ion-icon name="logo-googleplus"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab left top #fab3>
|
||||
<button ion-fab color="secondary"><ion-icon name="arrow-dropright"></ion-icon></button>
|
||||
<ion-fab-list side="right">
|
||||
<button ion-fab (click)="openSocial('facebook', fab3)"><ion-icon name="logo-facebook"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('twitter', fab3)"><ion-icon name="logo-twitter"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('vimeo', fab3)"><ion-icon name="logo-vimeo"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('googleplus', fab3)"><ion-icon name="logo-googleplus"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab left bottom #fab4>
|
||||
<button ion-fab color="dark"><ion-icon name="arrow-dropup"></ion-icon></button>
|
||||
<ion-fab-list side="top">
|
||||
<button ion-fab (click)="openSocial('facebook', fab4)"><ion-icon name="logo-facebook"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('twitter', fab4)"><ion-icon name="logo-twitter"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('vimeo', fab4)"><ion-icon name="logo-vimeo"></ion-icon></button>
|
||||
<button ion-fab (click)="openSocial('googleplus', fab4)"><ion-icon name="logo-googleplus"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab center middle #fab5>
|
||||
<button ion-fab color="danger" (click)="clickMainFAB()"><ion-icon name="md-share"></ion-icon></button>
|
||||
<ion-fab-list side="top">
|
||||
<button ion-fab (click)="openSocial('vimeo', fab5)"><ion-icon name="logo-vimeo"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="bottom">
|
||||
<button ion-fab (click)="openSocial('facebook', fab5)"><ion-icon name="logo-facebook"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="left">
|
||||
<button ion-fab (click)="openSocial('googleplus', fab5)"><ion-icon name="logo-googleplus"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="right">
|
||||
<button ion-fab (click)="openSocial('twitter', fab5)"><ion-icon name="logo-twitter"></ion-icon></button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab right middle>
|
||||
<button ion-fab color="danger" (click)="add()"><ion-icon name="add"></ion-icon></button>
|
||||
</ion-fab>
|
||||
|
||||
</ion-content>
|
0
src/components/fab/test/fab.spec.ts
Executable file
0
src/components/fab/test/fab.spec.ts
Executable file
@ -290,10 +290,9 @@ export class FullPage {
|
||||
<p><button ion-button id="insert" (click)="insert()">Insert first page into history before this</button></p>
|
||||
<p><button ion-button id="remove" (click)="removeSecond()">Remove second page in history</button></p>
|
||||
<div class="yellow"><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div><div f></div></div>
|
||||
<ion-fixed style="bottom:0">
|
||||
<button ion-button (click)="presentAlert()">fixed button (alert)</button>
|
||||
</ion-fixed>
|
||||
<ion-fixed style="pointer-events: none; top:0; bottom:0; right:0; width:50%; background: rgba(0,0,0,0.5);"></ion-fixed>
|
||||
|
||||
<button ion-button ion-fixed (click)="presentAlert()">fixed button (alert)</button>
|
||||
<div ion-fixed style="position: absolute; pointer-events: none; top:10px; bottom:10px; right:10px; width:50%; background: rgba(0,0,0,0.5);"></div>
|
||||
</ion-content>
|
||||
<ion-footer>
|
||||
<ion-toolbar no-border-bottom>
|
||||
|
@ -11,6 +11,7 @@ import { Checkbox } from './components/checkbox/checkbox';
|
||||
import { Chip } from './components/chip/chip';
|
||||
import { Content } from './components/content/content';
|
||||
import { DateTime } from './components/datetime/datetime';
|
||||
import { Fab, FabButton, FabList } from './components/fab/fab';
|
||||
import { Fixed } from './components/fixed/fixed';
|
||||
import { Grid, Row, Col } from './components/grid/grid';
|
||||
import { Icon } from './components/icon/icon';
|
||||
@ -79,6 +80,7 @@ export { Checkbox } from './components/checkbox/checkbox';
|
||||
export { Chip } from './components/chip/chip';
|
||||
export { Content } from './components/content/content';
|
||||
export { DateTime } from './components/datetime/datetime';
|
||||
export { Fab, FabButton, FabList } from './components/fab/fab';
|
||||
export { Fixed } from './components/fixed/fixed';
|
||||
export { Grid, Row, Col } from './components/grid/grid';
|
||||
export { Icon } from './components/icon/icon';
|
||||
@ -171,6 +173,9 @@ export const IONIC_DIRECTIVES: any[] = [
|
||||
Col,
|
||||
Content,
|
||||
DateTime,
|
||||
Fab,
|
||||
FabButton,
|
||||
FabList,
|
||||
Fixed,
|
||||
Footer,
|
||||
Grid,
|
||||
|
@ -31,7 +31,6 @@
|
||||
|
||||
@import
|
||||
"../components/button/button",
|
||||
"../components/button/button-fab",
|
||||
"../components/button/button-icon",
|
||||
"../components/button/button.ios",
|
||||
"../components/button/button.md",
|
||||
@ -66,6 +65,12 @@
|
||||
"../components/datetime/datetime.md",
|
||||
"../components/datetime/datetime.wp";
|
||||
|
||||
@import
|
||||
"../components/fab/fab",
|
||||
"../components/fab/fab.ios",
|
||||
"../components/fab/fab.md",
|
||||
"../components/fab/fab.wp";
|
||||
|
||||
@import
|
||||
"../components/grid/grid";
|
||||
|
||||
|
@ -32,7 +32,7 @@ $z-index-menu-backdrop: 79;
|
||||
$z-index-overlay: 1000;
|
||||
$z-index-click-block: 9999;
|
||||
|
||||
$z-index-fixed-content: 2;
|
||||
$z-index-fixed-content: 100;
|
||||
$z-index-scroll-content: 1;
|
||||
$z-index-refresher: 0;
|
||||
|
||||
|
@ -4,8 +4,8 @@ export interface PointerEventsConfig {
|
||||
element?: HTMLElement;
|
||||
elementRef?: ElementRef;
|
||||
pointerDown: (ev: any) => boolean;
|
||||
pointerMove: (ev: any) => void;
|
||||
pointerUp: (ev: any) => void;
|
||||
pointerMove?: (ev: any) => void;
|
||||
pointerUp?: (ev: any) => void;
|
||||
nativeOptions?: any;
|
||||
zone?: boolean;
|
||||
}
|
||||
@ -37,7 +37,6 @@ export class PointerEvents {
|
||||
private zone: boolean,
|
||||
private option: any
|
||||
) {
|
||||
|
||||
this.bindTouchEnd = this.handleTouchEnd.bind(this);
|
||||
this.bindMouseUp = this.handleMouseUp.bind(this);
|
||||
|
||||
@ -50,7 +49,7 @@ export class PointerEvents {
|
||||
if (!this.pointerDown(ev)) {
|
||||
return;
|
||||
}
|
||||
if (!this.rmTouchMove) {
|
||||
if (!this.rmTouchMove && this.pointerMove) {
|
||||
this.rmTouchMove = listenEvent(this.ele, 'touchmove', this.zone, this.option, this.pointerMove);
|
||||
}
|
||||
if (!this.rmTouchEnd) {
|
||||
@ -69,7 +68,7 @@ export class PointerEvents {
|
||||
if (!this.pointerDown(ev)) {
|
||||
return;
|
||||
}
|
||||
if (!this.rmMouseMove) {
|
||||
if (!this.rmMouseMove && this.pointerMove) {
|
||||
this.rmMouseMove = listenEvent(document, 'mousemove', this.zone, this.option, this.pointerMove);
|
||||
}
|
||||
if (!this.rmMouseUp) {
|
||||
@ -79,12 +78,12 @@ export class PointerEvents {
|
||||
|
||||
private handleTouchEnd(ev: any) {
|
||||
this.stopTouch();
|
||||
this.pointerUp(ev);
|
||||
this.pointerUp && this.pointerUp(ev);
|
||||
}
|
||||
|
||||
private handleMouseUp(ev: any) {
|
||||
this.stopMouse();
|
||||
this.pointerUp(ev);
|
||||
this.pointerUp && this.pointerUp(ev);
|
||||
}
|
||||
|
||||
private stopTouch() {
|
||||
@ -147,7 +146,7 @@ export class UIEventManager {
|
||||
element = config.elementRef.nativeElement;
|
||||
}
|
||||
|
||||
if (!element || !config.pointerDown || !config.pointerMove || !config.pointerUp) {
|
||||
if (!element || !config.pointerDown) {
|
||||
console.error('PointerEvents config is invalid');
|
||||
return;
|
||||
}
|
||||
|
Reference in New Issue
Block a user