mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(popup): Added popup support
This commit is contained in:
@@ -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',
|
||||
|
||||
63
js/ext/angular/src/directive/ionicPopup.js
vendored
Normal file
63
js/ext/angular/src/directive/ionicPopup.js
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('ionic.ui.popup', [])
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
.directive('ionPopupBackdrop', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template: '<div class="popup-backdrop"></div>'
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @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: '<div class="popup">' +
|
||||
'<div class="popup-head">' +
|
||||
'<h3 class="popup-title" ng-bind-html="title"></h3>' +
|
||||
'<h5 class="popup-sub-title" ng-bind-html="subTitle" ng-if="subTitle"></h5>' +
|
||||
'</div>' +
|
||||
'<div class="popup-body" ng-transclude>' +
|
||||
'</div>' +
|
||||
'<div class="popup-buttons row">' +
|
||||
'<button ng-repeat="button in buttons" ng-click="_buttonTapped(button, $event)" class="button col" ng-class="button.type || \'button-default\'" ng-bind-html="button.text"></button>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
};
|
||||
}]);
|
||||
|
||||
})();
|
||||
3
js/ext/angular/src/ionicAngular.js
vendored
3
js/ext/angular/src/ionicAngular.js
vendored
@@ -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'
|
||||
]);
|
||||
|
||||
|
||||
|
||||
558
js/ext/angular/src/service/ionicPopup.js
vendored
558
js/ext/angular/src/service/ionicPopup.js
vendored
@@ -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: '<b>Save</b>',
|
||||
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('<popup>' + opts.content + '</popup>')(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('<ion-popup-backdrop></ion-popup-backdrop>')($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 '<ion-popup title="' + opts.title + '" buttons="buttons" on-button-tap="onButtonTap(button, event)" on-close="onClose(button, result, event)">'
|
||||
+ (content || '') +
|
||||
'</ion-popup>';
|
||||
};
|
||||
|
||||
|
||||
// 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 || '<input ng-model="data.response" type="' + (opts.inputType || 'text') + '" placeholder="' + (opts.inputPlaceholder || '') + '">',
|
||||
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);
|
||||
|
||||
97
js/ext/angular/test/directive/ionicPopup.unit.js
Normal file
97
js/ext/angular/test/directive/ionicPopup.unit.js
Normal file
@@ -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('<ion-popup></ion-popup>');
|
||||
popupEl = compile(popupEl)(scope);
|
||||
|
||||
expect(popupEl[0].classList.contains('popup')).toBe(true);
|
||||
});
|
||||
|
||||
it('Set scope', function() {
|
||||
popupEl = angular.element('<ion-popup title="Cats"></ion-popup>');
|
||||
|
||||
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('<ion-popup title="Cats" on-button-tap="onTap(button, $event)" on-close="onClose(button, $event)"></ion-popup>');
|
||||
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
107
js/ext/angular/test/popup.html
Normal file
107
js/ext/angular/test/popup.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<html ng-app="ionic.example">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Popups</title>
|
||||
|
||||
<!-- Sets initial viewport load and disables zooming -->
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="stylesheet" href="../../../../dist/css/ionic.css">
|
||||
|
||||
<script src="../../../../dist/js/ionic.bundle.js"></script>
|
||||
|
||||
</head>
|
||||
<body ng-controller="PopupCtrl">
|
||||
<button class="button button-dark" ng-click="showPopup()">Generic</button>
|
||||
<button class="button button-primary" ng-click="showConfirm()">Confirm</button>
|
||||
<button class="button button-balanced" ng-click="showPrompt()">Prompt</button>
|
||||
<button class="button button-balanced" ng-click="showPasswordPrompt()">Password Prompt</button>
|
||||
<button class="button button-positive" ng-click="showAlert()">Alert</button>
|
||||
|
||||
<script id="popup-template.html" type="text/ng-template">
|
||||
<input ng-model="data.wifi" type="text" placeholder="Password">
|
||||
</script>
|
||||
|
||||
<script>
|
||||
angular.module('ionic.example', ['ionic'])
|
||||
|
||||
.controller('PopupCtrl', function($scope, $timeout, $q, $ionicPopup) {
|
||||
$scope.showPopup = function() {
|
||||
$scope.data = {}
|
||||
|
||||
$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: '<b>Save</b>',
|
||||
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);
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
$ionicPopup.confirm({
|
||||
title: 'Do you like ice cream?',
|
||||
cancelText: 'No',
|
||||
okText: 'Yes, of course'
|
||||
}).then(function(res) {
|
||||
console.log('Your love for ice cream:', res);
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
$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');
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.showPrompt = function() {
|
||||
$ionicPopup.prompt({
|
||||
title: 'ID Check',
|
||||
subTitle: 'What is your name?'
|
||||
}).then(function(res) {
|
||||
console.log('Your name is', res);
|
||||
});
|
||||
};
|
||||
$scope.showPasswordPrompt = function() {
|
||||
$ionicPopup.prompt({
|
||||
title: 'Password Check',
|
||||
subTitle: 'Enter your secret password',
|
||||
inputType: 'password',
|
||||
inputPlaceholder: 'Your password'
|
||||
}).then(function(res) {
|
||||
console.log('Your name is', res);
|
||||
});
|
||||
};
|
||||
$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');
|
||||
});
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
70
js/ext/angular/test/service/ionicPopup.unit.js
Normal file
70
js/ext/angular/test/service/ionicPopup.unit.js
Normal file
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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%;
|
||||
|
||||
@@ -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
|
||||
// -------------------------------
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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
|
||||
// -------------------------------
|
||||
|
||||
Reference in New Issue
Block a user