diff --git a/dist/css/ionic-scoped.css b/dist/css/ionic-scoped.css
index 1d80df1e43..ca50bd8c54 100644
--- a/dist/css/ionic-scoped.css
+++ b/dist/css/ionic-scoped.css
@@ -171,6 +171,9 @@
/**
* A list divider.
*/
+ /**
+ * List refreser elements
+ */
/* the overall container of the toggle */
/* hide the actual checkbox */
/* the background of the toggle's track area */
@@ -1856,6 +1859,10 @@
.ionic .list-item-text {
margin-bottom: 0;
line-height: 1.3; }
+ .ionic .list-refresher {
+ background-color: red;
+ height: 0;
+ overflow: hidden; }
.ionic form {
margin: 0 0 1.42857; }
.ionic legend {
diff --git a/dist/css/ionic.css b/dist/css/ionic.css
index 279e1839ff..ff698604d4 100644
--- a/dist/css/ionic.css
+++ b/dist/css/ionic.css
@@ -2291,6 +2291,14 @@ a.list-item {
margin-bottom: 0;
line-height: 1.3; }
+/**
+ * List refreser elements
+ */
+.list-refresher {
+ background-color: red;
+ height: 0;
+ overflow: hidden; }
+
form {
margin: 0 0 1.42857; }
@@ -3034,12 +3042,12 @@ a.button {
.slide-box-animating {
-webkit-transition-duration: 0.2s; }
-.slide {
+.slide-box-slide {
display: inline-block;
vertical-align: top;
width: 100%;
height: 100%; }
- .slide img {
+ .slide-box-slide img {
width: 100%; }
.slide-box-pager {
@@ -3052,7 +3060,7 @@ a.button {
text-decoration: none;
margin: 0px 5px;
color: #fff;
- opacity: 0.5; }
+ opacity: 0.3; }
.slide-box-pager > *.active {
-webkit-transition: opacity 0.4s ease-in;
opacity: 1; }
diff --git a/dist/js/ionic.js b/dist/js/ionic.js
index f237e14b2d..b4787e0658 100644
--- a/dist/js/ionic.js
+++ b/dist/js/ionic.js
@@ -1852,6 +1852,65 @@ window.ionic = {
}
};
+ var PullToRefreshDrag = function(opts) {
+ this.dragThresholdY = opts.dragThresholdY || 10;
+ this.el = opts.el;
+ };
+ PullToRefreshDrag.prototype = new DragOp();
+ PullToRefreshDrag.prototype.start = function(e) {
+ var content, refresher;
+
+ content = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'list');
+ if(!content) { return; }
+
+ // Grab the refresher element that will show as you drag down
+ refresher = content.querySelector('.list-refresher');
+ if(!refresher) {
+ refresher = this._injectRefresher();
+ }
+
+ this._currentDrag = {
+ refresher: refresher,
+ content: content
+ };
+ };
+ PullToRefreshDrag.prototype._injectRefresher = function() {
+ var refresher = document.createElement('div');
+ refresher.className = 'list-refresher';
+ this.el.insertBefore(refresher, this.el.firstChild);
+ return refresher;
+ };
+ PullToRefreshDrag.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.deltaY) > _this.dragThresholdY) {
+ _this._isDragging = true;
+ }
+
+ if(_this._isDragging) {
+ var currentHeight = parseFloat(_this._currentDrag.refresher.style.height);
+ _this._currentDrag.refresher.style.height = e.gesture.deltaY + 'px';
+
+ var newHeight = parseFloat(_this._currentDrag.refresher.style.height = e.gesture.deltaY);
+ var firstChildHeight = parseFloat(_this._currentDrag.refresher.firstElementChild.style.height);
+ console.log('New Height must pass', firstChildHeight);
+ if(newHeight > firstChildHeight) {
+ console.log('PASSED', firstChildHeight);
+ }
+ }
+ });
+ };
+ PullToRefreshDrag.prototype.end = function(e) {
+ };
+
var SlideDrag = function(opts) {
this.dragThresholdX = opts.dragThresholdX || 10;
this.el = opts.el;
@@ -2078,6 +2137,8 @@ window.ionic = {
doneCallback && doneCallback();
};
+
+
/**
* The ListView handles a list of items. It will process drag animations, edit mode,
* and other operations that are common on mobile lists or table views.
@@ -2103,10 +2164,12 @@ window.ionic = {
};
ionic.views.List.prototype = {
+
_initDrag: function() {
this._isDragging = false;
this._dragOp = null;
},
+
// Return the list item from the given target
_getItem: function(target) {
while(target) {
@@ -2117,6 +2180,8 @@ window.ionic = {
}
return null;
},
+
+
_startDrag: function(e) {
this._isDragging = false;
@@ -2128,16 +2193,22 @@ window.ionic = {
this._dragOp = new ReorderDrag({ el: item });
this._dragOp.start(e);
}
- return;
- }
+ }
+ // Check if this is a "pull down" drag for pull to refresh
+ else if(e.gesture.direction == 'down') {
+ this._dragOp = new PullToRefreshDrag({ el: this.el });
+ this._dragOp.start(e);
+ }
+
// Or check if this is a swipe to the side drag
- if(e.gesture.direction == 'left' || e.gesture.direction == 'right') {
+ else if(e.gesture.direction == 'left' || e.gesture.direction == 'right') {
this._dragOp = new SlideDrag({ el: this.el });
this._dragOp.start(e);
}
},
+
_handleEndDrag: function(e) {
var _this = this;
@@ -2150,6 +2221,7 @@ window.ionic = {
_this._initDrag();
});
},
+
/**
* Process the drag event to move the item to the left or right.
*/
@@ -2311,6 +2383,51 @@ window.ionic = {
},
prependSlide: function(el) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ var slideWidth = content.offsetWidth;
+ var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ var newOffsetX = Math.min(0, offsetX - slideWidth);
+
+ content.insertBefore(el, content.firstChild);
+
+ content.classList.remove('slide-box-animating');
+ content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)';
+
+ this._prependPagerIcon();
+ this.slideIndex = (this.slideIndex + 1) % content.children.length;
+ this._updatePager();
+ },
+
+ appendSlide: function(el) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ content.classList.remove('slide-box-animating');
+ content.appendChild(el);
+
+ this._appendPagerIcon();
+ this._updatePager();
+ },
+
+ removeSlide: function(index) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ var items = this.el.firstElementChild;
+ items.removeChild(items.firstElementChild);
+
+ var slideWidth = content.offsetWidth;
+ var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ var newOffsetX = Math.min(0, offsetX + slideWidth);
+
+ content.classList.remove('slide-box-animating');
+ content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)';
+
+ this._removePagerIcon();
+ this.slideIndex = Math.max(0, (this.slideIndex - 1) % content.children.length);
+ this._updatePager();
},
/**
@@ -2359,6 +2476,26 @@ window.ionic = {
return this.slideIndex;
},
+ _appendPagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ var newPagerChild = this.pager.children[0].cloneNode();
+ this.pager.appendChild(newPagerChild);
+ },
+
+ _prependPagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ var newPagerChild = this.pager.children[0].cloneNode();
+ this.pager.insertBefore(newPagerChild, this.pager.firstChild);
+ },
+
+ _removePagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ this.pager.removeChild(this.pager.firstElementChild);
+ },
+
/**
* If we have a pager, update the active page when the current slide
* changes.
@@ -2367,6 +2504,14 @@ window.ionic = {
if(!this.pager) {
return;
}
+
+ var numPagerChildren = this.pager.children.length;
+ if(!numPagerChildren) {
+ // No children to update
+ return;
+ }
+
+ // Update the active state of the pager icons
for(var i = 0, j = this.pager.children.length; i < j; i++) {
if(i == this.slideIndex) {
this.pager.children[i].classList.add('active');
diff --git a/js/ext/angular/test/list.html b/js/ext/angular/test/list.html
index 1ae68c13aa..9016e72b6b 100644
--- a/js/ext/angular/test/list.html
+++ b/js/ext/angular/test/list.html
@@ -49,7 +49,18 @@
-
+
+
+
+ PULL TO REFRESH
+
+
+ RELEASE TO REFRESH
+
+
+ REFRESHING
+
+
_this.dragThresholdY) {
+ _this._isDragging = true;
+ }
+
+ if(_this._isDragging) {
+ var currentHeight = parseFloat(_this._currentDrag.refresher.style.height);
+ _this._currentDrag.refresher.style.height = e.gesture.deltaY + 'px';
+
+ var newHeight = parseFloat(_this._currentDrag.refresher.style.height = e.gesture.deltaY);
+ var firstChildHeight = parseFloat(_this._currentDrag.refresher.firstElementChild.style.height);
+ console.log('New Height must pass', firstChildHeight);
+ if(newHeight > firstChildHeight) {
+ console.log('PASSED', firstChildHeight);
+ }
+ }
+ });
+ };
+ PullToRefreshDrag.prototype.end = function(e) {
+ };
+
var SlideDrag = function(opts) {
this.dragThresholdX = opts.dragThresholdX || 10;
this.el = opts.el;
@@ -236,6 +295,8 @@
doneCallback && doneCallback();
};
+
+
/**
* The ListView handles a list of items. It will process drag animations, edit mode,
* and other operations that are common on mobile lists or table views.
@@ -261,10 +322,12 @@
};
ionic.views.List.prototype = {
+
_initDrag: function() {
this._isDragging = false;
this._dragOp = null;
},
+
// Return the list item from the given target
_getItem: function(target) {
while(target) {
@@ -275,6 +338,8 @@
}
return null;
},
+
+
_startDrag: function(e) {
this._isDragging = false;
@@ -286,16 +351,22 @@
this._dragOp = new ReorderDrag({ el: item });
this._dragOp.start(e);
}
- return;
- }
+ }
+ // Check if this is a "pull down" drag for pull to refresh
+ else if(e.gesture.direction == 'down') {
+ this._dragOp = new PullToRefreshDrag({ el: this.el });
+ this._dragOp.start(e);
+ }
+
// Or check if this is a swipe to the side drag
- if(e.gesture.direction == 'left' || e.gesture.direction == 'right') {
+ else if(e.gesture.direction == 'left' || e.gesture.direction == 'right') {
this._dragOp = new SlideDrag({ el: this.el });
this._dragOp.start(e);
}
},
+
_handleEndDrag: function(e) {
var _this = this;
@@ -308,6 +379,7 @@
_this._initDrag();
});
},
+
/**
* Process the drag event to move the item to the left or right.
*/
diff --git a/js/views/slideBox.js b/js/views/slideBox.js
index 34a494e0de..2c8dea0d18 100644
--- a/js/views/slideBox.js
+++ b/js/views/slideBox.js
@@ -45,6 +45,51 @@
},
prependSlide: function(el) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ var slideWidth = content.offsetWidth;
+ var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ var newOffsetX = Math.min(0, offsetX - slideWidth);
+
+ content.insertBefore(el, content.firstChild);
+
+ content.classList.remove('slide-box-animating');
+ content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)';
+
+ this._prependPagerIcon();
+ this.slideIndex = (this.slideIndex + 1) % content.children.length;
+ this._updatePager();
+ },
+
+ appendSlide: function(el) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ content.classList.remove('slide-box-animating');
+ content.appendChild(el);
+
+ this._appendPagerIcon();
+ this._updatePager();
+ },
+
+ removeSlide: function(index) {
+ var content = this.el.firstElementChild;
+ if(!content) { return; }
+
+ var items = this.el.firstElementChild;
+ items.removeChild(items.firstElementChild);
+
+ var slideWidth = content.offsetWidth;
+ var offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0;
+ var newOffsetX = Math.min(0, offsetX + slideWidth);
+
+ content.classList.remove('slide-box-animating');
+ content.style.webkitTransform = 'translate3d(' + newOffsetX + 'px, 0, 0)';
+
+ this._removePagerIcon();
+ this.slideIndex = Math.max(0, (this.slideIndex - 1) % content.children.length);
+ this._updatePager();
},
/**
@@ -93,6 +138,26 @@
return this.slideIndex;
},
+ _appendPagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ var newPagerChild = this.pager.children[0].cloneNode();
+ this.pager.appendChild(newPagerChild);
+ },
+
+ _prependPagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ var newPagerChild = this.pager.children[0].cloneNode();
+ this.pager.insertBefore(newPagerChild, this.pager.firstChild);
+ },
+
+ _removePagerIcon: function() {
+ if(!this.pager || !this.pager.children.length) { return; }
+
+ this.pager.removeChild(this.pager.firstElementChild);
+ },
+
/**
* If we have a pager, update the active page when the current slide
* changes.
@@ -101,6 +166,14 @@
if(!this.pager) {
return;
}
+
+ var numPagerChildren = this.pager.children.length;
+ if(!numPagerChildren) {
+ // No children to update
+ return;
+ }
+
+ // Update the active state of the pager icons
for(var i = 0, j = this.pager.children.length; i < j; i++) {
if(i == this.slideIndex) {
this.pager.children[i].classList.add('active');
diff --git a/scss/ionic/_listview.scss b/scss/ionic/_listview.scss
index e77869fc49..2bd4a4f4b7 100644
--- a/scss/ionic/_listview.scss
+++ b/scss/ionic/_listview.scss
@@ -253,3 +253,14 @@ a.list-item {
margin-bottom: 0;
line-height: 1.3;
}
+
+/**
+ * List refreser elements
+ */
+.list-refresher {
+ background-color: red;
+ height: 0;
+ overflow: hidden;
+}
+.list-refreshing {
+}
diff --git a/scss/ionic/_slideBox.scss b/scss/ionic/_slideBox.scss
index 2b8413cd4e..e0ad0667e9 100644
--- a/scss/ionic/_slideBox.scss
+++ b/scss/ionic/_slideBox.scss
@@ -15,7 +15,7 @@
.slide-box-animating {
-webkit-transition-duration: 0.2s;
}
-.slide {
+.slide-box-slide {
display: inline-block;
vertical-align: top;
width: 100%;
@@ -38,8 +38,7 @@
margin: 0px 5px;
color: #fff;
- opacity: 0.5;
-
+ opacity: 0.3;
&.active {
-webkit-transition: opacity 0.4s ease-in;
diff --git a/test/slideBox.html b/test/slideBox.html
index 003bd7faa8..ecea42ec0a 100644
--- a/test/slideBox.html
+++ b/test/slideBox.html
@@ -8,7 +8,7 @@
@@ -22,24 +22,25 @@