7
7
Input ,
8
8
Output ,
9
9
EventEmitter ,
10
- AfterContentInit
10
+ AfterContentInit ,
11
+ ViewChild ,
12
+ AfterViewInit
11
13
} from '@angular/core' ;
12
14
import {
13
15
ControlValueAccessor ,
@@ -46,7 +48,7 @@ let nextId = 0;
46
48
providers : [ MD_SLIDE_TOGGLE_VALUE_ACCESSOR ] ,
47
49
changeDetection : ChangeDetectionStrategy . OnPush
48
50
} )
49
- export class MdSlideToggle implements AfterContentInit , ControlValueAccessor {
51
+ export class MdSlideToggle implements AfterContentInit , AfterViewInit , ControlValueAccessor {
50
52
51
53
private onChange = ( _ : any ) => { } ;
52
54
private onTouched = ( ) => { } ;
@@ -59,6 +61,12 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
59
61
private _isMousedown : boolean = false ;
60
62
private _isInitialized : boolean = false ;
61
63
64
+ // Thumb Container Element, which is required for the dragging functionality.
65
+ @ViewChild ( 'thumbContainer' ) private _thumbContainerRef : ElementRef ;
66
+
67
+ // Pointer Object for the dragging functionality of the thumb.
68
+ private _dragPointer : any ;
69
+
62
70
@Input ( ) @BooleanFieldValue ( ) disabled : boolean = false ;
63
71
@Input ( ) name : string = null ;
64
72
@Input ( ) id : string = this . _uniqueId ;
@@ -84,6 +92,23 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
84
92
this . _isInitialized = true ;
85
93
}
86
94
95
+ /** TODO: internal */
96
+ ngAfterViewInit ( ) {
97
+ let hammerManager = new Hammer ( this . _thumbContainerRef . nativeElement ) ;
98
+
99
+ // The Pan / Drag recognition should immediately detect the starting drag event, without
100
+ // waiting for the drag to exceed the elements width.
101
+ hammerManager . add ( new Hammer . Pan ( {
102
+ threshold : 0 ,
103
+ pointers : 0 ,
104
+ direction : Hammer . DIRECTION_HORIZONTAL
105
+ } ) ) ;
106
+
107
+ hammerManager . on ( 'panstart' , this . _onDragStart . bind ( this ) ) ;
108
+ hammerManager . on ( 'panmove' , this . _onDrag . bind ( this ) ) ;
109
+ hammerManager . on ( 'panend' , this . _onDragEnd . bind ( this ) ) ;
110
+ }
111
+
87
112
/**
88
113
* The onChangeEvent method will be also called on click.
89
114
* This is because everything for the slide-toggle is wrapped inside of a label,
@@ -96,7 +121,8 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
96
121
// emit its event object to the component's `change` output.
97
122
event . stopPropagation ( ) ;
98
123
99
- if ( ! this . disabled ) {
124
+ // Once a drag is currently in progress, we do not want to toggle the slide-toggle on a click.
125
+ if ( ! this . disabled && ! this . _dragPointer ) {
100
126
this . toggle ( ) ;
101
127
}
102
128
}
@@ -214,6 +240,54 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
214
240
this . _change . emit ( event ) ;
215
241
}
216
242
243
+
244
+ private _onDragStart ( ) {
245
+ if ( this . _dragPointer ) {
246
+ return ;
247
+ }
248
+
249
+ let thumbBarEl = this . _elementRef . nativeElement . querySelector ( '.md-slide-toggle-bar' ) ;
250
+
251
+ this . _dragPointer = {
252
+ barWidth : thumbBarEl . getBoundingClientRect ( ) . width
253
+ } ;
254
+
255
+ // Mark the thumb container with the `md-dragging` class to remove the duration of
256
+ // transform transition.
257
+ this . _thumbContainerRef . nativeElement . classList . add ( 'md-dragging' ) ;
258
+ }
259
+
260
+ private _onDrag ( event : HammerInput ) {
261
+ if ( ! this . _dragPointer ) {
262
+ return ;
263
+ }
264
+
265
+ let percentage = ( event . deltaX / this . _dragPointer . barWidth ) * 100 ;
266
+
267
+ // When the `slide-toggle` is currently checked, we invert the percentage, because it
268
+ // starts from the right.
269
+ percentage = this . checked ? 100 + percentage : percentage ;
270
+ percentage = Math . max ( 0 , Math . min ( percentage , 100 ) ) ;
271
+
272
+ this . _dragPointer . percentage = percentage ;
273
+ this . _thumbContainerRef . nativeElement . style . transform = `translate3d(${ percentage } %, 0, 0)` ;
274
+ }
275
+
276
+ private _onDragEnd ( ) {
277
+ if ( ! this . _dragPointer ) {
278
+ return ;
279
+ }
280
+
281
+ this . checked = this . _dragPointer . percentage > 50 ;
282
+
283
+ this . _thumbContainerRef . nativeElement . style . transform = '' ;
284
+ this . _thumbContainerRef . nativeElement . classList . remove ( 'md-dragging' ) ;
285
+
286
+ // We have to clear the drag after one tick, because otherwise
287
+ // the click event will fire and toggle the slide-toggle again.
288
+ setTimeout ( ( ) => { this . _dragPointer = null ; } , 1 ) ;
289
+ }
290
+
217
291
}
218
292
219
293
export const MD_SLIDE_TOGGLE_DIRECTIVES = [ MdSlideToggle ] ;
0 commit comments