Skip to content

Commit 6ed07c9

Browse files
committed
refactor(radio): align with new material spec
Aligns the radio button with the latest iteration of the Material spec. The radio itself was mostly aligned, but these changes introduce ripples hover, as well as varying opacity for the focused and active ripples.
1 parent f9b9e25 commit 6ed07c9

File tree

5 files changed

+64
-58
lines changed

5 files changed

+64
-58
lines changed

src/lib/radio/_radio-theme.scss

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
border-color: mat-color($palette);
88
}
99

10-
.mat-radio-inner-circle {
10+
.mat-radio-inner-circle,
11+
.mat-radio-ripple .mat-ripple-element:not(.mat-radio-persistent-ripple),
12+
&.mat-radio-checked .mat-radio-persistent-ripple,
13+
&:active .mat-radio-persistent-ripple {
1114
background-color: mat-color($palette);
1215
}
13-
14-
.mat-radio-ripple .mat-ripple-element {
15-
background-color: mat-color($palette, 0.26);
16-
}
1716
}
1817

1918
@mixin mat-radio-theme($theme) {
@@ -58,6 +57,12 @@
5857
color: mat-color($foreground, disabled);
5958
}
6059
}
60+
61+
// Switch this to a solid color since we're using `opacity`
62+
// to control how opaque the ripple should be.
63+
.mat-ripple-element {
64+
background-color: map_get($foreground, base);
65+
}
6166
}
6267
}
6368

src/lib/radio/radio.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
[matRippleTrigger]="label"
1010
[matRippleDisabled]="_isRippleDisabled()"
1111
[matRippleCentered]="true"
12-
[matRippleRadius]="23"
12+
[matRippleRadius]="20"
1313
[matRippleAnimation]="{enterDuration: 150}">
14+
15+
<div class="mat-ripple-element mat-radio-persistent-ripple"></div>
1416
</div>
1517
</div>
1618

src/lib/radio/radio.scss

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
$mat-radio-size: $mat-toggle-size !default;
7-
$mat-radio-ripple-radius: 25px;
7+
$mat-radio-ripple-radius: 20px;
88

99
// Top-level host container.
1010
.mat-radio-button {
@@ -111,7 +111,8 @@ $mat-radio-ripple-radius: 25px;
111111
}
112112

113113
// Basic disabled state.
114-
.mat-radio-disabled, .mat-radio-disabled .mat-radio-label {
114+
.mat-radio-disabled,
115+
.mat-radio-disabled .mat-radio-label {
115116
cursor: default;
116117
}
117118

@@ -123,4 +124,29 @@ $mat-radio-ripple-radius: 25px;
123124
width: $mat-radio-ripple-radius * 2;
124125
z-index: 1;
125126
pointer-events: none;
127+
128+
.mat-ripple-element:not(.mat-radio-persistent-ripple) {
129+
opacity: 0.16;
130+
}
131+
}
132+
133+
.mat-radio-persistent-ripple {
134+
width: 100%;
135+
height: 100%;
136+
transform: none;
137+
138+
.mat-radio-container:hover & {
139+
opacity: 0.04;
140+
}
141+
142+
.mat-radio-button.cdk-focused & {
143+
opacity: 0.12;
144+
}
145+
146+
// We do this here, rather than having a `:not(.mat-radio-disabled)`
147+
// above in the `:hover`, because the `:not` will bump the specificity
148+
// a lot and will cause it to overide the focus styles.
149+
&, .mat-radio-disabled .mat-radio-container:hover & {
150+
opacity: 0;
151+
}
126152
}

src/lib/radio/radio.spec.ts

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -197,25 +197,6 @@ describe('MatRadio', () => {
197197
expect(changeSpy).toHaveBeenCalledTimes(1);
198198
});
199199

200-
it('should show a ripple when focusing via the keyboard', fakeAsync(() => {
201-
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
202-
.toBe(0, 'Expected no ripples on init.');
203-
204-
dispatchFakeEvent(radioInputElements[0], 'keydown');
205-
dispatchFakeEvent(radioInputElements[0], 'focus');
206-
207-
tick(defaultRippleAnimationConfig.enterDuration);
208-
209-
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
210-
.toBe(1, 'Expected one ripple after keyboard focus.');
211-
212-
dispatchFakeEvent(radioInputElements[0], 'blur');
213-
tick(defaultRippleAnimationConfig.exitDuration);
214-
215-
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
216-
.toBe(0, 'Expected no ripples on blur.');
217-
}));
218-
219200
it('should update the group and radios when updating the group value', () => {
220201
expect(groupInstance.value).toBeFalsy();
221202

@@ -253,16 +234,21 @@ describe('MatRadio', () => {
253234
dispatchFakeEvent(radioLabelElements[0], 'mousedown');
254235
dispatchFakeEvent(radioLabelElements[0], 'mouseup');
255236

256-
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
257-
.toBe(0, 'Expected a disabled radio button to not show ripples');
237+
let rippleAmount = radioNativeElements[0]
238+
.querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length;
239+
240+
expect(rippleAmount).toBe(0, 'Expected a disabled radio button to not show ripples');
258241

259242
testComponent.isFirstDisabled = false;
260243
fixture.detectChanges();
261244

262245
dispatchFakeEvent(radioLabelElements[0], 'mousedown');
263246
dispatchFakeEvent(radioLabelElements[0], 'mouseup');
264247

265-
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
248+
rippleAmount = radioNativeElements[0]
249+
.querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length;
250+
251+
expect(rippleAmount)
266252
.toBe(1, 'Expected an enabled radio button to show ripples');
267253
});
268254

@@ -274,7 +260,10 @@ describe('MatRadio', () => {
274260
dispatchFakeEvent(radioLabel, 'mousedown');
275261
dispatchFakeEvent(radioLabel, 'mouseup');
276262

277-
expect(radioLabel.querySelectorAll('.mat-ripple-element').length).toBe(0);
263+
const rippleAmount = radioNativeElements[0]
264+
.querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length;
265+
266+
expect(rippleAmount).toBe(0);
278267
}
279268

280269
testComponent.disableRipple = false;
@@ -284,7 +273,10 @@ describe('MatRadio', () => {
284273
dispatchFakeEvent(radioLabel, 'mousedown');
285274
dispatchFakeEvent(radioLabel, 'mouseup');
286275

287-
expect(radioLabel.querySelectorAll('.mat-ripple-element').length).toBe(1);
276+
const rippleAmount = radioNativeElements[0]
277+
.querySelectorAll('.mat-ripple-element:not(.mat-radio-persistent-ripple)').length;
278+
279+
expect(rippleAmount).toBe(1);
288280
}
289281
});
290282

src/lib/radio/radio.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -459,12 +459,6 @@ export class MatRadioButton extends _MatRadioButtonMixinBase
459459
/** Value assigned to this radio. */
460460
private _value: any = null;
461461

462-
/** The child ripple instance. */
463-
@ViewChild(MatRipple) _ripple: MatRipple;
464-
465-
/** Reference to the current focus ripple. */
466-
private _focusRipple: RippleRef | null;
467-
468462
/** Unregister function for _radioDispatcher */
469463
private _removeUniqueSelectionListener: () => void = () => {};
470464

@@ -518,12 +512,16 @@ export class MatRadioButton extends _MatRadioButtonMixinBase
518512

519513
ngAfterViewInit() {
520514
this._focusMonitor
521-
.monitor(this._inputElement.nativeElement)
522-
.subscribe(focusOrigin => this._onInputFocusChange(focusOrigin));
515+
.monitor(this._elementRef.nativeElement, true)
516+
.subscribe(focusOrigin => {
517+
if (!focusOrigin && this.radioGroup) {
518+
this.radioGroup._touch();
519+
}
520+
});
523521
}
524522

525523
ngOnDestroy() {
526-
this._focusMonitor.stopMonitoring(this._inputElement.nativeElement);
524+
this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
527525
this._removeUniqueSelectionListener();
528526
}
529527

@@ -570,21 +568,4 @@ export class MatRadioButton extends _MatRadioButtonMixinBase
570568
}
571569
}
572570

573-
/** Function is called whenever the focus changes for the input element. */
574-
private _onInputFocusChange(focusOrigin: FocusOrigin) {
575-
// TODO(paul): support `program`. See https://github.com/angular/material2/issues/9889
576-
if (!this._focusRipple && focusOrigin === 'keyboard') {
577-
this._focusRipple = this._ripple.launch(0, 0, {persistent: true});
578-
} else if (!focusOrigin) {
579-
if (this.radioGroup) {
580-
this.radioGroup._touch();
581-
}
582-
583-
if (this._focusRipple) {
584-
this._focusRipple.fadeOut();
585-
this._focusRipple = null;
586-
}
587-
}
588-
}
589-
590571
}

0 commit comments

Comments
 (0)