Skip to content

Commit b30c37b

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 b30c37b

File tree

6 files changed

+177
-17
lines changed

6 files changed

+177
-17
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: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,69 @@
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.mat-button-toggle-appearance-legacy,
12+
.mat-button-toggle-group-appearance-legacy {
1113
@include _mat-theme-elevation(2, $theme);
1214
}
1315

14-
.mat-button-toggle {
16+
.mat-button-toggle-appearance-standard {
17+
color: mat-color($foreground, text);
18+
background: mat-color($background, card);
19+
20+
.mat-button-toggle-focus-overlay {
21+
background-color: mat-color($background, focused-button, 1);
22+
}
23+
}
24+
25+
.mat-button-toggle-appearance-legacy {
1526
color: mat-color($foreground, hint-text);
1627

1728
.mat-button-toggle-focus-overlay {
1829
background-color: mat-color($background, focused-button);
1930
}
2031
}
2132

33+
.mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
34+
border-left: solid 1px $divider-color;
35+
}
36+
37+
[dir='rtl'] .mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
38+
border-left: none;
39+
border-right: solid 1px $divider-color;
40+
}
41+
42+
.mat-button-toggle-vertical-appearance-standard .mat-button-toggle + .mat-button-toggle {
43+
border-left: none;
44+
border-right: none;
45+
border-top: solid 1px $divider-color;
46+
}
47+
2248
.mat-button-toggle-checked {
2349
background-color: mat-color($background, selected-button);
24-
color: mat-color($foreground, secondary-text);
50+
51+
&.mat-button-toggle-appearance-legacy {
52+
color: mat-color($foreground, secondary-text);
53+
}
2554
}
2655

2756
.mat-button-toggle-disabled {
28-
background-color: mat-color($background, disabled-button-toggle);
2957
color: mat-color($foreground, disabled-button);
3058

59+
&.mat-button-toggle-appearance-legacy {
60+
background-color: mat-color($background, disabled-button-toggle);
61+
}
62+
3163
&.mat-button-toggle-checked {
3264
background-color: mat-color($background, selected-disabled-button);
3365
}
3466
}
67+
68+
.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
69+
.mat-button-toggle-group-appearance-standard {
70+
border: solid 1px $divider-color;
71+
}
3572
}
3673

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

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

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,32 @@
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+
}
22+
23+
.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
24+
.mat-button-toggle-group-appearance-standard {
25+
border-radius: $mat-button-toggle-standard-border-radius;
26+
}
27+
28+
.mat-button-toggle-standalone.mat-button-toggle-appearance-legacy,
29+
.mat-button-toggle-group-appearance-legacy {
30+
border-radius: $mat-button-toggle-legacy-border-radius;
2031

2132
@include cdk-high-contrast {
2233
outline: solid 1px;
@@ -37,11 +48,23 @@ $mat-button-toggle-border-radius: 2px !default;
3748
white-space: nowrap;
3849
position: relative;
3950
-webkit-tap-highlight-color: transparent;
51+
}
52+
53+
.mat-button-toggle-appearance-standard {
54+
&:hover .mat-button-toggle-focus-overlay {
55+
opacity: 0.04;
56+
}
4057

4158
// Similar to components like the checkbox, slide-toggle and radio, we cannot show the focus
4259
// overlay for `.cdk-program-focused` because mouse clicks on the <label> element would be always
4360
// treated as programmatic focus.
4461
// TODO(paul): support `program` as well. See https://github.com/angular/material2/issues/9889
62+
&.cdk-keyboard-focused .mat-button-toggle-focus-overlay {
63+
opacity: 0.12;
64+
}
65+
}
66+
67+
.mat-button-toggle-appearance-legacy {
4568
&.cdk-keyboard-focused {
4669
.mat-button-toggle-focus-overlay {
4770
opacity: 1;
@@ -57,8 +80,16 @@ $mat-button-toggle-border-radius: 2px !default;
5780
.mat-button-toggle-label-content {
5881
@include user-select(none);
5982
display: inline-block;
60-
line-height: $mat-button-toggle-height;
61-
padding: $mat-button-toggle-padding;
83+
84+
.mat-button-toggle-appearance-standard & {
85+
line-height: $mat-button-toggle-standard-height;
86+
padding: $mat-button-toggle-standard-padding;
87+
}
88+
89+
.mat-button-toggle-appearance-legacy & {
90+
line-height: $mat-button-toggle-legacy-height;
91+
padding: $mat-button-toggle-legacy-padding;
92+
}
6293
}
6394

6495
.mat-button-toggle-label-content > * {
@@ -81,7 +112,20 @@ $mat-button-toggle-border-radius: 2px !default;
81112
@include cdk-high-contrast {
82113
opacity: 0.5;
83114
height: 0;
84-
border-bottom: solid $mat-button-toggle-height;
115+
}
116+
}
117+
}
118+
119+
@include cdk-high-contrast {
120+
.mat-button-toggle-checked {
121+
&.mat-button-toggle-group-appearance-standard .mat-button-toggle-focus-overlay {
122+
border-bottom: solid $mat-button-toggle-standard-height;
123+
}
124+
}
125+
126+
.mat-button-toggle-checked {
127+
&.mat-button-toggle-group-appearance-legacy .mat-button-toggle-focus-overlay {
128+
border-bottom: solid $mat-button-toggle-legacy-height;
85129
}
86130
}
87131
}

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

Lines changed: 53 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,13 @@ 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"',
107+
'[class.mat-button-toggle-group-appearance-legacy]': 'appearance === "legacy"',
84108
},
85109
exportAs: 'matButtonToggleGroup',
86110
})
87111
export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, AfterContentInit {
88-
89112
private _vertical = false;
90113
private _multiple = false;
91114
private _disabled = false;
@@ -111,6 +134,9 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
111134
/** Child button toggle buttons. */
112135
@ContentChildren(forwardRef(() => MatButtonToggle)) _buttonToggles: QueryList<MatButtonToggle>;
113136

137+
/** The appearance for all the buttons in the group. */
138+
@Input() appearance: MatButtonToggleAppearance;
139+
114140
/** `name` attribute for the underlying `input` element. */
115141
@Input()
116142
get name(): string { return this._name; }
@@ -181,7 +207,14 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
181207
@Output() readonly change: EventEmitter<MatButtonToggleChange> =
182208
new EventEmitter<MatButtonToggleChange>();
183209

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

186219
ngOnInit() {
187220
this._selectionModel = new SelectionModel<MatButtonToggle>(this.multiple, undefined, false);
@@ -331,6 +364,8 @@ export const _MatButtonToggleMixinBase: CanDisableRippleCtor & typeof MatButtonT
331364
'[class.mat-button-toggle-standalone]': '!buttonToggleGroup',
332365
'[class.mat-button-toggle-checked]': 'checked',
333366
'[class.mat-button-toggle-disabled]': 'disabled',
367+
'[class.mat-button-toggle-appearance-standard]': 'appearance === "standard"',
368+
'[class.mat-button-toggle-appearance-legacy]': 'appearance === "legacy"',
334369
'class': 'mat-button-toggle',
335370
// Clear out the native tabindex here since we forward it to the underlying button
336371
'[attr.tabindex]': 'null',
@@ -377,6 +412,16 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
377412
/** Tabindex for the toggle. */
378413
@Input() tabIndex: number | null;
379414

415+
/** The appearance style of the button. */
416+
@Input()
417+
get appearance(): MatButtonToggleAppearance {
418+
return this.buttonToggleGroup ? this.buttonToggleGroup.appearance : this._appearance;
419+
}
420+
set appearance(value: MatButtonToggleAppearance) {
421+
this._appearance = value;
422+
}
423+
private _appearance: MatButtonToggleAppearance;
424+
380425
/** Whether the button is checked. */
381426
@Input()
382427
get checked(): boolean {
@@ -413,12 +458,16 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
413458
private _elementRef: ElementRef<HTMLElement>,
414459
private _focusMonitor: FocusMonitor,
415460
// @breaking-change 8.0.0 `defaultTabIndex` to be made a required parameter.
416-
@Attribute('tabindex') defaultTabIndex: string) {
461+
@Attribute('tabindex') defaultTabIndex: string,
462+
@Optional() @Inject(MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS)
463+
defaultOptions?: MatButtonToggleDefaultOptions) {
417464
super();
418465

419466
const parsedTabIndex = Number(defaultTabIndex);
420467
this.tabIndex = (parsedTabIndex || parsedTabIndex === 0) ? parsedTabIndex : null;
421468
this.buttonToggleGroup = toggleGroup;
469+
this.appearance =
470+
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
422471
}
423472

424473
ngOnInit() {

0 commit comments

Comments
 (0)