Skip to content

Commit 196bad7

Browse files
authored
feature(radio): Add ripple effect for radio buttons (#1551)
1 parent aa250d4 commit 196bad7

File tree

6 files changed

+53
-11
lines changed

6 files changed

+53
-11
lines changed

src/lib/radio/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ The `md-radio-group` component has no button initially selected.
5757
| `aria-label` | `string` | Used to set the `aria-label` attribute of the underlying input element. |
5858
| `aria-labelledby` | `string` | Used to set the `aria-labelledby` attribute of the underlying input element.
5959
If provided, this attribute takes precedence as the element's text alternative. |
60+
| `disableRipple` | boolean | Whether the ripple effect when the radio button is clicked should be disabled
6061

6162
When checked, an event is emitted from the `change` EventEmitter property.
6263

src/lib/radio/_radio-theme.scss

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929
}
3030
}
3131

32-
// TODO(jelbourn): remove style for temporary ripple once the real ripple is applied.
33-
.md-radio-focused .md-ink-ripple {
32+
.md-radio-focused .md-radio-ripple .md-ripple-foreground {
3433
background-color: md-color($accent, 0.26);
3534
}
3635
}

src/lib/radio/radio.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<!-- TODO(jelbourn): render the radio on either side of the content -->
22
<!-- TODO(mtlin): Evaluate trade-offs of using native radio vs. cost of additional bindings. -->
33
<label [attr.for]="inputId" class="md-radio-label">
4+
45
<!-- The actual 'radio' part of the control. -->
56
<div class="md-radio-container">
67
<div class="md-radio-outer-circle"></div>
78
<div class="md-radio-inner-circle"></div>
8-
<div class="md-ink-ripple"></div>
9+
<div md-ripple *ngIf="isRippleEnabled()" class="md-radio-ripple"
10+
[md-ripple-trigger]="getHostElement()"
11+
[md-ripple-centered]="true"
12+
md-ripple-background-color="rgba(0, 0, 0, 0)"></div>
913
</div>
1014

1115
<input #input class="md-radio-input md-visually-hidden" type="radio"

src/lib/radio/radio.scss

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
$md-radio-size: $md-toggle-size !default;
6+
$md-radio-ripple-size: $md-radio-size * 0.75;
67

78
// Top-level host container.
89
md-radio-button {
@@ -89,4 +90,12 @@ md-radio-button {
8990
cursor: default;
9091
}
9192

92-
@include md-temporary-ink-ripple(radio);
93+
.md-radio-ripple {
94+
position: absolute;
95+
left: -$md-radio-ripple-size;
96+
top: -$md-radio-ripple-size;
97+
right: -$md-radio-ripple-size;
98+
bottom: -$md-radio-ripple-size;
99+
border-radius: 50%;
100+
z-index: 1;
101+
}

src/lib/radio/radio.spec.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,21 @@ describe('MdRadio', () => {
216216

217217
expect(radioInstances.every(radio => !radio.checked)).toBe(true);
218218
});
219+
220+
it('should remove ripple if md-ripple-disabled input is set', async(() => {
221+
fixture.detectChanges();
222+
for (let radioNativeElement of radioNativeElements)
223+
{
224+
expect(radioNativeElement.querySelectorAll('[md-ripple]').length).toBe(1);
225+
}
226+
227+
testComponent.disableRipple = true;
228+
fixture.detectChanges();
229+
for (let radioNativeElement of radioNativeElements)
230+
{
231+
expect(radioNativeElement.querySelectorAll('[md-ripple]').length).toBe(0);
232+
}
233+
}));
219234
});
220235

221236
describe('group with ngModel', () => {
@@ -426,26 +441,25 @@ describe('MdRadio', () => {
426441
});
427442
});
428443

429-
430444
@Component({
431445
template: `
432446
<md-radio-group [disabled]="isGroupDisabled"
433447
[align]="alignment"
434448
[value]="groupValue"
435449
name="test-name">
436-
<md-radio-button value="fire">Charmander</md-radio-button>
437-
<md-radio-button value="water">Squirtle</md-radio-button>
438-
<md-radio-button value="leaf">Bulbasaur</md-radio-button>
450+
<md-radio-button value="fire" [disableRipple]="disableRipple">Charmander</md-radio-button>
451+
<md-radio-button value="water" [disableRipple]="disableRipple">Squirtle</md-radio-button>
452+
<md-radio-button value="leaf" [disableRipple]="disableRipple">Bulbasaur</md-radio-button>
439453
</md-radio-group>
440454
`
441455
})
442456
class RadiosInsideRadioGroup {
457+
disableRipple: boolean = false;
443458
alignment: string;
444459
isGroupDisabled: boolean = false;
445460
groupValue: string = null;
446461
}
447462

448-
449463
@Component({
450464
template: `
451465
<md-radio-button name="season" value="spring">Spring</md-radio-button>
@@ -464,7 +478,6 @@ class RadiosInsideRadioGroup {
464478
})
465479
class StandaloneRadioButtons { }
466480

467-
468481
@Component({
469482
template: `
470483
<md-radio-group [(ngModel)]="modelValue" (change)="lastEvent = $event">

src/lib/radio/radio.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,26 @@ import {
33
Component,
44
ContentChildren,
55
Directive,
6+
ElementRef,
67
EventEmitter,
78
HostBinding,
89
Input,
910
OnInit,
1011
Optional,
1112
Output,
1213
QueryList,
14+
ViewChild,
1315
ViewEncapsulation,
1416
forwardRef,
1517
NgModule,
1618
ModuleWithProviders,
1719
} from '@angular/core';
20+
import {CommonModule} from '@angular/common';
1821
import {
1922
NG_VALUE_ACCESSOR,
2023
ControlValueAccessor
2124
} from '@angular/forms';
22-
import {MdUniqueSelectionDispatcher} from '../core';
25+
import {BooleanFieldValue, MdRippleModule, MdUniqueSelectionDispatcher} from '../core';
2326

2427

2528

@@ -258,6 +261,9 @@ export class MdRadioButton implements OnInit {
258261
/** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */
259262
@Input('aria-labelledby') ariaLabelledby: string;
260263

264+
/** Whether the ripple effect on click should be disabled. */
265+
@Input() @BooleanFieldValue() disableRipple: boolean = false;
266+
261267
/** Whether this radio is disabled. */
262268
private _disabled: boolean;
263269

@@ -272,6 +278,7 @@ export class MdRadioButton implements OnInit {
272278
change: EventEmitter<MdRadioChange> = new EventEmitter<MdRadioChange>();
273279

274280
constructor(@Optional() radioGroup: MdRadioGroup,
281+
private _elementRef: ElementRef,
275282
public radioDispatcher: MdUniqueSelectionDispatcher) {
276283
// Assertions. Ideally these should be stripped out by the compiler.
277284
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
@@ -411,10 +418,19 @@ export class MdRadioButton implements OnInit {
411418
this.radioGroup._touch();
412419
}
413420
}
421+
422+
getHostElement() {
423+
return this._elementRef.nativeElement;
424+
}
425+
426+
isRippleEnabled() {
427+
return !this.disableRipple;
428+
}
414429
}
415430

416431

417432
@NgModule({
433+
imports: [CommonModule, MdRippleModule],
418434
exports: [MdRadioGroup, MdRadioButton],
419435
declarations: [MdRadioGroup, MdRadioButton],
420436
})

0 commit comments

Comments
 (0)