@@ -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.
@@ -157,6 +165,8 @@ export class MdCheckbox implements ControlValueAccessor {
157165 /** The native `<input type="checkbox"> element */
158166 @ViewChild ( 'input' ) _inputElement : ElementRef ;
159167
168+ @ViewChild ( MdRipple ) _ripple : MdRipple ;
169+
160170 /**
161171 * Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.
162172 * @docs -private
@@ -175,14 +185,38 @@ export class MdCheckbox implements ControlValueAccessor {
175185
176186 private _controlValueAccessorChangeFn : ( value : any ) => void = ( value ) => { } ;
177187
178- _hasFocus : boolean = false ;
188+ /** Reference to the focused state ripple. */
189+ private _focusedRipple : RippleRef ;
190+
191+ /** Reference to the focus origin monitor subscription. */
192+ private _focusedSubscription : Subscription ;
179193
180194 constructor ( private _renderer : Renderer ,
181195 private _elementRef : ElementRef ,
182- private _changeDetectorRef : ChangeDetectorRef ) {
196+ private _changeDetectorRef : ChangeDetectorRef ,
197+ private _focusOriginMonitor : FocusOriginMonitor ) {
183198 this . color = 'accent' ;
184199 }
185200
201+ ngAfterViewInit ( ) {
202+ this . _focusedSubscription = this . _focusOriginMonitor
203+ . monitor ( this . _inputElement . nativeElement , this . _renderer , false )
204+ . subscribe ( focusOrigin => {
205+ if ( ! this . _focusedRipple && focusOrigin === 'keyboard' ) {
206+ this . _focusedRipple = this . _ripple . launch ( 0 , 0 , { persistent : true , centered : true } ) ;
207+ }
208+ } ) ;
209+ }
210+
211+ ngOnDestroy ( ) {
212+ this . _focusOriginMonitor . unmonitor ( this . _inputElement . nativeElement ) ;
213+
214+ if ( this . _focusedSubscription ) {
215+ this . _focusedSubscription . unsubscribe ( ) ;
216+ this . _focusedSubscription = null ;
217+ }
218+ }
219+
186220 /**
187221 * Whether the checkbox is checked. Note that setting `checked` will immediately set
188222 * `indeterminate` to false.
@@ -315,14 +349,9 @@ export class MdCheckbox implements ControlValueAccessor {
315349 this . change . emit ( event ) ;
316350 }
317351
318- /** Informs the component when the input has focus so that we can style accordingly */
319- _onInputFocus ( ) {
320- this . _hasFocus = true ;
321- }
322-
323352 /** Informs the component when we lose focus in order to style accordingly */
324353 _onInputBlur ( ) {
325- this . _hasFocus = false ;
354+ this . _removeFocusedRipple ( ) ;
326355 this . onTouched ( ) ;
327356 }
328357
@@ -348,6 +377,8 @@ export class MdCheckbox implements ControlValueAccessor {
348377 // Preventing bubbling for the second event will solve that issue.
349378 event . stopPropagation ( ) ;
350379
380+ this . _removeFocusedRipple ( ) ;
381+
351382 if ( ! this . disabled ) {
352383 this . toggle ( ) ;
353384 this . _transitionCheckState (
@@ -362,8 +393,7 @@ export class MdCheckbox implements ControlValueAccessor {
362393
363394 /** Focuses the checkbox. */
364395 focus ( ) : void {
365- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
366- this . _onInputFocus ( ) ;
396+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'keyboard' ) ;
367397 }
368398
369399 _onInteractionEvent ( event : Event ) {
@@ -405,13 +435,21 @@ export class MdCheckbox implements ControlValueAccessor {
405435 return `mat-checkbox-anim-${ animSuffix } ` ;
406436 }
407437
438+ /** Fades out the focused state ripple. */
439+ private _removeFocusedRipple ( ) : void {
440+ if ( this . _focusedRipple ) {
441+ this . _focusedRipple . fadeOut ( ) ;
442+ this . _focusedRipple = null ;
443+ }
444+ }
408445}
409446
410447
411448@NgModule ( {
412449 imports : [ CommonModule , MdRippleModule , CompatibilityModule ] ,
413450 exports : [ MdCheckbox , CompatibilityModule ] ,
414451 declarations : [ MdCheckbox ] ,
452+ providers : [ FocusOriginMonitor ]
415453} )
416454export class MdCheckboxModule {
417455 /** @deprecated */
0 commit comments