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

Aligns the button toggle component with the latest Material design spec.
This commit is contained in:
Kristiyan Kostadinov
2018-09-27 21:54:25 +02:00
committed by Jeremy Elbourn
parent 3192dfa1f3
commit fd3eb6acc5
6 changed files with 180 additions and 19 deletions
@@ -25,6 +25,23 @@
</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>
@@ -50,9 +67,18 @@
<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>
@@ -0,0 +1,3 @@
section {
margin-bottom: 16px;
}
@@ -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;
@@ -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);
@@ -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) {
+53 -13
View File
@@ -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;
@@ -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;
@@ -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 > * {
@@ -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;
}
}
}
+51 -4
View File
@@ -28,6 +28,8 @@ import {
QueryList,
ViewChild,
ViewEncapsulation,
InjectionToken,
Inject,
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {
@@ -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)].
@@ -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;
@@ -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; }
@@ -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);
@@ -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',
@@ -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;
/** Whether the button is checked. */
@Input()
get checked(): boolean {
@@ -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() {