Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

refactor(modal): use $animate for animation #1772

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion misc/demo/assets/app.js
Original file line number Diff line number Diff line change
@@ -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){
Expand Down
1 change: 1 addition & 0 deletions misc/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<script src="http://cdnjs.cloudflare.com/ajax/libs/fastclick/0.6.7/fastclick.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/<%= ngversion %>/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/<%= ngversion %>/angular-touch.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/<%= ngversion %>/angular-animate.min.js"></script>
<script src="ui-bootstrap-tpls-<%= pkg.version%>.min.js"></script>
<script src="assets/plunker.js"></script>
<script src="assets/app.js"></script>
Expand Down
133 changes: 72 additions & 61 deletions src/modal/modal.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -57,43 +57,28 @@ 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,
templateUrl: 'template/modal/window.html',
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)) {
Expand All @@ -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';

Expand Down Expand Up @@ -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();
Expand All @@ -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;
});
Expand All @@ -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;

Expand Down Expand Up @@ -222,18 +175,19 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
backdropScope = $rootScope.$new(true);
backdropScope.index = currBackdropIndex;
backdropDomEl = $compile('<div modal-backdrop></div>')(backdropScope);
body.append(backdropDomEl);
modalAnimator.enter(backdropDomEl, body);
}

var angularDomEl = angular.element('<div modal-window></div>');
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);
};

Expand Down Expand Up @@ -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;
}]);

}])

;
11 changes: 0 additions & 11 deletions src/modal/test/modal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand Down Expand Up @@ -130,7 +122,6 @@ describe('$modal', function () {

expect($document).toHaveModalsOpen(0);

waitForBackdropAnimation();
expect($document).not.toHaveBackdrop();
});

Expand All @@ -147,7 +138,6 @@ describe('$modal', function () {

expect($document).toHaveModalsOpen(0);

waitForBackdropAnimation();
expect($document).not.toHaveBackdrop();
});

Expand Down Expand Up @@ -411,7 +401,6 @@ describe('$modal', function () {
expect(backdropEl).toHaveClass('in');

dismiss(modal);
waitForBackdropAnimation();

modal = open({ template: '<div>With backdrop</div>' });
backdropEl = $document.find('body > div.modal-backdrop');
Expand Down
1 change: 0 additions & 1 deletion template/modal/backdrop.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<div class="modal-backdrop fade"
ng-class="{in: animate}"
ng-style="{'z-index': 1040 + (index && 1 || 0) + index*10}"
></div>
2 changes: 1 addition & 1 deletion template/modal/window.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div tabindex="-1" class="modal fade {{ windowClass }}" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
<div tabindex="-1" class="modal fade {{ windowClass }}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
<div class="modal-dialog"><div class="modal-content" ng-transclude></div></div>
</div>