mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
feat(fab): add fab container and buttons
This commit is contained in:
107
packages/core/src/components/fab/fab-container.tsx
Normal file
107
packages/core/src/components/fab/fab-container.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import { Component, h, Method } from '@stencil/core';
|
||||
|
||||
|
||||
/**
|
||||
* @name FabContainer
|
||||
* @module ionic
|
||||
*
|
||||
* @description
|
||||
* `<ion-fab>` is not a FAB button by itself but a container that assist the fab button (`<button ion-fab>`) allowing it
|
||||
* to be placed in fixed position that does not scroll with the content. It is also used to implement "material design speed dial",
|
||||
* ie. a FAB buttons displays a small lists of related actions when clicked.
|
||||
*
|
||||
* @property [top] - Places the container on the top of the content
|
||||
* @property [bottom] - Places the container on the bottom of the content
|
||||
* @property [left] - Places the container on the left
|
||||
* @property [right] - Places the container on the right
|
||||
* @property [middle] - Places the container on the middle vertically
|
||||
* @property [center] - Places the container on the center horizontally
|
||||
* @property [edge] - Used to place the container between the content and the header/footer
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
* <!-- this fab is placed at top right -->
|
||||
* <ion-content>
|
||||
* <ion-fab top right>
|
||||
* <button ion-fab>Button</button>
|
||||
* </ion-fab>
|
||||
*
|
||||
* <!-- this fab is placed at the center of the content viewport -->
|
||||
* <ion-fab center middle>
|
||||
* <button ion-fab>Button</button>
|
||||
* </ion-fab>
|
||||
* </ion-content>
|
||||
* ```
|
||||
*
|
||||
* Ionic's FAB also supports "material design's fab speed dial". It is a normal fab button
|
||||
* that shows a list of related actions when clicked.
|
||||
*
|
||||
* The same `ion-fab` container can contain several `ion-fab-list` with different side values:
|
||||
* `top`, `bottom`, `left` and `right`. For example, if you want to have a list of button that are
|
||||
* on the top of the main button, you should use `side="top"` and so on. By default, if side is ommited, `side="bottom"`.
|
||||
*
|
||||
* ```html
|
||||
* <ion-content>
|
||||
* <!-- this fab is placed at bottom right -->
|
||||
* <ion-fab bottom right >
|
||||
* <button ion-fab>Share</button>
|
||||
* <ion-fab-list side="top">
|
||||
* <button ion-fab>Facebook</button>
|
||||
* <button ion-fab>Twitter</button>
|
||||
* <button ion-fab>Youtube</button>
|
||||
* </ion-fab-list>
|
||||
* <ion-fab-list side="left">
|
||||
* <button ion-fab>Vimeo</button>
|
||||
* </ion-fab-list>
|
||||
* </ion-fab>
|
||||
* </ion-content>
|
||||
* ```
|
||||
*
|
||||
* A FAB speed dial can also be closed programatically.
|
||||
*
|
||||
* ```html
|
||||
* <ion-content>
|
||||
* <ion-fab bottom right #fab>
|
||||
* <button ion-fab>Share</button>
|
||||
* <ion-fab-list side="top">
|
||||
* <button ion-fab (click)="share('facebook', fab)">Facebook</button>
|
||||
* <button ion-fab (click)="share('twitter', fab)">Twitter</button>
|
||||
* </ion-fab-list>
|
||||
* </ion-fab>
|
||||
* </ion-content>
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* share(socialNet: string, fab: FabContainer) {
|
||||
* fab.close();
|
||||
* console.log("Sharing in", socialNet);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/fab/
|
||||
* @see {@link /docs/components#fabs FAB Component Docs}
|
||||
*/
|
||||
|
||||
@Component({
|
||||
tag: 'ion-fab',
|
||||
})
|
||||
export class FabContainer {
|
||||
$el: HTMLElement;
|
||||
|
||||
/**
|
||||
* Close an active FAB list container
|
||||
*/
|
||||
@Method()
|
||||
close() {
|
||||
const fab: any = this.$el.querySelector('ion-fab-button');
|
||||
fab.close();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<slot></slot>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
63
packages/core/src/components/fab/fab-list.tsx
Normal file
63
packages/core/src/components/fab/fab-list.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { Component, h, PropDidChange, State, VNodeData } from '@stencil/core';
|
||||
|
||||
import { HostElement } from '../../utils/interfaces';
|
||||
|
||||
|
||||
/**
|
||||
* @name FabList
|
||||
* @description
|
||||
* `ion-fab-list` is a container for multiple FAB buttons. They are components of `ion-fab` and allow you to specificy the buttons position, left, right, top, bottom.
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
* <ion-fab bottom right>
|
||||
* <button ion-fab>Share</button>
|
||||
* <ion-fab-list side="top">
|
||||
* <button ion-fab>Facebook</button>
|
||||
* <button ion-fab>Twitter</button>
|
||||
* <button ion-fab>Youtube</button>
|
||||
* </ion-fab-list>
|
||||
* <ion-fab-list side="left">
|
||||
* <button ion-fab>Vimeo</button>
|
||||
* </ion-fab-list>
|
||||
* </ion-fab>
|
||||
* ```
|
||||
* @module ionic
|
||||
*
|
||||
* @demo /docs/demos/src/fab/
|
||||
* @see {@link /docs/components#fab Fab Component Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-fab-list',
|
||||
})
|
||||
export class FabList {
|
||||
$el: HTMLElement;
|
||||
|
||||
@State() activated: boolean = false;
|
||||
|
||||
@PropDidChange('activated')
|
||||
activatedChange(activated: boolean) {
|
||||
const fabs = this.$el.querySelectorAll('ion-fab-button') as NodeListOf<HostElement>;
|
||||
|
||||
// if showing the fabs add a timeout, else show immediately
|
||||
var timeout = activated ? 30 : 0;
|
||||
for (var i = 0; i < fabs.length; i++) {
|
||||
const fab = fabs[i].$instance;
|
||||
setTimeout(() => fab.show = activated, i * timeout);
|
||||
}
|
||||
}
|
||||
|
||||
hostData(): VNodeData {
|
||||
return {
|
||||
class: {
|
||||
'fab-list-active': this.activated
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<slot></slot>
|
||||
);
|
||||
}
|
||||
}
|
75
packages/core/src/components/fab/fab.ios.scss
Executable file
75
packages/core/src/components/fab/fab.ios.scss
Executable file
@ -0,0 +1,75 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "./fab";
|
||||
|
||||
// iOS FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Background color of the button
|
||||
$fab-ios-background-color: color($colors-ios, primary) !default;
|
||||
|
||||
/// @prop - Text color of the button
|
||||
$fab-ios-text-color: color-contrast($colors-ios, $fab-ios-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button
|
||||
$fab-ios-background-color-activated: color-shade($fab-ios-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the button in a list
|
||||
$fab-ios-list-button-background-color: $fab-list-button-background-color !default;
|
||||
|
||||
/// @prop - Text color of the button in a list
|
||||
$fab-ios-list-button-text-color: color-contrast($colors-ios, $fab-ios-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button in a list
|
||||
$fab-ios-list-button-background-color-activated: color-shade($fab-ios-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Transition duration of the transform and opacity of the button in a list
|
||||
$fab-ios-list-button-transition-duration: 200ms !default;
|
||||
|
||||
/// @prop - Speed curve of the transition of the transform and opacity of the button in a list
|
||||
$fab-ios-list-button-transition-timing-function: ease !default;
|
||||
|
||||
/// @prop - Transition delay of the transform and opacity of the button in a list
|
||||
$fab-ios-list-button-transition-delay: 10ms !default;
|
||||
|
||||
|
||||
.fab-ios {
|
||||
color: $fab-ios-text-color;
|
||||
background-color: $fab-ios-background-color;
|
||||
}
|
||||
|
||||
.fab-ios.activated {
|
||||
background-color: $fab-ios-background-color-activated;
|
||||
}
|
||||
|
||||
.fab-ios-in-list {
|
||||
color: $fab-ios-list-button-text-color;
|
||||
background-color: $fab-ios-list-button-background-color;
|
||||
|
||||
transition: transform $fab-ios-list-button-transition-duration $fab-ios-list-button-transition-timing-function $fab-ios-list-button-transition-delay,
|
||||
opacity $fab-ios-list-button-transition-duration $fab-ios-list-button-transition-timing-function $fab-ios-list-button-transition-delay;
|
||||
}
|
||||
|
||||
.fab-ios-in-list.activated {
|
||||
background-color: $fab-ios-list-button-background-color-activated;
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
|
||||
|
||||
$bg-color: $color-base;
|
||||
$bg-color-activated: color-shade($bg-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-ios-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $bg-color;
|
||||
}
|
||||
|
||||
.fab-ios-#{$color-name}.activated {
|
||||
background-color: $bg-color-activated;
|
||||
}
|
||||
}
|
||||
|
106
packages/core/src/components/fab/fab.md.scss
Executable file
106
packages/core/src/components/fab/fab.md.scss
Executable file
@ -0,0 +1,106 @@
|
||||
@import "../../themes/ionic.globals.md";
|
||||
@import "./fab";
|
||||
|
||||
// Material Design FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Box shadow of the FAB button
|
||||
$fab-md-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
|
||||
$fab-md-box-shadow-activated: 0 5px 15px 0 rgba(0, 0, 0, .4), 0 4px 7px 0 rgba(0, 0, 0, .1) !default;
|
||||
|
||||
/// @prop - Background color of the button
|
||||
$fab-md-background-color: color($colors-md, primary) !default;
|
||||
|
||||
/// @prop - Text color of the button
|
||||
$fab-md-text-color: color-contrast($colors-md, $fab-md-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button
|
||||
$fab-md-background-color-activated: color-shade($fab-md-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the button in a list
|
||||
$fab-md-list-button-background-color: $fab-list-button-background-color !default;
|
||||
|
||||
/// @prop - Text color of the button in a list
|
||||
$fab-md-list-button-text-color: color-contrast($colors-md, $fab-md-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button in a list
|
||||
$fab-md-list-button-background-color-activated: color-shade($fab-md-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Transition duration of the transform and opacity of the button in a list
|
||||
$fab-md-list-button-transition-duration: 200ms !default;
|
||||
|
||||
/// @prop - Speed curve of the transition of the transform and opacity of the button in a list
|
||||
$fab-md-list-button-transition-timing-function: ease !default;
|
||||
|
||||
/// @prop - Transition delay of the transform and opacity of the button in a list
|
||||
$fab-md-list-button-transition-delay: 10ms !default;
|
||||
|
||||
$fab-button-md-transition-duration: 300ms !default;
|
||||
|
||||
$fab-button-md-transition-timing-function: cubic-bezier(.4, 0, .2, 1) !default;
|
||||
|
||||
|
||||
.fab-md {
|
||||
color: $fab-md-text-color;
|
||||
background-color: $fab-md-background-color;
|
||||
|
||||
box-shadow: $fab-md-box-shadow;
|
||||
|
||||
transition: box-shadow $fab-button-md-transition-duration $fab-button-md-transition-timing-function,
|
||||
background-color $fab-button-md-transition-duration $fab-button-md-transition-timing-function,
|
||||
color $fab-button-md-transition-duration $fab-button-md-transition-timing-function;
|
||||
}
|
||||
|
||||
.fab-md.activated {
|
||||
background-color: $fab-md-background-color-activated;
|
||||
box-shadow: $fab-md-box-shadow-activated;
|
||||
}
|
||||
|
||||
.fab-md-in-list {
|
||||
color: $fab-md-list-button-text-color;
|
||||
background-color: $fab-md-list-button-background-color;
|
||||
|
||||
transition: transform $fab-md-list-button-transition-duration $fab-md-list-button-transition-timing-function $fab-md-list-button-transition-delay,
|
||||
opacity $fab-md-list-button-transition-duration $fab-md-list-button-transition-timing-function $fab-md-list-button-transition-delay,
|
||||
box-shadow $fab-button-md-transition-duration $fab-button-md-transition-timing-function,
|
||||
background-color $fab-button-md-transition-duration $fab-button-md-transition-timing-function,
|
||||
color $fab-button-md-transition-duration $fab-button-md-transition-timing-function;
|
||||
}
|
||||
|
||||
.fab-md-in-list.activated {
|
||||
background-color: $fab-md-list-button-background-color-activated;
|
||||
}
|
||||
|
||||
// Material Design FAB Ripple
|
||||
// --------------------------------------------------
|
||||
|
||||
.fab-md .button-effect {
|
||||
background-color: color-contrast($colors-md, $fab-md-background-color);
|
||||
}
|
||||
|
||||
|
||||
// Generate MD FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
|
||||
|
||||
$bg-color: $color-base;
|
||||
$bg-color-activated: color-shade($bg-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-md-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $bg-color;
|
||||
}
|
||||
|
||||
.fab-md-#{$color-name}.activated {
|
||||
background-color: $bg-color-activated;
|
||||
}
|
||||
|
||||
.fab-md-#{$color-name} .button-effect {
|
||||
background-color: $fg-color;
|
||||
}
|
||||
}
|
||||
|
224
packages/core/src/components/fab/fab.scss
Executable file
224
packages/core/src/components/fab/fab.scss
Executable file
@ -0,0 +1,224 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Floating Action Buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Width and height of the FAB button
|
||||
$fab-size: 56px !default;
|
||||
|
||||
/// @prop - Width and height of the FAB button mini
|
||||
$fab-mini-size: 40px !default;
|
||||
|
||||
/// @prop - Margin of the FAB Container
|
||||
$fab-content-margin: 10px !default;
|
||||
|
||||
/// @prop - Margin of the FAB List
|
||||
$fab-list-margin: 10px !default;
|
||||
|
||||
/// @prop - Background color of the button in a list
|
||||
$fab-list-button-background-color: #f4f4f4 !default;
|
||||
|
||||
|
||||
.fab {
|
||||
@include text-align(center);
|
||||
@include appearance(none);
|
||||
@include border-radius(50%);
|
||||
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
width: $fab-size;
|
||||
height: $fab-size;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: $fab-size;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: background-color, opacity 100ms linear;
|
||||
|
||||
background-clip: padding-box;
|
||||
font-kerning: none;
|
||||
user-select: none;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.fab ion-icon {
|
||||
flex: 1;
|
||||
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
// FAB mini
|
||||
// --------------------------------------------------
|
||||
|
||||
.fab[mini] {
|
||||
@include margin(($fab-size - $fab-mini-size) / 2);
|
||||
|
||||
width: $fab-mini-size;
|
||||
height: $fab-mini-size;
|
||||
|
||||
line-height: $fab-mini-size;
|
||||
}
|
||||
|
||||
.fab[mini] .fab-close-icon {
|
||||
line-height: $fab-mini-size;
|
||||
}
|
||||
|
||||
|
||||
// FAB container
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fab {
|
||||
position: absolute;
|
||||
z-index: $z-index-fixed-content;
|
||||
|
||||
&[center] {
|
||||
@include position(null, null, null, 50%);
|
||||
@include margin-horizontal(-$fab-size / 2, null);
|
||||
}
|
||||
|
||||
&[middle] {
|
||||
@include margin(-$fab-size / 2, null, null, null);
|
||||
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&[top] {
|
||||
top: $fab-content-margin;
|
||||
}
|
||||
|
||||
&[right] {
|
||||
// scss-lint:disable PropertySpelling
|
||||
@include multi-dir() {
|
||||
right: $fab-content-margin;
|
||||
}
|
||||
}
|
||||
|
||||
&[end] {
|
||||
@include position-horizontal(null, $fab-content-margin);
|
||||
}
|
||||
|
||||
&[bottom] {
|
||||
bottom: $fab-content-margin;
|
||||
}
|
||||
|
||||
&[left] {
|
||||
// scss-lint:disable PropertySpelling
|
||||
@include multi-dir() {
|
||||
left: $fab-content-margin;
|
||||
}
|
||||
}
|
||||
|
||||
&[start] {
|
||||
@include position-horizontal($fab-content-margin, null);
|
||||
}
|
||||
|
||||
&[top][edge] {
|
||||
top: -$fab-size / 2;
|
||||
}
|
||||
|
||||
&[bottom][edge] {
|
||||
bottom: -$fab-size / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FAB list (speed dial)
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fab-list {
|
||||
@include margin($fab-size + $fab-list-margin, 0);
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: none;
|
||||
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
min-width: $fab-size;
|
||||
min-height: $fab-size;
|
||||
}
|
||||
|
||||
.fab-in-list {
|
||||
@include margin(8px, 0);
|
||||
|
||||
width: $fab-mini-size;
|
||||
height: $fab-mini-size;
|
||||
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.fab-in-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
ion-fab-list[side=left] .fab-in-list,
|
||||
ion-fab-list[side=right] .fab-in-list {
|
||||
@include margin(0, 8px);
|
||||
}
|
||||
|
||||
ion-fab-list[side=top] {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
ion-fab-list[side=left] {
|
||||
@include margin(0, $fab-size + $fab-list-margin);
|
||||
@include position-horizontal(null, 0);
|
||||
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
ion-fab-list[side=right] {
|
||||
@include margin(0, $fab-size + $fab-list-margin);
|
||||
@include position(null, null, null, 0);
|
||||
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
|
||||
// FAB animation
|
||||
// --------------------------------------------------
|
||||
|
||||
.fab-list-active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fab-close-icon {
|
||||
@include position(0, 0, null, 0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
line-height: $fab-size;
|
||||
opacity: 0;
|
||||
transform: scale(.4) rotateZ(-45deg);
|
||||
transition: all ease-in-out 300ms;
|
||||
transition-property: transform, opacity;
|
||||
}
|
||||
|
||||
.fab .button-inner {
|
||||
transition: all ease-in-out 300ms;
|
||||
transition-property: transform, opacity;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
201
packages/core/src/components/fab/fab.tsx
Executable file
201
packages/core/src/components/fab/fab.tsx
Executable file
@ -0,0 +1,201 @@
|
||||
import { Component, CssClassObject, h, Method, Prop, State } from '@stencil/core';
|
||||
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
import { HostElement } from '../../utils/interfaces';
|
||||
|
||||
|
||||
/**
|
||||
* @name FabButton
|
||||
* @module ionic
|
||||
*
|
||||
* @description
|
||||
* FABs (Floating Action Buttons) are standard material design components. They are shaped as a circle that represents a promoted action. When pressed, it may contain more related actions.
|
||||
* FABs as its name suggests are floating over the content in a fixed position. This is not achieved exclusively with `<button ion-fab>Button</button>` but it has to wrapped with the `<ion-fab>` component, like this:
|
||||
*
|
||||
* ```html
|
||||
* <ion-content>
|
||||
* <!-- Real floating action button, fixed. It will not scroll with the content -->
|
||||
* <ion-fab>
|
||||
* <button ion-fab>Button</button>
|
||||
* </ion-fab>
|
||||
*
|
||||
* <!-- Button shaped as a circle that just like a normal button scrolls with the content -->
|
||||
* <button ion-fab>Button</button>
|
||||
* </ion-content>
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* In case the button is not wrapped with `<ion-fab>`, the fab button will behave like a normal button, scrolling with the content.
|
||||
*
|
||||
* See [ion-fab] to learn more information about how to position the fab button.
|
||||
*
|
||||
* @property [mini] - Makes a fab button with a reduced size.
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
*
|
||||
* <!-- Colors -->
|
||||
* <ion-fab>
|
||||
* <button ion-fab color="primary">Button</button>
|
||||
* </ion-fab>
|
||||
*
|
||||
* <!-- Mini -->
|
||||
* <ion-fab>
|
||||
* <button ion-fab mini>Small</button>
|
||||
* </ion-fab>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/fab/
|
||||
* @see {@link /docs/components#fabs FAB Component Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-fab-button',
|
||||
styleUrls: {
|
||||
ios: 'fab.ios.scss',
|
||||
md: 'fab.md.scss',
|
||||
wp: 'fab.wp.scss'
|
||||
}
|
||||
})
|
||||
export class FabButton {
|
||||
$el: HTMLElement;
|
||||
mode: string;
|
||||
color: string;
|
||||
|
||||
@State() activated: boolean = false;
|
||||
|
||||
@State() closeActivated: boolean = false;
|
||||
|
||||
@State() show: boolean = false;
|
||||
|
||||
@State() inContainer: boolean = false;
|
||||
|
||||
@State() inList: boolean = false;
|
||||
|
||||
@Prop() href: string;
|
||||
|
||||
/**
|
||||
* @Prop {boolean} If true, sets the button into a disabled state.
|
||||
*/
|
||||
@Prop() disabled: boolean = false;
|
||||
|
||||
ionViewDidLoad() {
|
||||
const parentNode = this.$el.parentNode.nodeName;
|
||||
|
||||
this.inList = (parentNode === 'ION-FAB-LIST');
|
||||
this.inContainer = (parentNode === 'ION-FAB');
|
||||
}
|
||||
|
||||
clickedFab() {
|
||||
if (this.inContainer) {
|
||||
const activated = !this.activated;
|
||||
this.setActiveLists(activated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
setActiveLists(activated: boolean) {
|
||||
const lists = this.$el.parentElement.querySelectorAll('ion-fab-list') as NodeListOf<HostElement>;
|
||||
|
||||
if (lists.length > 0) {
|
||||
this.activated = activated;
|
||||
}
|
||||
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
const list = lists[i].$instance;
|
||||
list.activated = activated;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close an active FAB list container
|
||||
*/
|
||||
@Method()
|
||||
close() {
|
||||
this.setActiveLists(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Get the element classes to add to the child element
|
||||
*/
|
||||
getElementClassList() {
|
||||
let classList = [].concat(
|
||||
this.$el.className.length ? this.$el.className.split(' ') : []
|
||||
);
|
||||
|
||||
return classList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Get the classes for fab buttons in lists
|
||||
*/
|
||||
getFabListClassList() {
|
||||
if (!this.inList) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
`fab-in-list`,
|
||||
`fab-${this.mode}-in-list`
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Get the close active class for fab buttons
|
||||
*/
|
||||
getFabActiveClassList() {
|
||||
if (!this.activated) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
`fab-close-active`
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Get the show class for fab buttons
|
||||
*/
|
||||
getFabShowClassList() {
|
||||
if (!this.show) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
`show`
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const themedClasses = createThemedClasses(this.mode, this.color, 'fab');
|
||||
|
||||
var fabClasses: CssClassObject = []
|
||||
.concat(
|
||||
this.getElementClassList(),
|
||||
this.getFabListClassList(),
|
||||
this.getFabActiveClassList(),
|
||||
this.getFabShowClassList()
|
||||
)
|
||||
.reduce((prevValue, cssClass) => {
|
||||
prevValue[cssClass] = true;
|
||||
return prevValue;
|
||||
}, {});
|
||||
|
||||
const TagType = this.href ? 'a' : 'button';
|
||||
|
||||
fabClasses = Object.assign(fabClasses, themedClasses);
|
||||
|
||||
return (
|
||||
<TagType class={fabClasses} onclick={this.clickedFab.bind(this)} disabled={this.disabled}>
|
||||
<ion-icon name="close" class="fab-close-icon"></ion-icon>
|
||||
<span class='button-inner'>
|
||||
<slot></slot>
|
||||
</span>
|
||||
<div class='button-effect'></div>
|
||||
</TagType>
|
||||
);
|
||||
}
|
||||
}
|
74
packages/core/src/components/fab/fab.wp.scss
Executable file
74
packages/core/src/components/fab/fab.wp.scss
Executable file
@ -0,0 +1,74 @@
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
@import "./fab";
|
||||
|
||||
// Windows FAB Button
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Background color of the button
|
||||
$fab-wp-background-color: color($colors-wp, primary) !default;
|
||||
|
||||
/// @prop - Text color of the button
|
||||
$fab-wp-text-color: color-contrast($colors-wp, $fab-wp-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button
|
||||
$fab-wp-background-color-activated: color-shade($fab-wp-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the button in a list
|
||||
$fab-wp-list-button-background-color: $fab-list-button-background-color !default;
|
||||
|
||||
/// @prop - Text color of the button in a list
|
||||
$fab-wp-list-button-text-color: color-contrast($colors-wp, $fab-wp-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Background color of the activated button in a list
|
||||
$fab-wp-list-button-background-color-activated: color-shade($fab-wp-list-button-background-color) !default;
|
||||
|
||||
/// @prop - Transition duration of the transform and opacity of the button in a list
|
||||
$fab-wp-list-button-transition-duration: 200ms !default;
|
||||
|
||||
/// @prop - Speed curve of the transition of the transform and opacity of the button in a list
|
||||
$fab-wp-list-button-transition-timing-function: ease !default;
|
||||
|
||||
/// @prop - Transition delay of the transform and opacity of the button in a list
|
||||
$fab-wp-list-button-transition-delay: 10ms !default;
|
||||
|
||||
|
||||
.fab-wp {
|
||||
color: $fab-wp-text-color;
|
||||
background-color: $fab-wp-background-color;
|
||||
}
|
||||
|
||||
.fab-wp.activated {
|
||||
background-color: $fab-wp-background-color-activated;
|
||||
}
|
||||
|
||||
.fab-wp-in-list {
|
||||
color: $fab-wp-list-button-text-color;
|
||||
background-color: $fab-wp-list-button-background-color;
|
||||
|
||||
transition: transform $fab-wp-list-button-transition-duration $fab-wp-list-button-transition-timing-function $fab-wp-list-button-transition-delay,
|
||||
opacity $fab-wp-list-button-transition-duration $fab-wp-list-button-transition-timing-function $fab-wp-list-button-transition-delay;
|
||||
}
|
||||
|
||||
.fab-wp-in-list.activated {
|
||||
background-color: $fab-wp-list-button-background-color-activated;
|
||||
}
|
||||
|
||||
|
||||
// Generate WP FAB colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
|
||||
|
||||
$bg-color: $color-base;
|
||||
$bg-color-activated: color-shade($bg-color);
|
||||
$fg-color: $color-contrast;
|
||||
|
||||
.fab-wp-#{$color-name} {
|
||||
color: $fg-color;
|
||||
background-color: $bg-color;
|
||||
}
|
||||
|
||||
.fab-wp-#{$color-name}.activated {
|
||||
background-color: $bg-color-activated;
|
||||
}
|
||||
}
|
145
packages/core/src/components/fab/test/basic.html
Normal file
145
packages/core/src/components/fab/test/basic.html
Normal file
@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ionic FAB</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<script src="/dist/ionic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Header</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-fixed>
|
||||
<ion-fab top right edge id="fab1">
|
||||
<ion-fab-button onclick="clickMainFAB()" mini class="e2eFabTopRight"><ion-icon name="add"></ion-icon></ion-fab-button>
|
||||
<ion-fab-list>
|
||||
<ion-fab-button onclick="openSocial('facebook', 'fab1')"><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('twitter', 'fab1')"><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('vimeo', 'fab1')"><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('googleplus', 'fab1')"><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab bottom right edge if="fab2">
|
||||
<ion-fab-button onclick="clickMainFAB()" color="dark" class="e2eFabBottomRight"><ion-icon name="arrow-dropleft"></ion-icon></ion-fab-button>
|
||||
<ion-fab-list side="left">
|
||||
<ion-fab-button onclick="openSocial('facebook', 'fab2')"><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('twitter', 'fab2')"><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('vimeo', 'fab2')"><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('googleplus', 'fab2')"><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab top left id="fab3">
|
||||
<ion-fab-button onclick="clickMainFAB()" color="secondary" class="e2eFabTopLeft"><ion-icon name="arrow-dropright"></ion-icon></ion-fab-button>
|
||||
<ion-fab-list side="right">
|
||||
<ion-fab-button onclick="openSocial('facebook', 'fab3')"><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('twitter', 'fab3')"><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('vimeo', 'fab3')"><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('googleplus', 'fab3')"><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab bottom left id="fab4">
|
||||
<ion-fab-button onclick="clickMainFAB()" color="light" class="e2eFabBottomLeft"><ion-icon name="arrow-dropup"></ion-icon></ion-fab-button>
|
||||
<ion-fab-list side="top">
|
||||
<ion-fab-button onclick="openSocial('facebook', 'fab4')"><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('twitter', 'fab4')"><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('vimeo', 'fab4')"><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
|
||||
<ion-fab-button onclick="openSocial('googleplus', 'fab4')"><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab center middle id="fab5">
|
||||
<ion-fab-button onclick="clickMainFAB()" color="danger" class="e2eFabCenter"><ion-icon name="md-share"></ion-icon></ion-fab-button>
|
||||
<ion-fab-list side="top">
|
||||
<ion-fab-button onclick="openSocial('vimeo', 'fab5')" color="primary"><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="bottom">
|
||||
<ion-fab-button onclick="openSocial('facebook', 'fab5')" color="secondary"><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="left">
|
||||
<ion-fab-button onclick="openSocial('googleplus', 'fab5')" color="light"><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
<ion-fab-list side="right">
|
||||
<ion-fab-button onclick="openSocial('twitter', 'fab5')" color="dark"><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
|
||||
</ion-fab-list>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab right middle>
|
||||
<ion-fab-button color="danger" onclick="add()"><ion-icon name="add"></ion-icon></ion-fab-button>
|
||||
</ion-fab>
|
||||
</ion-fixed>
|
||||
|
||||
<ion-content padding fullscreen>
|
||||
<f></f>
|
||||
<f></f>
|
||||
|
||||
<pre id="log" ion-fixed style="right:10px; bottom:50px; text-shadow: 0 0 2px rgba(0, 0, 0, 0.24);">log</pre>
|
||||
<ion-button>Test</ion-button>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-title>Footer</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
|
||||
<script>
|
||||
function insertAfter(el, referenceNode) {
|
||||
referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);
|
||||
}
|
||||
|
||||
function insertLog(message) {
|
||||
console.log(message);
|
||||
var ele = document.querySelector('#log');
|
||||
const oldHTML = ele.innerHTML;
|
||||
ele.innerHTML = oldHTML + '\n' + message;
|
||||
}
|
||||
|
||||
function add() {
|
||||
var newEle = document.createElement('f');
|
||||
var ref = document.querySelector('f');
|
||||
insertAfter(newEle, ref);
|
||||
insertLog('add');
|
||||
}
|
||||
|
||||
function clickMainFAB() {
|
||||
let message = 'Clicked open social menu';
|
||||
insertLog(message);
|
||||
}
|
||||
|
||||
function openSocial(network, container) {
|
||||
let message = 'Share in ' + network;
|
||||
insertLog(message);
|
||||
|
||||
var fab = document.getElementById(container);
|
||||
fab.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
[ion-fixed] {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
f {
|
||||
display: block;
|
||||
margin: 15px auto;
|
||||
max-width: 150px;
|
||||
height: 150px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
f:last-of-type {
|
||||
background: yellow;
|
||||
}
|
||||
</style>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
@ -4,10 +4,11 @@ exports.config = {
|
||||
publicPath: '/dist',
|
||||
generateCollection: true,
|
||||
bundles: [
|
||||
{ components: ['ion-app', 'ion-content', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-app', 'ion-content', 'ion-fixed', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
||||
{ components: ['ion-button', 'ion-buttons', 'ion-icon'] },
|
||||
{ components: ['ion-card', 'ion-card-content', 'ion-card-header', 'ion-card-title'] },
|
||||
{ components: ['ion-fab', 'ion-fab-button', 'ion-fab-list'] },
|
||||
{ components: ['ion-gesture', 'ion-scroll'], priority: 'low' },
|
||||
{ components: ['ion-item', 'ion-item-divider', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
|
||||
{ components: ['ion-loading', 'ion-loading-controller'] },
|
||||
|
Reference in New Issue
Block a user