@@ -10,11 +10,20 @@ import {
1010 AfterContentInit ,
1111 ViewChild ,
1212 ViewEncapsulation ,
13+ OnDestroy ,
1314} from '@angular/core' ;
15+ import {
16+ applyCssTransform ,
17+ coerceBooleanProperty ,
18+ HammerInput ,
19+ FocusOriginMonitor ,
20+ FocusOrigin ,
21+ MdRipple ,
22+ RippleRef
23+ } from '../core' ;
1424import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
15- import { applyCssTransform , coerceBooleanProperty , HammerInput } from '../core' ;
1625import { Observable } from 'rxjs/Observable' ;
17-
26+ import { Subscription } from 'rxjs/Subscription' ;
1827
1928export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR : any = {
2029 provide : NG_VALUE_ACCESSOR ,
@@ -41,8 +50,6 @@ let nextId = 0;
4150 '[class.mat-slide-toggle]' : 'true' ,
4251 '[class.mat-checked]' : 'checked' ,
4352 '[class.mat-disabled]' : 'disabled' ,
44- // This mat-slide-toggle prefix will change, once the temporary ripple is removed.
45- '[class.mat-slide-toggle-focused]' : '_hasFocus' ,
4653 '[class.mat-slide-toggle-label-before]' : 'labelPosition == "before"' ,
4754 '(mousedown)' : '_setMousedown()'
4855 } ,
@@ -52,7 +59,7 @@ let nextId = 0;
5259 encapsulation : ViewEncapsulation . None ,
5360 changeDetection : ChangeDetectionStrategy . OnPush
5461} )
55- export class MdSlideToggle implements AfterContentInit , ControlValueAccessor {
62+ export class MdSlideToggle implements OnDestroy , AfterContentInit , ControlValueAccessor {
5663
5764 private onChange = ( _ : any ) => { } ;
5865 private onTouched = ( ) => { } ;
@@ -67,8 +74,11 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
6774 private _required : boolean = false ;
6875 private _disableRipple : boolean = false ;
6976
70- // Needs to be public to support AOT compilation (as host binding).
71- _hasFocus : boolean = false ;
77+ /** Reference to the focus state ripple. */
78+ private _focusRipple : RippleRef ;
79+
80+ /** Reference to the focus origin monitor subscription. */
81+ private _focusOriginSubscription : Subscription ;
7282
7383 /** Name value will be applied to the input element if present */
7484 @Input ( ) name : string = null ;
@@ -110,12 +120,31 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
110120 /** Returns the unique id for the visual hidden input. */
111121 get inputId ( ) : string { return `${ this . id || this . _uniqueId } -input` ; }
112122
123+ /** Reference to the underlying input element. */
113124 @ViewChild ( 'input' ) _inputElement : ElementRef ;
114125
115- constructor ( private _elementRef : ElementRef , private _renderer : Renderer ) { }
126+ /** Reference to the ripple directive on the thumb container. */
127+ @ViewChild ( MdRipple ) _ripple : MdRipple ;
128+
129+ constructor ( private _elementRef : ElementRef ,
130+ private _renderer : Renderer ,
131+ private _focusOriginMonitor : FocusOriginMonitor ) { }
116132
117133 ngAfterContentInit ( ) {
118134 this . _slideRenderer = new SlideToggleRenderer ( this . _elementRef ) ;
135+
136+ this . _focusOriginSubscription = this . _focusOriginMonitor
137+ . monitor ( this . _inputElement . nativeElement , this . _renderer , false )
138+ . subscribe ( focusOrigin => this . _onInputFocusChange ( focusOrigin ) ) ;
139+ }
140+
141+ ngOnDestroy ( ) {
142+ this . _focusOriginMonitor . unmonitor ( this . _inputElement . nativeElement ) ;
143+
144+ if ( this . _focusOriginSubscription ) {
145+ this . _focusOriginSubscription . unsubscribe ( ) ;
146+ this . _focusOriginSubscription = null ;
147+ }
119148 }
120149
121150 /**
@@ -162,19 +191,6 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
162191 setTimeout ( ( ) => this . _isMousedown = false , 100 ) ;
163192 }
164193
165- _onInputFocus ( ) {
166- // Only show the focus / ripple indicator when the focus was not triggered by a mouse
167- // interaction on the component.
168- if ( ! this . _isMousedown ) {
169- this . _hasFocus = true ;
170- }
171- }
172-
173- _onInputBlur ( ) {
174- this . _hasFocus = false ;
175- this . onTouched ( ) ;
176- }
177-
178194 /** Implemented as part of ControlValueAccessor. */
179195 writeValue ( value : any ) : void {
180196 this . checked = value ;
@@ -195,10 +211,9 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
195211 this . disabled = isDisabled ;
196212 }
197213
198- /** Focuses the slide-toggle. */
214+ /** Focuses the slide-toggle programmatically . */
199215 focus ( ) {
200- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
201- this . _onInputFocus ( ) ;
216+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'program' ) ;
202217 }
203218
204219 /** Whether the slide-toggle is checked. */
@@ -223,6 +238,22 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
223238 this . checked = ! this . checked ;
224239 }
225240
241+ /** Function is called whenever the focus changes for the input element. */
242+ private _onInputFocusChange ( focusOrigin : FocusOrigin ) {
243+ if ( ! this . _focusRipple && focusOrigin === 'keyboard' ) {
244+ // For keyboard focus show a persistent ripple as focus indicator.
245+ this . _focusRipple = this . _ripple . launch ( 0 , 0 , { persistent : true , centered : true } ) ;
246+ } else if ( ! focusOrigin ) {
247+ this . onTouched ( ) ;
248+
249+ // Fade out and clear the focus ripple if one is currently present.
250+ if ( this . _focusRipple ) {
251+ this . _focusRipple . fadeOut ( ) ;
252+ this . _focusRipple = null ;
253+ }
254+ }
255+ }
256+
226257 private _updateColor ( newColor : string ) {
227258 this . _setElementColor ( this . _color , false ) ;
228259 this . _setElementColor ( newColor , true ) ;
0 commit comments