diff --git a/config/build.config.js b/config/build.config.js
index 800d8a48ec..6db81e153f 100644
--- a/config/build.config.js
+++ b/config/build.config.js
@@ -51,7 +51,6 @@ module.exports = {
'js/views/loadingView.js',
'js/views/modalView.js',
'js/views/navBarView.js',
- 'js/views/popupView.js',
'js/views/sideMenuView.js',
'js/views/sliderView.js',
'js/views/tabBarView.js',
diff --git a/js/ext/angular/src/directive/ionicPopup.js b/js/ext/angular/src/directive/ionicPopup.js
new file mode 100644
index 0000000000..b1902e6ecb
--- /dev/null
+++ b/js/ext/angular/src/directive/ionicPopup.js
@@ -0,0 +1,63 @@
+(function() {
+'use strict';
+
+angular.module('ionic.ui.popup', [])
+
+/**
+ * @private
+ */
+.directive('ionPopupBackdrop', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ template: '
'
+ }
+})
+
+/**
+ * @private
+ */
+.directive('ionPopup', ['$ionicBind', function($ionicBind) {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ scope: true,
+ link: function($scope, $element, $attr) {
+ $ionicBind($scope, $attr, {
+ title: '@',
+ buttons: '=',
+ $onButtonTap: '&onButtonTap',
+ $onClose: '&onClose'
+ });
+
+ $scope._buttonTapped = function(button, event) {
+ var result = button.onTap && button.onTap(event);
+
+ // A way to return false
+ if(event.defaultPrevented) {
+ return $scope.$onClose({button: button, result: false, event: event });
+ }
+
+ // Truthy test to see if we should close the window
+ if(result) {
+ return $scope.$onClose({button: button, result: result, event: event });
+ }
+ $scope.$onButtonTap({button: button, event: event});
+ }
+ },
+ template: ''
+ };
+}]);
+
+})();
diff --git a/js/ext/angular/src/ionicAngular.js b/js/ext/angular/src/ionicAngular.js
index 78ccc4a46a..556d48f9f1 100644
--- a/js/ext/angular/src/ionicAngular.js
+++ b/js/ext/angular/src/ionicAngular.js
@@ -42,7 +42,8 @@ angular.module('ionic.ui', [
'ionic.ui.checkbox',
'ionic.ui.toggle',
'ionic.ui.radio',
- 'ionic.ui.touch'
+ 'ionic.ui.touch',
+ 'ionic.ui.popup'
]);
diff --git a/js/ext/angular/src/service/ionicPopup.js b/js/ext/angular/src/service/ionicPopup.js
index 1139f77319..6827d4ffce 100644
--- a/js/ext/angular/src/service/ionicPopup.js
+++ b/js/ext/angular/src/service/ionicPopup.js
@@ -1,57 +1,529 @@
+(function(ionic) {
+'use strict';
+
angular.module('ionic.service.popup', ['ionic.service.templateLoad'])
+/**
+ * @ngdoc service
+ * @name $ionicPopup
+ * @module ionic
+ * @restrict E
+ * @codepen jsHjf
+ * @description
+ *
+ * The Ionic Popup service makes it easy to programatically create and show popup
+ * windows that require the user to respond in order to continue:
+ *
+ * 
+ *
+ * The popup system has support for nicer versions of the built in `alert()` `prompt()` and `confirm()` functions
+ * you are used to in the browser, but with more powerful support for customizing input types in the case of
+ * prompt, or customizing the look of the window.
+ *
+ * But the true power of the Popup is when a built-in popup just won't cut it. Luckily, the popup window
+ * has full support for arbitrary popup content, and a simple promise-based system for returning data
+ * entered by the user.
+ *
+ * @usage
+ * To trigger a Popup in your code, use the $ionicPopup service in your angular controllers:
+ *
+ * ```js
+ * angular.module('mySuperApp', ['ionic'])
+ * .controller(function($scope, $ionicPopup) {
+ *
+ * // Triggered on a button click, or some other target
+ $scope.showPopup = function() {
+ $scope.data = {}
-.factory('$ionicPopup', ['$rootScope', '$document', '$compile', 'TemplateLoader', function($rootScope, $document, $compile, TemplateLoader) {
+ // An elaborate, custom popup
+ $ionicPopup.show({
+ templateUrl: 'popup-template.html',
+ title: 'Enter Wi-Fi Password',
+ subTitle: 'Please use normal things',
+ scope: $scope,
+ buttons: [
+ { text: 'Cancel', onTap: function(e) { return true; } },
+ {
+ text: 'Save',
+ type: 'button-positive',
+ onTap: function(e) {
+ return $scope.data.wifi;
+ }
+ },
+ ]
+ }).then(function(res) {
+ console.log('Tapped!', res);
+ }, function(err) {
+ console.log('Err:', err);
+ }, function(msg) {
+ console.log('message:', msg);
+ });
- var getPopup = function() {
- // Make sure there is only one loading element on the page at one point in time
- var existing = angular.element($document[0].querySelector('.popup'));
- if(existing.length) {
- var scope = existing.scope();
- if(scope.popup) {
- return scope;
- }
- }
- };
-
- return {
- alert: function(message, $scope) {
-
- // If there is an existing popup, just show that one
- var existing = getPopup();
- if(existing) {
- return existing.popup.alert(message);
- }
-
- var defaults = {
- title: message,
- animation: 'fade-in',
+ // A confirm dialog
+ $scope.showConfirm = function() {
+ $ionicPopup.confirm({
+ title: 'Consume Ice Cream',
+ content: 'Are you sure you want to eat this ice cream?'
+ }).then(function(res) {
+ if(res) {
+ console.log('You are sure');
+ } else {
+ console.log('You are not sure');
+ }
+ });
};
- opts = angular.extend(defaults, opts);
+ // A prompt dialog
+ $scope.showPrompt = function() {
+ $ionicPopup.prompt({
+ title: 'ID Check',
+ content: 'What is your name?'
+ }).then(function(res) {
+ console.log('Your name is', res);
+ });
+ };
- var scope = $scope && $scope.$new() || $rootScope.$new(true);
- angular.extend(scope, opts);
+ // A prompt with password input dialog
+ $scope.showPasswordPrompt = function() {
+ $ionicPopup.prompt({
+ title: 'Password Check',
+ content: 'Enter your secret password',
+ inputType: 'password',
+ inputPlaceholder: 'Your password'
+ }).then(function(res) {
+ console.log('Your password is', res);
+ });
+ };
- // Compile the template
- var element = $compile('' + opts.content + '')(scope);
- $document[0].body.appendChild(element[0]);
+ // An alert dialog
+ $scope.showAlert = function() {
+ $ionicPopup.alert({
+ title: 'Don\'t eat that!',
+ content: 'It might taste good'
+ }).then(function(res) {
+ console.log('Thank you for not eating my delicious ice cream cone');
+ });
+ };
+ };
+ });
+ ```
- var popup = new ionic.views.Popup({el: element[0] });
- popup.alert(message);
+
+ */
+.factory('$ionicPopup', ['$rootScope', '$q', '$document', '$compile', '$timeout', '$ionicTemplateLoader',
+ function($rootScope, $q, $document, $compile, $timeout, $ionicTemplateLoader) {
- scope.popup = popup;
+ // TODO: Make this configurable
+ var popupOptions = {
+ // How long to wait after a popup is already shown to show another one
+ stackPushDelay: 50
+ }
- return popup;
- },
- confirm: function(cb) {
- },
- prompt: function(cb) {
- },
- show: function(data) {
- // data.title
- // data.template
- // data.buttons
+ // Center the given popup
+ var positionPopup = function(popup) {
+ popup.el.style.marginLeft = (-popup.el.offsetWidth) / 2 + 'px';
+ popup.el.style.marginTop = (-popup.el.offsetHeight) / 2 + 'px';
+ };
+
+ // Hide the body of the given popup if it's empty
+ var hideBody = function(popup) {
+ var bodyEl = popup.el.querySelector('.popup-body');
+ if(bodyEl && bodyEl.innerHTML.trim() == '') {
+ bodyEl.style.display = 'none';
}
};
+
+ // Show a single popup
+ var showSinglePopup = function(popup, opts) {
+ var _this = this;
+
+ ionic.requestAnimationFrame(function() {
+ hideBody(popup);
+ positionPopup(popup);
+ popup.el.classList.remove('popup-hidden');
+ popup.el.classList.add('popup-showing');
+ popup.el.classList.add('active');
+ });
+ };
+
+ // Show a popup that was already shown at one point in the past
+ var reshowSinglePopup = function(popup) {
+ ionic.requestAnimationFrame(function() {
+ popup.el.classList.remove('popup-hidden');
+ popup.el.classList.add('popup-showing');
+ popup.el.classList.add('active');
+ });
+ };
+
+ // Hide a single popup
+ var hideSinglePopup = function(popup) {
+ ionic.requestAnimationFrame(function() {
+ popup.el.classList.remove('active');
+ popup.el.classList.add('popup-hidden');
+ });
+ };
+
+ // Remove a popup once and for all
+ var removeSinglePopup = function(popup) {
+ // Force a reflow so the animation will actually run
+ popup.el.offsetWidth;
+
+ popup.el.classList.remove('active');
+ popup.el.classList.add('popup-hidden');
+
+ $timeout(function() {
+ popup.el.remove();
+ }, 400);
+ };
+
+
+ /**
+ * Popup stack and directive
+ */
+
+ var popupStack = [];
+ var backdropEl = null;
+
+ // Show the backdrop element
+ var showBackdrop = function() {
+ var el = $compile('')($rootScope.$new(true));
+ $document[0].body.appendChild(el[0]);
+ backdropEl = el;
+ };
+
+ // Remove the backdrop element
+ var removeBackdrop = function() {
+ backdropEl.remove();
+ };
+
+ // Push the new popup onto the stack with the given data and scope.
+ // If this is the first one in the stack, show the backdrop, otherwise don't.
+ var pushAndShow = function(popup, data) {
+ var lastPopup = popupStack[popupStack.length-1];
+
+ popupStack.push(popup);
+
+ // If this is the first popup, show the backdrop
+ if(popupStack.length == 1) {
+ showBackdrop();
+ }
+
+ // If we have an existing popup, add a delay between hiding and showing it
+ if(lastPopup) {
+ hideSinglePopup(lastPopup);
+ $timeout(function() {
+ showSinglePopup(popup);
+ }, popupOptions.stackPushDelay);
+ } else {
+ // Otherwise, immediately show it
+ showSinglePopup(popup);
+ }
+
+ };
+
+ // Pop the current popup off the stack. If there are other popups, show them
+ // otherwise hide the backdrop.
+ var popAndRemove = function(popup) {
+ var lastPopup = popupStack.pop();
+ var nextPopup = popupStack[popupStack.length-1];
+ removeSinglePopup(lastPopup);
+
+ if(nextPopup) {
+ reshowSinglePopup(nextPopup);
+ } else {
+ removeBackdrop();
+ }
+ };
+
+ // Append the element to the screen, create the popup view,
+ // and add the popup to the scope
+ var constructPopupOnScope = function(element, scope) {
+ var popup = {
+ el: element[0],
+ scope: scope
+ };
+
+ scope.popup = popup;
+
+ return popup;
+ }
+
+ var buildPopupTemplate = function(opts, content) {
+ return ''
+ + (content || '') +
+ '';
+ };
+
+
+ // Given an options object, build a new popup window and return a promise
+ // which will contain the constructed popup at a later date. Perhaps at a later
+ // year even. At this point, it's hard to say.
+ var createPopup = function(opts, responseDeferred) {
+ var q = $q.defer();
+
+ // Create some defaults
+ var defaults = {
+ title: '',
+ animation: 'fade-in',
+ };
+
+ opts = angular.extend(defaults, opts);
+
+ // Create a new scope, and bind some of the options stuff to that scope
+ var scope = opts.scope && opts.scope.$new() || $rootScope.$new(true);
+ angular.extend(scope, opts);
+
+ scope.onClose = function(button, result, event) {
+ popAndRemove(scope.popup);
+ responseDeferred.resolve(result);
+ };
+
+ // Check if we need to load a template for the content of the popup
+ if(opts.templateUrl) {
+
+ // Load the template externally
+ $ionicTemplateLoader.load(opts.templateUrl).then(function(templateString) {
+
+ var popupTemplate = buildPopupTemplate(opts, templateString);
+ var element = $compile(popupTemplate)(scope);
+ $document[0].body.appendChild(element[0]);
+ q.resolve(constructPopupOnScope(element, scope));
+
+ }, function(err) {
+ // Error building the popup
+ q.reject(err);
+ });
+
+ } else {
+ // Compile the template
+ var popupTemplate = buildPopupTemplate(opts, opts.content);
+ var element = $compile(popupTemplate)(scope);
+ $document[0].body.appendChild(element[0]);
+ q.resolve(constructPopupOnScope(element, scope));
+ }
+
+ return q.promise;
+ };
+
+
+ // Public API
+ return {
+ showPopup: function(data) {
+ var q = $q.defer();
+
+ createPopup(data, q).then(function(popup, scope) {
+
+ // We constructed the popup, push it on the stack and show it
+ pushAndShow(popup, data);
+
+ }, function(err) {
+ console.error('Unable to load popup:', err);
+ });
+
+ return q.promise;
+ },
+
+ /**
+ * @ngdoc method
+ * @name $ionicPopup#show
+ * @description show a complex popup. This is the master show function for all popups
+ * @param {data} object The options for showing a popup, of the form:
+ *
+ * ```
+ * {
+ * content: '', // String. The content of the popup
+ * title: '', // String. The title of the popup
+ * subTitle: '', // String (optional). The sub-title of the popup
+ * templateUrl: '', // URL String (optional). The URL of a template to load as the content (instead of the `content` field)
+ * scope: null, // Scope (optional). A scope to apply to the popup content (for using ng-model in a template, for example)
+ * buttons:
+ * [
+ * {
+ * text: 'Cancel',
+ * type: 'button-default',
+ * onTap: function(e) {
+ * // e.preventDefault() is the only way to return a false value
+ * e.preventDefault();
+ * }
+ * },
+ * {
+ * text: 'OK',
+ * type: 'button-positive',
+ * onTap: function(e) {
+ * // When the user taps one of the buttons, you need to return the
+ * // Data you want back to the popup service which will then resolve
+ * // the promise waiting for a response.
+ * //
+ * // To return "false", call e.preventDefault();
+ * return scope.data.response;
+ * }
+ * }
+ * ]
+ *
+ * }
+ * ```
+ */
+ show: function(data) {
+ return this.showPopup(data);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $ionicPopup#alert
+ * @description show a simple popup with one button that the user has to tap
+ *
+ * Show a simple alert dialog
+ *
+ * ```javascript
+ * $ionicPopup.alert({
+ * title: 'Hey!;,
+ * content: 'Don\'t do that!'
+ * }).then(function(res) {
+ * // Accepted
+ * });
+ * ```
+ *
+ * @returns {Promise} that resolves when the alert is accepted
+ * @param {data} object The options for showing an alert, of the form:
+ *
+ * ```
+ * {
+ * content: '', // String. The content of the popup
+ * title: '', // String. The title of the popup
+ * okText: '', // String. The text of the OK button
+ * okType: '', // String (default: button-positive). The type of the OK button
+ * }
+ * ```
+ */
+ alert: function(opts) {
+ return this.showPopup({
+ content: opts.content || '',
+ title: opts.title || '',
+ buttons: [
+ {
+ text: opts.okText || 'OK',
+ type: opts.okType || 'button-positive',
+ onTap: function(e) {
+ return true;
+ }
+ }
+ ]
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name $ionicPopup#confirm
+ * @description
+ * Show a simple confirm popup with a cancel and accept button:
+ *
+ * ```javascript
+ * $ionicPopup.confirm({
+ * title: 'Consume Ice Cream',
+ * content: 'Are you sure you want to eat this ice cream?'
+ * }).then(function(res) {
+ * if(res) {
+ * console.log('You are sure');
+ * } else {
+ * console.log('You are not sure');
+ * }
+ * });
+ * ```
+ *
+ * @returns {Promise} that resolves with the chosen option
+ * @param {data} object The options for showing a confirm dialog, of the form:
+ *
+ * ```
+ * {
+ * content: '', // String. The content of the popup
+ * title: '', // String. The title of the popup
+ * cancelText: '', // String. The text of the Cancel button
+ * cancelType: '', // String (default: button-default). The type of the kCancel button
+ * okText: '', // String. The text of the OK button
+ * okType: '', // String (default: button-positive). The type of the OK button
+ * }
+ * ```
+ */
+ confirm: function(opts) {
+ return this.showPopup({
+ content: opts.content || '',
+ title: opts.title || '',
+ buttons: [
+ {
+ text: opts.cancelText || 'Cancel' ,
+ type: opts.cancelType || 'button-default',
+ onTap: function(e) { e.preventDefault(); }
+ },
+ {
+ text: opts.okText || 'OK',
+ type: opts.okType || 'button-positive',
+ onTap: function(e) {
+ return true;
+ }
+ }
+ ]
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name $ionicPopup#prompt
+ * @description show a simple prompt dialog.
+ *
+ * ```javascript
+ * $ionicPopup.prompt({
+ * title: 'Password Check',
+ * content: 'Enter your secret password',
+ * inputType: 'password',
+ * inputPlaceholder: 'Your password'
+ * }).then(function(res) {
+ * console.log('Your password is', res);
+ * });
+ * ```
+ *
+ * @returns {Promise} that resolves with the entered data
+ * @param {data} object The options for showing a prompt dialog, of the form:
+ *
+ * ```
+ * {
+ * content: // String. The content of the popup
+ * title: // String. The title of the popup
+ * subTitle: // String. The sub title of the popup
+ * inputType: // String (default: "text"). The type of input to use
+ * inputPlaceholder: // String (default: ""). A placeholder to use for the input.
+ * cancelText: // String. The text of the Cancel button
+ * cancelType: // String (default: button-default). The type of the kCancel button
+ * okText: // String. The text of the OK button
+ * okType: // String (default: button-positive). The type of the OK button
+ * }
+ * ```
+ */
+ prompt: function(opts) {
+ var scope = $rootScope.$new(true);
+ scope.data = {};
+ return this.showPopup({
+ content: opts.content || '',
+ title: opts.title || '',
+ subTitle: opts.subTitle || '',
+ scope: scope,
+ buttons: [
+ {
+ text: opts.cancelText || 'Cancel',
+ type: opts.cancelType|| 'button-default',
+ onTap: function(e) { e.preventDefault(); }
+ },
+ {
+ text: opts.okText || 'OK',
+ type: opts.okType || 'button-positive',
+ onTap: function(e) {
+ return scope.data.response;
+ }
+ }
+ ]
+ });
+ }
+
+ };
}]);
+
+})(ionic);
diff --git a/js/ext/angular/test/directive/ionicPopup.unit.js b/js/ext/angular/test/directive/ionicPopup.unit.js
new file mode 100644
index 0000000000..300c998fcc
--- /dev/null
+++ b/js/ext/angular/test/directive/ionicPopup.unit.js
@@ -0,0 +1,97 @@
+'use strict';
+
+describe('Ionic Popup Directive', function() {
+ var compile, scope, popupEl;
+
+ beforeEach(module('ionic'));
+
+ beforeEach(inject(function($compile, $rootScope, $controller) {
+ compile = $compile;
+ scope = $rootScope;
+
+ }));
+
+ it('Should build', function() {
+ popupEl = angular.element('');
+ popupEl = compile(popupEl)(scope);
+
+ expect(popupEl[0].classList.contains('popup')).toBe(true);
+ });
+
+ it('Set scope', function() {
+ popupEl = angular.element('');
+
+ var buttons = [
+ {
+ type: 'button-whack',
+ text: 'Whacky'
+ }
+ ];
+
+ scope.buttons = buttons;
+
+ popupEl = compile(popupEl)(scope);
+
+ scope.$apply();
+
+ scope = popupEl.scope();
+
+ expect(scope.title).toEqual('Cats');
+
+ expect(scope.buttons).toBe(buttons);
+ expect(scope.buttons[0].text).toEqual('Whacky');
+ expect(scope.buttons[0].type).toEqual('button-whack');
+
+ expect(popupEl[0].querySelector('button').classList.contains('button-whack')).toBe(true);
+
+ expect(popupEl[0].classList.contains('popup')).toBe(true);
+ });
+
+ it('Does call callbacks', function() {
+ popupEl = angular.element('');
+
+ // For spy
+ /* Not sure how to trigger these
+ var cb = {
+ onTap: function(e) {},
+ onClose: function(e) {}
+ };
+ */
+
+ var buttons = [
+ {
+ type: 'button-whack',
+ text: 'Whacky',
+ onTap: function(e) {}
+ }
+ ];
+
+ scope.buttons = buttons;
+
+ /*
+ scope.onTap = cb.onTap;
+ scope.onClose = cb.onClose;
+ */
+
+ popupEl = compile(popupEl)(scope);
+
+ scope.$apply();
+
+ spyOn(buttons[0], 'onTap');
+ /*
+ spyOn(cb, 'onTap');
+ spyOn(cb, 'onClose');
+ */
+
+ var button = popupEl[0].querySelector('button');
+ ionic.trigger('click', {
+ target: button
+ });
+
+ /*
+ expect(cb.onTap).toHaveBeenCalled();
+ expect(cb.onClose).toHaveBeenCalled();
+ */
+ expect(buttons[0].onTap).toHaveBeenCalled();
+ });
+});
diff --git a/js/ext/angular/test/popup.html b/js/ext/angular/test/popup.html
new file mode 100644
index 0000000000..6e3350cde2
--- /dev/null
+++ b/js/ext/angular/test/popup.html
@@ -0,0 +1,107 @@
+
+
+
+ Popups
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/ext/angular/test/service/ionicPopup.unit.js b/js/ext/angular/test/service/ionicPopup.unit.js
new file mode 100644
index 0000000000..99afff1b57
--- /dev/null
+++ b/js/ext/angular/test/service/ionicPopup.unit.js
@@ -0,0 +1,70 @@
+describe('Ionic Popup', function() {
+ var popup, timeout, scope;
+
+ beforeEach(module('ionic'));
+
+ beforeEach(inject(function($ionicPopup, $rootScope, $timeout) {
+ ionic.requestAnimationFrame = function(cb) { cb(); }
+ scope = $rootScope;
+ popup = $ionicPopup;
+ timeout = $timeout;
+ }));
+
+ it('Should show popup', function() {
+ popup.show({
+ title: 'Cats',
+ content: 'Dogs',
+ buttons: [
+ {
+ text: 'Okay',
+ type: 'button-balanced',
+ onTap: function(e) {}
+ }
+ ]
+ });
+
+ timeout.flush();
+
+ var popupBackdropEl = document.body.querySelector('.popup-backdrop');
+ expect(popupBackdropEl).not.toEqual(null);
+
+ var popupEl = document.body.querySelector('.popup');
+ expect(popupEl.classList.contains('active')).toBe(true);
+ expect(popupEl.classList.contains('popup-showing')).toBe(true);
+
+ popup.show({
+ title: 'Cats',
+ content: 'Dogs',
+ buttons: [
+ {
+ text: 'Okay',
+ type: 'button-balanced',
+ onTap: function(e) {}
+ }
+ ]
+ });
+
+ timeout.flush();
+
+ // Make sure there are two popups
+ expect(document.body.querySelectorAll('.popup').length).toEqual(2);
+ });
+
+ it('Should set correct element data', function() {
+ popup.show({
+ title: 'Cats',
+ content: 'Dogs',
+ buttons: [
+ {
+ text: 'Okay',
+ type: 'button-balanced',
+ onTap: function(e) {}
+ }
+ ]
+ });
+
+ var popupEl = document.body.querySelector('.popup');
+ expect(popupEl.querySelector('.popup-title').innerText).toEqual('Cats');
+ expect(popupEl.querySelector('.popup-body').innerText).toEqual('Dogs');
+ });
+});
diff --git a/js/views/popupView.js b/js/views/popupView.js
deleted file mode 100644
index 713113d45e..0000000000
--- a/js/views/popupView.js
+++ /dev/null
@@ -1,38 +0,0 @@
-(function(ionic) {
-'use strict';
- /**
- * An ActionSheet is the slide up menu popularized on iOS.
- *
- * You see it all over iOS apps, where it offers a set of options
- * triggered after an action.
- */
- ionic.views.Popup = ionic.views.View.inherit({
- initialize: function(opts) {
- var _this = this;
-
- this.el = opts.el;
- },
-
- setTitle: function(title) {
- var titleEl = el.querySelector('.popup-title');
- if(titleEl) {
- titleEl.innerHTML = title;
- }
- },
- alert: function(message) {
- var _this = this;
-
- ionic.requestAnimationFrame(function() {
- _this.setTitle(message);
- _this.el.classList.add('active');
- });
- },
- hide: function() {
- // Force a reflow so the animation will actually run
- this.el.offsetWidth;
-
- this.el.classList.remove('active');
- }
- });
-
-})(ionic);
diff --git a/release/css/ionic.css b/release/css/ionic.css
index 3fd8bbb462..faf14e14bb 100644
--- a/release/css/ionic.css
+++ b/release/css/ionic.css
@@ -1,3 +1,4 @@
+@charset "UTF-8";
/*!
* Copyright 2014 Drifty Co.
* http://drifty.com/
@@ -3175,15 +3176,39 @@ a.subdued {
* --------------------------------------------------
*/
.popup {
- position: fixed; }
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ border-radius: 8px;
+ background-color: rgba(255, 255, 255, 0.9); }
-.popup-content {
+.popup-head {
+ padding: 15px 0px;
+ text-align: center;
+ border-bottom: 1px solid #eee; }
+
+.popup-title {
+ font-size: 14px;
+ margin: 0;
+ padding: 0; }
+
+.popup-body {
padding: 10px; }
-.loading-backdrop {
- -webkit-transition: visibility 0s linear 0.3s;
- -moz-transition: visibility 0s linear 0.3s;
- transition: visibility 0s linear 0.3s;
+.popup-buttons.row {
+ padding: 0; }
+.popup-buttons .button {
+ line-height: 30px;
+ border: none;
+ border-right: 1px solid #eee;
+ border-radius: 0px 0px 8px 8px; }
+ .popup-buttons .button:last-child {
+ border-right: none; }
+
+.popup-backdrop.enabled {
+ background-color: rgba(0, 0, 0, 0.4); }
+
+.loading-backdrop, .popup-backdrop {
position: fixed;
top: 0;
left: 0;
@@ -3191,14 +3216,19 @@ a.subdued {
visibility: hidden;
width: 100%;
height: 100%; }
- .loading-backdrop.enabled {
- background-color: rgba(0, 0, 0, 0.7); }
- .loading-backdrop.active {
+ .loading-backdrop.active, .popup-backdrop.active {
-webkit-transition-delay: 0s;
-moz-transition-delay: 0s;
transition-delay: 0s;
visibility: visible; }
+.loading-backdrop {
+ -webkit-transition: visibility 0s linear 0.3s;
+ -moz-transition: visibility 0s linear 0.3s;
+ transition: visibility 0s linear 0.3s; }
+ .loading-backdrop.enabled {
+ background-color: rgba(0, 0, 0, 0.7); }
+
.loading {
position: fixed;
top: 50%;
diff --git a/scss/_animations.scss b/scss/_animations.scss
index 4a86fcc624..bebdd5486b 100644
--- a/scss/_animations.scss
+++ b/scss/_animations.scss
@@ -184,6 +184,56 @@ $slide-in-up-function: cubic-bezier(.1, .7, .1, 1);
to { background-color: rgba(0,0,0,0); }
}
+// Scale Out
+// Scale from hero (1 in this case) to zero
+// -------------------------------
+
+@-webkit-keyframes scaleOut {
+ from { -webkit-transform: scale(1); opacity: 1; }
+ to { -webkit-transform: scale(0.8); opacity: 0; }
+}
+@-moz-keyframes scaleOut {
+ from { -moz-transform: scale(1); opacity: 1; }
+ to { -moz-transform: scale(0.8); opacity: 0; }
+}
+@keyframes scaleOut {
+ from { transform: scale(1); opacity: 1; }
+ to { transform: scale(0.8); opacity: 0; }
+}
+
+// Scale In
+// Scale from 0 to hero (1 in this case)
+// -------------------------------
+
+@-webkit-keyframes scaleIn {
+ from { -webkit-transform: scale(0); }
+ to { -webkit-transform: scale(1); }
+}
+@-moz-keyframes scaleIn {
+ from { -moz-transform: scale(0); }
+ to { -moz-transform: scale(1); }
+}
+@keyframes scaleIn {
+ from { transform: scale(0); }
+ to { transform: scale(1); }
+}
+
+// Super Scale In
+// Scale from super (1.x) to duper (1 in this case)
+// -------------------------------
+
+@-webkit-keyframes superScaleIn {
+ from { -webkit-transform: scale(1.2); opacity: 0; }
+ to { -webkit-transform: scale(1); opacity: 1 }
+}
+@-moz-keyframes superScaleIn {
+ from { -moz-transform: scale(1.2); opacity: 0; }
+ to { -moz-transform: scale(1); opacity: 1; }
+}
+@keyframes superScaleIn {
+ from { transform: scale(1.2); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+}
// Spin
// -------------------------------
diff --git a/scss/_popup.scss b/scss/_popup.scss
index 1c7ea9ec45..cf2f45fa51 100644
--- a/scss/_popup.scss
+++ b/scss/_popup.scss
@@ -5,14 +5,95 @@
.popup {
position: fixed;
+ top: 50%;
+ left: 50%;
+ z-index: 11;
+
+ // Start hidden
+ visibility: hidden;
+
+ width: $popup-width;
+
+ border-radius: $popup-border-radius;
+ background-color: $popup-background-color;
+
+ &.popup-hidden {
+ @include animation-name(scaleOut);
+ @include animation-duration($popup-leave-animation-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+
+ &.popup-showing {
+ visibility: visible;
+ }
+
+ &.active {
+ @include animation-name(superScaleIn);
+ @include animation-duration($popup-enter-animation-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
}
-.popup-content {
+.popup-head {
+ padding: 15px 0px;
+ text-align: center;
+ border-bottom: 1px solid #eee;
+}
+.popup-title {
+ font-size: 15px;
+ margin: 0;
+ padding: 0;
+}
+.popup-sub-title {
+ font-size: 11px;
+ margin: 5px 0 0 0;
+ padding: 0;
+ font-weight: normal;
+}
+.popup-body {
padding: 10px;
}
+.popup-buttons {
+ &.row {
+ padding: 10px 10px;
+ }
+
+ .button {
+ margin: 0px 5px;
+ min-height: $popup-button-min-height;
+ line-height: $popup-button-line-height;
+ border-radius: $popup-button-border-radius;
+
+ &:first-child {
+ margin-left: 0px;
+ }
+ &:last-child {
+ margin-right: 0px;
+ }
+ }
+}
+
+.popup-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 10;
+
+ width: 100%;
+ height: 100%;
+
+ background-color: rgba(0,0,0,0.4);
+
+ @include animation-name(fadeIn);
+ @include animation-duration($popup-backdrop-fadein-duration);
+ @include animation-timing-function(linear);
+ @include animation-fill-mode(both);
+}
+
.loading-backdrop {
- @include transition(visibility 0s linear 0.3s);
position: fixed;
top: 0;
left: 0;
@@ -22,16 +103,19 @@
width: 100%;
height: 100%;
- &.enabled {
- background-color: rgba(0,0,0,0.7);
- }
-
&.active {
@include transition-delay(0s);
visibility: visible;
}
}
+.loading-backdrop {
+ @include transition(visibility 0s linear 0.3s);
+ &.active {
+ background-color: rgba(0,0,0,0.7);
+ }
+}
+
.loading {
position: fixed;
top: 50%;
diff --git a/scss/_variables.scss b/scss/_variables.scss
index caf63b6511..904ee95846 100644
--- a/scss/_variables.scss
+++ b/scss/_variables.scss
@@ -548,6 +548,22 @@ $sheet-border-radius: 3px 3px 3px 3px !default;
$sheet-border-radius-top: 3px 3px 0px 0px !default;
$sheet-border-radius-bottom: 0px 0px 3px 3px !default;
+// Popups
+// -------------------------------
+
+$popup-backdrop-fadein-duration: 0.1s;
+$popup-width: 250px;
+$popup-enter-animation: superScaleIn;
+$popup-enter-animation-duration: 0.2s;
+$popup-leave-animation-duration: 0.1s;
+
+$popup-border-radius: 0px;
+$popup-background-color: rgba(255,255,255,0.9);
+
+$popup-button-border-radius: 2px;
+$popup-button-line-height: 20px;
+$popup-button-min-height: 45px;
+
// Badges
// -------------------------------