diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 1a2b083a47b..a6a97b7b00e 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -239,12 +239,15 @@ export class Fab { } export declare interface FabButton extends StencilComponents<'IonFabButton'> {} -@Component({ selector: 'ion-fab-button', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['mode', 'color', 'activated', 'disabled', 'routerDirection', 'href', 'translucent', 'show'] }) +@Component({ selector: 'ion-fab-button', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['mode', 'color', 'activated', 'disabled', 'href', 'routerDirection', 'show', 'translucent', 'type'] }) export class FabButton { + ionFocus: EventEmitter; + ionBlur: EventEmitter; constructor(r: ElementRef) { const el = r.nativeElement; - proxyInputs(this, el, ['mode', 'color', 'activated', 'disabled', 'routerDirection', 'href', 'translucent', 'show']); + proxyInputs(this, el, ['mode', 'color', 'activated', 'disabled', 'href', 'routerDirection', 'show', 'translucent', 'type']); + proxyOutputs(this, el, ['ionFocus', 'ionBlur']); } } diff --git a/core/src/components.d.ts b/core/src/components.d.ts index c768d7ea3be..a07308d27ee 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1412,6 +1412,10 @@ export namespace Components { * If true, the fab button will be translucent. Defaults to `false`. */ 'translucent': boolean; + /** + * The type of the button. Possible values are: `"submit"`, `"reset"` and `"button"`. Default value is: `"button"` + */ + 'type': 'submit' | 'reset' | 'button'; } interface IonFabButtonAttributes extends StencilHTMLAttributes { /** @@ -1435,6 +1439,14 @@ export namespace Components { */ 'mode'?: Mode; /** + * Emitted when the button loses focus. + */ + 'onIonBlur'?: (event: CustomEvent) => void; + /** + * Emitted when the button has focus. + */ + 'onIonFocus'?: (event: CustomEvent) => void; + /** * When using a router, it specifies the transition direction when navigating to another page using `href`. */ 'routerDirection'?: RouterDirection; @@ -1446,6 +1458,10 @@ export namespace Components { * If true, the fab button will be translucent. Defaults to `false`. */ 'translucent'?: boolean; + /** + * The type of the button. Possible values are: `"submit"`, `"reset"` and `"button"`. Default value is: `"button"` + */ + 'type'?: 'submit' | 'reset' | 'button'; } interface IonFabList { diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 8cf84845f7e..5e3a36edc03 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -166,7 +166,6 @@ export class Button implements ComponentInterface { } render() { - const TagType = this.href === undefined ? 'button' : 'a'; const attrs = (TagType === 'button') ? { type: this.type } diff --git a/core/src/components/fab-button/fab-button.ios.scss b/core/src/components/fab-button/fab-button.ios.scss index 07afbc63a90..6f3e49bb6fa 100755 --- a/core/src/components/fab-button/fab-button.ios.scss +++ b/core/src/components/fab-button/fab-button.ios.scss @@ -10,7 +10,6 @@ } :host(.activated) { - --background: #{current-color(tint)}; --box-shadow: #{$fab-ios-box-shadow-activated}; --transform: #{$fab-ios-transform}; --transition: #{$fab-ios-transition-activated}; @@ -25,10 +24,10 @@ // -------------------------------------------------- :host(.fab-button-in-list) { - --ion-color-contrast: #{$fab-ios-list-button-text-color}; - --ion-color-base: #{$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}; + --background: #{$fab-ios-list-button-background-color}; + --color: #{$fab-ios-list-button-text-color}; + --transition: #{transform 200ms ease 10ms, + opacity 200ms ease 10ms}; } :host(.fab-button-in-list.activated) { diff --git a/core/src/components/fab-button/fab-button.ios.vars.scss b/core/src/components/fab-button/fab-button.ios.vars.scss index 08aa0ed94be..2c578aea79e 100755 --- a/core/src/components/fab-button/fab-button.ios.vars.scss +++ b/core/src/components/fab-button/fab-button.ios.vars.scss @@ -57,14 +57,5 @@ $fab-ios-list-button-icon-fill-color: $fab-ios-list-butt /// @prop - Background color of the activated button in a list $fab-ios-list-button-background-color-activated: ion-color(primary, tint) !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; - /// @prop - Filter of the translucent fab $fab-ios-translucent-filter: saturate(180%) blur(20px) !default; diff --git a/core/src/components/fab-button/fab-button.md.scss b/core/src/components/fab-button/fab-button.md.scss index 9f9e9ad5cf1..28101105597 100755 --- a/core/src/components/fab-button/fab-button.md.scss +++ b/core/src/components/fab-button/fab-button.md.scss @@ -6,9 +6,11 @@ :host { --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}; + --transition: #{ + box-shadow 300ms cubic-bezier(.4, 0, .2, 1), + background-color 300ms cubic-bezier(.4, 0, .2, 1), + color 300ms cubic-bezier(.4, 0, .2, 1) + }; } :host(.activated) { @@ -25,13 +27,15 @@ // -------------------------------------------------- :host(.fab-button-in-list) { - --ion-color-contrast: #{$fab-md-list-button-text-color}; - --ion-color-base: #{$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}; + --color: #{$fab-md-list-button-text-color}; + --background: #{$fab-md-list-button-background-color}; + --transition: #{ + transform 200ms ease 10ms ease 10ms + opacity 200ms ease 10ms ease 10ms + box-shadow 300ms cubic-bezier(.4, 0, .2, 1) + background-color 300ms cubic-bezier(.4, 0, .2, 1) + color 300ms cubic-bezier(.4, 0, .2, 1) + }; } :host(.fab-button-in-list.activated) { diff --git a/core/src/components/fab-button/fab-button.md.vars.scss b/core/src/components/fab-button/fab-button.md.vars.scss index d89f5b50b2f..7ad88066045 100755 --- a/core/src/components/fab-button/fab-button.md.vars.scss +++ b/core/src/components/fab-button/fab-button.md.vars.scss @@ -32,18 +32,3 @@ $fab-md-list-button-icon-fill-color: $fab-md-list-button-text-color /// @prop - Background color of the activated button in a list $fab-md-list-button-background-color-activated: ion-color(primary, tint) !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; - -/// @prop - Transition duration of the box-shadow and background-color of the button -$fab-button-md-transition-duration: 300ms !default; - -/// @prop - Speed curve of the transition of the box-shadow and background-color of the button -$fab-button-md-transition-timing-function: cubic-bezier(.4, 0, .2, 1) !default; diff --git a/core/src/components/fab-button/fab-button.scss b/core/src/components/fab-button/fab-button.scss index 3f8b60926d5..b930f959883 100755 --- a/core/src/components/fab-button/fab-button.scss +++ b/core/src/components/fab-button/fab-button.scss @@ -4,21 +4,56 @@ // -------------------------------------------------- :host { - --ion-color-base: #{ion-color(primary, base)}; - --ion-color-contrast: #{ion-color(primary, contrast)}; - --ion-color-tint: #{ion-color(primary, tint)}; - --size: #{$fab-size}; - --background: #{current-color(base)}; + /** + * @prop --background: Background of the button + * @prop --background-activated: Background of the button when activated + * @prop --background-focused: Background of the button when focused + * + * @prop --color: Text color of the button + * @prop --color-activated: Text color of the button when activated + * @prop --color-focused: Text color of the button when focused + * + * @prop --width: Width of the button + * @prop --height: Height of the button + * + * @prop --transition: Transition of the button + * + * @prop --border-radius: Border radius of the button + * @prop --border-width: Border width of the button + * @prop --border-style: Border style of the button + * @prop --border-color: Border color of the button + * + * @prop --ripple-color: Color of the button ripple effect + * + * @prop --box-shadow: Box shadow of the button + * + * @prop --margin-top: Margin top of the button + * @prop --margin-end: Margin end of the button + * @prop --margin-bottom: Margin bottom of the button + * @prop --margin-start: Margin start of the button + * + * @prop --padding-top: Padding top of the button + * @prop --padding-end: Padding end of the button + * @prop --padding-bottom: Padding bottom of the button + * @prop --padding-start: Padding start of the button + */ + --background: #{ion-color(primary, base)}; + --background-activated: #{ion-color(primary, shade)}; + --background-focused: #{ion-color(primary, shade)}; + --color: #{ion-color(primary, contrast)}; + --color-activated: #{ion-color(primary, contrast)}; + --color-focused: var(--color-activated); + --width: #{$fab-size}; + --height: var(--width); + --margin-start: calc((#{$fab-size} - var(--width)) / 2); + --margin-end: calc((#{$fab-size} - var(--width)) / 2); + --margin-top: calc((#{$fab-size} - var(--height)) / 2); + --margin-bottom: calc((#{$fab-size} - var(--height)) / 2); --transition: background-color, opacity 100ms linear; - --padding-start: calc((#{$fab-size} - var(--size)) / 2); - --padding-end: calc((#{$fab-size} - var(--size)) / 2); - --padding-top: calc((#{$fab-size} - var(--size)) / 2); - --padding-bottom: calc((#{$fab-size} - var(--size)) / 2); + --ripple-color: currentColor; display: block; - color: #{current-color(contrast)}; - font-size: 14px; text-align: center; @@ -29,24 +64,34 @@ font-kerning: none; } -:host(.fab-button-disabled) { - pointer-events: none; + +// FAB with Color +// -------------------------------------------------- + +// Default Button with Color +:host(.ion-color) .button-native { + background: #{current-color(base)}; + color: #{current-color(contrast)}; } -:host(.activated) { - --background: #{current-color(tint)}; +// Focused/Activated Button with Color +:host(.ion-color.focused) .button-native, +:host(.ion-color.activated) .button-native { + background: #{current-color(shade)}; + color: #{current-color(contrast)}; } .button-native { - @include text-inherit(); @include border-radius(50%); - @include margin(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start)); + @include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start)); + @include margin(var(--margin-top), var(--margin-end), var(--margin-bottom), var(--margin-start)); + @include text-inherit(); display: block; position: relative; - width: var(--size); - height: var(--size); + width: var(--width); + height: var(--height); transform: var(--transform); @@ -57,9 +102,11 @@ outline: none; background: var(--background); + background-clip: padding-box; + color: var(--color); - line-height: var(--size); + line-height: var(--heigh); box-shadow: var(--box-shadow); contain: strict; @@ -97,11 +144,45 @@ } +// FAB States +// -------------------------------------------------- + +// Activated / Pressed Button +:host(.activated) .button-native { + background: var(--background-activated); + color: var(--color-activated); +} + +// Focused Button +:host(.focused) .button-native { + background: var(--background-focused); + color: var(--color-focused); +} + +// Disabled Button +:host(.fab-button-disabled) { + pointer-events: none; +} + +.button-native[disabled] { + cursor: default; + opacity: .5; + pointer-events: none; +} + +// FAB Content +// -------------------------------------------------- + +::slotted(ion-icon) { + line-height: 1; +} + + // FAB Mini // -------------------------------------------------- :host([mini]) { - --size: #{$fab-mini-size}; + --width: #{$fab-mini-size}; } // FAB Close Icon diff --git a/core/src/components/fab-button/fab-button.tsx b/core/src/components/fab-button/fab-button.tsx index eaef8ac89f4..fa97bbba49f 100755 --- a/core/src/components/fab-button/fab-button.tsx +++ b/core/src/components/fab-button/fab-button.tsx @@ -1,4 +1,4 @@ -import { Component, ComponentInterface, Element, Prop } from '@stencil/core'; +import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, State } from '@stencil/core'; import { Color, Mode, RouterDirection } from '../../interface'; import { createColorClasses, hostContext, openURL } from '../../utils/theme'; @@ -12,10 +12,11 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme'; shadow: true }) export class FabButton implements ComponentInterface { + @Element() el!: HTMLElement; - @Prop({ context: 'window' }) win!: Window; + @State() keyFocus = false; - @Element() el!: HTMLElement; + @Prop({ context: 'window' }) win!: Window; /** * The mode determines which platform styles to use. @@ -40,6 +41,12 @@ export class FabButton implements ComponentInterface { */ @Prop() disabled = false; + /** + * Contains a URL or a URL fragment that the hyperlink points to. + * If this property is set, an anchor tag will be rendered. + */ + @Prop() href?: string; + /** * When using a router, it specifies the transition direction when navigating to * another page using `href`. @@ -47,10 +54,9 @@ export class FabButton implements ComponentInterface { @Prop() routerDirection?: RouterDirection; /** - * Contains a URL or a URL fragment that the hyperlink points to. - * If this property is set, an anchor tag will be rendered. + * If true, the fab button will show when in a fab-list. */ - @Prop() href?: string; + @Prop() show = false; /** * If true, the fab button will be translucent. Defaults to `false`. @@ -58,9 +64,34 @@ export class FabButton implements ComponentInterface { @Prop() translucent = false; /** - * If true, the fab button will show when in a fab-list. + * The type of the button. + * Possible values are: `"submit"`, `"reset"` and `"button"`. + * Default value is: `"button"` */ - @Prop() show = false; + @Prop() type: 'submit' | 'reset' | 'button' = 'button'; + + /** + * Emitted when the button has focus. + */ + @Event() ionFocus!: EventEmitter; + + /** + * Emitted when the button loses focus. + */ + @Event() ionBlur!: EventEmitter; + + private onFocus = () => { + this.ionFocus.emit(); + } + + private onKeyUp = () => { + this.keyFocus = true; + } + + private onBlur = () => { + this.keyFocus = false; + this.ionBlur.emit(); + } hostData() { const inList = hostContext('ion-fab-list', this.el); @@ -73,19 +104,26 @@ export class FabButton implements ComponentInterface { 'fab-button-close-active': this.activated, 'fab-button-show': this.show, 'fab-button-disabled': this.disabled, - 'fab-button-translucent': this.translucent + 'fab-button-translucent': this.translucent, + 'focused': this.keyFocus } }; } render() { const TagType = this.href === undefined ? 'button' : 'a'; + const attrs = (TagType === 'button') + ? { type: this.type } + : { href: this.href }; return ( openURL(this.win, this.href, ev, this.routerDirection)} > diff --git a/core/src/components/fab-button/test/standalone/index.html b/core/src/components/fab-button/test/standalone/index.html index 6c503e49bfe..b3165a0cc2e 100644 --- a/core/src/components/fab-button/test/standalone/index.html +++ b/core/src/components/fab-button/test/standalone/index.html @@ -14,6 +14,7 @@

Default

Default + Mini

diff --git a/core/src/components/fab-list/fab-list.scss b/core/src/components/fab-list/fab-list.scss index 792c6d65dd7..1afdab726bc 100644 --- a/core/src/components/fab-list/fab-list.scss +++ b/core/src/components/fab-list/fab-list.scss @@ -22,7 +22,7 @@ } ::slotted(.fab-button-in-list) { - --size: #{$fab-mini-size}; + --width: #{$fab-mini-size}; transform: scale(0); @@ -32,14 +32,14 @@ :host(.fab-list-side-top) ::slotted(.fab-button-in-list), :host(.fab-list-side-bottom) ::slotted(.fab-button-in-list) { - --padding-top: 5px; - --padding-bottom: 5px; + --margin-top: 5px; + --margin-bottom: 5px; } :host(.fab-list-side-start) ::slotted(.fab-button-in-list), :host(.fab-list-side-end) ::slotted(.fab-button-in-list) { - --padding-start: 5px; - --padding-end: 5px; + --margin-start: 5px; + --margin-end: 5px; } ::slotted(.fab-button-in-list.fab-button-show) {