Skip to content

feat(button-toggle): align with 2018 material design spec #12443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/demo-app/button-toggle/button-toggle-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ <h1>Exclusive Selection</h1>
</mat-button-toggle-group>
</section>

<section>
<mat-button-toggle-group appearance="legacy" name="alignment" [vertical]="isVertical">
<mat-button-toggle value="left" [disabled]="isDisabled">
<mat-icon>format_align_left</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="center" [disabled]="isDisabled">
<mat-icon>format_align_center</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="right" [disabled]="isDisabled">
<mat-icon>format_align_right</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="justify" [disabled]="isDisabled">
<mat-icon>format_align_justify</mat-icon>
</mat-button-toggle>
</mat-button-toggle-group>
</section>

<h1>Disabled Group</h1>

<section>
Expand All @@ -50,9 +67,18 @@ <h1>Multiple Selection</h1>
<mat-button-toggle [disabled]="isDisabled">Milk</mat-button-toggle>
</mat-button-toggle-group>
</section>
<section>
<mat-button-toggle-group appearance="legacy" multiple [vertical]="isVertical">
<mat-button-toggle>Flour</mat-button-toggle>
<mat-button-toggle>Eggs</mat-button-toggle>
<mat-button-toggle>Sugar</mat-button-toggle>
<mat-button-toggle [disabled]="isDisabled">Milk</mat-button-toggle>
</mat-button-toggle-group>
</section>

<h1>Single Toggle</h1>
<mat-button-toggle>Yes</mat-button-toggle>
<mat-button-toggle appearance="legacy">Yes</mat-button-toggle>

<h1>Dynamic Exclusive Selection</h1>
<section>
Expand Down
3 changes: 3 additions & 0 deletions src/demo-app/button-toggle/button-toggle-demo.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
section {
margin-bottom: 16px;
}
1 change: 1 addition & 0 deletions src/demo-app/button-toggle/button-toggle-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {Component} from '@angular/core';
moduleId: module.id,
selector: 'button-toggle-demo',
templateUrl: 'button-toggle-demo.html',
styleUrls: ['button-toggle-demo.css'],
})
export class ButtonToggleDemo {
isVertical = false;
Expand Down
48 changes: 46 additions & 2 deletions src/lib/button-toggle/_button-toggle-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
@mixin mat-button-toggle-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$divider-color: mat-color($foreground, divider);

.mat-button-toggle-standalone, .mat-button-toggle-group {
.mat-button-toggle-standalone,
.mat-button-toggle-group {
@include _mat-theme-elevation(2, $theme);
}

.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
.mat-button-toggle-group-appearance-standard {
box-shadow: none;
}

.mat-button-toggle {
color: mat-color($foreground, hint-text);

Expand All @@ -19,19 +26,56 @@
}
}

.mat-button-toggle-appearance-standard {
color: mat-color($foreground, text);
background: mat-color($background, card);

.mat-button-toggle-focus-overlay {
background-color: mat-color($background, focused-button, 1);
}
}

.mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
border-left: solid 1px $divider-color;
}

[dir='rtl'] .mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
border-left: none;
border-right: solid 1px $divider-color;
}

.mat-button-toggle-vertical-appearance-standard .mat-button-toggle + .mat-button-toggle {
border-left: none;
border-right: none;
border-top: solid 1px $divider-color;
}

.mat-button-toggle-checked {
background-color: mat-color($background, selected-button);
color: mat-color($foreground, secondary-text);

&.mat-button-toggle-appearance-standard {
color: mat-color($foreground, text);
}
}

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

&.mat-button-toggle-appearance-standard {
background: mat-color($background, card);
}

&.mat-button-toggle-checked {
background-color: mat-color($background, selected-disabled-button);
}
}

.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
.mat-button-toggle-group-appearance-standard {
border: solid 1px $divider-color;
}
}

@mixin mat-button-toggle-typography($config) {
Expand Down
66 changes: 53 additions & 13 deletions src/lib/button-toggle/button-toggle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,38 @@
@import '../core/style/layout-common';
@import '../../cdk/a11y/a11y';

$mat-button-toggle-padding: 0 16px !default;
$mat-button-toggle-height: 36px !default;
$mat-button-toggle-border-radius: 2px !default;
$mat-button-toggle-standard-padding: 0 12px !default;
$mat-button-toggle-standard-height: 48px !default;
$mat-button-toggle-standard-border-radius: 4px !default;

$mat-button-toggle-legacy-padding: 0 16px !default;
$mat-button-toggle-legacy-height: 36px !default;
$mat-button-toggle-legacy-border-radius: 2px !default;

.mat-button-toggle-standalone,
.mat-button-toggle-group {
position: relative;
display: inline-flex;
flex-direction: row;

border-radius: $mat-button-toggle-border-radius;

cursor: pointer;
white-space: nowrap;
overflow: hidden;
border-radius: $mat-button-toggle-legacy-border-radius;

@include cdk-high-contrast {
outline: solid 1px;
}
}

.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
.mat-button-toggle-group-appearance-standard {
border-radius: $mat-button-toggle-standard-border-radius;

@include cdk-high-contrast {
outline: 0;
}
}

.mat-button-toggle-vertical {
flex-direction: column;

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

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

.mat-button-toggle-appearance-standard {
&:not(.mat-button-toggle-disabled):hover .mat-button-toggle-focus-overlay {
opacity: 0.04;
}

// Similar to components like the checkbox, slide-toggle and radio, we cannot show the focus
// overlay for `.cdk-program-focused` because mouse clicks on the <label> element would be always
// treated as programmatic focus. Note that it needs the extra `:not` in order to have more
// specificity than the `:hover` above.
// TODO(paul): support `program` as well. See https://github.com/angular/material2/issues/9889
&.cdk-keyboard-focused:not(.mat-button-toggle-disabled) .mat-button-toggle-focus-overlay {
opacity: 0.12;

@include cdk-high-contrast {
opacity: 0.5;
}
}
}

.mat-button-toggle-label-content {
@include user-select(none);
display: inline-block;
line-height: $mat-button-toggle-height;
padding: $mat-button-toggle-padding;
line-height: $mat-button-toggle-legacy-height;
padding: $mat-button-toggle-legacy-padding;

.mat-button-toggle-appearance-standard & {
line-height: $mat-button-toggle-standard-height;
padding: $mat-button-toggle-standard-padding;
}
}

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

.mat-button-toggle-checked & {
border-bottom: solid $mat-button-toggle-legacy-height;

// Changing the background color for the selected item won't be visible in high contrast mode.
// We fall back to using the overlay to draw a brighter, semi-transparent tint on top instead.
// It uses a border, because the browser will render it using a brighter color.
@include cdk-high-contrast {
opacity: 0.5;
height: 0;
border-bottom: solid $mat-button-toggle-height;
}
}
}

@include cdk-high-contrast {
.mat-button-toggle-checked {
&.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay {
border-bottom: solid $mat-button-toggle-standard-height;
}
}
}
Expand Down
55 changes: 51 additions & 4 deletions src/lib/button-toggle/button-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
QueryList,
ViewChild,
ViewEncapsulation,
InjectionToken,
Inject,
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {
Expand All @@ -40,6 +42,26 @@ import {
/** Acceptable types for a button toggle. */
export type ToggleType = 'checkbox' | 'radio';

/** Possible appearance styles for the button toggle. */
export type MatButtonToggleAppearance = 'legacy' | 'standard';

/**
* Represents the default options for the button toggle that can be configured
* using the `MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS` injection token.
*/
export interface MatButtonToggleDefaultOptions {
appearance?: MatButtonToggleAppearance;
}

/**
* Injection token that can be used to configure the
* default options for all button toggles within an app.
*/
export const MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS =
new InjectionToken<MatButtonToggleDefaultOptions>('MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS');



/**
* Provider Expression that allows mat-button-toggle-group to register as a ControlValueAccessor.
* This allows it to support [(ngModel)].
Expand Down Expand Up @@ -80,12 +102,12 @@ export class MatButtonToggleChange {
'role': 'group',
'class': 'mat-button-toggle-group',
'[attr.aria-disabled]': 'disabled',
'[class.mat-button-toggle-vertical]': 'vertical'
'[class.mat-button-toggle-vertical]': 'vertical',
'[class.mat-button-toggle-group-appearance-standard]': 'appearance === "standard"',
},
exportAs: 'matButtonToggleGroup',
})
export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, AfterContentInit {

private _vertical = false;
private _multiple = false;
private _disabled = false;
Expand All @@ -111,6 +133,9 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
/** Child button toggle buttons. */
@ContentChildren(forwardRef(() => MatButtonToggle)) _buttonToggles: QueryList<MatButtonToggle>;

/** The appearance for all the buttons in the group. */
@Input() appearance: MatButtonToggleAppearance;

/** `name` attribute for the underlying `input` element. */
@Input()
get name(): string { return this._name; }
Expand Down Expand Up @@ -181,7 +206,14 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
@Output() readonly change: EventEmitter<MatButtonToggleChange> =
new EventEmitter<MatButtonToggleChange>();

constructor(private _changeDetector: ChangeDetectorRef) {}
constructor(
private _changeDetector: ChangeDetectorRef,
@Optional() @Inject(MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS)
defaultOptions?: MatButtonToggleDefaultOptions) {

this.appearance =
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
}

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

/** The appearance style of the button. */
@Input()
get appearance(): MatButtonToggleAppearance {
return this.buttonToggleGroup ? this.buttonToggleGroup.appearance : this._appearance;
}
set appearance(value: MatButtonToggleAppearance) {
this._appearance = value;
}
private _appearance: MatButtonToggleAppearance;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add docs for this in a follow-up PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I've put it on my list.


/** Whether the button is checked. */
@Input()
get checked(): boolean {
Expand Down Expand Up @@ -413,12 +456,16 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
private _elementRef: ElementRef<HTMLElement>,
private _focusMonitor: FocusMonitor,
// @breaking-change 8.0.0 `defaultTabIndex` to be made a required parameter.
@Attribute('tabindex') defaultTabIndex: string) {
@Attribute('tabindex') defaultTabIndex: string,
@Optional() @Inject(MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS)
defaultOptions?: MatButtonToggleDefaultOptions) {
super();

const parsedTabIndex = Number(defaultTabIndex);
this.tabIndex = (parsedTabIndex || parsedTabIndex === 0) ? parsedTabIndex : null;
this.buttonToggleGroup = toggleGroup;
this.appearance =
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
}

ngOnInit() {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/form-field/_form-field-legacy-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ $mat-form-field-legacy-dedupe: 0;
}
}

// translateZ causes the label to not appear while printing, so we override it to not
// translateZ causes the label to not appear while printing, so we override it to not
// apply translateZ while printing
@media print {
.mat-form-field-appearance-legacy {
Expand Down