@@ -12,11 +12,20 @@ import {
1212 NgModule ,
1313 ModuleWithProviders ,
1414 ViewChild ,
15+ AfterViewInit ,
16+ OnDestroy ,
1517} from '@angular/core' ;
1618import { CommonModule } from '@angular/common' ;
1719import { NG_VALUE_ACCESSOR , ControlValueAccessor } from '@angular/forms' ;
1820import { coerceBooleanProperty } from '../core/coercion/boolean-property' ;
19- import { MdRippleModule , CompatibilityModule } from '../core' ;
21+ import { Subscription } from 'rxjs/Subscription' ;
22+ import {
23+ CompatibilityModule ,
24+ MdRippleModule ,
25+ MdRipple ,
26+ RippleRef ,
27+ FocusOriginMonitor ,
28+ } from '../core' ;
2029
2130
2231/** Monotonically increasing integer used to auto-generate unique ids for checkbox components. */
@@ -73,13 +82,12 @@ export class MdCheckboxChange {
7382 '[class.mat-checkbox-checked]' : 'checked' ,
7483 '[class.mat-checkbox-disabled]' : 'disabled' ,
7584 '[class.mat-checkbox-label-before]' : 'labelPosition == "before"' ,
76- '[class.mat-checkbox-focused]' : '_hasFocus' ,
7785 } ,
7886 providers : [ MD_CHECKBOX_CONTROL_VALUE_ACCESSOR ] ,
7987 encapsulation : ViewEncapsulation . None ,
8088 changeDetection : ChangeDetectionStrategy . OnPush
8189} )
82- export class MdCheckbox implements ControlValueAccessor {
90+ export class MdCheckbox implements ControlValueAccessor , AfterViewInit , OnDestroy {
8391 /**
8492 * Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
8593 * take precedence so this may be omitted.
@@ -154,6 +162,8 @@ export class MdCheckbox implements ControlValueAccessor {
154162 /** The native `<input type="checkbox"> element */
155163 @ViewChild ( 'input' ) _inputElement : ElementRef ;
156164
165+ @ViewChild ( MdRipple ) _ripple : MdRipple ;
166+
157167 /**
158168 * Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.
159169 * @docs -private
@@ -172,14 +182,38 @@ export class MdCheckbox implements ControlValueAccessor {
172182
173183 private _controlValueAccessorChangeFn : ( value : any ) => void = ( value ) => { } ;
174184
175- _hasFocus : boolean = false ;
185+ /** Reference to the focused state ripple. */
186+ private _focusedRipple : RippleRef ;
187+
188+ /** Reference to the focus origin monitor subscription. */
189+ private _focusedSubscription : Subscription ;
176190
177191 constructor ( private _renderer : Renderer ,
178192 private _elementRef : ElementRef ,
179- private _changeDetectorRef : ChangeDetectorRef ) {
193+ private _changeDetectorRef : ChangeDetectorRef ,
194+ private _focusOriginMonitor : FocusOriginMonitor ) {
180195 this . color = 'accent' ;
181196 }
182197
198+ ngAfterViewInit ( ) {
199+ this . _focusedSubscription = this . _focusOriginMonitor
200+ . monitor ( this . _inputElement . nativeElement , this . _renderer , false )
201+ . subscribe ( focusOrigin => {
202+ if ( ! this . _focusedRipple && focusOrigin === 'keyboard' ) {
203+ this . _focusedRipple = this . _ripple . launch ( 0 , 0 , { persistent : true , centered : true } ) ;
204+ }
205+ } ) ;
206+ }
207+
208+ ngOnDestroy ( ) {
209+ this . _focusOriginMonitor . unmonitor ( this . _inputElement . nativeElement ) ;
210+
211+ if ( this . _focusedSubscription ) {
212+ this . _focusedSubscription . unsubscribe ( ) ;
213+ this . _focusedSubscription = null ;
214+ }
215+ }
216+
183217 /**
184218 * Whether the checkbox is checked. Note that setting `checked` will immediately set
185219 * `indeterminate` to false.
@@ -313,14 +347,9 @@ export class MdCheckbox implements ControlValueAccessor {
313347 this . change . emit ( event ) ;
314348 }
315349
316- /** Informs the component when the input has focus so that we can style accordingly */
317- _onInputFocus ( ) {
318- this . _hasFocus = true ;
319- }
320-
321350 /** Informs the component when we lose focus in order to style accordingly */
322351 _onInputBlur ( ) {
323- this . _hasFocus = false ;
352+ this . _removeFocusedRipple ( ) ;
324353 this . onTouched ( ) ;
325354 }
326355
@@ -346,6 +375,8 @@ export class MdCheckbox implements ControlValueAccessor {
346375 // Preventing bubbling for the second event will solve that issue.
347376 event . stopPropagation ( ) ;
348377
378+ this . _removeFocusedRipple ( ) ;
379+
349380 if ( ! this . disabled ) {
350381 this . toggle ( ) ;
351382
@@ -358,8 +389,7 @@ export class MdCheckbox implements ControlValueAccessor {
358389
359390 /** Focuses the checkbox. */
360391 focus ( ) : void {
361- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
362- this . _onInputFocus ( ) ;
392+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'keyboard' ) ;
363393 }
364394
365395 _onInteractionEvent ( event : Event ) {
@@ -399,13 +429,21 @@ export class MdCheckbox implements ControlValueAccessor {
399429 return `mat-checkbox-anim-${ animSuffix } ` ;
400430 }
401431
432+ /** Fades out the focused state ripple. */
433+ private _removeFocusedRipple ( ) : void {
434+ if ( this . _focusedRipple ) {
435+ this . _focusedRipple . fadeOut ( ) ;
436+ this . _focusedRipple = null ;
437+ }
438+ }
402439}
403440
404441
405442@NgModule ( {
406443 imports : [ CommonModule , MdRippleModule , CompatibilityModule ] ,
407444 exports : [ MdCheckbox , CompatibilityModule ] ,
408445 declarations : [ MdCheckbox ] ,
446+ providers : [ FocusOriginMonitor ]
409447} )
410448export class MdCheckboxModule {
411449 /** @deprecated */
0 commit comments