Skip to content

Commit 0bc33c8

Browse files
committed
Add a focus overlay for button-toggle
1 parent 37e4bad commit 0bc33c8

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

src/lib/button-toggle/button-toggle.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
[checked]="checked"
66
[disabled]="disabled"
77
[name]="name"
8+
(blur)="_onInputBlur()"
9+
(focus)="_onInputFocus()"
810
(change)="_onInputChange($event)"
911
(click)="_onInputClick($event)">
1012

1113
<div class="mat-button-toggle-label-content">
1214
<ng-content></ng-content>
1315
</div>
1416
</label>
17+
<!-- the touchstart handler prevents the overlay from capturing the initial tap on touch devices -->
18+
<div class="mat-button-toggle-focus-overlay" (touchstart)="$event.preventDefault()"></div>

src/lib/button-toggle/button-toggle.scss

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import '../core/style/elevation';
2+
@import '../core/a11y/a11y';
23

34
$mat-button-toggle-padding: 0 16px !default;
45
$mat-button-toggle-line-height: 36px !default;
@@ -34,6 +35,11 @@ $mat-button-toggle-border-radius: 2px !default;
3435
.mat-button-toggle {
3536
white-space: nowrap;
3637
font-family: $mat-font-family;
38+
position: relative;
39+
}
40+
41+
.mat-button-toggle.mat-button-toggle-focus .mat-button-toggle-focus-overlay {
42+
opacity: 1;
3743
}
3844

3945
.mat-button-toggle-label-content {
@@ -47,3 +53,27 @@ $mat-button-toggle-border-radius: 2px !default;
4753
.mat-button-toggle-label-content > * {
4854
vertical-align: middle;
4955
}
56+
57+
// Overlay to be used as a tint. Note that the same effect can be achieved by using a pseudo
58+
// element, however we use a proper DOM element in order to be able to disable the default
59+
// touch action. This makes the buttons more responsive on touch devices.
60+
.mat-button-toggle-focus-overlay {
61+
// The button spec calls for focus on raised buttons (and FABs) to be indicated with a
62+
// black, 12% opacity shade over the normal color (for both light and dark themes).
63+
background-color: rgba(black, 0.12);
64+
border-radius: inherit;
65+
pointer-events: none;
66+
opacity: 0;
67+
position: absolute;
68+
top: 0;
69+
left: 0;
70+
bottom: 0;
71+
right: 0;
72+
73+
@include cdk-high-contrast {
74+
// Note that IE will render this in the same way, no
75+
// matter whether the theme is light or dark. This helps
76+
// with the readability of focused buttons.
77+
background-color: rgba(white, 0.5);
78+
}
79+
}

src/lib/button-toggle/button-toggle.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ export class MdButtonToggleGroupMultiple {
276276
set vertical(value) {
277277
this._vertical = coerceBooleanProperty(value);
278278
}
279-
280279
}
281280

282281
/** Single button inside of a toggle group. */
@@ -287,13 +286,21 @@ export class MdButtonToggleGroupMultiple {
287286
styleUrls: ['button-toggle.css'],
288287
encapsulation: ViewEncapsulation.None,
289288
host: {
290-
'[class.mat-button-toggle]': 'true'
289+
'[class.mat-button-toggle-focus]': '_hasFocus',
290+
'[class.mat-button-toggle]': 'true',
291+
'(mousedown)': '_setMousedown()',
291292
}
292293
})
293294
export class MdButtonToggle implements OnInit {
294295
/** Whether or not this button toggle is checked. */
295296
private _checked: boolean = false;
296297

298+
/** Whether the button has focus. Used for class binding. */
299+
_hasFocus: boolean = false;
300+
301+
/** Whether a mousedown has occurred on this element in the last 100ms. */
302+
_isMouseDown: boolean = false;
303+
297304
/** Type of the button toggle. Either 'radio' or 'checkbox'. */
298305
_type: ToggleType;
299306

@@ -461,10 +468,31 @@ export class MdButtonToggle implements OnInit {
461468
event.stopPropagation();
462469
}
463470

471+
_onInputFocus() {
472+
// Only show the focus / ripple indicator when the focus was not triggered by a mouse
473+
// interaction on the component.
474+
if (!this._isMouseDown) {
475+
this._hasFocus = true;
476+
}
477+
}
478+
479+
_onInputBlur() {
480+
this._hasFocus = false;
481+
}
482+
464483
/** Focuses the button. */
465484
focus() {
466485
this._renderer.invokeElementMethod(this._inputElement.nativeElement, 'focus');
467486
}
487+
488+
_setMousedown() {
489+
// We only *show* the focus style when focus has come to the button via the keyboard.
490+
// The Material Design spec is silent on this topic, and without doing this, the
491+
// button continues to look :active after clicking.
492+
// @see http://marcysutton.com/button-focus-hell/
493+
this._isMouseDown = true;
494+
setTimeout(() => { this._isMouseDown = false; }, 100);
495+
}
468496
}
469497

470498

0 commit comments

Comments
 (0)