diff --git a/src/lib/radio/_radio-theme.scss b/src/lib/radio/_radio-theme.scss index 3a05ed0eb38b..c1ccc1f54057 100644 --- a/src/lib/radio/_radio-theme.scss +++ b/src/lib/radio/_radio-theme.scss @@ -7,13 +7,12 @@ border-color: mat-color($palette); } - .mat-radio-inner-circle { + .mat-radio-inner-circle, + .mat-radio-ripple .mat-ripple-element:not(.mat-radio-persistent-ripple), + &.mat-radio-checked .mat-radio-persistent-ripple, + &:active .mat-radio-persistent-ripple { background-color: mat-color($palette); } - - .mat-radio-ripple .mat-ripple-element { - background-color: mat-color($palette, 0.26); - } } @mixin mat-radio-theme($theme) { @@ -58,6 +57,12 @@ color: mat-color($foreground, disabled); } } + + // Switch this to a solid color since we're using `opacity` + // to control how opaque the ripple should be. + .mat-ripple-element { + background-color: map_get($foreground, base); + } } } diff --git a/src/lib/radio/radio.html b/src/lib/radio/radio.html index bbcdccb31f98..7dba9ee8d355 100644 --- a/src/lib/radio/radio.html +++ b/src/lib/radio/radio.html @@ -9,8 +9,10 @@ [matRippleTrigger]="label" [matRippleDisabled]="_isRippleDisabled()" [matRippleCentered]="true" - [matRippleRadius]="23" + [matRippleRadius]="20" [matRippleAnimation]="{enterDuration: 150}"> + +
diff --git a/src/lib/radio/radio.scss b/src/lib/radio/radio.scss index 3086d87833d4..2ddc04c6e742 100644 --- a/src/lib/radio/radio.scss +++ b/src/lib/radio/radio.scss @@ -4,7 +4,7 @@ $mat-radio-size: $mat-toggle-size !default; -$mat-radio-ripple-radius: 25px; +$mat-radio-ripple-radius: 20px; // Top-level host container. .mat-radio-button { @@ -111,7 +111,8 @@ $mat-radio-ripple-radius: 25px; } // Basic disabled state. -.mat-radio-disabled, .mat-radio-disabled .mat-radio-label { +.mat-radio-disabled, +.mat-radio-disabled .mat-radio-label { cursor: default; } @@ -125,4 +126,29 @@ $mat-radio-ripple-radius: 25px; width: $mat-radio-ripple-radius * 2; z-index: 1; pointer-events: none; + + .mat-ripple-element:not(.mat-radio-persistent-ripple) { + opacity: 0.16; + } +} + +.mat-radio-persistent-ripple { + width: 100%; + height: 100%; + transform: none; + + .mat-radio-container:hover & { + opacity: 0.04; + } + + .mat-radio-button.cdk-focused & { + opacity: 0.12; + } + + // We do this here, rather than having a `:not(.mat-radio-disabled)` + // above in the `:hover`, because the `:not` will bump the specificity + // a lot and will cause it to overide the focus styles. + &, .mat-radio-disabled .mat-radio-container:hover & { + opacity: 0; + } } diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index 5bd00772113b..0bf8a454ac8f 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -3,7 +3,6 @@ import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/f import {Component, DebugElement, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; import {dispatchFakeEvent} from '@angular/cdk/testing'; -import {defaultRippleAnimationConfig} from '@angular/material/core'; import {MatRadioButton, MatRadioChange, MatRadioGroup, MatRadioModule} from './index'; describe('MatRadio', () => { @@ -197,25 +196,6 @@ describe('MatRadio', () => { expect(changeSpy).toHaveBeenCalledTimes(1); }); - it('should show a ripple when focusing via the keyboard', fakeAsync(() => { - expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length) - .toBe(0, 'Expected no ripples on init.'); - - dispatchFakeEvent(radioInputElements[0], 'keydown'); - dispatchFakeEvent(radioInputElements[0], 'focus'); - - tick(defaultRippleAnimationConfig.enterDuration); - - expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length) - .toBe(1, 'Expected one ripple after keyboard focus.'); - - dispatchFakeEvent(radioInputElements[0], 'blur'); - tick(defaultRippleAnimationConfig.exitDuration); - - expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length) - .toBe(0, 'Expected no ripples on blur.'); - })); - it('should update the group and radios when updating the group value', () => { expect(groupInstance.value).toBeFalsy(); @@ -253,8 +233,10 @@ describe('MatRadio', () => { dispatchFakeEvent(radioLabelElements[0], 'mousedown'); dispatchFakeEvent(radioLabelElements[0], 'mouseup'); - expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length) - .toBe(0, 'Expected a disabled radio button to not show ripples'); + let rippleAmount = radioNativeElements[0] + .querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length; + + expect(rippleAmount).toBe(0, 'Expected a disabled radio button to not show ripples'); testComponent.isFirstDisabled = false; fixture.detectChanges(); @@ -262,7 +244,10 @@ describe('MatRadio', () => { dispatchFakeEvent(radioLabelElements[0], 'mousedown'); dispatchFakeEvent(radioLabelElements[0], 'mouseup'); - expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length) + rippleAmount = radioNativeElements[0] + .querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length; + + expect(rippleAmount) .toBe(1, 'Expected an enabled radio button to show ripples'); }); @@ -274,7 +259,10 @@ describe('MatRadio', () => { dispatchFakeEvent(radioLabel, 'mousedown'); dispatchFakeEvent(radioLabel, 'mouseup'); - expect(radioLabel.querySelectorAll('.mat-ripple-element').length).toBe(0); + const rippleAmount = radioNativeElements[0] + .querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length; + + expect(rippleAmount).toBe(0); } testComponent.disableRipple = false; @@ -284,7 +272,10 @@ describe('MatRadio', () => { dispatchFakeEvent(radioLabel, 'mousedown'); dispatchFakeEvent(radioLabel, 'mouseup'); - expect(radioLabel.querySelectorAll('.mat-ripple-element').length).toBe(1); + const rippleAmount = radioNativeElements[0] + .querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length; + + expect(rippleAmount).toBe(1); } }); diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index 5c1306219627..18fa9ead4d58 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {FocusMonitor, FocusOrigin} from '@angular/cdk/a11y'; +import {FocusMonitor} from '@angular/cdk/a11y'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; import { @@ -36,12 +36,10 @@ import { CanDisable, CanDisableRipple, HasTabIndex, - MatRipple, mixinColor, mixinDisabled, mixinDisableRipple, mixinTabIndex, - RippleRef, } from '@angular/material/core'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; @@ -459,12 +457,6 @@ export class MatRadioButton extends _MatRadioButtonMixinBase /** Value assigned to this radio. */ private _value: any = null; - /** The child ripple instance. */ - @ViewChild(MatRipple) _ripple: MatRipple; - - /** Reference to the current focus ripple. */ - private _focusRipple: RippleRef | null; - /** Unregister function for _radioDispatcher */ private _removeUniqueSelectionListener: () => void = () => {}; @@ -518,12 +510,16 @@ export class MatRadioButton extends _MatRadioButtonMixinBase ngAfterViewInit() { this._focusMonitor - .monitor(this._inputElement) - .subscribe(focusOrigin => this._onInputFocusChange(focusOrigin)); + .monitor(this._elementRef, true) + .subscribe(focusOrigin => { + if (!focusOrigin && this.radioGroup) { + this.radioGroup._touch(); + } + }); } ngOnDestroy() { - this._focusMonitor.stopMonitoring(this._inputElement); + this._focusMonitor.stopMonitoring(this._elementRef); this._removeUniqueSelectionListener(); } @@ -570,21 +566,4 @@ export class MatRadioButton extends _MatRadioButtonMixinBase } } - /** Function is called whenever the focus changes for the input element. */ - private _onInputFocusChange(focusOrigin: FocusOrigin) { - // TODO(paul): support `program`. See https://github.com/angular/material2/issues/9889 - if (!this._focusRipple && focusOrigin === 'keyboard') { - this._focusRipple = this._ripple.launch(0, 0, {persistent: true}); - } else if (!focusOrigin) { - if (this.radioGroup) { - this.radioGroup._touch(); - } - - if (this._focusRipple) { - this._focusRipple.fadeOut(); - this._focusRipple = null; - } - } - } - }