From 097f8ce98ad976822f56175eaf54561f920d4e35 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Sun, 17 Nov 2013 14:26:41 -0600 Subject: [PATCH] Fixed up some nav stuff --- dist/css/ionic.css | 110 ++++-- dist/js/ionic-angular.js | 339 +++++++++++-------- js/ext/angular/src/directive/ionicContent.js | 2 +- js/ext/angular/src/directive/ionicNav.js | 337 ++++++++++-------- js/ext/angular/test/nav.html | 29 +- js/ext/angular/test/navPush.html | 83 +++++ scss/_animations.scss | 98 ++++-- scss/_nav.scss | 2 +- 8 files changed, 637 insertions(+), 363 deletions(-) create mode 100644 js/ext/angular/test/navPush.html diff --git a/dist/css/ionic.css b/dist/css/ionic.css index 8ca4caaa0a..44e17e2101 100644 --- a/dist/css/ionic.css +++ b/dist/css/ionic.css @@ -3811,7 +3811,7 @@ button.item-button-right:after { * Nav * -------------------------------------------------- */ -.nav-content { +.nav-page { position: absolute; width: 100%; height: 100%; @@ -4801,50 +4801,94 @@ a.button { 100% { -webkit-transform: translate3d(100%, 0, 0); } } -.slide-in-left.ng-enter, .slide-in-left > .ng-enter { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideInLeft; } -.slide-in-left.ng-leave, .slide-in-left > .ng-leave { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutLeft; } +.slide-in-left { + -webkit-transform: translate3d(0%, 0, 0); } + .slide-in-left.ng-enter, .slide-in-left > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideInLeft; } + .slide-in-left.ng-leave, .slide-in-left > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutLeft; } -.slide-out-left.ng-enter, .slide-out-left > .ng-enter { +.slide-in-left-add { + -webkit-transform: translate3d(100%, 0, 0); -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutLeft; } -.slide-out-left.ng-leave, .slide-out-left > .ng-leave { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; } + +.slide-in-left-add-active { -webkit-animation-name: slideInLeft; } -.slide-in-right.ng-enter, .slide-in-right > .ng-enter { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideInRight; } -.slide-in-right.ng-leave, .slide-in-right > .ng-leave { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutRight; } +.slide-out-left { + -webkit-transform: translate3d(-100%, 0, 0); } + .slide-out-left.ng-enter, .slide-out-left > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutLeft; } + .slide-out-left.ng-leave, .slide-out-left > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutLeft; } -.slide-out-right.ng-enter, .slide-out-right > .ng-enter { +.slide-out-left-add { + -webkit-transform: translate3d(0, 0, 0); -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutRight; } -.slide-out-right.ng-leave, .slide-out-right > .ng-leave { + -webkit-animation-timing-function: ease-in-out; } + +.slide-out-left-add-active { + -webkit-animation-name: slideOutLeft; } + +.slide-in-right { + -webkit-transform: translate3d(0%, 0, 0); } + .slide-in-right.ng-enter, .slide-in-right > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideInRight; } + .slide-in-right.ng-leave, .slide-in-right > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideInRight; } + +.slide-in-right-add { + -webkit-transform: translate3d(-100%, 0, 0); -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; } + +.slide-in-right-add-active { -webkit-animation-name: slideInRight; } +.slide-out-right { + -webkit-transform: translate3d(100%, 0, 0); } + .slide-out-right.ng-enter, .slide-out-right > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutRight; } + .slide-out-right.ng-leave, .slide-out-right > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutRight; } + +.slide-out-right-add { + -webkit-transform: translate3d(0, 0, 0); + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; } + +.slide-out-right-add-active { + -webkit-animation-name: slideOutRight; } + @-webkit-keyframes slideInUp { 0% { -webkit-transform: translate3d(0, 100%, 0); diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js index 8a381ac94e..b9af873332 100644 --- a/dist/js/ionic-angular.js +++ b/dist/js/ionic-angular.js @@ -23597,7 +23597,7 @@ angular.module('ionic.ui.content', []) .directive('pane', function() { return { restrict: 'E', - compile: function(element, attr) { + link: function(scope, element, attr) { element.addClass('pane'); } } @@ -23877,28 +23877,78 @@ angular.module('ionic.ui.loading', []) (function() { 'use strict'; +/** + * @description + * The NavController is a navigation stack View Controller modelled off of + * UINavigationController from Cocoa Touch. With the Nav Controller, you can + * "push" new "pages" on to the navigation stack, and then pop them off to go + * back. The NavController controls a navigation bar with a back button and title + * which updates as the pages switch. + * + * The NavController makes sure to not recycle scopes of old pages + * so that a pop will still show the same state that the user left. + * + * However, once a page is popped, its scope is destroyed and will have to be + * recreated then next time it is pushed. + * + */ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.gesture', 'ionic.service.platform', 'ngAnimate']) -.controller('NavCtrl', ['$scope', '$element', '$animate', '$compile', 'TemplateLoader', 'Platform', function($scope, $element, $animate, $compile, TemplateLoader, Platform) { +.controller('NavCtrl', ['$scope', '$element', '$animate', '$compile', '$timeout', 'TemplateLoader', 'Platform', function($scope, $element, $animate, $compile, $timeout, TemplateLoader, Platform) { var _this = this; angular.extend(this, ionic.controllers.NavController.prototype); + var pushInAnimation = $scope.pushInAnimation || 'slide-in-left'; + var pushOutAnimation = $scope.pushOutAnimation || 'slide-out-left'; + var popInAnimation = $scope.popInAnimation || 'slide-in-right'; + var popOutAnimation = $scope.popOutAnimation || 'slide-out-right'; + + // Remove some push classnames + var cleanElementAnimations = function(el) { + el.removeClass(pushInAnimation); + el.removeClass(pushOutAnimation); + el.removeClass(popInAnimation); + el.removeClass(popOutAnimation); + } + /** * Push a template onto the navigation stack. * @param {string} templateUrl the URL of the template to load. */ this.pushFromTemplate = ionic.throttle(function(templateUrl) { var childScope = $scope.$new(); - childScope.isVisible = true; + var last = _this.getTopController(); // Load the given template TemplateLoader.load(templateUrl).then(function(templateString) { - // Compile the template with the new scrope, and append it to the navigation's content area + // Compile the template with the new scope, and append + // it to the navigation's content area var el = $compile(templateString)(childScope, function(cloned, scope) { - var content = angular.element($element[0].querySelector('.content, .scroll')); - $animate.enter(cloned, angular.element(content)); + + // If there was a last controller, remove it and mark the new + // one to animate + if(last) { + // Push animate + cleanElementAnimations(last.element); + $animate.addClass(last.element, pushOutAnimation, function() { + last.element[0].style.display = 'none'; + last.element.removeClass(pushOutAnimation); + }); + + } + + + if(last) { + // We will need to animate in the new page since we have an old page + cloned.addClass(pushInAnimation); + $animate.addClass(cloned, pushInAnimation); + } + + $animate.enter(cloned, $element, null, function() { + }); + }); }); }, 300, { @@ -23907,13 +23957,34 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges // Pop function, throttled this.popController = ionic.throttle(function() { - _this.pop(); - $scope.$broadcast('navs.pop'); + var last = _this.pop(); + + var next = _this.getTopController(); + + if(last) { + + cleanElementAnimations(last.element); + $animate.addClass(last.element, popOutAnimation, function() { + last.scope.$destroy(); + last.element.remove(); + }); + } + + // Animate the next one in + if(next) { + cleanElementAnimations(next.element); + $animate.addClass(next.element, popInAnimation) + next.element[0].style.display = 'block'; + } + + $scope.$parent.$broadcast('navigation.pop'); }, 300, { trailing: false }); + // Extend the low-level navigation controller + // for this angular controller ionic.controllers.NavController.call(this, { content: { }, @@ -23955,8 +24026,19 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges * nav-content directive when it is linked to a scope on the page. */ $scope.pushController = function(scope, element) { - _this.push(scope); - $scope.$broadcast('navs.push', scope); + _this.push({ + scope: scope, + element: element + }); + $scope.$parent.$broadcast('navigation.push', scope); + }; + + this.pushController = function(scope, element) { + _this.push({ + scope: scope, + element: element + }); + $scope.$parent.$broadcast('navigation.push', scope); }; $scope.navController = this; @@ -23967,7 +24049,10 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges }); }]) -.directive('navs', function() { +/** + * The main directive for the controller. + */ +.directive('navigation', function() { return { restrict: 'E', replace: true, @@ -23975,26 +24060,54 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges controller: 'NavCtrl', //templateUrl: 'ext/angular/tmpl/ionicTabBar.tmpl.html', template: '
', + scope: { + first: '@', + pushAnimation: '@', + popAnimation: '@' + }, + link: function($scope, $element, $attr, navCtrl) { + $scope.pushAnimation = $scope.pushAnimation || 'slide-in-left'; + $scope.popAnimation = $scope.popAnimation || 'slide-out-left'; + + if($scope.first) { + navCtrl.pushFromTemplate($scope.first); + } + } }; }) +/** + * Our Nav Bar directive which updates as the controller state changes. + */ .directive('navBar', function() { return { restrict: 'E', - require: '^navs', + require: '^navigation', replace: true, scope: { type: '@', backButtonType: '@', + backButtonLabel: '@', + backButtonIcon: '@', alignTitle: '@' }, template: '', link: function($scope, $element, $attr, navCtrl) { var backButton; + $scope.backButtonContent = ''; + + if($scope.backButtonIcon) { + $scope.backButtonContent += ''; + } + if($scope.backButtonLabel) { + $scope.backButtonContent += ' ' + $scope.backButtonLabel + } + + $scope.navController = navCtrl; $scope.goBack = function() { @@ -24011,12 +24124,12 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges $scope.headerBarView = hb; - $scope.$parent.$on('navs.push', function() { + $scope.$parent.$on('navigation.push', function() { backButton = angular.element($element[0].querySelector('.button')); backButton.addClass($scope.backButtonType); hb.align(); }); - $scope.$parent.$on('navs.pop', function() { + $scope.$parent.$on('navigation.pop', function() { hb.align(); }); @@ -24027,141 +24140,79 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges }; }) -.directive('navContent', ['Gesture', '$animate', '$compile', function(Gesture, $animate, $compile) { - - // We need to animate the new controller into view. - var animatePushedController = function(childScope, clone, $element, isForward) { - var parent = angular.element($element.parent().parent().parent()); - - var title = angular.element(parent[0].querySelector('.title')); - - // Clone the old title and insert it so we can animate it back into place for the new controller - var newTitle = angular.element(title.clone()); - $compile(newTitle)(childScope); - title.after(newTitle); - // Grab the button so we can slide it in - var button = angular.element(parent[0].querySelector('.button')); - - if(isForward) { - - // Slide the button in - $animate.addClass(button, childScope.slideButtonInAnimation, function() { - $animate.removeClass(button, childScope.slideButtonInAnimation, function() {}); - }) - - // Slide the new title in - $animate.addClass(newTitle, childScope.slideTitleInAnimation, function() { - $animate.removeClass(newTitle, childScope.slideTitleInAnimation, function() { - newTitle.scope().$destroy(); - newTitle.remove(); - }); - }); - - // Grab the old title and slide it out - var title = $element.parent().parent().parent()[0].querySelector('.title'); - $animate.addClass(angular.element(title), childScope.slideTitleOutAnimation, function() { - $animate.removeClass(angular.element(title), childScope.slideTitleOutAnimation, function() { - }); - }); - } else { - clone.addClass(childScope.slideBackInAnimation); - } - }; +.directive('navPage', ['Gesture', '$animate', '$compile', function(Gesture, $animate, $compile) { return { - restrict: 'ECA', - require: '^navs', - transclude: 'element', - compile: function(element, attr, transclude) { - return function($scope, $element, $attr, navCtrl) { - var lastParent, lastIndex, childScope, childElement; + restrict: 'AC', + require: '^navigation', + link: function($scope, $element, $attr, navCtrl) { + var lastParent, lastIndex, childScope, childElement; - // Store that we should go forwards on the animation. This toggles - // based on the visibility sequence (to support reverse transitions) - var lastDirection = null; + // Store that we should go forwards on the animation. This toggles + // based on the visibility sequence (to support reverse transitions) + var lastDirection = null; - $scope.title = $attr.title; - $scope.pushAnimation = $attr.pushAnimation || 'slide-in-left'; - $scope.popAnimation = $attr.popAnimation || 'slide-in-right'; - $scope.slideTitleInAnimation = $attr.slideTitleInAnimation || 'bar-title-in'; - $scope.slideTitleOutAnimation = $attr.slideTitleOutAnimation || 'bar-title-out'; - $scope.slideButtonInAnimation = $attr.slideButtonInAnimation || 'bar-button-in'; - $scope.slideButtonOutAnimation = $attr.slideButtonOutAnimation || 'bar-button-out'; + $scope.title = $attr.title; - if($attr.navBar === "false") { - navCtrl.hideNavBar(); - } else { - navCtrl.showNavBar(); - } - - $scope.visibilityChanged = function(direction) { - lastDirection = direction; - - if($scope.isVisible) { - $scope.$broadcast('navContent.shown'); - } else { - $scope.$broadcast('navContent.hidden'); - } - - if(!childElement) { - return; - } - - var clone = childElement; - - if(direction == 'push') { - clone.addClass(childScope.pushAnimation); - clone.removeClass(childScope.popAnimation); - } else if(direction == 'pop') { - clone.addClass(childScope.popAnimation); - clone.removeClass(childScope.pushAnimation); - } - }; - - // Push this controller onto the stack - $scope.pushController($scope, $element); - - $scope.$watch('isVisible', function(value) { - - if(value) { - childScope = $scope.$new(); - - transclude(childScope, function(clone) { - childElement = clone; - - if(lastDirection == 'push') { - clone.addClass(childScope.pushAnimation); - } else if(lastDirection == 'pop') { - clone.addClass(childScope.popAnimation); - } - - $animate.enter(clone, $element.parent(), $element, function() { - clone.removeClass(childScope.pushAnimation); - clone.removeClass(childScope.popAnimation); - }); - }); - } else { - // Taken from ngIf - if(childElement) { - // Check if this is visible, and if so, create it and show it - $animate.leave(childElement, function() { - if(childScope) { - childElement.removeClass(childScope.pushAnimation); - childElement.removeClass(childScope.popAnimation); - } - }); - childElement = undefined; - } - if(childScope) { - childScope.$destroy(); - childScope = undefined; - } - } - }); + if($attr.navBar === "false") { + navCtrl.hideNavBar(); + } else { + navCtrl.showNavBar(); } + + $scope.$on('$destroy', function() { + if(childElement) { + childElement.remove(); + } + }); + + // Push this controller onto the stack + navCtrl.pushController($scope, $element); } - }; -}]); + } +}]) + +.directive('navPush', function() { + return { + restrict: 'A', + link: function($scope, $element, $attr) { + var templateUrl = $attr.navPush; + + var pushTemplate = function(e) { + $scope.$apply(function() { + $scope.navController && $scope.navController.pushFromTemplate(templateUrl); + }); + return false; + }; + + $element.bind('tap', pushTemplate); + + $scope.$on('$destroy', function() { + $element.unbind('tap', pushTemplate); + }); + } + } +}) + +.directive('navPop', function() { + return { + restrict: 'A', + link: function($scope, $element, $attr, navCtrl) { + var popTemplate = function(e) { + $scope.$apply(function() { + $scope.navController && navController.pop(); + }); + return false; + }; + + $element.bind('tap', popTemplate); + + $scope.$on('$destroy', function() { + $element.unbind('tap', popTemplate); + }); + } + } +}) })(); ; diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js index 074324dd83..1cc395a36d 100644 --- a/js/ext/angular/src/directive/ionicContent.js +++ b/js/ext/angular/src/directive/ionicContent.js @@ -10,7 +10,7 @@ angular.module('ionic.ui.content', []) .directive('pane', function() { return { restrict: 'E', - compile: function(element, attr) { + link: function(scope, element, attr) { element.addClass('pane'); } } diff --git a/js/ext/angular/src/directive/ionicNav.js b/js/ext/angular/src/directive/ionicNav.js index efab4025d7..503d3fe602 100644 --- a/js/ext/angular/src/directive/ionicNav.js +++ b/js/ext/angular/src/directive/ionicNav.js @@ -1,28 +1,78 @@ (function() { 'use strict'; +/** + * @description + * The NavController is a navigation stack View Controller modelled off of + * UINavigationController from Cocoa Touch. With the Nav Controller, you can + * "push" new "pages" on to the navigation stack, and then pop them off to go + * back. The NavController controls a navigation bar with a back button and title + * which updates as the pages switch. + * + * The NavController makes sure to not recycle scopes of old pages + * so that a pop will still show the same state that the user left. + * + * However, once a page is popped, its scope is destroyed and will have to be + * recreated then next time it is pushed. + * + */ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.gesture', 'ionic.service.platform', 'ngAnimate']) -.controller('NavCtrl', ['$scope', '$element', '$animate', '$compile', 'TemplateLoader', 'Platform', function($scope, $element, $animate, $compile, TemplateLoader, Platform) { +.controller('NavCtrl', ['$scope', '$element', '$animate', '$compile', '$timeout', 'TemplateLoader', 'Platform', function($scope, $element, $animate, $compile, $timeout, TemplateLoader, Platform) { var _this = this; angular.extend(this, ionic.controllers.NavController.prototype); + var pushInAnimation = $scope.pushInAnimation || 'slide-in-left'; + var pushOutAnimation = $scope.pushOutAnimation || 'slide-out-left'; + var popInAnimation = $scope.popInAnimation || 'slide-in-right'; + var popOutAnimation = $scope.popOutAnimation || 'slide-out-right'; + + // Remove some push classnames + var cleanElementAnimations = function(el) { + el.removeClass(pushInAnimation); + el.removeClass(pushOutAnimation); + el.removeClass(popInAnimation); + el.removeClass(popOutAnimation); + } + /** * Push a template onto the navigation stack. * @param {string} templateUrl the URL of the template to load. */ this.pushFromTemplate = ionic.throttle(function(templateUrl) { var childScope = $scope.$new(); - childScope.isVisible = true; + var last = _this.getTopController(); // Load the given template TemplateLoader.load(templateUrl).then(function(templateString) { - // Compile the template with the new scrope, and append it to the navigation's content area + // Compile the template with the new scope, and append + // it to the navigation's content area var el = $compile(templateString)(childScope, function(cloned, scope) { - var content = angular.element($element[0].querySelector('.content, .scroll')); - $animate.enter(cloned, angular.element(content)); + + // If there was a last controller, remove it and mark the new + // one to animate + if(last) { + // Push animate + cleanElementAnimations(last.element); + $animate.addClass(last.element, pushOutAnimation, function() { + last.element[0].style.display = 'none'; + last.element.removeClass(pushOutAnimation); + }); + + } + + + if(last) { + // We will need to animate in the new page since we have an old page + cloned.addClass(pushInAnimation); + $animate.addClass(cloned, pushInAnimation); + } + + $animate.enter(cloned, $element, null, function() { + }); + }); }); }, 300, { @@ -31,13 +81,34 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges // Pop function, throttled this.popController = ionic.throttle(function() { - _this.pop(); - $scope.$broadcast('navs.pop'); + var last = _this.pop(); + + var next = _this.getTopController(); + + if(last) { + + cleanElementAnimations(last.element); + $animate.addClass(last.element, popOutAnimation, function() { + last.scope.$destroy(); + last.element.remove(); + }); + } + + // Animate the next one in + if(next) { + cleanElementAnimations(next.element); + $animate.addClass(next.element, popInAnimation) + next.element[0].style.display = 'block'; + } + + $scope.$parent.$broadcast('navigation.pop'); }, 300, { trailing: false }); + // Extend the low-level navigation controller + // for this angular controller ionic.controllers.NavController.call(this, { content: { }, @@ -79,8 +150,19 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges * nav-content directive when it is linked to a scope on the page. */ $scope.pushController = function(scope, element) { - _this.push(scope); - $scope.$broadcast('navs.push', scope); + _this.push({ + scope: scope, + element: element + }); + $scope.$parent.$broadcast('navigation.push', scope); + }; + + this.pushController = function(scope, element) { + _this.push({ + scope: scope, + element: element + }); + $scope.$parent.$broadcast('navigation.push', scope); }; $scope.navController = this; @@ -91,7 +173,10 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges }); }]) -.directive('navs', function() { +/** + * The main directive for the controller. + */ +.directive('navigation', function() { return { restrict: 'E', replace: true, @@ -99,26 +184,54 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges controller: 'NavCtrl', //templateUrl: 'ext/angular/tmpl/ionicTabBar.tmpl.html', template: '
', + scope: { + first: '@', + pushAnimation: '@', + popAnimation: '@' + }, + link: function($scope, $element, $attr, navCtrl) { + $scope.pushAnimation = $scope.pushAnimation || 'slide-in-left'; + $scope.popAnimation = $scope.popAnimation || 'slide-out-left'; + + if($scope.first) { + navCtrl.pushFromTemplate($scope.first); + } + } }; }) +/** + * Our Nav Bar directive which updates as the controller state changes. + */ .directive('navBar', function() { return { restrict: 'E', - require: '^navs', + require: '^navigation', replace: true, scope: { type: '@', backButtonType: '@', + backButtonLabel: '@', + backButtonIcon: '@', alignTitle: '@' }, template: '', link: function($scope, $element, $attr, navCtrl) { var backButton; + $scope.backButtonContent = ''; + + if($scope.backButtonIcon) { + $scope.backButtonContent += ''; + } + if($scope.backButtonLabel) { + $scope.backButtonContent += ' ' + $scope.backButtonLabel + } + + $scope.navController = navCtrl; $scope.goBack = function() { @@ -135,12 +248,12 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges $scope.headerBarView = hb; - $scope.$parent.$on('navs.push', function() { + $scope.$parent.$on('navigation.push', function() { backButton = angular.element($element[0].querySelector('.button')); backButton.addClass($scope.backButtonType); hb.align(); }); - $scope.$parent.$on('navs.pop', function() { + $scope.$parent.$on('navigation.pop', function() { hb.align(); }); @@ -151,140 +264,78 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges }; }) -.directive('navContent', ['Gesture', '$animate', '$compile', function(Gesture, $animate, $compile) { - - // We need to animate the new controller into view. - var animatePushedController = function(childScope, clone, $element, isForward) { - var parent = angular.element($element.parent().parent().parent()); - - var title = angular.element(parent[0].querySelector('.title')); - - // Clone the old title and insert it so we can animate it back into place for the new controller - var newTitle = angular.element(title.clone()); - $compile(newTitle)(childScope); - title.after(newTitle); - // Grab the button so we can slide it in - var button = angular.element(parent[0].querySelector('.button')); - - if(isForward) { - - // Slide the button in - $animate.addClass(button, childScope.slideButtonInAnimation, function() { - $animate.removeClass(button, childScope.slideButtonInAnimation, function() {}); - }) - - // Slide the new title in - $animate.addClass(newTitle, childScope.slideTitleInAnimation, function() { - $animate.removeClass(newTitle, childScope.slideTitleInAnimation, function() { - newTitle.scope().$destroy(); - newTitle.remove(); - }); - }); - - // Grab the old title and slide it out - var title = $element.parent().parent().parent()[0].querySelector('.title'); - $animate.addClass(angular.element(title), childScope.slideTitleOutAnimation, function() { - $animate.removeClass(angular.element(title), childScope.slideTitleOutAnimation, function() { - }); - }); - } else { - clone.addClass(childScope.slideBackInAnimation); - } - }; +.directive('navPage', ['Gesture', '$animate', '$compile', function(Gesture, $animate, $compile) { return { - restrict: 'ECA', - require: '^navs', - transclude: 'element', - compile: function(element, attr, transclude) { - return function($scope, $element, $attr, navCtrl) { - var lastParent, lastIndex, childScope, childElement; + restrict: 'AC', + require: '^navigation', + link: function($scope, $element, $attr, navCtrl) { + var lastParent, lastIndex, childScope, childElement; - // Store that we should go forwards on the animation. This toggles - // based on the visibility sequence (to support reverse transitions) - var lastDirection = null; + // Store that we should go forwards on the animation. This toggles + // based on the visibility sequence (to support reverse transitions) + var lastDirection = null; - $scope.title = $attr.title; - $scope.pushAnimation = $attr.pushAnimation || 'slide-in-left'; - $scope.popAnimation = $attr.popAnimation || 'slide-in-right'; - $scope.slideTitleInAnimation = $attr.slideTitleInAnimation || 'bar-title-in'; - $scope.slideTitleOutAnimation = $attr.slideTitleOutAnimation || 'bar-title-out'; - $scope.slideButtonInAnimation = $attr.slideButtonInAnimation || 'bar-button-in'; - $scope.slideButtonOutAnimation = $attr.slideButtonOutAnimation || 'bar-button-out'; + $scope.title = $attr.title; - if($attr.navBar === "false") { - navCtrl.hideNavBar(); - } else { - navCtrl.showNavBar(); - } - - $scope.visibilityChanged = function(direction) { - lastDirection = direction; - - if($scope.isVisible) { - $scope.$broadcast('navContent.shown'); - } else { - $scope.$broadcast('navContent.hidden'); - } - - if(!childElement) { - return; - } - - var clone = childElement; - - if(direction == 'push') { - clone.addClass(childScope.pushAnimation); - clone.removeClass(childScope.popAnimation); - } else if(direction == 'pop') { - clone.addClass(childScope.popAnimation); - clone.removeClass(childScope.pushAnimation); - } - }; - - // Push this controller onto the stack - $scope.pushController($scope, $element); - - $scope.$watch('isVisible', function(value) { - - if(value) { - childScope = $scope.$new(); - - transclude(childScope, function(clone) { - childElement = clone; - - if(lastDirection == 'push') { - clone.addClass(childScope.pushAnimation); - } else if(lastDirection == 'pop') { - clone.addClass(childScope.popAnimation); - } - - $animate.enter(clone, $element.parent(), $element, function() { - clone.removeClass(childScope.pushAnimation); - clone.removeClass(childScope.popAnimation); - }); - }); - } else { - // Taken from ngIf - if(childElement) { - // Check if this is visible, and if so, create it and show it - $animate.leave(childElement, function() { - if(childScope) { - childElement.removeClass(childScope.pushAnimation); - childElement.removeClass(childScope.popAnimation); - } - }); - childElement = undefined; - } - if(childScope) { - childScope.$destroy(); - childScope = undefined; - } - } - }); + if($attr.navBar === "false") { + navCtrl.hideNavBar(); + } else { + navCtrl.showNavBar(); } + + $scope.$on('$destroy', function() { + if(childElement) { + childElement.remove(); + } + }); + + // Push this controller onto the stack + navCtrl.pushController($scope, $element); } - }; -}]); + } +}]) + +.directive('navPush', function() { + return { + restrict: 'A', + link: function($scope, $element, $attr) { + var templateUrl = $attr.navPush; + + var pushTemplate = function(e) { + $scope.$apply(function() { + $scope.navController && $scope.navController.pushFromTemplate(templateUrl); + }); + return false; + }; + + $element.bind('tap', pushTemplate); + + $scope.$on('$destroy', function() { + $element.unbind('tap', pushTemplate); + }); + } + } +}) + +.directive('navPop', function() { + return { + restrict: 'A', + link: function($scope, $element, $attr, navCtrl) { + var popTemplate = function(e) { + $scope.$apply(function() { + $scope.navController && navController.pop(); + }); + return false; + }; + + $element.bind('tap', popTemplate); + + $scope.$on('$destroy', function() { + $element.unbind('tap', popTemplate); + }); + } + } +}) })(); diff --git a/js/ext/angular/test/nav.html b/js/ext/angular/test/nav.html index 9289396f41..9a620fe50a 100644 --- a/js/ext/angular/test/nav.html +++ b/js/ext/angular/test/nav.html @@ -12,7 +12,6 @@ position: fixed; width: 100%; height: 100%; - background-color: black; } .slide-in-slide-out { /* @@ -28,22 +27,21 @@ - - + + - -
- - -
-
+ @@ -68,9 +66,6 @@ angular.module('navTest', ['ionic', 'ngAnimate']) - .controller('AppCtrl', function($scope, $compile, $element) { - $scope.navController.pushFromTemplate('page.html'); - }) .controller('CatsCtrl', function($scope, $compile, $element) { $scope.$on('navContent.shown', function() { diff --git a/js/ext/angular/test/navPush.html b/js/ext/angular/test/navPush.html new file mode 100644 index 0000000000..94f620e5c6 --- /dev/null +++ b/js/ext/angular/test/navPush.html @@ -0,0 +1,83 @@ + + + + Nav Bars + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scss/_animations.scss b/scss/_animations.scss index 07c97fb6de..77ff8b6ad3 100644 --- a/scss/_animations.scss +++ b/scss/_animations.scss @@ -41,6 +41,7 @@ } .slide-in-left { + -webkit-transform: translate3d(0%,0,0); &.ng-enter, > .ng-enter { -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; @@ -55,28 +56,80 @@ } } + +.slide-in-left-add { + -webkit-transform: translate3d(100%,0,0); + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; +} +.slide-in-left-add-active { + -webkit-animation-name: slideInLeft; +} + +.slide-out-left { + -webkit-transform: translate3d(-100%,0,0); + &.ng-enter, > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutLeft; + } + &.ng-leave, > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutLeft; + } +} + .slide-out-left { - &.ng-enter, > .ng-enter { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutLeft; - } - &.ng-leave, > .ng-leave { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideInLeft; - } +} + +.slide-out-left-add { + -webkit-transform: translate3d(0,0,0); + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; +} +.slide-out-left-add-active { + -webkit-animation-name: slideOutLeft; } .slide-in-right { + -webkit-transform: translate3d(0%,0,0); &.ng-enter, > .ng-enter { -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; -webkit-animation-timing-function: ease-in-out; -webkit-animation-name: slideInRight; } + &.ng-leave, > .ng-leave { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideInRight; + } +} + +.slide-in-right-add { + -webkit-transform: translate3d(-100%,0,0); + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; +} +.slide-in-right-add-active { + -webkit-animation-name: slideInRight; +} + +.slide-out-right { + -webkit-transform: translate3d(100%,0,0); + &.ng-enter, > .ng-enter { + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-name: slideOutRight; + } &.ng-leave, > .ng-leave { -webkit-animation-duration: 250ms; -webkit-animation-fill-mode: both; @@ -86,18 +139,15 @@ } .slide-out-right { - &.ng-enter, > .ng-enter { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideOutRight; - } - &.ng-leave, > .ng-leave { - -webkit-animation-duration: 250ms; - -webkit-animation-fill-mode: both; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-name: slideInRight; - } +} +.slide-out-right-add { + -webkit-transform: translate3d(0,0,0); + -webkit-animation-duration: 250ms; + -webkit-animation-fill-mode: both; + -webkit-animation-timing-function: ease-in-out; +} +.slide-out-right-add-active { + -webkit-animation-name: slideOutRight; } $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); diff --git a/scss/_nav.scss b/scss/_nav.scss index 3f023ec375..163c55814a 100644 --- a/scss/_nav.scss +++ b/scss/_nav.scss @@ -4,7 +4,7 @@ * -------------------------------------------------- */ - .nav-content { + .nav-page { position: absolute; width: 100%; height: 100%;