Skip to content

Commit

Permalink
feat(list): reordering scrolls page, reordering performance better
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ajoslin committed Feb 6, 2014
1 parent 5ebbbab commit 7f4b28d
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 182 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion config/build.js
Expand Up @@ -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/
Expand Down
44 changes: 44 additions & 0 deletions js/ext/angular/src/controller/ionicScrollController.js
@@ -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');
});
}
});

}]);

})();
125 changes: 47 additions & 78 deletions js/ext/angular/src/directive/ionicContent.js
@@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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 they want plain overflow scrolling, add that as a class
if(attr.overflowScroll === "true") {
$element.addClass('overflow-scroll');
return;
}
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,
Expand All @@ -103,54 +90,36 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
scrollLeft: this.__scrollLeft
});
}
});

//Save scroll onto viewHistoryData when scope is destroyed
$scope.$on('$destroy', function() {
viewHistoryData.scrollValues = sv.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');
}
};
}
});
//Publish scrollView to parent so children can access it
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;

// 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');
});
$scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
viewHistoryData || (viewHistoryData = {});
if (viewHistoryData.scrollValues) {
scrollView.scrollTo(viewHistoryData.scrollValues);
}

// 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();
//Save scroll onto viewHistoryData when scope is destroyed
$scope.$on('$destroy', function() {
viewHistoryData.scrollValues = scrollView.getValues();
});

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
Expand All @@ -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');
};
Expand Down
10 changes: 6 additions & 4 deletions js/ext/angular/src/directive/ionicList.js
Expand Up @@ -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;

Expand All @@ -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) {
Expand Down Expand Up @@ -100,7 +100,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
restrict: 'E',
replace: true,
transclude: true,

require: '^?$ionicScroll',
scope: {
itemType: '@',
canDelete: '@',
Expand All @@ -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});
Expand Down

0 comments on commit 7f4b28d

Please sign in to comment.