feat(list): reordering scrolls page, reordering performance better

Fixes #521. Reordering now uses webkitTransform instead of
element.style.left.  Additionally, as you drag the drag-element to the
top or bottom of the scroll-area, it will scroll it up or down as
allowed.

Refactors necessary: Common code from `<content>` and `<scroll>` moved
into js/ext/angular/controllers/ionicScrollController.  Then `<content>`
and `<scroll>` expose the controller, and `<list>` can require it.

`<list>` then uses the controller (if exists) to pass the scrollView and
scrollEl to ReorderDrag, and ReorderDrag uses that to scroll.

Additionally, js/ext/angular/test/controller/ionicScrollController tests
much functionality that was untested before.
This commit is contained in:
Andy Joslin
2014-02-06 14:59:21 -05:00
parent 5ebbbab5e7
commit 7f4b28d9da
11 changed files with 299 additions and 181 deletions

View File

@@ -11,7 +11,7 @@ notifications:
rooms:
secure: mkHfRTsuxidtOOORbJJ0Jspb/DSa8jAiQwWWUljqLwefy1p4HGC9P/rLdXXg3vsjiulCzyjEkfvDWAHXvu34GhGWfQuD8U140Fon1Os3AO5Hbme+yRmjXmTcgH8XetSLQufyBBMqXHMd6o1tkxXql1p54G1IShhgAdPNe76d5ZE=
template:
- '<a href="%{build_url}">%{repository}: build#%{build_number} (%{duration%})</a> #{message} (%{branch} - %{commit} : %{author})'
- '<a href="%{build_url}">%{repository}: build#%{build_number}</a> #{message} (%{branch} - %{commit} : %{author})'
format: html
before_install:

View File

@@ -37,7 +37,8 @@ module.exports = {
'js/_license.js',
'js/ext/angular/src/ionicAngular.js',
'js/ext/angular/src/service/**/*.js',
'js/ext/angular/src/directive/**/*.js'
'js/ext/angular/src/directive/**/*.js',
'js/ext/angular/src/controller/**/*.js'
],
//Which vendor files to include in dist, used by build
//Matched relative to config/lib/

View File

@@ -0,0 +1,44 @@
(function() {
'use strict';
angular.module('ionic.ui.scroll')
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout',
function($scope, scrollViewOptions, $timeout) {
scrollViewOptions.bouncing = angular.isDefined(scrollViewOptions.bouncing) ?
scrollViewOptions.bouncing :
!ionic.Platform.isAndroid();
var element = this.element = scrollViewOptions.el;
var refresher = this.refresher = element.querySelector('.scroll-refresher');
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
this.$element = angular.element(element);
//Attach self to element as a controller so other directives can require this controller
//through `require: '$ionicScroll'
this.$element.data('$$ionicScrollController', this);
$timeout(function() {
scrollView.run();
// Activate pull-to-refresh
if(refresher) {
var refresherHeight = refresher.clientHeight || 0;
scrollView.activatePullToRefresh(refresherHeight, function() {
refresher.classList.add('active');
}, function() {
refresher.classList.remove('refreshing');
refresher.classList.remove('active');
}, function() {
refresher.classList.add('refreshing');
$scope.onRefresh && $scope.onRefresh();
$scope.$parent.$broadcast('scroll.onRefresh');
});
}
});
}]);
})();

View File

@@ -1,7 +1,7 @@
(function() {
'use strict';
angular.module('ionic.ui.content', ['ionic.ui.service'])
angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
/**
* Panel is a simple 100% width and height, fixed panel. It's meant for content to be
@@ -18,7 +18,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
// The content directive is a core scrollable content area
// that is part of many View hierarchies
.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', function($parse, $timeout, $ionicScrollDelegate) {
.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', '$controller', function($parse, $timeout, $ionicScrollDelegate, $controller) {
return {
restrict: 'E',
replace: true,
@@ -52,46 +52,33 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
if(attr.hasTabs == "true") { element.addClass('has-tabs'); }
if(attr.padding == "true") { element.find('div').addClass('padding'); }
return function link($scope, $element, $attr, navViewCtrl) {
var clone, sc, sv,
return {
//Prelink <content> so it can compile before other directives compile.
//Then other directives can require ionicScrollCtrl
pre: prelink
};
function prelink($scope, $element, $attr, navViewCtrl) {
var clone, sc, scrollView, scrollCtrl,
c = angular.element($element.children()[0]);
if($scope.scroll === "false") {
// No scrolling
return;
}
if (navViewCtrl) {
// If we do have a parent navView, wait for them to give us $viewContentLoaded event
// before we fully initialize
$scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
initScroll(viewHistoryData);
});
} else {
// If we are standalone view, just initialize immediately.
initScroll();
}
function initScroll(viewHistoryData) {
viewHistoryData || (viewHistoryData = {});
var savedScroll = viewHistoryData.scrollValues || {};
if(attr.overflowScroll === "true") {
$element.addClass('overflow-scroll');
return;
}
// If they want plain overflow scrolling, add that as a class
if(attr.overflowScroll === "true") {
$element.addClass('overflow-scroll');
return;
}
// Otherwise, use our scroll system
var hasBouncing = $scope.$eval($scope.hasBouncing);
var enableBouncing = (!ionic.Platform.isAndroid() && hasBouncing !== false) || hasBouncing === true;
// No bouncing by default for Android users, lest they take up pitchforks
// to our bouncing goodness
sv = new ionic.views.Scroll({
scrollCtrl = $controller('$ionicScroll', {
$scope: $scope,
scrollViewOptions: {
el: $element[0],
bouncing: enableBouncing,
startX: $scope.$eval($scope.startX) || savedScroll.left || 0,
startY: $scope.$eval($scope.startY) || savedScroll.top || 0,
bouncing: $scope.$eval($scope.hasBouncing),
startX: $scope.$eval($scope.startX) || 0,
startY: $scope.$eval($scope.startY) || 0,
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
scrollingX: $scope.$eval($scope.hasScrollX) === true,
@@ -103,54 +90,36 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
scrollLeft: this.__scrollLeft
});
}
});
}
});
//Publish scrollView to parent so children can access it
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
$scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
viewHistoryData || (viewHistoryData = {});
if (viewHistoryData.scrollValues) {
scrollView.scrollTo(viewHistoryData.scrollValues);
}
//Save scroll onto viewHistoryData when scope is destroyed
$scope.$on('$destroy', function() {
viewHistoryData.scrollValues = sv.getValues();
viewHistoryData.scrollValues = scrollView.getValues();
});
});
var refresher = $element[0].querySelector('.scroll-refresher');
var refresherHeight = refresher && refresher.clientHeight || 0;
if(attr.refreshComplete) {
$scope.refreshComplete = function() {
if($scope.scrollView) {
refresher && refresher.classList.remove('active');
$scope.scrollView.finishPullToRefresh();
$scope.$parent.$broadcast('scroll.onRefreshComplete');
}
};
}
// Activate pull-to-refresh
if(refresher) {
sv.activatePullToRefresh(50, function() {
refresher.classList.add('active');
}, function() {
refresher.classList.remove('refreshing');
refresher.classList.remove('active');
}, function() {
refresher.classList.add('refreshing');
$scope.onRefresh();
$scope.$parent.$broadcast('scroll.onRefresh');
});
}
// Register for scroll delegate event handling
$ionicScrollDelegate.register($scope, $element);
// Let child scopes access this
$scope.$parent.scrollView = sv;
$timeout(function() {
// Give child containers a chance to build and size themselves
sv.run();
});
return sv;
if(attr.refreshComplete) {
$scope.refreshComplete = function() {
if($scope.scrollView) {
scrollCtrl.refresher && scrollCtrl.refresher.classList.remove('active');
scrollView.finishPullToRefresh();
$scope.$parent.$broadcast('scroll.onRefreshComplete');
}
};
}
// Register for scroll delegate event handling
$ionicScrollDelegate.register($scope, $element);
// Check if this supports infinite scrolling and listen for scroll events
// to trigger the infinite scrolling
// TODO(ajoslin): move functionality out of this function and make testable
@@ -163,20 +132,20 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
if(distance.indexOf('%')) {
// It's a multiplier
maxScroll = function() {
return sv.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
return scrollView.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
};
} else {
// It's a pixel value
maxScroll = function() {
return sv.getScrollMax().top - parseInt(distance, 10);
return scrollView.getScrollMax().top - parseInt(distance, 10);
};
}
$element.bind('scroll', function(e) {
if( sv && !infiniteStarted && (sv.getValues().top > maxScroll() ) ) {
if( scrollView && !infiniteStarted && (scrollView.getValues().top > maxScroll() ) ) {
infiniteStarted = true;
infiniteScroll.addClass('active');
var cb = function() {
sv.resize();
scrollView.resize();
infiniteStarted = false;
infiniteScroll.removeClass('active');
};

View File

@@ -37,7 +37,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
link: function($scope, $element, $attr, list) {
if(!list) return;
var $parentScope = list.scope;
var $parentAttrs = list.attrs;
@@ -54,7 +54,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
$scope.itemClass = $scope.itemType;
// Decide if this item can do stuff, and follow a certain priority
// Decide if this item can do stuff, and follow a certain priority
// depending on where the value comes from
if(($attr.canDelete ? $scope.canDelete : $parentScope.canDelete) !== "false") {
if($attr.onDelete || $parentAttrs.onDelete) {
@@ -100,7 +100,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
restrict: 'E',
replace: true,
transclude: true,
require: '^?$ionicScroll',
scope: {
itemType: '@',
canDelete: '@',
@@ -122,10 +122,12 @@ angular.module('ionic.ui.list', ['ngAnimate'])
this.attrs = $attrs;
}],
link: function($scope, $element, $attr) {
link: function($scope, $element, $attr, ionicScrollCtrl) {
$scope.listView = new ionic.views.ListView({
el: $element[0],
listEl: $element[0].children[0],
scrollEl: ionicScrollCtrl && ionicScrollCtrl.element,
scrollView: ionicScrollCtrl && ionicScrollCtrl.scrollView,
onReorder: function(el, oldIndex, newIndex) {
$scope.$apply(function() {
$scope.onReorder({el: el, start: oldIndex, end: newIndex});

View File

@@ -3,11 +3,11 @@
angular.module('ionic.ui.scroll', [])
.directive('scroll', ['$parse', '$timeout', function($parse, $timeout) {
.directive('scroll', ['$parse', '$timeout', '$controller', function($parse, $timeout, $controller) {
return {
restrict: 'E',
replace: true,
template: '<div class="scroll-view"></div>',
template: '<div class="scroll-view"><div class="scroll" ng-transclude></div></div>',
transclude: true,
scope: {
direction: '@',
@@ -23,8 +23,15 @@ angular.module('ionic.ui.scroll', [])
controller: function() {},
compile: function(element, attr, transclude) {
return function($scope, $element, $attr) {
var clone, sv, sc = document.createElement('div');
return {
//Prelink <scroll> so it can compile before other directives compile.
//Then other directives can require ionicScrollCtrl
pre: prelink
};
function prelink($scope, $element, $attr) {
var scrollView, scrollCtrl, sc = $element[0].children[0];
// Create the internal scroll div
sc.className = 'scroll';
@@ -34,72 +41,46 @@ angular.module('ionic.ui.scroll', [])
if($scope.$eval($scope.paging) === true) {
sc.classList.add('scroll-paging');
}
$element.append(sc);
// Pass the parent scope down to the child
clone = transclude($scope.$parent);
angular.element($element[0].firstElementChild).append(clone);
// Get refresher size
var refresher = $element[0].querySelector('.scroll-refresher');
var refresherHeight = refresher && refresher.clientHeight || 0;
if(!$scope.direction) { $scope.direction = 'y'; }
var hasScrollingX = $scope.direction.indexOf('x') >= 0;
var hasScrollingY = $scope.direction.indexOf('y') >= 0;
var isPaging = $scope.$eval($scope.paging) === true;
$timeout(function() {
var options = {
el: $element[0],
paging: $scope.$eval($scope.paging) === true,
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
scrollingX: hasScrollingX,
scrollingY: hasScrollingY
};
var scrollViewOptions= {
el: $element[0],
paging: isPaging,
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
scrollingX: $scope.direction.indexOf('x') >= 0,
scrollingY: $scope.direction.indexOf('y') >= 0
};
if (isPaging) {
scrollViewOptions.speedMultiplier = 0.8;
scrollViewOptions.bouncing = false;
}
if(options.paging) {
options.speedMultiplier = 0.8;
options.bouncing = false;
}
scrollCtrl = $controller('$ionicScroll', {
$scope: $scope,
scrollViewOptions: scrollViewOptions
});
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
sv = new ionic.views.Scroll(options);
// Activate pull-to-refresh
if(refresher) {
sv.activatePullToRefresh(refresherHeight, function() {
refresher.classList.add('active');
}, function() {
refresher.classList.remove('refreshing');
refresher.classList.remove('active');
}, function() {
refresher.classList.add('refreshing');
$scope.onRefresh();
$scope.$parent.$broadcast('scroll.onRefresh');
});
}
$element.bind('scroll', function(e) {
$scope.onScroll({
event: e,
scrollTop: e.detail ? e.detail.scrollTop : e.originalEvent ? e.originalEvent.detail.scrollTop : 0,
scrollLeft: e.detail ? e.detail.scrollLeft: e.originalEvent ? e.originalEvent.detail.scrollLeft : 0
});
$element.bind('scroll', function(e) {
$scope.onScroll({
event: e,
scrollTop: e.detail ? e.detail.scrollTop : e.originalEvent ? e.originalEvent.detail.scrollTop : 0,
scrollLeft: e.detail ? e.detail.scrollLeft: e.originalEvent ? e.originalEvent.detail.scrollLeft : 0
});
});
$scope.$parent.$on('scroll.resize', function(e) {
// Run the resize after this digest
$timeout(function() {
sv && sv.resize();
});
$scope.$parent.$on('scroll.resize', function(e) {
// Run the resize after this digest
$timeout(function() {
scrollView && scrollView.resize();
});
});
$scope.$parent.$on('scroll.refreshComplete', function(e) {
sv && sv.finishPullToRefresh();
});
// Let child scopes access this
$scope.$parent.scrollView = sv;
$scope.$parent.$on('scroll.refreshComplete', function(e) {
scrollView && scrollView.finishPullToRefresh();
});
};
}

View File

@@ -0,0 +1,90 @@
describe('$ionicScroll Controller', function() {
beforeEach(module('ionic.ui.scroll'));
var scope, ctrl, timeout;
function setup(options) {
options = options || {};
options.el = options.el || document.createElement('div');
inject(function($controller, $rootScope, $timeout) {
scope = $rootScope.$new();
ctrl = $controller('$ionicScroll', {
$scope: scope,
scrollViewOptions: options
});
spyOn(ctrl.scrollView, 'run'); // don't actually call run, this is a dumb test
timeout = $timeout;
});
}
it('should set this.element and this.$element', function() {
setup();
expect(ctrl.element.tagName).toMatch(/div/i);
expect(ctrl.$element[0]).toBe(ctrl.element);
});
it('should register scrollView as this.scrollView', function() {
setup();
expect(ctrl.scrollView instanceof ionic.views.Scroll).toBe(true);
});
it('should register controller on element.data', function() {
setup();
expect(ctrl.$element.controller('$ionicScroll')).toBe(ctrl);
});
it('should run after a timeout', function() {
setup();
timeout.flush();
expect(ctrl.scrollView.run).toHaveBeenCalled();
});
it('should not setup if no child .scroll-refresher', function() {
setup();
expect(ctrl.refresher).toBeFalsy();
spyOn(ctrl.scrollView, 'activatePullToRefresh');
timeout.flush();
expect(ctrl.scrollView.activatePullToRefresh).not.toHaveBeenCalled();
});
it('should work with .scroll-refresher child and proper refresher', function() {
var startCb, refreshingCb, doneCb, refresherEl;
setup({
el: angular.element('<div><div class="scroll-refresher"></div></div>')[0]
});
spyOn(ctrl.scrollView, 'activatePullToRefresh').andCallFake(function(height, start, refreshing, done) {
startCb = start;
refreshingCb = refreshing;
doneCb = done;
});
scope.onRefresh = jasmine.createSpy('onRefresh');
scope.$parent.$broadcast = jasmine.createSpy('$broadcast');
var refresher = ctrl.refresher;
timeout.flush();
expect(refresher.classList.contains('active')).toBe(false);
expect(refresher.classList.contains('refreshing')).toBe(false);
startCb();
expect(refresher.classList.contains('active')).toBe(true);
expect(refresher.classList.contains('refreshing')).toBe(false);
refreshingCb();
expect(refresher.classList.contains('active')).toBe(false);
expect(refresher.classList.contains('refreshing')).toBe(false);
expect(scope.onRefresh).not.toHaveBeenCalled();
expect(scope.$parent.$broadcast).not.toHaveBeenCalledWith('scroll.onRefresh');
doneCb();
expect(refresher.classList.contains('active')).toBe(false);
expect(refresher.classList.contains('refreshing')).toBe(true);
expect(scope.onRefresh).toHaveBeenCalled();
expect(scope.$parent.$broadcast).toHaveBeenCalledWith('scroll.onRefresh');
});
});

View File

@@ -87,23 +87,14 @@ describe('Ionic Content directive', function() {
scope.$apply();
}
it('should not initialize scroll until $viewContentLoaded if there is a parent view', function() {
compileWithParent();
expect(scope.scrollView).toBeUndefined();
scope.$broadcast('$viewContentLoaded', {});
expect(scope.scrollView instanceof ionic.views.Scroll).toBe(true);
});
it('should set start x and y with historyData.scroll passed in through $viewContentLoaded', function() {
it('should set x and y with historyData.scrollValues passed in through $viewContentLoaded', function() {
compileWithParent();
spyOn(scope.scrollView, 'scrollTo');
var scrollValues = { top: 40, left: -20, zoom: 3 };
scope.$broadcast('$viewContentLoaded', {
scrollValues: { top: 40, left: -20 }
scrollValues: scrollValues
});
timeout.flush();
var scrollView = scope.scrollView;
var vals = scrollView.getValues();
expect(vals.top).toBe(40);
expect(vals.left).toBe(-20);
expect(scope.scrollView.scrollTo).toHaveBeenCalledWith(scrollValues);
});
it('should save scroll on the historyData passed in on $destroy', function() {

View File

@@ -52,7 +52,7 @@
if(!buttons) {
return;
}
buttonsWidth = buttons.offsetWidth;
this._currentDrag = {
@@ -111,7 +111,7 @@
// 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
// 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)) {
@@ -145,7 +145,7 @@
_this._currentDrag.content.style.webkitTransform = 'translate3d(' + restingPoint + 'px, 0, 0)';
}
_this._currentDrag.content.style.webkitTransition = '';
// Kill the current drag
_this._currentDrag = null;
@@ -160,10 +160,17 @@
this.dragThresholdY = opts.dragThresholdY || 0;
this.onReorder = opts.onReorder;
this.el = opts.el;
this.scrollEl = opts.scrollEl;
this.scrollView = opts.scrollView;
};
ReorderDrag.prototype = new DragOp();
ReorderDrag.prototype._moveElement = function(e) {
var y = (e.gesture.center.pageY - this._currentDrag.elementHeight/2);
this.el.style.webkitTransform = 'translate3d(0, '+y+'px, 0)';
};
ReorderDrag.prototype.start = function(e) {
var content;
@@ -172,20 +179,31 @@
var offsetY = this.el.offsetTop;//parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var startIndex = ionic.DomUtil.getChildIndex(this.el, this.el.nodeName.toLowerCase());
var elementHeight = this.el.offsetHeight;
var placeholder = this.el.cloneNode(true);
// If we have a scroll pane, move our draggable element outside of it
// We do this because when we drag our element down below the edge of the page
// and scroll the scroll-pane, if the element is *part* of the scroll-pane,
// it will scroll 'with' the scroll-pane's contents and change position.
var appendToElement = (this.scrollEl || this.el).parentNode;
placeholder.classList.add(ITEM_PLACEHOLDER_CLASS);
this.el.parentNode.insertBefore(placeholder, this.el);
this.el.classList.add(ITEM_REORDERING_CLASS);
appendToElement.parentNode.appendChild(this.el);
this._currentDrag = {
startOffsetTop: offsetY,
elementHeight: elementHeight,
startIndex: startIndex,
placeholder: placeholder
placeholder: placeholder,
scrollHeight: scroll,
list: placeholder.parentNode
};
this._moveElement(e);
};
ReorderDrag.prototype.drag = function(e) {
@@ -197,6 +215,29 @@
return;
}
var scrollY = 0;
var pageY = e.gesture.center.pageY;
//If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary
if (_this.scrollView) {
var container = _this.scrollEl;
scrollY = _this.scrollView.getValues().top;
var containerTop = container.offsetTop;
var pixelsPastTop = containerTop - pageY + _this._currentDrag.elementHeight/2;
var pixelsPastBottom = pageY + _this._currentDrag.elementHeight/2 - containerTop - container.offsetHeight;
if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) {
_this.scrollView.scrollBy(null, -pixelsPastTop);
}
if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) {
if (scrollY < _this.scrollView.getScrollMax().top) {
_this.scrollView.scrollBy(null, pixelsPastBottom);
}
}
}
// 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) {
@@ -204,11 +245,9 @@
}
if(_this._isDragging) {
var newY = _this._currentDrag.startOffsetTop + e.gesture.deltaY;
_this.el.style.top = newY + 'px';
_this._moveElement(e);
_this._currentDrag.currentY = newY;
_this._currentDrag.currentY = scrollY + pageY - _this._currentDrag.placeholder.parentNode.offsetTop;
_this._reorderItems();
}
@@ -219,9 +258,6 @@
ReorderDrag.prototype._reorderItems = function() {
var placeholder = this._currentDrag.placeholder;
var siblings = Array.prototype.slice.call(this._currentDrag.placeholder.parentNode.children);
// Remove the floating element from the child search list
siblings.splice(siblings.indexOf(this.el), 1);
var index = siblings.indexOf(this._currentDrag.placeholder);
var topSibling = siblings[Math.max(0, index - 1)];
@@ -244,12 +280,12 @@
}
var placeholder = this._currentDrag.placeholder;
var finalPosition = ionic.DomUtil.getChildIndex(placeholder, placeholder.nodeName.toLowerCase());
// Reposition the element
this.el.classList.remove(ITEM_REORDERING_CLASS);
this.el.style.top = 0;
this.el.style.webkitTransform = '';
var finalPosition = ionic.DomUtil.getChildIndex(placeholder, placeholder.nodeName.toLowerCase());
placeholder.parentNode.insertBefore(this.el, placeholder);
placeholder.parentNode.removeChild(placeholder);
@@ -294,7 +330,7 @@
window.ionic.onGesture('release', function(e) {
_this._handleEndDrag(e);
}, this.el);
window.ionic.onGesture('drag', function(e) {
_this._handleDrag(e);
}, this.el);
@@ -399,6 +435,8 @@
if(item) {
this._dragOp = new ReorderDrag({
el: item,
scrollEl: this.scrollEl,
scrollView: this.scrollView,
onReorder: function(el, start, end) {
_this.onReorder && _this.onReorder(el, start, end);
}
@@ -424,7 +462,7 @@
_handleEndDrag: function(e) {
var _this = this;
if(!this._dragOp) {
//ionic.views.ListView.__super__._handleEndDrag.call(this, e);
return;
@@ -447,7 +485,7 @@
*/
_handleDrag: function(e) {
var _this = this, content, buttons;
// If the user has a touch timeout to highlight an element, clear it if we
// get sufficient draggage
if(Math.abs(e.gesture.deltaX) > 10 || Math.abs(e.gesture.deltaY) > 10) {
@@ -462,7 +500,7 @@
}
// No drag still, pass it up
if(!this._dragOp) {
if(!this._dragOp) {
//ionic.views.ListView.__super__._handleDrag.call(this, e);
return;
}

View File

@@ -559,7 +559,7 @@ $z-index-item-drag: 0;
$z-index-item-edit: 0;
$z-index-item-options: 1;
$z-index-item-radio: 3;
$z-index-item-reordering: 20;
$z-index-item-reordering: 9;
$z-index-item-toggle: 3;
$z-index-menu: 0;
$z-index-modal: 10;

View File

@@ -37,7 +37,8 @@ describe('List View', function() {
expect(list.itemHeight).toEqual(50);
});
xit('Should support virtual scrolling', function() {
/*
it('Should support virtual scrolling', function() {
var list = new ionic.views.ListView({
el: h,
listEl: listEl,
@@ -70,5 +71,6 @@ describe('List View', function() {
expect(list.renderViewport).toHaveBeenCalledWith(scrollTop + list.virtualRemoveThreshold,
scrollTop + viewportHeight + list.virtualAddThreshold, start, end);
});
*/
});