Skip to content

Commit 015a666

Browse files
committed
feat(button-toggle): align with 2018 material design spec
Aligns the button toggle component with the latest Material design spec.
1 parent 411fce3 commit 015a666

File tree

7 files changed

+181
-20
lines changed

7 files changed

+181
-20
lines changed

src/demo-app/button-toggle/button-toggle-demo.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ <h1>Exclusive Selection</h1>
2525
</mat-button-toggle-group>
2626
</section>
2727

28+
<section>
29+
<mat-button-toggle-group appearance="legacy" name="alignment" [vertical]="isVertical">
30+
<mat-button-toggle value="left" [disabled]="isDisabled">
31+
<mat-icon>format_align_left</mat-icon>
32+
</mat-button-toggle>
33+
<mat-button-toggle value="center" [disabled]="isDisabled">
34+
<mat-icon>format_align_center</mat-icon>
35+
</mat-button-toggle>
36+
<mat-button-toggle value="right" [disabled]="isDisabled">
37+
<mat-icon>format_align_right</mat-icon>
38+
</mat-button-toggle>
39+
<mat-button-toggle value="justify" [disabled]="isDisabled">
40+
<mat-icon>format_align_justify</mat-icon>
41+
</mat-button-toggle>
42+
</mat-button-toggle-group>
43+
</section>
44+
2845
<h1>Disabled Group</h1>
2946

3047
<section>
@@ -50,9 +67,18 @@ <h1>Multiple Selection</h1>
5067
<mat-button-toggle [disabled]="isDisabled">Milk</mat-button-toggle>
5168
</mat-button-toggle-group>
5269
</section>
70+
<section>
71+
<mat-button-toggle-group appearance="legacy" multiple [vertical]="isVertical">
72+
<mat-button-toggle>Flour</mat-button-toggle>
73+
<mat-button-toggle>Eggs</mat-button-toggle>
74+
<mat-button-toggle>Sugar</mat-button-toggle>
75+
<mat-button-toggle [disabled]="isDisabled">Milk</mat-button-toggle>
76+
</mat-button-toggle-group>
77+
</section>
5378

5479
<h1>Single Toggle</h1>
5580
<mat-button-toggle>Yes</mat-button-toggle>
81+
<mat-button-toggle appearance="legacy">Yes</mat-button-toggle>
5682

5783
<h1>Dynamic Exclusive Selection</h1>
5884
<section>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
section {
2+
margin-bottom: 16px;
3+
}

src/demo-app/button-toggle/button-toggle-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {Component} from '@angular/core';
1313
moduleId: module.id,
1414
selector: 'button-toggle-demo',
1515
templateUrl: 'button-toggle-demo.html',
16+
styleUrls: ['button-toggle-demo.css'],
1617
})
1718
export class ButtonToggleDemo {
1819
isVertical = false;

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66
@mixin mat-button-toggle-theme($theme) {
77
$foreground: map-get($theme, foreground);
88
$background: map-get($theme, background);
9+
$divider-color: mat-color($foreground, divider);
910

10-
.mat-button-toggle-standalone, .mat-button-toggle-group {
11+
.mat-button-toggle-standalone,
12+
.mat-button-toggle-group {
1113
@include _mat-theme-elevation(2, $theme);
1214
}
1315

16+
.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
17+
.mat-button-toggle-group-appearance-standard {
18+
box-shadow: none;
19+
}
20+
1421
.mat-button-toggle {
1522
color: mat-color($foreground, hint-text);
1623

@@ -19,19 +26,56 @@
1926
}
2027
}
2128

29+
.mat-button-toggle-appearance-standard {
30+
color: mat-color($foreground, text);
31+
background: mat-color($background, card);
32+
33+
.mat-button-toggle-focus-overlay {
34+
background-color: mat-color($background, focused-button, 1);
35+
}
36+
}
37+
38+
.mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
39+
border-left: solid 1px $divider-color;
40+
}
41+
42+
[dir='rtl'] .mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
43+
border-left: none;
44+
border-right: solid 1px $divider-color;
45+
}
46+
47+
.mat-button-toggle-vertical-appearance-standard .mat-button-toggle + .mat-button-toggle {
48+
border-left: none;
49+
border-right: none;
50+
border-top: solid 1px $divider-color;
51+
}
52+
2253
.mat-button-toggle-checked {
2354
background-color: mat-color($background, selected-button);
2455
color: mat-color($foreground, secondary-text);
56+
57+
&.mat-button-toggle-appearance-standard {
58+
color: mat-color($foreground, text);
59+
}
2560
}
2661

2762
.mat-button-toggle-disabled {
28-
background-color: mat-color($background, disabled-button-toggle);
2963
color: mat-color($foreground, disabled-button);
64+
background-color: mat-color($background, disabled-button-toggle);
65+
66+
&.mat-button-toggle-appearance-standard {
67+
background: mat-color($background, card);
68+
}
3069

3170
&.mat-button-toggle-checked {
3271
background-color: mat-color($background, selected-disabled-button);
3372
}
3473
}
74+
75+
.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
76+
.mat-button-toggle-group-appearance-standard {
77+
border: solid 1px $divider-color;
78+
}
3579
}
3680

3781
@mixin mat-button-toggle-typography($config) {

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

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,38 @@
22
@import '../core/style/layout-common';
33
@import '../../cdk/a11y/a11y';
44

5-
$mat-button-toggle-padding: 0 16px !default;
6-
$mat-button-toggle-height: 36px !default;
7-
$mat-button-toggle-border-radius: 2px !default;
5+
$mat-button-toggle-standard-padding: 0 12px !default;
6+
$mat-button-toggle-standard-height: 48px !default;
7+
$mat-button-toggle-standard-border-radius: 4px !default;
8+
9+
$mat-button-toggle-legacy-padding: 0 16px !default;
10+
$mat-button-toggle-legacy-height: 36px !default;
11+
$mat-button-toggle-legacy-border-radius: 2px !default;
812

913
.mat-button-toggle-standalone,
1014
.mat-button-toggle-group {
1115
position: relative;
1216
display: inline-flex;
1317
flex-direction: row;
14-
15-
border-radius: $mat-button-toggle-border-radius;
16-
1718
cursor: pointer;
1819
white-space: nowrap;
1920
overflow: hidden;
21+
border-radius: $mat-button-toggle-legacy-border-radius;
2022

2123
@include cdk-high-contrast {
2224
outline: solid 1px;
2325
}
2426
}
2527

28+
.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
29+
.mat-button-toggle-group-appearance-standard {
30+
border-radius: $mat-button-toggle-standard-border-radius;
31+
32+
@include cdk-high-contrast {
33+
outline: 0;
34+
}
35+
}
36+
2637
.mat-button-toggle-vertical {
2738
flex-direction: column;
2839

@@ -38,10 +49,6 @@ $mat-button-toggle-border-radius: 2px !default;
3849
position: relative;
3950
-webkit-tap-highlight-color: transparent;
4051

41-
// Similar to components like the checkbox, slide-toggle and radio, we cannot show the focus
42-
// overlay for `.cdk-program-focused` because mouse clicks on the <label> element would be always
43-
// treated as programmatic focus.
44-
// TODO(paul): support `program` as well. See https://github.com/angular/material2/issues/9889
4552
&.cdk-keyboard-focused {
4653
.mat-button-toggle-focus-overlay {
4754
opacity: 1;
@@ -54,11 +61,35 @@ $mat-button-toggle-border-radius: 2px !default;
5461
}
5562
}
5663

64+
.mat-button-toggle-appearance-standard {
65+
&:not(.mat-button-toggle-disabled):hover .mat-button-toggle-focus-overlay {
66+
opacity: 0.04;
67+
}
68+
69+
// Similar to components like the checkbox, slide-toggle and radio, we cannot show the focus
70+
// overlay for `.cdk-program-focused` because mouse clicks on the <label> element would be always
71+
// treated as programmatic focus. Note that it needs the extra `:not` in order to have more
72+
// specificity than the `:hover` above.
73+
// TODO(paul): support `program` as well. See https://github.com/angular/material2/issues/9889
74+
&.cdk-keyboard-focused:not(.mat-button-toggle-disabled) .mat-button-toggle-focus-overlay {
75+
opacity: 0.12;
76+
77+
@include cdk-high-contrast {
78+
opacity: 0.5;
79+
}
80+
}
81+
}
82+
5783
.mat-button-toggle-label-content {
5884
@include user-select(none);
5985
display: inline-block;
60-
line-height: $mat-button-toggle-height;
61-
padding: $mat-button-toggle-padding;
86+
line-height: $mat-button-toggle-legacy-height;
87+
padding: $mat-button-toggle-legacy-padding;
88+
89+
.mat-button-toggle-appearance-standard & {
90+
line-height: $mat-button-toggle-standard-height;
91+
padding: $mat-button-toggle-standard-padding;
92+
}
6293
}
6394

6495
.mat-button-toggle-label-content > * {
@@ -75,13 +106,22 @@ $mat-button-toggle-border-radius: 2px !default;
75106
@include mat-fill;
76107

77108
.mat-button-toggle-checked & {
109+
border-bottom: solid $mat-button-toggle-legacy-height;
110+
78111
// Changing the background color for the selected item won't be visible in high contrast mode.
79112
// We fall back to using the overlay to draw a brighter, semi-transparent tint on top instead.
80113
// It uses a border, because the browser will render it using a brighter color.
81114
@include cdk-high-contrast {
82115
opacity: 0.5;
83116
height: 0;
84-
border-bottom: solid $mat-button-toggle-height;
117+
}
118+
}
119+
}
120+
121+
@include cdk-high-contrast {
122+
.mat-button-toggle-checked {
123+
&.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay {
124+
border-bottom: solid $mat-button-toggle-standard-height;
85125
}
86126
}
87127
}

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

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
QueryList,
2929
ViewChild,
3030
ViewEncapsulation,
31+
InjectionToken,
32+
Inject,
3133
} from '@angular/core';
3234
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
3335
import {
@@ -40,6 +42,26 @@ import {
4042
/** Acceptable types for a button toggle. */
4143
export type ToggleType = 'checkbox' | 'radio';
4244

45+
/** Possible appearance styles for the button toggle. */
46+
export type MatButtonToggleAppearance = 'legacy' | 'standard';
47+
48+
/**
49+
* Represents the default options for the button toggle that can be configured
50+
* using the `MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS` injection token.
51+
*/
52+
export interface MatButtonToggleDefaultOptions {
53+
appearance?: MatButtonToggleAppearance;
54+
}
55+
56+
/**
57+
* Injection token that can be used to configure the
58+
* default options for all button toggles within an app.
59+
*/
60+
export const MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS =
61+
new InjectionToken<MatButtonToggleDefaultOptions>('MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS');
62+
63+
64+
4365
/**
4466
* Provider Expression that allows mat-button-toggle-group to register as a ControlValueAccessor.
4567
* This allows it to support [(ngModel)].
@@ -80,12 +102,12 @@ export class MatButtonToggleChange {
80102
'role': 'group',
81103
'class': 'mat-button-toggle-group',
82104
'[attr.aria-disabled]': 'disabled',
83-
'[class.mat-button-toggle-vertical]': 'vertical'
105+
'[class.mat-button-toggle-vertical]': 'vertical',
106+
'[class.mat-button-toggle-group-appearance-standard]': 'appearance === "standard"',
84107
},
85108
exportAs: 'matButtonToggleGroup',
86109
})
87110
export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, AfterContentInit {
88-
89111
private _vertical = false;
90112
private _multiple = false;
91113
private _disabled = false;
@@ -111,6 +133,9 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
111133
/** Child button toggle buttons. */
112134
@ContentChildren(forwardRef(() => MatButtonToggle)) _buttonToggles: QueryList<MatButtonToggle>;
113135

136+
/** The appearance for all the buttons in the group. */
137+
@Input() appearance: MatButtonToggleAppearance;
138+
114139
/** `name` attribute for the underlying `input` element. */
115140
@Input()
116141
get name(): string { return this._name; }
@@ -181,7 +206,14 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
181206
@Output() readonly change: EventEmitter<MatButtonToggleChange> =
182207
new EventEmitter<MatButtonToggleChange>();
183208

184-
constructor(private _changeDetector: ChangeDetectorRef) {}
209+
constructor(
210+
private _changeDetector: ChangeDetectorRef,
211+
@Optional() @Inject(MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS)
212+
defaultOptions?: MatButtonToggleDefaultOptions) {
213+
214+
this.appearance =
215+
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
216+
}
185217

186218
ngOnInit() {
187219
this._selectionModel = new SelectionModel<MatButtonToggle>(this.multiple, undefined, false);
@@ -331,6 +363,7 @@ export const _MatButtonToggleMixinBase: CanDisableRippleCtor & typeof MatButtonT
331363
'[class.mat-button-toggle-standalone]': '!buttonToggleGroup',
332364
'[class.mat-button-toggle-checked]': 'checked',
333365
'[class.mat-button-toggle-disabled]': 'disabled',
366+
'[class.mat-button-toggle-appearance-standard]': 'appearance === "standard"',
334367
'class': 'mat-button-toggle',
335368
// Clear out the native tabindex here since we forward it to the underlying button
336369
'[attr.tabindex]': 'null',
@@ -377,6 +410,16 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
377410
/** Tabindex for the toggle. */
378411
@Input() tabIndex: number | null;
379412

413+
/** The appearance style of the button. */
414+
@Input()
415+
get appearance(): MatButtonToggleAppearance {
416+
return this.buttonToggleGroup ? this.buttonToggleGroup.appearance : this._appearance;
417+
}
418+
set appearance(value: MatButtonToggleAppearance) {
419+
this._appearance = value;
420+
}
421+
private _appearance: MatButtonToggleAppearance;
422+
380423
/** Whether the button is checked. */
381424
@Input()
382425
get checked(): boolean {
@@ -413,12 +456,16 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
413456
private _elementRef: ElementRef<HTMLElement>,
414457
private _focusMonitor: FocusMonitor,
415458
// @breaking-change 8.0.0 `defaultTabIndex` to be made a required parameter.
416-
@Attribute('tabindex') defaultTabIndex: string) {
459+
@Attribute('tabindex') defaultTabIndex: string,
460+
@Optional() @Inject(MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS)
461+
defaultOptions?: MatButtonToggleDefaultOptions) {
417462
super();
418463

419464
const parsedTabIndex = Number(defaultTabIndex);
420465
this.tabIndex = (parsedTabIndex || parsedTabIndex === 0) ? parsedTabIndex : null;
421466
this.buttonToggleGroup = toggleGroup;
467+
this.appearance =
468+
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
422469
}
423470

424471
ngOnInit() {

src/lib/form-field/_form-field-legacy-theme.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ $mat-form-field-legacy-dedupe: 0;
142142
}
143143
}
144144

145-
// translateZ causes the label to not appear while printing, so we override it to not
145+
// translateZ causes the label to not appear while printing, so we override it to not
146146
// apply translateZ while printing
147147
@media print {
148148
.mat-form-field-appearance-legacy {

0 commit comments

Comments
 (0)