6
6
ElementRef ,
7
7
Renderer ,
8
8
EventEmitter ,
9
- HostBinding ,
10
9
Input ,
11
10
OnInit ,
12
11
Optional ,
@@ -17,17 +16,23 @@ import {
17
16
NgModule ,
18
17
ModuleWithProviders ,
19
18
ViewChild ,
19
+ OnDestroy ,
20
+ AfterViewInit ,
20
21
} from '@angular/core' ;
21
22
import { CommonModule } from '@angular/common' ;
22
23
import { NG_VALUE_ACCESSOR , ControlValueAccessor } from '@angular/forms' ;
23
24
import {
24
25
MdRippleModule ,
26
+ RippleRef ,
25
27
UniqueSelectionDispatcher ,
26
28
CompatibilityModule ,
27
29
UNIQUE_SELECTION_DISPATCHER_PROVIDER ,
30
+ MdRipple ,
31
+ FocusOriginMonitor ,
28
32
} from '../core' ;
29
33
import { coerceBooleanProperty } from '../core/coercion/boolean-property' ;
30
34
import { VIEWPORT_RULER_PROVIDER } from '../core/overlay/position/viewport-ruler' ;
35
+ import { Subscription } from 'rxjs/Subscription' ;
31
36
32
37
33
38
/**
@@ -265,24 +270,21 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
265
270
encapsulation : ViewEncapsulation . None ,
266
271
host : {
267
272
'[class.mat-radio-button]' : 'true' ,
273
+ '[class.mat-radio-checked]' : 'checked' ,
274
+ '[class.mat-radio-disabled]' : 'disabled' ,
275
+ '[attr.id]' : 'id' ,
268
276
}
269
277
} )
270
- export class MdRadioButton implements OnInit {
271
-
272
- @HostBinding ( 'class.mat-radio-focused' )
273
- _isFocused : boolean ;
278
+ export class MdRadioButton implements OnInit , AfterViewInit , OnDestroy {
274
279
275
280
/** Whether this radio is checked. */
276
281
private _checked : boolean = false ;
277
282
278
283
/** 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 ++ } ` ;
282
285
283
286
/** Analog to HTML 'name' attribute used to group radios for unique selection. */
284
- @Input ( )
285
- name : string ;
287
+ @Input ( ) name : string ;
286
288
287
289
/** Used to set the 'aria-label' attribute on the underlying input element. */
288
290
@Input ( 'aria-label' ) ariaLabel : string ;
@@ -299,6 +301,15 @@ export class MdRadioButton implements OnInit {
299
301
/** Whether the ripple effect on click should be disabled. */
300
302
private _disableRipple : boolean ;
301
303
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
+
302
313
/** The parent radio group. May or may not be present. */
303
314
radioGroup : MdRadioGroup ;
304
315
@@ -321,6 +332,7 @@ export class MdRadioButton implements OnInit {
321
332
constructor ( @Optional ( ) radioGroup : MdRadioGroup ,
322
333
private _elementRef : ElementRef ,
323
334
private _renderer : Renderer ,
335
+ private _focusOriginMonitor : FocusOriginMonitor ,
324
336
public radioDispatcher : UniqueSelectionDispatcher ) {
325
337
// Assertions. Ideally these should be stripped out by the compiler.
326
338
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
@@ -340,7 +352,6 @@ export class MdRadioButton implements OnInit {
340
352
}
341
353
342
354
/** Whether this radio button is checked. */
343
- @HostBinding ( 'class.mat-radio-checked' )
344
355
@Input ( )
345
356
get checked ( ) : boolean {
346
357
return this . _checked ;
@@ -415,7 +426,6 @@ export class MdRadioButton implements OnInit {
415
426
}
416
427
417
428
/** Whether the radio button is disabled. */
418
- @HostBinding ( 'class.mat-radio-disabled' )
419
429
@Input ( )
420
430
get disabled ( ) : boolean {
421
431
return this . _disabled || ( this . radioGroup != null && this . radioGroup . disabled ) ;
@@ -435,6 +445,25 @@ export class MdRadioButton implements OnInit {
435
445
}
436
446
}
437
447
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
+
438
467
/** Dispatch change event with current value. */
439
468
private _emitChangeEvent ( ) : void {
440
469
let event = new MdRadioChange ( ) ;
@@ -447,23 +476,16 @@ export class MdRadioButton implements OnInit {
447
476
return this . disableRipple || this . disabled ;
448
477
}
449
478
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
-
459
479
/** Focuses the radio button. */
460
480
focus ( ) : void {
461
- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
462
- this . _onInputFocus ( ) ;
481
+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'keyboard' ) ;
463
482
}
464
483
465
484
_onInputBlur ( ) {
466
- this . _isFocused = false ;
485
+ if ( this . _focusedRippleRef ) {
486
+ this . _focusedRippleRef . fadeOut ( ) ;
487
+ this . _focusedRippleRef = null ;
488
+ }
467
489
468
490
if ( this . radioGroup ) {
469
491
this . radioGroup . _touch ( ) ;
@@ -503,13 +525,14 @@ export class MdRadioButton implements OnInit {
503
525
}
504
526
}
505
527
}
528
+
506
529
}
507
530
508
531
509
532
@NgModule ( {
510
533
imports : [ CommonModule , MdRippleModule , CompatibilityModule ] ,
511
534
exports : [ MdRadioGroup , MdRadioButton , CompatibilityModule ] ,
512
- providers : [ UNIQUE_SELECTION_DISPATCHER_PROVIDER , VIEWPORT_RULER_PROVIDER ] ,
535
+ providers : [ UNIQUE_SELECTION_DISPATCHER_PROVIDER , VIEWPORT_RULER_PROVIDER , FocusOriginMonitor ] ,
513
536
declarations : [ MdRadioGroup , MdRadioButton ] ,
514
537
} )
515
538
export class MdRadioModule {
0 commit comments