diff --git a/dist/css/ionic-ios7.css b/dist/css/ionic-ios7.css index 2cb8251fec..d6c46fe274 100644 --- a/dist/css/ionic-ios7.css +++ b/dist/css/ionic-ios7.css @@ -181,7 +181,7 @@ sub { fieldset { margin: 0 2px; padding: 0.35em 0.625em 0.75em; - border: 1px solid silver; } + border: 1px solid #c0c0c0; } /** * 1. Correct `color` not being inherited in IE 8/9. @@ -1381,7 +1381,7 @@ select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { - outline: thin dotted #333333; + outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } @@ -1468,7 +1468,7 @@ input[type="checkbox"][readonly] { right: 20px; transition: 0.2s ease; transition-property: left, right; - transition-delay: 0s, 0.05s; } + transition-delay: 0s, .05s; } .toggle :checked + .track { /* When the toggle is "on" */ @@ -1483,7 +1483,7 @@ input[type="checkbox"][readonly] { right: 0; left: 20px; -webkit-transform: none; - transition-delay: 0.05s, 0s; } + transition-delay: .05s, 0s; } /* hide a radio button's icon by default */ .radio-item [class^="icon-"], @@ -1708,7 +1708,7 @@ input[type="checkbox"][readonly] { border: none; background: none; } .button.button-icon:active, .button.button-icon.active { - text-shadow: 0px 0px 10px white; + text-shadow: 0px 0px 10px #fff; box-shadow: none; background: none; } @@ -1891,7 +1891,7 @@ a.button { width: 100%; background-color: white; border-radius: 2px; - border: 1px solid #dddddd; } + border: 1px solid #ddd; } .card-header { padding: 10px; diff --git a/dist/css/ionic.css b/dist/css/ionic.css index ece124c893..5045e8f3c2 100644 --- a/dist/css/ionic.css +++ b/dist/css/ionic.css @@ -1245,7 +1245,7 @@ sub { fieldset { margin: 0 2px; padding: 0.35em 0.625em 0.75em; - border: 1px solid silver; } + border: 1px solid #c0c0c0; } /** * 1. Correct `color` not being inherited in IE 8/9. @@ -2468,7 +2468,7 @@ select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { - outline: thin dotted #333333; + outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } @@ -2802,7 +2802,7 @@ input[type="checkbox"][readonly] { border: none; background: none; } .button.button-icon:active, .button.button-icon.active { - text-shadow: 0px 0px 10px white; + text-shadow: 0px 0px 10px #fff; box-shadow: none; background: none; } @@ -2985,7 +2985,7 @@ a.button { width: 100%; background-color: white; border-radius: 2px; - border: 1px solid #dddddd; } + border: 1px solid #ddd; } .card-header { padding: 10px; diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js index a95f2de4c0..a8786ae15c 100644 --- a/dist/js/ionic-angular.js +++ b/dist/js/ionic-angular.js @@ -247,7 +247,7 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
\
\
\ - \ + \
\
\ \ diff --git a/dist/js/ionic.js b/dist/js/ionic.js index 1af8362494..1f292b88eb 100644 --- a/dist/js/ionic.js +++ b/dist/js/ionic.js @@ -62,6 +62,35 @@ window.ionic = { }; })(ionic); ; +(function(ionic) { + ionic.DomUtil = { + /** + * {returns} the closest parent matching the className + */ + getParentWithClass: function(e, className) { + while(e.parentNode) { + if(e.parentNode.classList && e.parentNode.classList.contains(className)) { + return e.parentNode; + } + e = e.parentNode; + } + return null; + }, + /** + * {returns} the closest parent or self matching the className + */ + getParentOrSelfWithClass: function(e, className) { + while(e) { + if(e.classList && e.classList.contains(className)) { + return e; + } + e = e.parentNode; + } + return null; + } + } +})(window.ionic); +; /** * ion-events.js * @@ -1805,6 +1834,144 @@ window.ionic = { })(ionic); ; (function(ionic) { + var DragOp = function() {} + DragOp.prototype = { + start: function(e) { + }, + drag: function(e) { + }, + end: function(e) { + } + } + + var SlideDrag = function(opts) { + this.dragThresholdX = opts.dragThresholdX || 10; + }; + + SlideDrag.prototype = new DragOp(); + SlideDrag.prototype.start = function(e) { + var content; + + if(e.target.classList.contains('list-item-content')) { + content = e.target; + } else if(e.target.classList.contains('list-item')) { + content = e.target.querySelector('.list-item-content'); + } + + // If we don't have a content area as one of our children (or ourselves), skip + if(!content) { + return; + } + + // Make sure we aren't animating as we slide + content.classList.remove('list-item-sliding'); + + // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) + var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; + + // Grab the buttons + buttons = content.parentNode.querySelector('.list-item-buttons'); + if(!buttons) { + return; + } + + buttonsWidth = buttons.offsetWidth; + + this._currentDrag = { + buttonsWidth: buttonsWidth, + content: content, + startOffsetX: offsetX + }; + }; + + SlideDrag.prototype.drag = function(e) { + var _this = this; + + window.requestAnimationFrame(function() { + // We really aren't dragging + if(!_this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!_this._isDragging && + ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || + (Math.abs(_this._currentDrag.startOffsetX) > 0))) + { + _this._isDragging = true; + } + + if(_this._isDragging) { + + // Grab the new X point, capping it at zero + var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); + + // If the new X position is past the buttons, we need to slow down the drag (rubber band style) + if(newX < -buttonsWidth) { + // Calculate the new X position, capped at the top of the buttons + newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); + } + + console.log(newX); + + _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; + } + }); + }; + + SlideDrag.prototype.end = function(e, doneCallback) { + var _this = this; + + // There is no drag, just end immediately + if(!this._currentDrag) { + doneCallback && doneCallback(); + return; + } + + // If we are currently dragging, we want to snap back into place + // 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 mid-point + // and we aren't moving fast enough to swipe open + if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) { + + // If we are going left but too slow, or going right, go back to resting + 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)'; + + // + doneCallback && doneCallback(); + }); + } + + var ReorderDrag = function() {} + ReorderDrag.prototype = new DragOp(); + ReorderDrag.prototype.start = function(e) { + }; /** * The ListView handles a list of items. It will process drag animations, edit mode, @@ -1834,176 +2001,49 @@ window.ionic = { _initDrag: function() { this._isDragging = false; this._currentDrag = null; + this._dragOp = 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 we don't have a content area as one of our children (or ourselves), skip - if(!content) { + // Check if this is a reorder drag + if(ionic.DomUtil.getParentOrSelfWithClass(e.target, 'list-item-drag') && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) { + this._dragOp = new ReorderDrag(this.el); + this._dragOp.start(e); return; + } + + // Or check if this is a swipe to the side drag + if(e.gesture.direction == 'left' || e.gesture.direction == 'right') { + this._dragOp = new SlideDrag(this.el); + this._dragOp.start(e); } - - // Make sure we aren't animating as we slide - content.classList.remove('list-item-sliding'); - - // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) - var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - - // Grab the buttons - buttons = content.parentNode.querySelector('.list-item-buttons'); - if(!buttons) { - return; - } - - buttonsWidth = buttons.offsetWidth; - - this._currentDrag = { - buttonsWidth: buttonsWidth, - content: content, - startOffsetX: offsetX - }; }, + _handleEndDrag: function(e) { var _this = this; - if(!this._currentDrag) { + if(!this._dragOp) { this._initDrag(); return; } - // If we are currently dragging, we want to snap back into place - - // 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 mid-point - // and we aren't moving fast enough to swipe open - if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) { - - // If we are going left but too slow, or going right, go back to resting - 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._dragOp.end(e, function() { _this._initDrag(); }); }, - /** * Process the drag event to move the item to the left or right. */ _handleDrag: function(e) { var _this = this, content, buttons; - window.requestAnimationFrame(function() { + if(!this._dragOp) { + this._startDrag(e); + } - if(!_this._currentDrag) { - _this._startDrag(e); - } - - // We really aren't dragging - if(!_this._currentDrag) { - return; - } - - // Check if we should start dragging. Check if we've dragged past the threshold, - // or we are starting from the open state. - if(!_this._isDragging && - ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || - (Math.abs(_this._currentDrag.startOffsetX) > 0))) - { - _this._isDragging = true; - } - - if(_this._isDragging) { - - // Grab the new X point, capping it at zero - var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); - - // If the new X position is past the buttons, we need to slow down the drag (rubber band style) - if(newX < -buttonsWidth) { - // Calculate the new X position, capped at the top of the buttons - newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); - } - - console.log(newX); - - _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; - } - }); - }, - - - _handleSwipeLeft: function(e) { - - window.requestAnimationFrame(function() { - var item = e.target, - cl = item.classList, - content, buttons, buttonsWidth; - - - if(!content) { - return; - } - - // Grab the buttons - buttons = content.parentNode.querySelector('.list-item-buttons'); - if(buttons) { - buttonsWidth = buttons.offsetWidth; - - // Slide the content over left by the button width - content.style.left = -buttonsWidth + 'px'; - } - }); - }, - _handleSwipeRight: function(e) { - - window.requestAnimationFrame(function() { - var item = e.target, - cl = item.classList, - content; - - if(cl.contains('list-item')) { - content = item.querySelector('.list-item-content'); - } else if(cl.contains('list-item-content')) { - content = item; - } - - // This item didn't have content - if(!content) { - return; - } - - content.style.left = 0; - }); - }, + this._dragOp.drag(e); + } }; })(ionic); diff --git a/js/ext/angular/src/directive/ionicList.js b/js/ext/angular/src/directive/ionicList.js index c7b6d27c40..06509dbbe5 100644 --- a/js/ext/angular/src/directive/ionicList.js +++ b/js/ext/angular/src/directive/ionicList.js @@ -22,7 +22,7 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
\
\
\ - \ + \
\
\ \ diff --git a/js/utils/dom.js b/js/utils/dom.js new file mode 100644 index 0000000000..3c9baba5cf --- /dev/null +++ b/js/utils/dom.js @@ -0,0 +1,28 @@ +(function(ionic) { + ionic.DomUtil = { + /** + * {returns} the closest parent matching the className + */ + getParentWithClass: function(e, className) { + while(e.parentNode) { + if(e.parentNode.classList && e.parentNode.classList.contains(className)) { + return e.parentNode; + } + e = e.parentNode; + } + return null; + }, + /** + * {returns} the closest parent or self matching the className + */ + getParentOrSelfWithClass: function(e, className) { + while(e) { + if(e.classList && e.classList.contains(className)) { + return e; + } + e = e.parentNode; + } + return null; + } + } +})(window.ionic); diff --git a/js/views/listView.js b/js/views/listView.js index bc71e845c3..c67ee654fb 100644 --- a/js/views/listView.js +++ b/js/views/listView.js @@ -1,4 +1,142 @@ (function(ionic) { + var DragOp = function() {} + DragOp.prototype = { + start: function(e) { + }, + drag: function(e) { + }, + end: function(e) { + } + } + + var SlideDrag = function(opts) { + this.dragThresholdX = opts.dragThresholdX || 10; + }; + + SlideDrag.prototype = new DragOp(); + SlideDrag.prototype.start = function(e) { + var content; + + if(e.target.classList.contains('list-item-content')) { + content = e.target; + } else if(e.target.classList.contains('list-item')) { + content = e.target.querySelector('.list-item-content'); + } + + // If we don't have a content area as one of our children (or ourselves), skip + if(!content) { + return; + } + + // Make sure we aren't animating as we slide + content.classList.remove('list-item-sliding'); + + // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) + var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; + + // Grab the buttons + buttons = content.parentNode.querySelector('.list-item-buttons'); + if(!buttons) { + return; + } + + buttonsWidth = buttons.offsetWidth; + + this._currentDrag = { + buttonsWidth: buttonsWidth, + content: content, + startOffsetX: offsetX + }; + }; + + SlideDrag.prototype.drag = function(e) { + var _this = this; + + window.requestAnimationFrame(function() { + // We really aren't dragging + if(!_this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!_this._isDragging && + ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || + (Math.abs(_this._currentDrag.startOffsetX) > 0))) + { + _this._isDragging = true; + } + + if(_this._isDragging) { + + // Grab the new X point, capping it at zero + var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); + + // If the new X position is past the buttons, we need to slow down the drag (rubber band style) + if(newX < -buttonsWidth) { + // Calculate the new X position, capped at the top of the buttons + newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); + } + + console.log(newX); + + _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; + } + }); + }; + + SlideDrag.prototype.end = function(e, doneCallback) { + var _this = this; + + // There is no drag, just end immediately + if(!this._currentDrag) { + doneCallback && doneCallback(); + return; + } + + // If we are currently dragging, we want to snap back into place + // 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 mid-point + // and we aren't moving fast enough to swipe open + if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) { + + // If we are going left but too slow, or going right, go back to resting + 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)'; + + // + doneCallback && doneCallback(); + }); + } + + var ReorderDrag = function() {} + ReorderDrag.prototype = new DragOp(); + ReorderDrag.prototype.start = function(e) { + }; /** * The ListView handles a list of items. It will process drag animations, edit mode, @@ -28,176 +166,49 @@ _initDrag: function() { this._isDragging = false; this._currentDrag = null; + this._dragOp = 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 we don't have a content area as one of our children (or ourselves), skip - if(!content) { + // Check if this is a reorder drag + if(ionic.DomUtil.getParentOrSelfWithClass(e.target, 'list-item-drag') && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) { + this._dragOp = new ReorderDrag(this.el); + this._dragOp.start(e); return; + } + + // Or check if this is a swipe to the side drag + if(e.gesture.direction == 'left' || e.gesture.direction == 'right') { + this._dragOp = new SlideDrag(this.el); + this._dragOp.start(e); } - - // Make sure we aren't animating as we slide - content.classList.remove('list-item-sliding'); - - // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) - var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; - - // Grab the buttons - buttons = content.parentNode.querySelector('.list-item-buttons'); - if(!buttons) { - return; - } - - buttonsWidth = buttons.offsetWidth; - - this._currentDrag = { - buttonsWidth: buttonsWidth, - content: content, - startOffsetX: offsetX - }; }, + _handleEndDrag: function(e) { var _this = this; - if(!this._currentDrag) { + if(!this._dragOp) { this._initDrag(); return; } - // If we are currently dragging, we want to snap back into place - - // 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 mid-point - // and we aren't moving fast enough to swipe open - if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) { - - // If we are going left but too slow, or going right, go back to resting - 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._dragOp.end(e, function() { _this._initDrag(); }); }, - /** * Process the drag event to move the item to the left or right. */ _handleDrag: function(e) { var _this = this, content, buttons; - window.requestAnimationFrame(function() { + if(!this._dragOp) { + this._startDrag(e); + } - if(!_this._currentDrag) { - _this._startDrag(e); - } - - // We really aren't dragging - if(!_this._currentDrag) { - return; - } - - // Check if we should start dragging. Check if we've dragged past the threshold, - // or we are starting from the open state. - if(!_this._isDragging && - ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || - (Math.abs(_this._currentDrag.startOffsetX) > 0))) - { - _this._isDragging = true; - } - - if(_this._isDragging) { - - // Grab the new X point, capping it at zero - var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); - - // If the new X position is past the buttons, we need to slow down the drag (rubber band style) - if(newX < -buttonsWidth) { - // Calculate the new X position, capped at the top of the buttons - newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); - } - - console.log(newX); - - _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; - } - }); - }, - - - _handleSwipeLeft: function(e) { - - window.requestAnimationFrame(function() { - var item = e.target, - cl = item.classList, - content, buttons, buttonsWidth; - - - if(!content) { - return; - } - - // Grab the buttons - buttons = content.parentNode.querySelector('.list-item-buttons'); - if(buttons) { - buttonsWidth = buttons.offsetWidth; - - // Slide the content over left by the button width - content.style.left = -buttonsWidth + 'px'; - } - }); - }, - _handleSwipeRight: function(e) { - - window.requestAnimationFrame(function() { - var item = e.target, - cl = item.classList, - content; - - if(cl.contains('list-item')) { - content = item.querySelector('.list-item-content'); - } else if(cl.contains('list-item-content')) { - content = item; - } - - // This item didn't have content - if(!content) { - return; - } - - content.style.left = 0; - }); - }, + this._dragOp.drag(e); + } }; })(ionic);