@@ -7,14 +7,16 @@ import {
77 Input ,
88 Output ,
99 EventEmitter ,
10- AfterContentInit
10+ AfterContentInit ,
11+ ViewChild
1112} from '@angular/core' ;
1213import {
1314 ControlValueAccessor ,
1415 NG_VALUE_ACCESSOR
1516} from '@angular/forms' ;
16- import { BooleanFieldValue } from '@angular2-material/core/annotations/field-value' ;
17- import { Observable } from 'rxjs/Observable' ;
17+ import { BooleanFieldValue } from '@angular2-material/core/annotations/field-value' ;
18+ import { Observable } from 'rxjs/Observable' ;
19+ import { applyCssTransform } from '@angular2-material/core/style/apply-transform' ;
1820
1921export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR : any = {
2022 provide : NG_VALUE_ACCESSOR ,
@@ -58,6 +60,13 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
5860 private _hasFocus : boolean = false ;
5961 private _isMousedown : boolean = false ;
6062 private _isInitialized : boolean = false ;
63+ private _domRenderer : MdSlideToggleRenderer = null ;
64+
65+ // Drag pointer, which holds information about the current drag.
66+ private _dragPointer : {
67+ barWidth : number ;
68+ percentage ?: number ;
69+ } ;
6170
6271 @Input ( ) @BooleanFieldValue ( ) disabled : boolean = false ;
6372 @Input ( ) name : string = null ;
@@ -74,6 +83,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
7483
7584 constructor ( private _elementRef : ElementRef ,
7685 private _renderer : Renderer ) {
86+ this . _domRenderer = new MdSlideToggleRenderer ( this . _elementRef ) ;
7787 }
7888
7989 /** TODO: internal */
@@ -95,7 +105,8 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
95105 // emit its event object to the component's `change` output.
96106 event . stopPropagation ( ) ;
97107
98- if ( ! this . disabled ) {
108+ // Once a drag is currently in progress, we do not want to toggle the slide-toggle on a click.
109+ if ( ! this . disabled && ! this . _dragPointer ) {
99110 this . toggle ( ) ;
100111 }
101112 }
@@ -202,13 +213,101 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
202213 }
203214 }
204215
216+ /** Emits the change event to the `change` output EventEmitter */
205217 private _emitChangeEvent ( ) {
206218 let event = new MdSlideToggleChange ( ) ;
207219 event . source = this ;
208220 event . checked = this . checked ;
209221 this . _change . emit ( event ) ;
210222 }
211223
224+
225+ /** @internal */
226+ _onDragStart ( ) {
227+ if ( this . _dragPointer ) {
228+ return ;
229+ }
230+
231+ let thumbBarRect = this . _domRenderer . getThumbBarClientRect ( ) ;
232+ let thumbRect = this . _domRenderer . getThumbClientRect ( ) ;
233+
234+ this . _dragPointer = {
235+ barWidth : thumbBarRect . width - thumbRect . width
236+ } ;
237+
238+ this . _domRenderer . toggleDragging ( true ) ;
239+ }
240+
241+ /** @internal */
242+ _onDrag ( event : HammerInput ) {
243+ if ( ! this . _dragPointer ) {
244+ return ;
245+ }
246+
247+ let barWidth = this . _dragPointer . barWidth ;
248+ let distance = Math . max ( - barWidth , Math . min ( event . deltaX , barWidth ) ) ;
249+
250+ let percentage = ( distance / barWidth ) * 100 ;
251+
252+ if ( percentage < 0 ) {
253+ percentage += 100 ;
254+ }
255+
256+ this . _domRenderer . updateThumbPosition ( percentage ) ;
257+ this . _dragPointer . percentage = percentage ;
258+ }
259+
260+ /** @internal */
261+ _onDragEnd ( ) {
262+ if ( ! this . _dragPointer ) {
263+ return ;
264+ }
265+
266+ this . checked = this . _dragPointer . percentage > 50 ;
267+
268+ this . _domRenderer . updateThumbPosition ( null ) ;
269+ this . _domRenderer . toggleDragging ( false ) ;
270+
271+ // We have to clear the drag after one tick, because otherwise
272+ // the click event will fire and toggle the slide-toggle again.
273+ setTimeout ( ( ) => { this . _dragPointer = null ; } , 0 ) ;
274+ }
275+
276+ }
277+
278+ /**
279+ * Renderer for the Slide Toggle component, which separates DOM modification in it's own class
280+ */
281+ export class MdSlideToggleRenderer {
282+
283+ constructor ( private _elementRef : ElementRef ) { }
284+
285+ getThumbClientRect ( ) : ClientRect {
286+ let thumbEl = this . _elementRef . nativeElement . querySelector ( '.md-slide-toggle-thumb-container' ) ;
287+ return thumbEl . getBoundingClientRect ( ) ;
288+ }
289+
290+ getThumbBarClientRect ( ) : ClientRect {
291+ let thumbBarEl = this . _elementRef . nativeElement . querySelector ( '.md-slide-toggle-bar' ) ;
292+ return thumbBarEl . getBoundingClientRect ( ) ;
293+ }
294+
295+ /**
296+ * Updates the thumb containers position by using the specified percentage.
297+ * When the percentage is set to `null`, the custom thumb position will be removed.
298+ */
299+ updateThumbPosition ( percentage : number ) {
300+ let thumbEl = this . _elementRef . nativeElement . querySelector ( '.md-slide-toggle-thumb-container' ) ;
301+ applyCssTransform ( thumbEl , percentage === null ? '' : `translate3d(${ percentage } %, 0, 0)` ) ;
302+ }
303+
304+ /** Toggles the dragging class for the thumb container to toggle the transition duration. */
305+ toggleDragging ( isDragging : boolean ) {
306+ let thumbEl = this . _elementRef . nativeElement . querySelector ( '.md-slide-toggle-thumb-container' ) ;
307+ thumbEl . classList . toggle ( 'md-dragging' , isDragging ) ;
308+ }
309+
310+
212311}
213312
214313export const MD_SLIDE_TOGGLE_DIRECTIVES = [ MdSlideToggle ] ;
0 commit comments