feat(md-button): enhance button focus appearance.
This commit is contained in:
parent
7354206c74
commit
6d280ea31f
|
@ -4,6 +4,10 @@
|
||||||
// TODO(jelbourn): This goes away.
|
// TODO(jelbourn): This goes away.
|
||||||
@import "../../core/style/default-theme";
|
@import "../../core/style/default-theme";
|
||||||
|
|
||||||
|
// TODO(jelbourn): Move variables and mixins into a partial file.
|
||||||
|
// TODO(jelbourn): Measure perf benefits for translate3d and will-change.
|
||||||
|
// TODO(jelbourn): Figure out if anchor hover underline actually happens in any browser.
|
||||||
|
|
||||||
|
|
||||||
// Standard button sizing.
|
// Standard button sizing.
|
||||||
$md-button-padding: 0 rem(0.600) !default;
|
$md-button-padding: 0 rem(0.600) !default;
|
||||||
|
@ -19,7 +23,7 @@ $md-fab-padding: rem(1.60) !default;
|
||||||
$md-fab-mini-size: rem(4.00) !default;
|
$md-fab-mini-size: rem(4.00) !default;
|
||||||
$md-fab-mini-line-height: rem(4.00) !default;
|
$md-fab-mini-line-height: rem(4.00) !default;
|
||||||
|
|
||||||
/** Mixin to create distinct classes for fab positions, e.g. ".md-fab-bottom-right". */
|
/** Mixin to create distinct classes for fab positions, e.g. ".md-fab-position-bottom-right". */
|
||||||
@mixin md-fab-position($spot, $top: auto, $right: auto, $bottom: auto, $left: auto) {
|
@mixin md-fab-position($spot, $top: auto, $right: auto, $bottom: auto, $left: auto) {
|
||||||
.md-fab-position-#{$spot} {
|
.md-fab-position-#{$spot} {
|
||||||
top: $top;
|
top: $top;
|
||||||
|
@ -30,14 +34,17 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base styles for all buttons.
|
/** Styles for all disabled buttons. */
|
||||||
|
@mixin md-button-disabled() {
|
||||||
|
color: md-color($md-foreground, disabled);
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base styles for all buttons. */
|
||||||
@mixin md-button-base() {
|
@mixin md-button-base() {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
font-size: $md-body-font-size-base;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
// Reset browser <button> styles.
|
// Reset browser <button> styles.
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
@ -48,9 +55,15 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
// Apply nowrap and remove underline for anchor md-buttons.
|
// Make anchors render like buttons.
|
||||||
|
display: inline-block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
// Typography.
|
||||||
|
font-size: $md-body-font-size-base;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
// Sizing.
|
// Sizing.
|
||||||
padding: $md-button-padding;
|
padding: $md-button-padding;
|
||||||
|
@ -60,10 +73,10 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
border-radius: $md-button-border-radius;
|
border-radius: $md-button-border-radius;
|
||||||
|
|
||||||
// Animation.
|
// Animation.
|
||||||
// TODO(jelbourn): figure out where will-change would be beneficial.
|
transition: background $swift-ease-out-duration $swift-ease-out-timing-function,
|
||||||
transition: background $swift-ease-out-duration $swift-ease-out-timing-function;
|
box-shadow $swift-ease-out-duration $swift-ease-out-timing-function;
|
||||||
|
|
||||||
// Hide the default browser focus indicator.
|
// Hide the browser focus indicator, instead applying our own focus style on background-color.
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +84,11 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
// Remove anchor underline again for more specific modifiers.
|
// Remove anchor underline again for more specific modifiers.
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a CSS class for focus style because we only want to render the focus style when
|
||||||
|
// the focus originated from a keyboard event (see JS source for more details).
|
||||||
|
&:hover, &.md-button-focus {
|
||||||
background: md-color($md-background, 500, 0.2);
|
background: md-color($md-background, 500, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +100,10 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
color: md-color($md-accent);
|
color: md-color($md-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
// Use the [disabled] attribute instead of the :disabled pseudo-class because anchors
|
||||||
color: md-color($md-foreground, disabled);
|
// cannot technically be :disabled.
|
||||||
background-color: transparent;
|
&[disabled] {
|
||||||
cursor: default;
|
@include md-button-disabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,29 +112,40 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
@include md-button-base();
|
@include md-button-base();
|
||||||
|
|
||||||
// Force hardware acceleration.
|
// Force hardware acceleration.
|
||||||
// TODO(jelbourn): determine if this actually has an impact.
|
|
||||||
transform: translate3d(0, 0, 0);
|
transform: translate3d(0, 0, 0);
|
||||||
box-shadow: $md-shadow-bottom-z-1;
|
|
||||||
|
|
||||||
transition: background $swift-ease-out-duration $swift-ease-out-timing-function,
|
box-shadow: $md-shadow-bottom-z-1;
|
||||||
box-shadow $swift-ease-out-duration $swift-ease-out-timing-function;
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
box-shadow: $md-shadow-bottom-z-2;
|
box-shadow: $md-shadow-bottom-z-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&[disabled] {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.md-primary {
|
&.md-primary {
|
||||||
color: md-color($md-primary, default-contrast);
|
color: md-color($md-primary, default-contrast);
|
||||||
background-color: md-color($md-primary);
|
background-color: md-color($md-primary);
|
||||||
|
|
||||||
|
&:hover, &.md-button-focus {
|
||||||
|
background-color: md-color($md-primary, 600);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.md-accent {
|
&.md-accent {
|
||||||
color: md-color($md-accent, default-contrast);
|
color: md-color($md-accent, default-contrast);
|
||||||
background-color: md-color($md-accent);
|
background-color: md-color($md-accent);
|
||||||
|
|
||||||
|
&:hover, &.md-button-focus {
|
||||||
|
background-color: md-color($md-accent, A700);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.md-primary, &.md-accent {
|
||||||
|
&[disabled] {
|
||||||
|
@include md-button-disabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +173,6 @@ $md-fab-mini-line-height: rem(4.00) !default;
|
||||||
line-height: $md-fab-line-height;
|
line-height: $md-fab-line-height;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
// TODO(jelbourn): May need `background-clip: padding-box;` depending on ripple implementation.
|
|
||||||
|
|
||||||
&.md-mini {
|
&.md-mini {
|
||||||
line-height: $md-fab-mini-line-height;
|
line-height: $md-fab-mini-line-height;
|
||||||
width: $md-fab-mini-size;
|
width: $md-fab-mini-size;
|
||||||
|
|
|
@ -1,36 +1,86 @@
|
||||||
import {Component, View, LifecycleEvent, ViewEncapsulation} from 'angular2/angular2';
|
import {Component, View, LifecycleEvent, ViewEncapsulation} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {TimerWrapper} from 'angular2/src/facade/async';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
// TODO(jelbourn): Ink ripples.
|
||||||
|
// TODO(jelbourn): Make the `isMosueDown` stuff done with one global listener.
|
||||||
|
|
||||||
@Component({selector: '[md-button]:not(a), [md-fab]:not(a), [md-raised-button]:not(a)'})
|
@Component({
|
||||||
|
selector: '[md-button]:not(a), [md-fab]:not(a), [md-raised-button]:not(a)',
|
||||||
|
host: {
|
||||||
|
'(^mousedown)': 'onMousedown()',
|
||||||
|
'(focus)': 'onFocus()',
|
||||||
|
'(blur)': 'onBlur()',
|
||||||
|
'[class.md-button-focus]': 'isKeyboardFocused',
|
||||||
|
},
|
||||||
|
})
|
||||||
@View({
|
@View({
|
||||||
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
||||||
encapsulation: ViewEncapsulation.NONE
|
encapsulation: ViewEncapsulation.NONE,
|
||||||
})
|
})
|
||||||
export class MdButton {
|
export class MdButton {
|
||||||
// TODO(jelbourn): Ink ripples.
|
/** Whether a mousedown has occured on this element in the last 100ms. */
|
||||||
|
isMouseDown: boolean = false;
|
||||||
|
|
||||||
|
/** Whether the button has focus from the keyboard (not the mouse). Used for class binding. */
|
||||||
|
isKeyboardFocused: boolean = false;
|
||||||
|
|
||||||
|
onMousedown() {
|
||||||
|
// We only *show* the focus style when focus has come to the button via the keyboard.
|
||||||
|
// The Material Design spec is silent on this topic, and without doing this, the
|
||||||
|
// button continues to look :active after clicking.
|
||||||
|
// @see http://marcysutton.com/button-focus-hell/
|
||||||
|
this.isMouseDown = true;
|
||||||
|
TimerWrapper.setTimeout(() => {this.isMouseDown = false}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
this.isKeyboardFocused = !this.isMouseDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
|
this.isKeyboardFocused = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[md-button][href]',
|
selector: 'a[md-button], a[md-raised-button], a[md-fab]',
|
||||||
properties: ['disabled'],
|
properties: ['disabled'],
|
||||||
host: {'(click)': 'onClick($event)', '[tabIndex]': 'tabIndex'},
|
lifecycle: [LifecycleEvent.onChange],
|
||||||
lifecycle: [LifecycleEvent.onChange]
|
host: {
|
||||||
|
'(^click)': 'onClick($event)',
|
||||||
|
'(^mousedown)': 'onMousedown()',
|
||||||
|
'(focus)': 'onFocus()',
|
||||||
|
'(blur)': 'onBlur()',
|
||||||
|
'[tabIndex]': 'tabIndex',
|
||||||
|
'[class.md-button-focus]': 'isKeyboardFocused',
|
||||||
|
'[attr.aria-disabled]': 'disabled',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
||||||
encapsulation: ViewEncapsulation.NONE
|
encapsulation: ViewEncapsulation.NONE
|
||||||
})
|
})
|
||||||
export class MdAnchor {
|
export class MdAnchor extends MdButton {
|
||||||
tabIndex: number;
|
tabIndex: number;
|
||||||
|
|
||||||
/** Whether the component is disabled. */
|
/** Whether the component is disabled. */
|
||||||
disabled: boolean;
|
disabled_: boolean;
|
||||||
|
|
||||||
|
get disabled(): boolean {
|
||||||
|
return this.disabled_;
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(value) {
|
||||||
|
// The presence of *any* disabled value makes the component disabled, *except* for false.
|
||||||
|
this.disabled_ = isPresent(value) && this.disabled !== false;
|
||||||
|
}
|
||||||
|
|
||||||
onClick(event) {
|
onClick(event) {
|
||||||
// A disabled anchor shouldn't navigate anywhere.
|
// A disabled anchor shouldn't navigate anywhere.
|
||||||
if (isPresent(this.disabled) && this.disabled !== false) {
|
if (this.disabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
|
@ -14,6 +15,16 @@
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom {
|
||||||
|
background-color: #ae0001;
|
||||||
|
color: #eeba30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom:hover, .custom.md-button-focus {
|
||||||
|
background-color: #740001;
|
||||||
|
color: #d3a625;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h1>Button demo</h1>
|
<h1>Button demo</h1>
|
||||||
|
@ -40,6 +51,7 @@
|
||||||
<button md-button disabled="disabled" (^click)="click('disabled')">DISABLED</button>
|
<button md-button disabled="disabled" (^click)="click('disabled')">DISABLED</button>
|
||||||
<button md-button class="md-accent" (^click)="click('accent')">ACCENT</button>
|
<button md-button class="md-accent" (^click)="click('accent')">ACCENT</button>
|
||||||
<button md-button class="md-warn" (^click)="click('warn')">WARN</button>
|
<button md-button class="md-warn" (^click)="click('warn')">WARN</button>
|
||||||
|
<button md-button class="custom" (^click)="click('custom')">CUSTOM</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
@ -49,6 +61,7 @@
|
||||||
<button md-raised-button disabled="disabled" (^click)="click('raised disabled')">DISABLED</button>
|
<button md-raised-button disabled="disabled" (^click)="click('raised disabled')">DISABLED</button>
|
||||||
<button md-raised-button class="md-accent" (^click)="click('raised accent')">ACCENT</button>
|
<button md-raised-button class="md-accent" (^click)="click('raised accent')">ACCENT</button>
|
||||||
<button md-raised-button class="md-warn" (^click)="click('raised warn')">WARN</button>
|
<button md-raised-button class="md-warn" (^click)="click('raised warn')">WARN</button>
|
||||||
|
<button md-raised-button class="custom" (^click)="click('custom raised')">CUSTOM</button>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<span class="label">Fab button</span>
|
<span class="label">Fab button</span>
|
||||||
|
@ -57,6 +70,7 @@
|
||||||
<button md-fab disabled="disabled" (^click)="click('fab disabled')">DIS</button>
|
<button md-fab disabled="disabled" (^click)="click('fab disabled')">DIS</button>
|
||||||
<button md-fab class="md-accent" (^click)="click('fab accent')">ACC</button>
|
<button md-fab class="md-accent" (^click)="click('fab accent')">ACC</button>
|
||||||
<button md-fab class="md-warn" (^click)="click('fab warn')">WRN</button>
|
<button md-fab class="md-warn" (^click)="click('fab warn')">WRN</button>
|
||||||
|
<button md-fab class="custom" (^click)="click('custom fab')">CSTM</button>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<span class="label">Anchor / hyperlink</span>
|
<span class="label">Anchor / hyperlink</span>
|
||||||
|
@ -65,9 +79,10 @@
|
||||||
<a md-raised-button target="_blank" href="http://google.com">RAISED HREF</a>
|
<a md-raised-button target="_blank" href="http://google.com">RAISED HREF</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section dir="rtl">
|
||||||
|
<span class="label" dir="ltr">Right-to-left</span>
|
||||||
|
<button md-button (^click)="click('Hebrew button')">לחצן</button>
|
||||||
|
<button md-raised-button (^click)="click('Hebrew raised button')">העלה</button>
|
||||||
|
<a md-button href="http://translate.google.com">עוגן</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
<p template="ng-for #item of items">
|
|
||||||
Repeated button:
|
|
||||||
<button md-button tabindex="-1" (^click)="increment()">{{action}}</button>
|
|
||||||
{{clickCount}}
|
|
||||||
</p>
|
|
||||||
|
|
Loading…
Reference in New Issue