From 49a44cb208f05befbde0c7af04ef3b452cd8a6ac Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Sat, 21 Dec 2013 15:34:01 -0800 Subject: [PATCH 1/2] chore(demo): update demo to include animations --- misc/demo/assets/app.js | 2 +- misc/demo/index.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/demo/assets/app.js b/misc/demo/assets/app.js index 93101bdf4e..84fa7cc4d3 100644 --- a/misc/demo/assets/app.js +++ b/misc/demo/assets/app.js @@ -1,4 +1,4 @@ -angular.module('bootstrapDemoApp', ['ui.bootstrap', 'plunker', 'ngTouch'], function($httpProvider){ +angular.module('bootstrapDemoApp', ['ui.bootstrap', 'plunker', 'ngTouch', 'ngAnimate'], function($httpProvider){ FastClick.attach(document.body); delete $httpProvider.defaults.headers.common['X-Requested-With']; }).run(['$location', function($location){ diff --git a/misc/demo/index.html b/misc/demo/index.html index a131e28b9c..04bafc192c 100644 --- a/misc/demo/index.html +++ b/misc/demo/index.html @@ -11,6 +11,7 @@ + From eea1ae8bb321495c47a57859c2878aea52c31acf Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Mon, 10 Feb 2014 00:49:31 -0800 Subject: [PATCH 2/2] refactor(modal): use $animate for animations --- src/modal/modal.js | 133 +++++++++++++++++++---------------- src/modal/test/modal.spec.js | 11 --- template/modal/backdrop.html | 1 - template/modal/window.html | 2 +- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/modal/modal.js b/src/modal/modal.js index 3b30469a29..ac4a46a378 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -1,4 +1,4 @@ -angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) +angular.module('ui.bootstrap.modal', []) /** * A helper, internal data structure that acts as a map but also allows getting / removing @@ -57,29 +57,21 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) /** * A helper directive for the $modal service. It creates a backdrop element. */ - .directive('modalBackdrop', ['$timeout', function ($timeout) { + .directive('modalBackdrop', [function () { return { restrict: 'EA', replace: true, templateUrl: 'template/modal/backdrop.html', link: function (scope) { - - scope.animate = false; - - //trigger CSS transitions - $timeout(function () { - scope.animate = true; - }); } }; }]) - .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) { + .directive('modalWindow', ['$modalStack', function ($modalStack) { return { restrict: 'EA', scope: { - index: '@', - animate: '=' + index: '@' }, replace: true, transclude: true, @@ -87,13 +79,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) link: function (scope, element, attrs) { scope.windowClass = attrs.windowClass || ''; - $timeout(function () { - // trigger CSS transitions - scope.animate = true; - // focus a freshly-opened modal - element[0].focus(); - }); - scope.close = function (evt) { var modal = $modalStack.getTop(); if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) { @@ -106,8 +91,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) }; }]) - .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap', - function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) { + .factory('$modalStack', ['modalAnimator', '$document', '$compile', '$rootScope', '$$stackedMap', + function (modalAnimator, $document, $compile, $rootScope, $$stackedMap) { var OPENED_MODAL_CLASS = 'modal-open'; @@ -140,8 +125,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) //clean up the stack openedWindows.remove(modalInstance); - //remove window DOM element - removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() { + modalAnimator.leave(modalWindow.modalDomEl, function () { modalWindow.modalScope.$destroy(); body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0); checkRemoveBackdrop(); @@ -151,8 +135,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) function checkRemoveBackdrop() { //remove backdrop if no longer needed if (backdropDomEl && backdropIndex() == -1) { - var backdropScopeRef = backdropScope; - removeAfterAnimate(backdropDomEl, backdropScope, 150, function () { + var backdropScopeRef = backdropScope, + backdropDomElRef = backdropDomEl; + modalAnimator.leave(backdropDomEl, function () { backdropScopeRef.$destroy(); backdropScopeRef = null; }); @@ -161,38 +146,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) } } - function removeAfterAnimate(domEl, scope, emulateTime, done) { - // Closing animation - scope.animate = false; - - var transitionEndEventName = $transition.transitionEndEventName; - if (transitionEndEventName) { - // transition out - var timeout = $timeout(afterAnimating, emulateTime); - - domEl.bind(transitionEndEventName, function () { - $timeout.cancel(timeout); - afterAnimating(); - scope.$apply(); - }); - } else { - // Ensure this call is async - $timeout(afterAnimating, 0); - } - - function afterAnimating() { - if (afterAnimating.done) { - return; - } - afterAnimating.done = true; - - domEl.remove(); - if (done) { - done(); - } - } - } - $document.bind('keydown', function (evt) { var modal; @@ -222,18 +175,19 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) backdropScope = $rootScope.$new(true); backdropScope.index = currBackdropIndex; backdropDomEl = $compile('
')(backdropScope); - body.append(backdropDomEl); + modalAnimator.enter(backdropDomEl, body); } var angularDomEl = angular.element('
'); angularDomEl.attr('window-class', modal.windowClass); angularDomEl.attr('index', openedWindows.length() - 1); - angularDomEl.attr('animate', 'animate'); angularDomEl.html(modal.content); var modalDomEl = $compile(angularDomEl)(modal.scope); openedWindows.top().value.modalDomEl = modalDomEl; - body.append(modalDomEl); + modalAnimator.enter(modalDomEl, body, function () { + modalDomEl[0].focus(); + }); body.addClass(OPENED_MODAL_CLASS); }; @@ -374,4 +328,61 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) }; return $modalProvider; - }); + }) + + .factory('modalAnimator', [ + '$animate', + function ($animate) { + + var modalAnimator = { + afterEnter: nullAnimation, + beforeLeave: nullAnimation, + + enter: function (element, parent, done) { + $animate.enter(element, parent, null, function () { + modalAnimator.afterEnter(element, done || angular.noop); + }); + }, + leave: function (element, done) { + modalAnimator.beforeLeave(element, function () { + // Note calling .leave() doesn't work as expected as AngularJS attempts to + // to animate the leaving classes. + element.remove(); + (done || angular.noop)(); + }); + } + }; + + function nullAnimation(element, done) { + done(); + } + + return modalAnimator; + + }]) + + .config(['$provide', function ($provide) { + + $provide.decorator('modalAnimator', [ + '$animate', '$delegate', + function ($animate , $delegate) { + + $delegate.visibleClass = 'in'; + + $delegate.afterEnter = function (element, done) { + element.addClass($delegate.visibleClass); + done(); + }; + + $delegate.beforeLeave = function (element, done) { + $animate.removeClass(element, $delegate.visibleClass, function () { + done(); + }); + }; + + return $delegate; + }]); + + }]) + + ; diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 6c6259a15b..c0f7f6d69a 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -8,14 +8,6 @@ describe('$modal', function () { element.trigger(e); }; - var waitForBackdropAnimation = function () { - inject(function ($transition) { - if ($transition.transitionEndEventName) { - $timeout.flush(); - } - }); - }; - beforeEach(module('ui.bootstrap.modal')); beforeEach(module('template/modal/backdrop.html')); beforeEach(module('template/modal/window.html')); @@ -130,7 +122,6 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(0); - waitForBackdropAnimation(); expect($document).not.toHaveBackdrop(); }); @@ -147,7 +138,6 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(0); - waitForBackdropAnimation(); expect($document).not.toHaveBackdrop(); }); @@ -411,7 +401,6 @@ describe('$modal', function () { expect(backdropEl).toHaveClass('in'); dismiss(modal); - waitForBackdropAnimation(); modal = open({ template: '
With backdrop
' }); backdropEl = $document.find('body > div.modal-backdrop'); diff --git a/template/modal/backdrop.html b/template/modal/backdrop.html index 2e6fbec9ee..90a6d7e433 100644 --- a/template/modal/backdrop.html +++ b/template/modal/backdrop.html @@ -1,4 +1,3 @@ diff --git a/template/modal/window.html b/template/modal/window.html index 5c3b1aa27b..3e639a1d1b 100644 --- a/template/modal/window.html +++ b/template/modal/window.html @@ -1,3 +1,3 @@ -