@@ -10,11 +10,20 @@ import {
10
10
AfterContentInit ,
11
11
ViewChild ,
12
12
ViewEncapsulation ,
13
+ OnDestroy ,
13
14
} from '@angular/core' ;
15
+ import {
16
+ applyCssTransform ,
17
+ coerceBooleanProperty ,
18
+ HammerInput ,
19
+ FocusOriginMonitor ,
20
+ FocusOrigin ,
21
+ MdRipple ,
22
+ RippleRef
23
+ } from '../core' ;
14
24
import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
15
- import { applyCssTransform , coerceBooleanProperty , HammerInput } from '../core' ;
16
25
import { Observable } from 'rxjs/Observable' ;
17
-
26
+ import { Subscription } from 'rxjs/Subscription' ;
18
27
19
28
export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR : any = {
20
29
provide : NG_VALUE_ACCESSOR ,
@@ -41,8 +50,6 @@ let nextId = 0;
41
50
'[class.mat-slide-toggle]' : 'true' ,
42
51
'[class.mat-checked]' : 'checked' ,
43
52
'[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' ,
46
53
'[class.mat-slide-toggle-label-before]' : 'labelPosition == "before"' ,
47
54
'(mousedown)' : '_setMousedown()'
48
55
} ,
@@ -52,7 +59,7 @@ let nextId = 0;
52
59
encapsulation : ViewEncapsulation . None ,
53
60
changeDetection : ChangeDetectionStrategy . OnPush
54
61
} )
55
- export class MdSlideToggle implements AfterContentInit , ControlValueAccessor {
62
+ export class MdSlideToggle implements OnDestroy , AfterContentInit , ControlValueAccessor {
56
63
57
64
private onChange = ( _ : any ) => { } ;
58
65
private onTouched = ( ) => { } ;
@@ -67,8 +74,11 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
67
74
private _required : boolean = false ;
68
75
private _disableRipple : boolean = false ;
69
76
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
+ /** Subscription to focus-origin changes. */
81
+ private _focusOriginSubscription : Subscription ;
72
82
73
83
/** Name value will be applied to the input element if present */
74
84
@Input ( ) name : string = null ;
@@ -110,12 +120,31 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
110
120
/** Returns the unique id for the visual hidden input. */
111
121
get inputId ( ) : string { return `${ this . id || this . _uniqueId } -input` ; }
112
122
123
+ /** Reference to the underlying input element. */
113
124
@ViewChild ( 'input' ) _inputElement : ElementRef ;
114
125
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 ) { }
116
132
117
133
ngAfterContentInit ( ) {
118
134
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
+ }
119
148
}
120
149
121
150
/**
@@ -162,19 +191,6 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
162
191
setTimeout ( ( ) => this . _isMousedown = false , 100 ) ;
163
192
}
164
193
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
-
178
194
/** Implemented as part of ControlValueAccessor. */
179
195
writeValue ( value : any ) : void {
180
196
this . checked = value ;
@@ -197,8 +213,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
197
213
198
214
/** Focuses the slide-toggle. */
199
215
focus ( ) {
200
- this . _renderer . invokeElementMethod ( this . _inputElement . nativeElement , 'focus' ) ;
201
- this . _onInputFocus ( ) ;
216
+ this . _focusOriginMonitor . focusVia ( this . _inputElement . nativeElement , this . _renderer , 'program' ) ;
202
217
}
203
218
204
219
/** Whether the slide-toggle is checked. */
@@ -223,6 +238,22 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
223
238
this . checked = ! this . checked ;
224
239
}
225
240
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
+
226
257
private _updateColor ( newColor : string ) {
227
258
this . _setElementColor ( this . _color , false ) ;
228
259
this . _setElementColor ( newColor , true ) ;
0 commit comments