From e538fa77c995bd73cc6673ab4706bd6362db585f Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sat, 1 Mar 2014 15:52:47 -0600 Subject: [PATCH] Draggable slider working --- js/ext/angular/src/directive/ionicToggle.js | 48 +++++++----- .../test/directive/ionicToggle.unit.js | 13 +++- js/ext/angular/test/toggle.html | 5 +- js/views/toggleView.js | 78 ++++++++++++++++--- test/unit/views/toggleView.unit.js | 30 +++++++ 5 files changed, 141 insertions(+), 33 deletions(-) create mode 100644 test/unit/views/toggleView.unit.js diff --git a/js/ext/angular/src/directive/ionicToggle.js b/js/ext/angular/src/directive/ionicToggle.js index 6c217027da..ca410e4c14 100644 --- a/js/ext/angular/src/directive/ionicToggle.js +++ b/js/ext/angular/src/directive/ionicToggle.js @@ -5,7 +5,7 @@ angular.module('ionic.ui.toggle', []) // The Toggle directive is a toggle switch that can be tapped to change // its value -.directive('ionToggle', function() { +.directive('ionToggle', ['$ionicGesture', '$timeout', function($ionicGesture, $timeout) { return { restrict: 'E', @@ -36,30 +36,38 @@ angular.module('ionic.ui.toggle', []) if(attr.ngTrueValue) input.attr('ng-true-value', attr.ngTrueValue); if(attr.ngFalseValue) input.attr('ng-false-value', attr.ngFalseValue); - // return function link($scope, $element, $attr, ngModel) { - // var el, checkbox, track, handle; + return function($scope, $element, $attr) { + var el, checkbox, track, handle; - // el = $element[0].getElementsByTagName('label')[0]; - // checkbox = el.children[0]; - // track = el.children[1]; - // handle = track.children[0]; + el = $element[0].getElementsByTagName('label')[0]; + checkbox = el.children[0]; + track = el.children[1]; + handle = track.children[0]; + + var ngModelController = angular.element(checkbox).controller('ngModel'); - // $scope.toggle = new ionic.views.Toggle({ - // el: el, - // track: track, - // checkbox: checkbox, - // handle: handle - // }); + $scope.toggle = new ionic.views.Toggle({ + el: el, + track: track, + checkbox: checkbox, + handle: handle, + onChange: function() { + if(checkbox.checked) { + ngModelController.$setViewValue(true); + } else { + ngModelController.$setViewValue(false); + } + $scope.$apply(); + } + }); - // ionic.on('drag', function(e) { - // console.log('drag'); - // $scope.toggle.drag(e); - // }, handle); - - // } + $scope.$on('$destroy', function() { + $scope.toggle.destroy(); + }); + }; } }; -}); +}]); })(window.ionic); diff --git a/js/ext/angular/test/directive/ionicToggle.unit.js b/js/ext/angular/test/directive/ionicToggle.unit.js index e36e1e330d..6657571bbd 100644 --- a/js/ext/angular/test/directive/ionicToggle.unit.js +++ b/js/ext/angular/test/directive/ionicToggle.unit.js @@ -1,7 +1,7 @@ describe('Ionic Toggle', function() { var el, rootScope, compile; - beforeEach(module('ionic.ui.toggle')); + beforeEach(module('ionic')); beforeEach(inject(function($compile, $rootScope) { compile = $compile; @@ -9,14 +9,21 @@ describe('Ionic Toggle', function() { el = $compile('')($rootScope); })); - /* - it('Should load', function() { + iit('Should load', function() { var toggleView = el.isolateScope().toggle; expect(toggleView).not.toEqual(null); expect(toggleView.checkbox).not.toEqual(null); expect(toggleView.handle).not.toEqual(null); }); + iit('Should destroy', function() { + var toggleView = el.isolateScope().toggle; + spyOn(toggleView, 'destroy'); + el.isolateScope().$destroy(); + expect(toggleView.destroy).toHaveBeenCalled(); + }); + + /* it('Should toggle', function() { var toggle = el.isolateScope().toggle; expect(toggle.val()).toBe(false); diff --git a/js/ext/angular/test/toggle.html b/js/ext/angular/test/toggle.html index 058808adf0..d17479c96c 100644 --- a/js/ext/angular/test/toggle.html +++ b/js/ext/angular/test/toggle.html @@ -18,6 +18,7 @@
myModel ({{!!myModel}}) + Cats or dogs? ({{catModel}}) Disable myModel ({{!!isDisabled}})
@@ -25,7 +26,9 @@ diff --git a/js/views/toggleView.js b/js/views/toggleView.js index cb23895911..224632f8f1 100644 --- a/js/views/toggleView.js +++ b/js/views/toggleView.js @@ -3,11 +3,41 @@ ionic.views.Toggle = ionic.views.View.inherit({ initialize: function(opts) { + var self = this; + this.el = opts.el; this.checkbox = opts.checkbox; this.track = opts.track; this.handle = opts.handle; this.openPercent = -1; + this.onChange = opts.onChange || function() {}; + + this.triggerThreshold = opts.triggerThreshold || 10; + + this.dragStartHandler = function(e) { + self.dragStart(e); + }; + this.dragHandler = function(e) { + self.drag(e); + }; + this.holdHandler = function(e) { + self.hold(e); + }; + this.releaseHandler = function(e) { + self.release(e); + }; + + this.dragStartGesture = ionic.onGesture('dragstart', this.dragStartHandler, this.el); + this.dragGesture = ionic.onGesture('drag', this.dragHandler, this.el); + this.dragHoldGesture = ionic.onGesture('hold', this.holdHandler, this.el); + this.dragReleaseGesture = ionic.onGesture('release', this.releaseHandler, this.el); + }, + + destroy: function() { + ionic.offGesture(this.dragStartGesture, 'dragstart', this.dragStartGesture); + ionic.offGesture(this.dragGesture, 'drag', this.dragGesture); + ionic.offGesture(this.dragHoldGesture, 'hold', this.holdHandler); + ionic.offGesture(this.dragReleaseGesture, 'release', this.releaseHandler); }, tap: function(e) { @@ -16,19 +46,52 @@ } }, + dragStart: function(e) { + this._dragInfo = { + width: this.el.offsetWidth, + left: this.el.offsetLeft, + right: this.el.offsetLeft + this.el.offsetWidth, + triggerX: this.el.offsetWidth / 2, + initialState: this.checkbox.checked + } + + // Trigger hold styles + this.hold(e); + }, + drag: function(e) { + if(!this._dragInfo) { return; } + var slidePageLeft = this.track.offsetLeft + (this.handle.offsetWidth / 2); var slidePageRight = this.track.offsetLeft + this.track.offsetWidth - (this.handle.offsetWidth / 2); + var dx = e.gesture.deltaX; - if(e.pageX >= slidePageRight - 4) { - this.val(true); - } else if(e.pageX <= slidePageLeft) { - this.val(false); + var px = e.gesture.touches[0].pageX - this._dragInfo.left; + var mx = this._dragInfo.width - this.triggerThreshold; + + // The initial state was on, so "tend towards" on + if(this._dragInfo.initialState) { + if(px < this.triggerThreshold) { + this.setOpenPercent(0); + } else if(px > this._dragInfo.triggerX) { + this.setOpenPercent(100); + } } else { - this.setOpenPercent( Math.round( (1 - ((slidePageRight - e.pageX) / (slidePageRight - slidePageLeft) )) * 100) ); + // The initial state was off, so "tend towards" off + if(px < this._dragInfo.triggerX) { + this.setOpenPercent(0); + } else if(px > mx) { + this.setOpenPercent(100); + } } }, + hold: function(e) { + }, + release: function(e) { + }, + + setOpenPercent: function(openPercent) { // only make a change if the new open percent has changed if(this.openPercent < 0 || (openPercent < (this.openPercent - 3) || openPercent > (this.openPercent + 3) ) ) { @@ -46,10 +109,6 @@ } }, - release: function(e) { - this.val( this.openPercent >= 50 ); - }, - val: function(value) { if(value === true || value === false) { if(this.handle.style[ionic.CSS.TRANSFORM] !== "") { @@ -57,6 +116,7 @@ } this.checkbox.checked = value; this.openPercent = (value ? 100 : 0); + this.onChange && this.onChange(); } return this.checkbox.checked; } diff --git a/test/unit/views/toggleView.unit.js b/test/unit/views/toggleView.unit.js new file mode 100644 index 0000000000..65893086e0 --- /dev/null +++ b/test/unit/views/toggleView.unit.js @@ -0,0 +1,30 @@ +describe('Toggle view', function() { + var element, toggle; + + beforeEach(function() { + element = $('
' + + '
Cats
' + + '' + + '
'); + + el = element[0].getElementsByTagName('label')[0]; + checkbox = el.children[0]; + track = el.children[1]; + handle = track.children[0]; + toggle = new ionic.views.Toggle({ + el: el, + checkbox: checkbox, + track: track, + handle: handle + }); + }); + + it('Should init', function() { + expect(toggle.el).not.toBe(undefined); + }); +});