mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
367 lines
12 KiB
JavaScript
367 lines
12 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
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
|
|
* added to it, or animated around.
|
|
*/
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ionPane
|
|
* @module ionic
|
|
* @group page layout
|
|
* @restrict E
|
|
*
|
|
* @description A simple container that fits content, with no side effects. Adds the 'pane' class to the element.
|
|
*/
|
|
.directive('ionPane', function() {
|
|
return {
|
|
restrict: 'E',
|
|
link: function(scope, element, attr) {
|
|
element.addClass('pane');
|
|
}
|
|
};
|
|
})
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ionContent
|
|
* @module ionic
|
|
* @group page layout
|
|
* @groupMainItem
|
|
*
|
|
* @description
|
|
* The ionContent directive provides an easy to use content area that can be configured
|
|
* to use Ionic's custom Scroll View, or the built in overflow scorlling of the browser.
|
|
*
|
|
* While we recommend using the custom Scroll features in Ionic in most cases, sometimes
|
|
* (for performance reasons) only the browser's native overflow scrolling will suffice,
|
|
* and so we've made it easy to toggle between the Ionic scroll implementation and
|
|
* overflow scrolling.
|
|
*
|
|
* You can implement pull-to-refresh with the {@link ionic.directive:ionRefresher}
|
|
* directive, and infinite scrolling with the {@link ionic.directive:ionInfiniteScroll}
|
|
* directive.
|
|
*
|
|
* @restrict E
|
|
* @param {boolean=} scroll Whether to allow scrolling of content. Defaults to true.
|
|
* @param {boolean=} overflow-scroll Whether to use overflow-scrolling instead of
|
|
* Ionic scroll.
|
|
* @param {boolean=} padding Whether to add padding to the content.
|
|
* @param {boolean=} has-header Whether to offset the content for a header bar.
|
|
* @param {boolean=} has-subheader Whether to offset the content for a subheader bar.
|
|
* @param {boolean=} has-footer Whether to offset the content for a footer bar.
|
|
* @param {boolean=} has-bouncing Whether to allow scrolling to bounce past the edges
|
|
* of the content. Defaults to true on iOS, false on Android.
|
|
* @param {expression=} on-scroll Expression to evaluate when the content is scrolled.
|
|
* @param {expression=} on-scroll-complete Expression to evaluate when a scroll action completes.
|
|
*/
|
|
.directive('ionContent', [
|
|
'$parse',
|
|
'$timeout',
|
|
'$ionicScrollDelegate',
|
|
'$controller',
|
|
'$ionicBind',
|
|
function($parse, $timeout, $ionicScrollDelegate, $controller, $ionicBind) {
|
|
return {
|
|
restrict: 'E',
|
|
replace: true,
|
|
transclude: true,
|
|
require: '^?ionNavView',
|
|
scope: true,
|
|
template:
|
|
'<div class="scroll-content">' +
|
|
'<div class="scroll"></div>' +
|
|
'</div>',
|
|
compile: function(element, attr, transclude) {
|
|
if(attr.hasHeader == "true") { element.addClass('has-header'); }
|
|
if(attr.hasSubheader == "true") { element.addClass('has-subheader'); }
|
|
if(attr.hasFooter == "true") { element.addClass('has-footer'); }
|
|
if(attr.hasTabs == "true") { element.addClass('has-tabs'); }
|
|
if(attr.padding == "true") { element.find('div').addClass('padding'); }
|
|
|
|
return {
|
|
//Prelink <ion-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,
|
|
scrollContent = angular.element($element[0].querySelector('.scroll'));
|
|
|
|
$ionicBind($scope, $attr, {
|
|
$onScroll: '&onScroll',
|
|
$onScrollComplete: '&onScrollComplete',
|
|
hasBouncing: '@',
|
|
scroll: '@',
|
|
padding: '@',
|
|
hasScrollX: '@',
|
|
hasScrollY: '@',
|
|
scrollbarX: '@',
|
|
scrollbarY: '@',
|
|
startX: '@',
|
|
startY: '@',
|
|
scrollEventInterval: '@'
|
|
});
|
|
|
|
if ($scope.scroll === "false") {
|
|
//do nothing
|
|
} else if(attr.overflowScroll === "true") {
|
|
$element.addClass('overflow-scroll');
|
|
} else {
|
|
|
|
scrollCtrl = $controller('$ionicScroll', {
|
|
$scope: $scope,
|
|
scrollViewOptions: {
|
|
el: $element[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,
|
|
scrollingY: $scope.$eval($scope.hasScrollY) !== false,
|
|
scrollEventInterval: parseInt($scope.scrollEventInterval, 10) || 20,
|
|
scrollingComplete: function() {
|
|
$scope.$onScrollComplete({
|
|
scrollTop: this.__scrollTop,
|
|
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 = {});
|
|
var scroll = viewHistoryData.scrollValues;
|
|
if (scroll) {
|
|
$timeout(function() {
|
|
scrollView.scrollTo(+scroll.left || null, +scroll.top || null);
|
|
}, 0);
|
|
}
|
|
|
|
//Save scroll onto viewHistoryData when scope is destroyed
|
|
$scope.$on('$destroy', function() {
|
|
viewHistoryData.scrollValues = scrollView.getValues();
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
transclude($scope, function(clone) {
|
|
if (scrollCtrl) {
|
|
clone.data('$$ionicScrollController', scrollCtrl);
|
|
}
|
|
scrollContent.append(clone);
|
|
});
|
|
|
|
}
|
|
}
|
|
};
|
|
}])
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ionRefresher
|
|
* @module ionic
|
|
* @restrict E
|
|
* @group page layout
|
|
* @parent ionic.directive:ionContent, ionic.directive:ionScroll
|
|
* @description
|
|
* Allows you to add pull-to-refresh to a scrollView.
|
|
*
|
|
* Place it as the first child of your {@link ionic.directive:ionContent} or
|
|
* {@link ionic.directive:ionScroll} element.
|
|
*
|
|
* When refreshing is complete, $broadcast the 'scroll.refreshComplete' event
|
|
* from your controller.
|
|
*
|
|
* @param {expression=} on-refresh Called when the user pulls down enough and lets go
|
|
* of the refresher.
|
|
* @param {expression=} on-pulling Called when the user starts to pull down
|
|
* on the refresher.
|
|
* @param {string=} pulling-icon The icon to display while the user is pulling down.
|
|
* Default: 'ion-arrow-down-c'.
|
|
* @param {string=} pulling-text The text to display while the user is pulling down.
|
|
* @param {string=} refreshing-icon The icon to display after user lets go of the
|
|
* refresher.
|
|
* @param {string=} refreshing-text The text to display after the user lets go of
|
|
* the refresher.
|
|
*
|
|
* @usage
|
|
* ```html
|
|
* <ion-content ng-controller="MyController">
|
|
* <ion-refresher
|
|
* pulling-text="Pull to refresh..."
|
|
* on-refresh="doRefresh()">
|
|
* </ion-refresher>
|
|
* <ion-list>
|
|
* <ion-item ng-repeat="item in items"></ion-item>
|
|
* </ion-list>
|
|
* </ion-content>
|
|
* ```
|
|
* ```js
|
|
* angular.module('testApp', ['ionic'])
|
|
* .controller('MyController', function($scope, $http) {
|
|
* $scope.items = [1,2,3];
|
|
* $scope.doRefresh = function() {
|
|
* $http.get('/new-items').success(function(newItems) {
|
|
* $scope.items = newItems;
|
|
* //Stop the ion-refresher from spinning
|
|
* $scope.$broadcast('scroll.refreshComplete');
|
|
* });
|
|
* };
|
|
* });
|
|
* ```
|
|
*/
|
|
.directive('ionRefresher', ['$ionicBind', function($ionicBind) {
|
|
return {
|
|
restrict: 'E',
|
|
replace: true,
|
|
require: '^$ionicScroll',
|
|
template:
|
|
'<div class="scroll-refresher">' +
|
|
'<div class="ionic-refresher-content">' +
|
|
'<i class="icon {{pullingIcon}} icon-pulling"></i>' +
|
|
'<span class="icon-pulling" ng-bind-html="pullingText"></span>' +
|
|
'<i class="icon {{refreshingIcon}} icon-refreshing"></i>' +
|
|
'<span class="icon-refreshing" ng-bind-html="refreshingText"></span>' +
|
|
'</div>' +
|
|
'</div>',
|
|
compile: function($element, $attrs) {
|
|
if (angular.isUndefined($attrs.pullingIcon)) {
|
|
$attrs.$set('pullingIcon', 'ion-arrow-down-c');
|
|
}
|
|
if (angular.isUndefined($attrs.refreshingIcon)) {
|
|
$attrs.$set('refreshingIcon', 'ion-loading-d');
|
|
}
|
|
return function($scope, $element, $attrs, scrollCtrl) {
|
|
$ionicBind($scope, $attrs, {
|
|
pullingIcon: '@',
|
|
pullingText: '@',
|
|
refreshingIcon: '@',
|
|
refreshingText: '@',
|
|
$onRefresh: '&onRefresh',
|
|
$onPulling: '&onPulling'
|
|
});
|
|
|
|
scrollCtrl.setRefresher($scope, $element[0]);
|
|
$scope.$on('scroll.refreshComplete', function() {
|
|
$element[0].classList.remove('active');
|
|
scrollCtrl.scrollView.finishPullToRefresh();
|
|
});
|
|
};
|
|
}
|
|
};
|
|
}])
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ionInfiniteScroll
|
|
* @module ionic
|
|
* @group page layout
|
|
* @parent ionic.directive:ionContent, ionic.directive:ionScroll
|
|
* @restrict E
|
|
*
|
|
* @description
|
|
* The ionInfiniteScroll directive allows you to call a function whenever
|
|
* the user gets to the bottom of the page or near the bottom of the page.
|
|
*
|
|
* The expression you pass in for `on-infinite` is called when the user scrolls
|
|
* greater than `distance` away from the bottom of the content.
|
|
*
|
|
* @param {expression} on-infinite What to call when the scroller reaches the
|
|
* bottom.
|
|
* @param {string=} distance The distance from the bottom that the scroll must
|
|
* reach to trigger the on-infinite expression. Default: 1%.
|
|
* @param {string=} icon The icon to show while loading. Default: 'ion-loading-d'.
|
|
*
|
|
* @usage
|
|
* ```html
|
|
* <ion-content ng-controller="MyController">
|
|
* <ion-infinite-scroll
|
|
* on-infinite="loadMore()"
|
|
* distance="1%">
|
|
* </ion-infinite-scroll>
|
|
* </ion-content>
|
|
* ```
|
|
* ```js
|
|
* function MyController($scope, $http) {
|
|
* $scope.items = [];
|
|
* $scope.loadMore = function() {
|
|
* $http.get('/more-items').success(function(items) {
|
|
* useItems(items);
|
|
* $scope.$broadcast('scroll.infiniteScrollComplete');
|
|
* });
|
|
* };
|
|
* }
|
|
* ```
|
|
*
|
|
* An easy to way to stop infinite scroll once there is no more data to load
|
|
* is to use angular's `ng-if` directive:
|
|
*
|
|
* ```html
|
|
* <ion-infinite-scroll
|
|
* ng-if="moreDataCanBeLoaded()"
|
|
* icon="ion-loading-c"
|
|
* on-infinite="loadMoreData()">
|
|
* </ion-infinite-scroll>
|
|
* ```
|
|
*/
|
|
.directive('ionInfiniteScroll', ['$timeout', function($timeout) {
|
|
return {
|
|
restrict: 'E',
|
|
require: ['^$ionicScroll', 'ionInfiniteScroll'],
|
|
template:
|
|
'<div class="scroll-infinite">' +
|
|
'<div class="scroll-infinite-content">' +
|
|
'<i class="icon {{icon()}} icon-refreshing"></i>' +
|
|
'</div>' +
|
|
'</div>',
|
|
scope: true,
|
|
controller: ['$scope', '$attrs', function($scope, $attrs) {
|
|
this.isLoading = false;
|
|
this.scrollView = null; //given by link function
|
|
this.getMaxScroll = function() {
|
|
var dist = $attrs.distance || '1%';
|
|
return dist.indexOf('%') > -1 ?
|
|
this.scrollView.getScrollMax().top * (1 - parseInt(dist,10) / 100) :
|
|
this.scrollView.getScrollMax().top - parseInt(dist, 10);
|
|
};
|
|
}],
|
|
link: function($scope, $element, $attrs, ctrls) {
|
|
var scrollCtrl = ctrls[0];
|
|
var infiniteScrollCtrl = ctrls[1];
|
|
var scrollView = infiniteScrollCtrl.scrollView = scrollCtrl.scrollView;
|
|
|
|
$scope.icon = function() {
|
|
return angular.isDefined($attrs.icon) ? $attrs.icon : 'ion-loading-d';
|
|
};
|
|
|
|
$scope.$on('scroll.infiniteScrollComplete', function() {
|
|
$element[0].classList.remove('active');
|
|
$timeout(function() {
|
|
scrollView.resize();
|
|
}, 0, false);
|
|
infiniteScrollCtrl.isLoading = false;
|
|
});
|
|
|
|
scrollCtrl.$element.on('scroll', ionic.animationFrameThrottle(function() {
|
|
if (!infiniteScrollCtrl.isLoading &&
|
|
scrollView.getValues().top >= infiniteScrollCtrl.getMaxScroll()) {
|
|
$element[0].classList.add('active');
|
|
infiniteScrollCtrl.isLoading = true;
|
|
$scope.$parent.$apply($attrs.onInfinite || '');
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
}]);
|
|
|
|
})();
|