diff --git a/dist/ionic-angular.js b/dist/ionic-angular.js
index e2b3c01dc5..4c931ec782 100644
--- a/dist/ionic-angular.js
+++ b/dist/ionic-angular.js
@@ -209,7 +209,6 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
return {
restrict: 'E',
replace: true,
- transclude: true,
scope: {
isEditing: '=',
items: '=',
@@ -229,8 +228,31 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
if(attr.animation) {
$element.addClass(attr.animation);
}
+ }
+ }
+ }
+})
- $element.append(transclude($scope));
+.directive('listSimple', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ scope: {
+ isEditing: '=',
+ items: '=',
+ animation: '@',
+ deleteIcon: '@'
+ },
+ template: '
',
+ compile: function(element, attr, transclude) {
+ return function($scope, $element, $attr) {
+ var lv = new ionic.views.List({el: $element[0]});
+
+ if(attr.animation) {
+ $element.addClass(attr.animation);
+ }
}
}
}
diff --git a/dist/ionic-ios7.css b/dist/ionic-ios7.css
index 3bf8dc2d30..1179755611 100644
--- a/dist/ionic-ios7.css
+++ b/dist/ionic-ios7.css
@@ -307,6 +307,13 @@ table {
-moz-box-sizing: border-box;
box-sizing: border-box; }
+a {
+ -webkit-user-drag: none;
+ -webkit-tap-highlight-color: transparent; }
+
+a:focus, button:focus {
+ outline: 0; }
+
body {
position: fixed;
top: 0;
@@ -1078,6 +1085,9 @@ a.list-item {
.list-item-content > i:last-child {
float: right; }
+.list-item-sliding {
+ -webkit-transition: -webkit-transform 0.1s ease-in-out; }
+
.list-item-edit {
position: absolute;
z-index: 0;
@@ -1096,8 +1106,10 @@ a.list-item {
position: absolute;
z-index: 0;
right: 0;
- top: 0; }
+ top: 0;
+ height: 100%; }
.list-item-buttons .button {
+ height: 100%;
border-radius: 0;
border: none; }
diff --git a/dist/ionic.css b/dist/ionic.css
index e7cd72a2b5..17e218f997 100644
--- a/dist/ionic.css
+++ b/dist/ionic.css
@@ -1371,6 +1371,13 @@ table {
-moz-box-sizing: border-box;
box-sizing: border-box; }
+a {
+ -webkit-user-drag: none;
+ -webkit-tap-highlight-color: transparent; }
+
+a:focus, button:focus {
+ outline: 0; }
+
body {
position: fixed;
top: 0;
@@ -2165,6 +2172,9 @@ a.list-item {
.list-item-content > i:last-child {
float: right; }
+.list-item-sliding {
+ -webkit-transition: -webkit-transform 0.1s ease-in-out; }
+
.list-item-edit {
position: absolute;
z-index: 0;
@@ -2183,8 +2193,10 @@ a.list-item {
position: absolute;
z-index: 0;
right: 0;
- top: 0; }
+ top: 0;
+ height: 100%; }
.list-item-buttons .button {
+ height: 100%;
border-radius: 0;
border: none; }
diff --git a/dist/ionic.js b/dist/ionic.js
index abf069fa17..9433bf4c11 100644
--- a/dist/ionic.js
+++ b/dist/ionic.js
@@ -1785,6 +1785,18 @@ window.ionic = {
this.el = opts.el;
+ this.dragThresholdX = 10;
+
+ this._initDrag();
+
+ window.ionic.onGesture('drag', function(e) {
+ _this._handleDrag(e);
+ }, this.el);
+ window.ionic.onGesture('release', function(e) {
+ _this._handleEndDrag(e);
+ }, this.el);
+
+ /*
window.ionic.onGesture('swipeleft', function(e) {
_this._handleSwipeLeft(e);
e.gesture.stopDetect();
@@ -1796,9 +1808,130 @@ window.ionic = {
e.gesture.stopDetect();
return false;
}, this.el);
+ */
};
ionic.views.List.prototype = {
+ _initDrag: function() {
+ this._offsetX = 0;
+ this._isDragging = false;
+ this._currentDrag = null;
+ },
+ _startDrag: function(e) {
+ this._isDragging = false;
+
+ // Grab the content item
+ if(e.target.classList.contains('list-item')) {
+ content = e.target.querySelector('.list-item-content');
+ } else if(e.target.classList.contains('list-item-content')) {
+ content = e.target;
+ }
+
+ if(!content) {
+ return;
+ }
+
+ content.classList.remove('list-item-sliding');
+
+ // Grab the buttons
+ buttons = content.parentNode.querySelector('.list-item-buttons');
+ if(!buttons) {
+ return;
+ }
+
+ buttonsWidth = buttons.offsetWidth;
+
+ this._currentDrag = {
+ buttonsWidth: buttonsWidth,
+ content: content
+ };
+ },
+ _handleEndDrag: function(e) {
+ var _this = this;
+
+ // If we are currently dragging, we want to snap back into place
+ if(this._currentDrag) {
+
+ // The final resting point X will be the width of the exposed buttons
+ var restingPoint = -this._currentDrag.buttonsWidth;
+
+ // Check if the drag didn't clear the buttons and we aren't moving fast enough to swipe open
+ if(e.gesture.deltaX > -this._currentDrag.buttonsWidth) {
+ if(e.gesture.direction == "left" && Math.abs(e.gesture.velocityX) < 0.3) {
+ restingPoint = 0;
+ } else if(e.gesture.direction == "right") {
+ restingPoint = 0;
+ }
+ }
+
+ var content = this._currentDrag.content;
+
+ var onRestingAnimationEnd = function(e) {
+ if(e.propertyName == '-webkit-transform') {
+ content.classList.remove('list-item-sliding');
+ }
+ e.target.removeEventListener('webkitTransitionEnd', onRestingAnimationEnd);
+ }
+
+ window.requestAnimationFrame(function() {
+ var currentX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ if(currentX !== restingPoint) {
+ _this._currentDrag.content.classList.add('list-item-sliding');
+ _this._currentDrag.content.addEventListener('webkitTransitionEnd', onRestingAnimationEnd);
+ }
+ _this._currentDrag.content.style.webkitTransform = 'translate3d(' + restingPoint + 'px, 0, 0)';
+ _this._initDrag();
+ });
+
+ } else {
+ this._initDrag();
+ }
+ },
+
+ /**
+ * Process the drag event to move the item to the left or right.
+ */
+ _handleDrag: function(e) {
+ var _this = this, content, buttons;
+
+ if(!this._currentDrag) {
+ this._startDrag(e);
+ }
+
+ window.requestAnimationFrame(function() {
+
+ if(!_this._currentDrag) {
+ return;
+ }
+
+ // Calculate difference from the tap points
+ if(!_this._isDragging && Math.abs(e.gesture.deltaX) > _this.dragThresholdX) {
+ _this._isDragging = true;
+
+ // Grab the starting X point for this item
+ _this._offsetX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ }
+
+ if(_this._isDragging) {
+
+ // Grab the new X point, capping it at zero
+
+ var newX = Math.min(0, _this._offsetX + e.gesture.deltaX);
+ if(newX < -buttonsWidth && !_this._deltaSlowX) {
+ _this._deltaSlowX = e.gesture.deltaX;
+ }
+ if(newX < -buttonsWidth) {
+ newX = Math.min(_this._deltaSlowX, _this._deltaSlowX + (((e.gesture.deltaX - _this._deltaSlowX) * 0.4)));
+ }
+
+ console.log(newX);
+
+ _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)';
+ }
+ });
+ },
+
+
_handleSwipeLeft: function(e) {
window.requestAnimationFrame(function() {
@@ -1806,12 +1939,6 @@ window.ionic = {
cl = item.classList,
content, buttons, buttonsWidth;
- // Grab the content item
- if(cl.contains('list-item')) {
- content = item.querySelector('.list-item-content');
- } else if(cl.contains('list-item-content')) {
- content = item;
- }
if(!content) {
return;
diff --git a/example/toderp2/index.html b/example/toderp2/index.html
index 708f05041d..1783be94a8 100644
--- a/example/toderp2/index.html
+++ b/example/toderp2/index.html
@@ -127,18 +127,24 @@
+
+
diff --git a/example/toderp2/js/controllers.js b/example/toderp2/js/controllers.js
index 3b6a1f5c8e..1453268be1 100644
--- a/example/toderp2/js/controllers.js
+++ b/example/toderp2/js/controllers.js
@@ -74,6 +74,8 @@ angular.module('ionic.todo.controllers', ['ionic.todo'])
$scope.lastProject = null;
*/
+ $scope.isEditingProjects = false;
+
// Load our settings modal
Modal.fromTemplateUrl('settings.html', function(modal) {
$scope.settingsModal = modal;
@@ -183,6 +185,10 @@ angular.module('ionic.todo.controllers', ['ionic.todo'])
});
};
+ $scope.toggleProjectEditing = function() {
+ $scope.isEditingProjects = !$scope.isEditingProjects;
+ };
+
var projectsRef = new Firebase(FIREBASE_URL + '/project_list');
$scope.projects = angularFireCollection(projectsRef.limit(100), function(snapshot) {
if(!snapshot.val()) {
diff --git a/js/ext/angular/src/directive/ionicList.js b/js/ext/angular/src/directive/ionicList.js
index 3a05611be2..bbc780579d 100644
--- a/js/ext/angular/src/directive/ionicList.js
+++ b/js/ext/angular/src/directive/ionicList.js
@@ -27,7 +27,6 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
return {
restrict: 'E',
replace: true,
- transclude: true,
scope: {
isEditing: '=',
items: '=',
@@ -47,8 +46,31 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
if(attr.animation) {
$element.addClass(attr.animation);
}
-
- $element.append(transclude($scope));
+ }
+ }
+ }
+})
+
+.directive('listSimple', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ scope: {
+ isEditing: '=',
+ items: '=',
+ animation: '@',
+ deleteIcon: '@'
+ },
+ template: '',
+ compile: function(element, attr, transclude) {
+ return function($scope, $element, $attr) {
+ var lv = new ionic.views.List({el: $element[0]});
+
+ if(attr.animation) {
+ $element.addClass(attr.animation);
+ }
}
}
}
diff --git a/js/views/listView.js b/js/views/listView.js
index fb63690afc..1e347d319a 100644
--- a/js/views/listView.js
+++ b/js/views/listView.js
@@ -5,6 +5,18 @@
this.el = opts.el;
+ this.dragThresholdX = 10;
+
+ this._initDrag();
+
+ window.ionic.onGesture('drag', function(e) {
+ _this._handleDrag(e);
+ }, this.el);
+ window.ionic.onGesture('release', function(e) {
+ _this._handleEndDrag(e);
+ }, this.el);
+
+ /*
window.ionic.onGesture('swipeleft', function(e) {
_this._handleSwipeLeft(e);
e.gesture.stopDetect();
@@ -16,9 +28,130 @@
e.gesture.stopDetect();
return false;
}, this.el);
+ */
};
ionic.views.List.prototype = {
+ _initDrag: function() {
+ this._offsetX = 0;
+ this._isDragging = false;
+ this._currentDrag = null;
+ },
+ _startDrag: function(e) {
+ this._isDragging = false;
+
+ // Grab the content item
+ if(e.target.classList.contains('list-item')) {
+ content = e.target.querySelector('.list-item-content');
+ } else if(e.target.classList.contains('list-item-content')) {
+ content = e.target;
+ }
+
+ if(!content) {
+ return;
+ }
+
+ content.classList.remove('list-item-sliding');
+
+ // Grab the buttons
+ buttons = content.parentNode.querySelector('.list-item-buttons');
+ if(!buttons) {
+ return;
+ }
+
+ buttonsWidth = buttons.offsetWidth;
+
+ this._currentDrag = {
+ buttonsWidth: buttonsWidth,
+ content: content
+ };
+ },
+ _handleEndDrag: function(e) {
+ var _this = this;
+
+ // If we are currently dragging, we want to snap back into place
+ if(this._currentDrag) {
+
+ // The final resting point X will be the width of the exposed buttons
+ var restingPoint = -this._currentDrag.buttonsWidth;
+
+ // Check if the drag didn't clear the buttons and we aren't moving fast enough to swipe open
+ if(e.gesture.deltaX > -this._currentDrag.buttonsWidth) {
+ if(e.gesture.direction == "left" && Math.abs(e.gesture.velocityX) < 0.3) {
+ restingPoint = 0;
+ } else if(e.gesture.direction == "right") {
+ restingPoint = 0;
+ }
+ }
+
+ var content = this._currentDrag.content;
+
+ var onRestingAnimationEnd = function(e) {
+ if(e.propertyName == '-webkit-transform') {
+ content.classList.remove('list-item-sliding');
+ }
+ e.target.removeEventListener('webkitTransitionEnd', onRestingAnimationEnd);
+ }
+
+ window.requestAnimationFrame(function() {
+ var currentX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ if(currentX !== restingPoint) {
+ _this._currentDrag.content.classList.add('list-item-sliding');
+ _this._currentDrag.content.addEventListener('webkitTransitionEnd', onRestingAnimationEnd);
+ }
+ _this._currentDrag.content.style.webkitTransform = 'translate3d(' + restingPoint + 'px, 0, 0)';
+ _this._initDrag();
+ });
+
+ } else {
+ this._initDrag();
+ }
+ },
+
+ /**
+ * Process the drag event to move the item to the left or right.
+ */
+ _handleDrag: function(e) {
+ var _this = this, content, buttons;
+
+ if(!this._currentDrag) {
+ this._startDrag(e);
+ }
+
+ window.requestAnimationFrame(function() {
+
+ if(!_this._currentDrag) {
+ return;
+ }
+
+ // Calculate difference from the tap points
+ if(!_this._isDragging && Math.abs(e.gesture.deltaX) > _this.dragThresholdX) {
+ _this._isDragging = true;
+
+ // Grab the starting X point for this item
+ _this._offsetX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ }
+
+ if(_this._isDragging) {
+
+ // Grab the new X point, capping it at zero
+
+ var newX = Math.min(0, _this._offsetX + e.gesture.deltaX);
+ if(newX < -buttonsWidth && !_this._deltaSlowX) {
+ _this._deltaSlowX = e.gesture.deltaX;
+ }
+ if(newX < -buttonsWidth) {
+ newX = Math.min(_this._deltaSlowX, _this._deltaSlowX + (((e.gesture.deltaX - _this._deltaSlowX) * 0.4)));
+ }
+
+ console.log(newX);
+
+ _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)';
+ }
+ });
+ },
+
+
_handleSwipeLeft: function(e) {
window.requestAnimationFrame(function() {
@@ -26,12 +159,6 @@
cl = item.classList,
content, buttons, buttonsWidth;
- // Grab the content item
- if(cl.contains('list-item')) {
- content = item.querySelector('.list-item-content');
- } else if(cl.contains('list-item-content')) {
- content = item;
- }
if(!content) {
return;
diff --git a/scss/ionic/_listview.scss b/scss/ionic/_listview.scss
index 53cb1f645c..10559aabe8 100644
--- a/scss/ionic/_listview.scss
+++ b/scss/ionic/_listview.scss
@@ -98,6 +98,7 @@ a.list-item {
}
}
+
.list-item-content {
position: relative;
@@ -115,6 +116,10 @@ a.list-item {
}
}
+.list-item-sliding {
+ -webkit-transition: -webkit-transform 0.1s ease-in-out;
+}
+
.list-item-edit {
position: absolute;
z-index: 0;
@@ -138,8 +143,10 @@ a.list-item {
z-index: 0;
right: 0;
top: 0;
+ height: 100%;
.button {
+ height: 100%;
border-radius: 0;
border: none;
}
diff --git a/scss/ionic/_scaffolding.scss b/scss/ionic/_scaffolding.scss
index 4dd9d4b067..aba5445845 100644
--- a/scss/ionic/_scaffolding.scss
+++ b/scss/ionic/_scaffolding.scss
@@ -4,6 +4,17 @@
@include box-sizing(border-box);
}
+a {
+ -webkit-user-drag: none;
+ -webkit-tap-highlight-color: transparent;
+}
+
+a, button {
+ &:focus {
+ outline: 0;
+ }
+}
+
body {
position: fixed;
top: 0;
@@ -20,8 +31,10 @@ body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
+
-webkit-text-size-adjust: none;
text-size-adjust: none;
+
-webkit-tap-highlight-color: transparent;
-webkit-user-drag: none;
-webkit-user-select: none;