@@ -13,8 +13,9 @@ import {
1313 ControlValueAccessor ,
1414 NG_VALUE_ACCESSOR
1515} from '@angular/forms' ;
16- import { BooleanFieldValue } from '@angular2-material/core/annotations/field-value' ;
17- import { Observable } from 'rxjs/Observable' ;
16+ import { BooleanFieldValue } from '@angular2-material/core/annotations/field-value' ;
17+ import { Observable } from 'rxjs/Observable' ;
18+ import { applyCssTransform } from '@angular2-material/core/style/apply-transform' ;
1819
1920export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR : any = {
2021 provide : NG_VALUE_ACCESSOR ,
@@ -58,6 +59,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
5859 private _hasFocus : boolean = false ;
5960 private _isMousedown : boolean = false ;
6061 private _isInitialized : boolean = false ;
62+ private _slideRenderer : SlideToggleRenderer = null ;
6163
6264 @Input ( ) @BooleanFieldValue ( ) disabled : boolean = false ;
6365 @Input ( ) name : string = null ;
@@ -72,12 +74,12 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
7274 // Returns the unique id for the visual hidden input.
7375 getInputId = ( ) => `${ this . id || this . _uniqueId } -input` ;
7476
75- constructor ( private _elementRef : ElementRef ,
76- private _renderer : Renderer ) {
77- }
77+ constructor ( private _elementRef : ElementRef , private _renderer : Renderer ) { }
7878
7979 /** TODO: internal */
8080 ngAfterContentInit ( ) {
81+ this . _slideRenderer = new SlideToggleRenderer ( this . _elementRef ) ;
82+
8183 // Mark this component as initialized in AfterContentInit because the initial checked value can
8284 // possibly be set by NgModel or the checked attribute. This would cause the change event to
8385 // be emitted, before the component is actually initialized.
@@ -95,7 +97,8 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
9597 // emit its event object to the component's `change` output.
9698 event . stopPropagation ( ) ;
9799
98- if ( ! this . disabled ) {
100+ // Once a drag is currently in progress, we do not want to toggle the slide-toggle on a click.
101+ if ( ! this . disabled && ! this . _slideRenderer . isDragging ( ) ) {
99102 this . toggle ( ) ;
100103 }
101104 }
@@ -202,13 +205,98 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
202205 }
203206 }
204207
208+ /** Emits the change event to the `change` output EventEmitter */
205209 private _emitChangeEvent ( ) {
206210 let event = new MdSlideToggleChange ( ) ;
207211 event . source = this ;
208212 event . checked = this . checked ;
209213 this . _change . emit ( event ) ;
210214 }
211215
216+
217+ /** TODO: internal */
218+ _onDragStart ( ) {
219+ this . _slideRenderer . startThumbDrag ( this . checked ) ;
220+ }
221+
222+ /** TODO: internal */
223+ _onDrag ( event : HammerInput ) {
224+ this . _slideRenderer . updateThumbPosition ( event . deltaX ) ;
225+ }
226+
227+ /** TODO: internal */
228+ _onDragEnd ( ) {
229+ // Notice that we have to stop outside of the current event handler,
230+ // because otherwise the click event will be fired and will reset the new checked variable.
231+ setTimeout ( ( ) => {
232+ this . checked = this . _slideRenderer . stopThumbDrag ( ) ;
233+ } , 0 ) ;
234+ }
235+
236+ }
237+
238+ /**
239+ * Renderer for the Slide Toggle component, which separates DOM modification in its own class
240+ */
241+ class SlideToggleRenderer {
242+
243+ private _thumbEl : HTMLElement ;
244+ private _thumbBarEl : HTMLElement ;
245+ private _thumbBarWidth : number ;
246+ private _checked : boolean ;
247+ private _percentage : number ;
248+
249+ constructor ( private _elementRef : ElementRef ) {
250+ this . _thumbEl = _elementRef . nativeElement . querySelector ( '.md-slide-toggle-thumb-container' ) ;
251+ this . _thumbBarEl = _elementRef . nativeElement . querySelector ( '.md-slide-toggle-bar' ) ;
252+ }
253+
254+ /** Whether the slide-toggle is currently dragging. */
255+ isDragging ( ) : boolean {
256+ return ! ! this . _thumbBarWidth ;
257+ }
258+
259+ /** Initializes the drag of the slide-toggle. */
260+ startThumbDrag ( checked : boolean ) {
261+ if ( ! this . _thumbBarWidth ) {
262+ this . _thumbBarWidth = this . _thumbBarEl . clientWidth - this . _thumbEl . clientWidth ;
263+ this . _checked = checked ;
264+ this . _thumbEl . classList . add ( 'md-dragging' ) ;
265+ }
266+ }
267+
268+ /** Stops the current drag and returns the new checked value. */
269+ stopThumbDrag ( ) : boolean {
270+ if ( this . _thumbBarWidth ) {
271+ this . _thumbBarWidth = null ;
272+ this . _thumbEl . classList . remove ( 'md-dragging' ) ;
273+
274+ applyCssTransform ( this . _thumbEl , '' ) ;
275+
276+ return this . _percentage > 50 ;
277+ }
278+ }
279+
280+ /** Updates the thumb containers position from the specified distance. */
281+ updateThumbPosition ( distance : number ) {
282+ if ( this . _thumbBarWidth ) {
283+ this . _percentage = this . _getThumbPercentage ( distance ) ;
284+ applyCssTransform ( this . _thumbEl , `translate3d(${ this . _percentage } %, 0, 0)` ) ;
285+ }
286+ }
287+
288+ /** Retrieves the percentage of thumb from the moved distance. */
289+ private _getThumbPercentage ( distance : number ) {
290+ let percentage = ( distance / this . _thumbBarWidth ) * 100 ;
291+
292+ // When the toggle was initially checked, then we have to start the drag at the end.
293+ if ( this . _checked ) {
294+ percentage += 100 ;
295+ }
296+
297+ return Math . max ( 0 , Math . min ( percentage , 100 ) ) ;
298+ }
299+
212300}
213301
214302export const MD_SLIDE_TOGGLE_DIRECTIVES = [ MdSlideToggle ] ;
0 commit comments