66 ElementRef ,
77 Renderer ,
88 EventEmitter ,
9- HostBinding ,
109 Input ,
1110 OnInit ,
1211 Optional ,
@@ -17,17 +16,23 @@ import {
1716 NgModule ,
1817 ModuleWithProviders ,
1918 ViewChild ,
19+ OnDestroy ,
20+ AfterViewInit ,
2021} from '@angular/core' ;
2122import { CommonModule } from '@angular/common' ;
2223import { NG_VALUE_ACCESSOR , ControlValueAccessor } from '@angular/forms' ;
2324import {
2425 MdRippleModule ,
26+ RippleRef ,
2527 UniqueSelectionDispatcher ,
2628 CompatibilityModule ,
2729 UNIQUE_SELECTION_DISPATCHER_PROVIDER ,
30+ MdRipple ,
31+ FocusOriginMonitor ,
2832} from '../core' ;
2933import { coerceBooleanProperty } from '../core/coercion/boolean-property' ;
3034import { VIEWPORT_RULER_PROVIDER } from '../core/overlay/position/viewport-ruler' ;
35+ import { Subscription } from 'rxjs/Subscription' ;
3136
3237
3338/**
@@ -265,24 +270,21 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
265270 encapsulation : ViewEncapsulation . None ,
266271 host : {
267272 '[class.mat-radio-button]' : 'true' ,
273+ '[class.mat-radio-checked]' : 'checked' ,
274+ '[class.mat-radio-disabled]' : 'disabled' ,
275+ '[attr.id]' : 'id' ,
268276 }
269277} )
270- export class MdRadioButton implements OnInit {
271-
272- @HostBinding ( 'class.mat-radio-focused' )
273- _isFocused : boolean ;
278+ export class MdRadioButton implements OnInit , AfterViewInit , OnDestroy {
274279
275280 /** Whether this radio is checked. */
276281 private _checked : boolean = false ;
277282
278283 /** The unique ID for the radio button. */
279- @HostBinding ( 'id' )
280- @Input ( )
281- id : string = `md-radio-${ _uniqueIdCounter ++ } ` ;
284+ @Input ( ) id : string = `md-radio-${ _uniqueIdCounter ++ } ` ;
282285
283286 /** Analog to HTML 'name' attribute used to group radios for unique selection. */
284- @Input ( )
285- name : string ;
287+ @Input ( ) name : string ;
286288
287289 /** Used to set the 'aria-label' attribute on the underlying input element. */
288290 @Input ( 'aria-label' ) ariaLabel : string ;
@@ -299,6 +301,15 @@ export class MdRadioButton implements OnInit {
299301 /** Whether the ripple effect on click should be disabled. */
300302 private _disableRipple : boolean ;
301303
304+ /** The child ripple instance. */
305+ @ViewChild ( MdRipple ) _ripple : MdRipple ;
306+
307+ /** Stream of focus event from the focus origin monitor. */
308+ private _focusOriginMonitorSubscription : Subscription ;
309+
310+ /** Reference to the current focus ripple. */
311+ private _focusedRippleRef : RippleRef ;
312+
302313 /** The parent radio group. May or may not be present. */
303314 radioGroup : MdRadioGroup ;
304315
@@ -321,6 +332,7 @@ export class MdRadioButton implements OnInit {
321332 constructor ( @Optional ( ) radioGroup : MdRadioGroup ,
322333 private _elementRef : ElementRef ,
323334 private _renderer : Renderer ,
335+ private _focusOriginMonitor : FocusOriginMonitor ,
324336 public radioDispatcher : UniqueSelectionDispatcher ) {
325337 // Assertions. Ideally these should be stripped out by the compiler.
326338 // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
@@ -340,7 +352,6 @@ export class MdRadioButton implements OnInit {
340352 }
341353
342354 /** Whether this radio button is checked. */
343- @HostBinding ( 'class.mat-radio-checked' )
344355 @Input ( )
345356 get checked ( ) : boolean {
346357 return this . _checked ;
@@ -415,7 +426,6 @@ export class MdRadioButton implements OnInit {
415426 }
416427
417428 /** Whether the radio button is disabled. */
418- @HostBinding ( 'class.mat-radio-disabled' )
419429 @Input ( )
420430 get disabled ( ) : boolean {
421431 return this . _disabled || ( this . radioGroup != null && this . radioGroup . disabled ) ;
@@ -435,6 +445,25 @@ export class MdRadioButton implements OnInit {
435445 }
436446 }
437447
448+ ngAfterViewInit ( ) {
449+ this . _focusOriginMonitorSubscription = this . _focusOriginMonitor
450+ . monitor ( this . _inputElement . nativeElement , this . _renderer , false )
451+ . subscribe ( focusOrigin => {
452+ if ( focusOrigin === 'keyboard' && ! this . _focusedRippleRef ) {
453+ this . _focusedRippleRef = this . _ripple . launch ( 0 , 0 , { persistent : true , centered : true } ) ;
454+ }
455+ } ) ;
456+ }
457+
458+ ngOnDestroy ( ) {
459+ this . _focusOriginMonitor . unmonitor ( this . _inputElement . nativeElement ) ;
460+
461+ if ( this . _focusOriginMonitorSubscription ) {
462+ this . _focusOriginMonitorSubscription . unsubscribe ( ) ;
463+ this . _focusOriginMonitorSubscription = null ;
464+ }
465+ }
466+
438467 /** Dispatch change event with current value. */
439468 private _emitChangeEvent ( ) : void {
440469 let event = new MdRadioChange ( ) ;
@@ -447,23 +476,16 @@ export class MdRadioButton implements OnInit {
447476 return this . disableRipple || this . disabled ;
448477 }
449478
450- /**
451- * We use a hidden native input field to handle changes to focus state via keyboard navigation,
452- * with visual rendering done separately. The native element is kept in sync with the overall
453- * state of the component.
454- */
455- _onInputFocus ( ) {
456- this . _isFocused = true ;
457- }
458-
459479 /** Focuses the radio button. */
460480 focus ( ) : void {
461- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
462- this . _onInputFocus ( ) ;
481+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'keyboard' ) ;
463482 }
464483
465484 _onInputBlur ( ) {
466- this . _isFocused = false ;
485+ if ( this . _focusedRippleRef ) {
486+ this . _focusedRippleRef . fadeOut ( ) ;
487+ this . _focusedRippleRef = null ;
488+ }
467489
468490 if ( this . radioGroup ) {
469491 this . radioGroup . _touch ( ) ;
@@ -503,13 +525,14 @@ export class MdRadioButton implements OnInit {
503525 }
504526 }
505527 }
528+
506529}
507530
508531
509532@NgModule ( {
510533 imports : [ CommonModule , MdRippleModule , CompatibilityModule ] ,
511534 exports : [ MdRadioGroup , MdRadioButton , CompatibilityModule ] ,
512- providers : [ UNIQUE_SELECTION_DISPATCHER_PROVIDER , VIEWPORT_RULER_PROVIDER ] ,
535+ providers : [ UNIQUE_SELECTION_DISPATCHER_PROVIDER , VIEWPORT_RULER_PROVIDER , FocusOriginMonitor ] ,
513536 declarations : [ MdRadioGroup , MdRadioButton ] ,
514537} )
515538export class MdRadioModule {
0 commit comments