Skip to content

Commit ab9fa61

Browse files
committed
Move drag handlers to custom slide-toggle renderer.
1 parent 226dc3e commit ab9fa61

File tree

1 file changed

+60
-64
lines changed

1 file changed

+60
-64
lines changed

src/components/slide-toggle/slide-toggle.ts

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
5959
private _hasFocus: boolean = false;
6060
private _isMousedown: boolean = false;
6161
private _isInitialized: boolean = false;
62-
private _slideRenderer: MdSlideToggleRenderer = null;
62+
private _slideRenderer: SlideToggleRenderer = null;
6363

6464
// State of the current drag, which holds required variables for the drag.
6565
private _dragState: {
@@ -80,13 +80,12 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
8080
// Returns the unique id for the visual hidden input.
8181
getInputId = () => `${this.id || this._uniqueId}-input`;
8282

83-
constructor(private _elementRef: ElementRef,
84-
private _renderer: Renderer) {
85-
this._slideRenderer = new MdSlideToggleRenderer(this._elementRef);
86-
}
83+
constructor(private _elementRef: ElementRef, private _renderer: Renderer) {}
8784

8885
/** TODO: internal */
8986
ngAfterContentInit() {
87+
this._slideRenderer = new SlideToggleRenderer(this._elementRef);
88+
9089
// Mark this component as initialized in AfterContentInit because the initial checked value can
9190
// possibly be set by NgModel or the checked attribute. This would cause the change event to
9291
// be emitted, before the component is actually initialized.
@@ -105,7 +104,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
105104
event.stopPropagation();
106105

107106
// Once a drag is currently in progress, we do not want to toggle the slide-toggle on a click.
108-
if (!this.disabled && !this._dragState) {
107+
if (!this.disabled && !this._slideRenderer.isDragging()) {
109108
this.toggle();
110109
}
111110
}
@@ -223,89 +222,86 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {
223222

224223
/** TODO: internal */
225224
_onDragStart() {
226-
if (this._dragState) {
227-
return;
228-
}
229-
230-
let thumbBarRect = this._slideRenderer.getThumbBarClientRect();
231-
let thumbRect = this._slideRenderer.getThumbClientRect();
232-
233-
this._dragState = {
234-
barWidth: thumbBarRect.width - thumbRect.width
235-
};
236-
237-
this._slideRenderer.toggleDragging(true);
225+
this._slideRenderer.startThumbDrag(this.checked);
238226
}
239227

240228
/** TODO: internal */
241229
_onDrag(event: HammerInput) {
242-
if (!this._dragState) {
243-
return;
244-
}
245-
246-
let percentage = (event.deltaX / this._dragState.barWidth) * 100;
247-
248-
// When the slide-toggle was initially checked, then we have to start the drag at 100%
249-
if (this.checked) {
250-
percentage += 100;
251-
}
252-
253-
percentage = Math.max(0, Math.min(percentage, 100));
254-
255-
this._slideRenderer.updateThumbPosition(percentage);
256-
this._dragState.percentage = percentage;
230+
this._slideRenderer.updateThumbPosition(event.deltaX);
257231
}
258232

259233
/** TODO: internal */
260234
_onDragEnd() {
261-
if (!this._dragState) {
262-
return;
263-
}
264-
265-
this.checked = this._dragState.percentage > 50;
266-
267-
this._slideRenderer.updateThumbPosition(null);
268-
this._slideRenderer.toggleDragging(false);
269-
270-
// We have to clear the drag after one tick, because otherwise
271-
// the click event will fire and toggle the slide-toggle again.
272-
setTimeout(() => { this._dragState = null; }, 0);
235+
// Notice that we have to stop outside of the current event handler,
236+
// because otherwise the click event will be fired and will reset the new checked variable.
237+
setTimeout(() => {
238+
this.checked = this._slideRenderer.stopThumbDrag();
239+
}, 0);
273240
}
274241

275242
}
276243

277244
/**
278245
* Renderer for the Slide Toggle component, which separates DOM modification in it's own class
279246
*/
280-
export class MdSlideToggleRenderer {
247+
class SlideToggleRenderer {
281248

282-
constructor(private _elementRef: ElementRef) {}
249+
private _thumbEl: HTMLElement;
250+
private _thumbBarEl: HTMLElement;
251+
private _thumbBarWidth: number;
252+
private _checked: boolean;
253+
private _percentage: number;
283254

284-
getThumbClientRect(): ClientRect {
285-
let thumbEl = this._elementRef.nativeElement.querySelector('.md-slide-toggle-thumb-container');
286-
return thumbEl.getBoundingClientRect();
255+
constructor(private _elementRef: ElementRef) {
256+
this._thumbEl = _elementRef.nativeElement.querySelector('.md-slide-toggle-thumb-container');
257+
this._thumbBarEl = _elementRef.nativeElement.querySelector('.md-slide-toggle-bar');
287258
}
288259

289-
getThumbBarClientRect(): ClientRect {
290-
let thumbBarEl = this._elementRef.nativeElement.querySelector('.md-slide-toggle-bar');
291-
return thumbBarEl.getBoundingClientRect();
260+
/** Whether the slide-toggle is currently dragging. */
261+
isDragging(): boolean {
262+
return !!this._thumbBarWidth;
292263
}
293264

294-
/**
295-
* Updates the thumb containers position by using the specified percentage.
296-
* When the percentage is set to `null`, the custom thumb position will be removed.
297-
*/
298-
updateThumbPosition(percentage: number) {
299-
let thumbEl = this._elementRef.nativeElement.querySelector('.md-slide-toggle-thumb-container');
300-
applyCssTransform(thumbEl, percentage === null ? '' : `translate3d(${percentage}%, 0, 0)`);
265+
/** Initializes the drag of the slide-toggle. */
266+
startThumbDrag(checked: boolean) {
267+
if (!this._thumbBarWidth) {
268+
this._thumbBarWidth = this._thumbBarEl.clientWidth - this._thumbEl.clientWidth;
269+
this._checked = checked;
270+
this._thumbEl.classList.add('md-dragging');
271+
}
301272
}
302273

303-
/** Toggles the dragging class for the thumb container to toggle the transition duration. */
304-
toggleDragging(isDragging: boolean) {
305-
let thumbEl = this._elementRef.nativeElement.querySelector('.md-slide-toggle-thumb-container');
306-
thumbEl.classList.toggle('md-dragging', isDragging);
274+
/** Stops the current drag and returns the new checked value. */
275+
stopThumbDrag(): boolean {
276+
if (this._thumbBarWidth) {
277+
this._thumbBarWidth = null;
278+
this._thumbEl.classList.remove('md-dragging');
279+
280+
applyCssTransform(this._thumbEl, '');
281+
282+
return this._percentage > 50;
283+
}
307284
}
308285

286+
/** Updates the thumb containers position from the specified distance. */
287+
updateThumbPosition(distance: number) {
288+
if (this._thumbBarWidth) {
289+
this._percentage = this._getThumbPercentage(distance);
290+
applyCssTransform(this._thumbEl, `translate3d(${this._percentage}%, 0, 0)`);
291+
}
292+
}
293+
294+
/** Retrieves the percentage of thumb from the moved distance. */
295+
private _getThumbPercentage(distance: number) {
296+
let percentage = (distance / this._thumbBarWidth) * 100;
297+
298+
// When the toggle was initially checked, then we have to start the drag at the end.
299+
if (this._checked) {
300+
percentage += 100;
301+
}
302+
303+
return Math.max(0, Math.min(percentage, 100));
304+
}
309305

310306
}
311307

0 commit comments

Comments
 (0)