diff --git a/js/ext/angular/src/service/ionicModal.js b/js/ext/angular/src/service/ionicModal.js index 1cacb8689fb..f6e1c597a5f 100644 --- a/js/ext/angular/src/service/ionicModal.js +++ b/js/ext/angular/src/service/ionicModal.js @@ -10,63 +10,72 @@ angular.module('ionic.service.modal', ['ionic.service.templateLoad', 'ionic.serv // Show the modal show: function() { var self = this; - var element = angular.element(this.el); + var element = angular.element(self.el); document.body.classList.add('modal-open'); self._isShown = true; if(!element.parent().length) { - element.addClass(this.animation); - $animate.enter(element, angular.element($document[0].body), null, function() { - }); - ionic.views.Modal.prototype.show.call(self); - } else { - $animate.addClass(element, this.animation, function() { - }); + self.el.classList.add(self.animation); + $document[0].body.appendChild(self.el); } - if(!this.didInitEvents) { - var onHardwareBackButton = function() { - self.hide(); - }; + element.addClass('ng-enter active'); + element.removeClass('ng-leave ng-leave-active'); - self.scope.$on('$destroy', function() { - $ionicPlatform.offHardwareBackButton(onHardwareBackButton); - }); + $timeout(function(){ + element.addClass('ng-enter-active'); - // Support Android back button to close - $ionicPlatform.onHardwareBackButton(onHardwareBackButton); + if(!self.didInitEvents) { + var onHardwareBackButton = function() { + self.hide(); + }; - this.didInitEvents = true; - } + self.scope.$on('$destroy', function() { + $ionicPlatform.offHardwareBackButton(onHardwareBackButton); + }); + + // Support Android back button to close + $ionicPlatform.onHardwareBackButton(onHardwareBackButton); + + self.didInitEvents = true; + } - this.scope.$parent.$broadcast('modal.shown', this); + self.scope.$parent.$broadcast('modal.shown'); + }, 20); }, // Hide the modal hide: function() { this._isShown = false; var element = angular.element(this.el); - $animate.removeClass(element, this.animation, function() { - onHideModal(element[0]); - }); + + element.addClass('ng-leave'); + + $timeout(function(){ + element.addClass('ng-leave-active'); + element.removeClass('ng-enter ng-enter-active active'); + }, 20); + + $timeout(function(){ + document.body.classList.remove('modal-open'); + }, 400); ionic.views.Modal.prototype.hide.call(this); - this.scope.$parent.$broadcast('modal.hidden', this); + this.scope.$parent.$broadcast('modal.hidden'); }, // Remove and destroy the modal scope remove: function() { - var self = this, - element = angular.element(this.el); - this._isShown = false; - $animate.leave(angular.element(this.el), function() { - onHideModal(element[0]); - self.scope.$parent.$broadcast('modal.removed', self); + var self = this; + self.hide(); + self.scope.$parent.$broadcast('modal.removed'); + + $timeout(function(){ self.scope.$destroy(); - }); + }, 500); }, isShown: function() { @@ -74,10 +83,6 @@ angular.module('ionic.service.modal', ['ionic.service.templateLoad', 'ionic.serv } }); - function onHideModal(element) { - document.body.classList.remove('modal-open'); - } - var createModal = function(templateString, options) { // Create a new scope for the modal var scope = options.scope && options.scope.$new() || $rootScope.$new(true); @@ -116,6 +121,6 @@ angular.module('ionic.service.modal', ['ionic.service.templateLoad', 'ionic.serv cb ? cb(modal) : null; return modal; }); - }, + } }; }]); diff --git a/js/ext/angular/test/modal.html b/js/ext/angular/test/modal.html index 4752000d898..c8cd5fb659a 100644 --- a/js/ext/angular/test/modal.html +++ b/js/ext/angular/test/modal.html @@ -7,6 +7,7 @@ + @@ -23,7 +24,7 @@ diff --git a/js/ext/angular/test/service/ionicModal.unit.js b/js/ext/angular/test/service/ionicModal.unit.js index 22290a55dd1..c164719f3b8 100644 --- a/js/ext/angular/test/service/ionicModal.unit.js +++ b/js/ext/angular/test/service/ionicModal.unit.js @@ -53,25 +53,21 @@ describe('Ionic Modal', function() { expect(m.isShown()).toBe(false); }); - it('show & remove should add .model-open to body', inject(function($animate) { + it('show & remove should add .model-open to body', inject(function() { var m = modal.fromTemplate(''); m.show(); expect(angular.element(document.body).hasClass('modal-open')).toBe(true); - spyOn($animate, 'leave').andCallFake(function(el, cb) { - cb(); - }); m.remove(); + timeout.flush(); expect(angular.element(document.body).hasClass('modal-open')).toBe(false); })); - it('show & hide should add .model-open body', inject(function($animate) { + it('show & hide should add .model-open body', inject(function() { var m = modal.fromTemplate(''); m.show(); expect(angular.element(document.body).hasClass('modal-open')).toBe(true); - spyOn($animate, 'removeClass').andCallFake(function(el, cls, cb) { - cb(); - }); m.hide(); + timeout.flush(); expect(angular.element(document.body).hasClass('modal-open')).toBe(false); })); @@ -80,7 +76,7 @@ describe('Ionic Modal', function() { spyOn($animate, 'leave').andCallFake(function(el, cb) { cb(); }); spyOn(m.scope, '$destroy'); m.remove(); - expect($animate.leave).toHaveBeenCalled(); + timeout.flush(); expect(m.scope.$destroy).toHaveBeenCalled(); })); @@ -89,7 +85,7 @@ describe('Ionic Modal', function() { var modalInstance = modal.fromTemplate(template); modalInstance.show(); - //timeout.flush(); + timeout.flush(); expect(modalInstance.el.classList.contains('active')).toBe(true); @@ -97,6 +93,7 @@ describe('Ionic Modal', function() { target: document }); + timeout.flush(); expect(modalInstance.el.classList.contains('active')).toBe(false); }); @@ -105,14 +102,15 @@ describe('Ionic Modal', function() { var m = modal.fromTemplate(template, {}); spyOn(m.scope.$parent, '$broadcast'); m.show(); - expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.shown', m); + timeout.flush(); + expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.shown'); }); it('should broadcast "modal.hidden" on hide', function() { var template = ''; var m = modal.fromTemplate(template, {}); spyOn(m.scope.$parent, '$broadcast'); m.hide(); - expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.hidden', m); + expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.hidden'); }); it('should broadcast "modal.removed" on remove', inject(function($animate) { var template = ''; @@ -125,9 +123,9 @@ describe('Ionic Modal', function() { spyOn(m.scope.$parent, '$broadcast').andCallFake(function(e, modal) { broadcastedModal = modal; }); - spyOn($animate, 'leave').andCallFake(function(el, cb) { cb(); }); m.remove(); - expect(broadcastedModal).toBe(m); + expect(m.scope.$parent.$broadcast).toHaveBeenCalledWith('modal.removed'); + timeout.flush(); })); }); diff --git a/js/views/modalView.js b/js/views/modalView.js index 08a92b21502..4b3484e8df6 100644 --- a/js/views/modalView.js +++ b/js/views/modalView.js @@ -16,19 +16,15 @@ show: function() { var self = this; - this.el.classList.add('active'); - - if(this.focusFirstInput) { + if(self.focusFirstInput) { // Let any animations run first window.setTimeout(function() { var input = self.el.querySelector('input, textarea'); input && input.focus && input.focus(); - }, this.focusFirstDelay); + }, self.focusFirstDelay); } }, hide: function() { - this.el.classList.remove('active'); - // Unfocus all elements if(this.unfocusOnHide) { var inputs = this.el.querySelectorAll('input, textarea'); diff --git a/scss/_animations.scss b/scss/_animations.scss index 6716bc6b4cc..dba15bc65e2 100644 --- a/scss/_animations.scss +++ b/scss/_animations.scss @@ -17,7 +17,7 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); @-webkit-keyframes slideInUp { 0% { @include translate3d(0, 100%, 0); - opacity: 0; + opacity: 1; } 100% { @include translate3d(0, 0, 0); @@ -27,7 +27,7 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); @-moz-keyframes slideInUp { 0% { @include translate3d(0, 100%, 0); - opacity: 0; + opacity: 1; } 100% { @include translate3d(0, 0, 0); @@ -41,7 +41,7 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); } 100% { @include translate3d(0, 100%, 0); - opacity: 0; + opacity: 1; } } @-moz-keyframes slideOutUp { @@ -51,7 +51,7 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); } 100% { @include translate3d(0, 100%, 0); - opacity: 0; + opacity: 1; } } @@ -160,14 +160,14 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); // Spin // ------------------------------- -@-webkit-keyframes spin { - 100% { -webkit-transform: rotate(360deg); } +@-webkit-keyframes spin { + 100% { -webkit-transform: rotate(360deg); } } -@-moz-keyframes spin { - 100% { -moz-transform: rotate(360deg); } +@-moz-keyframes spin { + 100% { -moz-transform: rotate(360deg); } } -@keyframes spin { - 100% { transform: rotate(360deg); } +@keyframes spin { + 100% { transform: rotate(360deg); } } .no-animation { @@ -201,7 +201,7 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); */ .slide-left-right { - > .ng-enter, &.ng-enter, + > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave { @include transition(all ease-in-out $transition-duration); position: absolute; @@ -249,14 +249,14 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1); /** - * iOS7 style slide left to right + * iOS7 style slide left to right * -------------------------------------------------- */ $ios7-timing-function: ease-in-out; $ios7-transition-duration: 250ms; .slide-left-right-ios7 { - > .ng-enter, &.ng-enter, + > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave { @include transition(all $ios7-timing-function $ios7-transition-duration); position: absolute; @@ -280,7 +280,7 @@ $ios7-transition-duration: 250ms; /* OLD content ACTIVELY sliding OUT to the LEFT */ @include translate3d(-15%, 0, 0); } - + &.reverse { > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave { @include transition(all $ios7-timing-function $ios7-transition-duration); @@ -420,55 +420,30 @@ $ios7-transition-duration: 250ms; @include animation-name(slideOutToRight); } -.slide-in-up { - // Start it down low - @include translate3d(0, 0, 0); - opacity: 1; - - &.ng-enter, .ng-enter { - // Start it down low - @include translate3d(0, 100%, 0); - - @include animation-duration(400ms); - @include animation-timing-function($slide-in-up-function); - @include animation-fill-mode(both); - // Start hidden - opacity: 0; - } - - &.ng-enter-active, .ng-enter-active { - @include animation-name(slideInUp); - } - &.ng-leave, .ng-leave { - @include animation-duration(400ms); - @include animation-timing-function($slide-in-up-function); - @include animation-fill-mode(both); - } +/** + * Slide up from the bottom, used for modals + * -------------------------------------------------- + */ - &.ng-leave-active, .ng-leave { - @include animation-name(slideOutUp); - } +.slide-in-up { + @include translate3d(0, 100%, 0); } - -.slide-in-up-add { - @include animation-duration(400ms); - @include animation-timing-function($slide-in-up-function); - @include animation-fill-mode(forwards); +.slide-in-up.ng-enter, +.slide-in-up .ng-enter { + @include transition(all $slide-in-up-function 400ms); } -.slide-in-up-add-active { - @include animation-name(slideInUp); +.slide-in-up.ng-enter-active, +.slide-in-up .ng-enter-active { + @include translate3d(0, 0, 0); } -.slide-in-up-remove { - @include animation-duration(400ms); - @include animation-timing-function($slide-in-up-function); - @include animation-fill-mode(forwards); -} -.slide-in-up-remove-active { - @include animation-name(slideOutUp); +.slide-in-up.ng-leave, +.slide-in-up .ng-leave { + @include transition(all ease-in-out 250ms); } + .fade-in { @include animation(fadeOut 0.3s); &.active { @@ -492,7 +467,7 @@ $ios7-transition-duration: 250ms; */ $nav-title-slide-ios7-delay: 250ms; .nav-title-slide-ios7 { - > .ng-enter, &.ng-enter, + > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave { @include transition(all $nav-title-slide-ios7-delay); @include transition-timing-function($ios7-timing-function); @@ -512,7 +487,7 @@ $nav-title-slide-ios7-delay: 250ms; } &.reverse { - > .ng-enter, &.ng-enter, + > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave { @include transition(all ease-in-out $transition-duration); opacity: 1; diff --git a/scss/_modal.scss b/scss/_modal.scss index c287f75ac14..7dce60a0cc9 100644 --- a/scss/_modal.scss +++ b/scss/_modal.scss @@ -6,9 +6,8 @@ */ .modal { - @include translate3d(0, 100%, 0); - position: fixed; - top: 0; + position: absolute; + top: 0; z-index: $z-index-modal; overflow: hidden; @@ -16,7 +15,6 @@ width: 100%; background-color: $modal-bg-color; - opacity: 0; // Active modal &.active { @@ -30,4 +28,4 @@ .modal { pointer-events: auto; } -} \ No newline at end of file +}