This commit is contained in:
Adam Bradley
2015-12-30 15:03:05 -06:00
parent c136d2143a
commit 2c169ff508
11 changed files with 372 additions and 379 deletions

View File

@ -1,33 +1,79 @@
import {Component, Injectable, Renderer} from 'angular2/core'; import {Component, Renderer} from 'angular2/core';
import {NgFor, NgIf} from 'angular2/common'; import {NgFor, NgIf} from 'angular2/common';
import {NavParams} from '../nav/nav-controller';
import {ViewController} from '../nav/view-controller';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {Icon} from '../icon/icon'; import {Icon} from '../icon/icon';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
import {NavParams} from '../nav/nav-controller';
import {extend} from '../../util/util';
/**
* @name ActionSheet
* @description
* The Action Sheet is a slide-up dialog that lets the user choose from a set
* of options. Dangerous options are made obvious. There are easy ways to
* cancel out of the action sheet, such as tapping the backdrop or even hitting
* escape key on desktop.
*
* @usage
* ```ts
* ```
*
* @demo /docs/v2/demos/action-sheet/
* @see {@link /docs/v2/components#action-sheets ActionSheet Component Docs}
*/
export class ActionSheet extends ViewController {
constructor(data={}) {
data.buttons = data.buttons || [];
super(ActionSheetCmp, data);
this.viewType = 'action-sheet';
}
getTransitionName(direction) {
let key = 'actionSheet' + (direction === 'back' ? 'Leave' : 'Enter');
return this._nav && this._nav.config.get(key);
}
setTitle(title) {
this._data.title = title;
}
setSubTitle(subTitle) {
this._data.subTitle = subTitle;
}
addButton(button) {
this._data.buttons.push(button);
}
static create(data={}) {
return new ActionSheet(data);
}
}
@Component({ @Component({
selector: 'ion-action-sheet', selector: 'ion-action-sheet',
template: template:
'<div (click)="cancel()" tappable disable-activated class="backdrop"></div>' + '<div (click)="dismiss()" tappable disable-activated class="backdrop" role="presentation"></div>' +
'<div class="action-sheet-wrapper">' + '<div class="action-sheet-wrapper">' +
'<div class="action-sheet-container">' + '<div class="action-sheet-container">' +
'<div class="action-sheet-group action-sheet-options">' + '<div class="action-sheet-group action-sheet-options">' +
'<div class="action-sheet-title" *ngIf="d.titleText">{{d.titleText}}</div>' + '<div class="action-sheet-title" *ngIf="d.title">{{d.title}}</div>' +
'<button (click)="buttonClicked(i)" *ngFor="#b of d.buttons; #i=index" class="action-sheet-button action-sheet-option disable-hover">' + '<div class="action-sheet-sub-title" *ngIf="d.subTitle">{{d.subTitle}}</div>' +
'<button (click)="click(b)" *ngFor="#b of d.buttons" class="action-sheet-button action-sheet-option disable-hover" [ngClass]="b.cssClass">' +
'<icon [name]="b.icon" *ngIf="b.icon" class="action-sheet-icon"></icon> ' + '<icon [name]="b.icon" *ngIf="b.icon" class="action-sheet-icon"></icon> ' +
'{{b.text}}' + '{{b.text}}' +
'</button>' + '</button>' +
'<button *ngIf="d.destructiveText" (click)="destructive()" class="action-sheet-button action-sheet-destructive disable-hover">' +
'<icon [name]="d.destructiveIcon" *ngIf="d.destructiveIcon" class="action-sheet-icon"></icon> ' +
'{{d.destructiveText}}' +
'</button>' +
'</div>' + '</div>' +
'<div class="action-sheet-group" *ngIf="d.cancelText">' + '<div class="action-sheet-group" *ngIf="d.cancelButton">' +
'<button (click)="cancel()" class="action-sheet-button action-sheet-cancel disable-hover">' + '<button (click)="click(d.cancelButton)" class="action-sheet-button action-sheet-cancel disable-hover" [ngClass]="d.cancelButton.cssClass">' +
'<icon [name]="d.cancelIcon" *ngIf="d.cancelIcon" class="action-sheet-icon"></icon> ' + '<icon [name]="d.cancelButton.icon" *ngIf="d.cancelButton.icon" class="action-sheet-icon"></icon> ' +
'{{d.cancelText}}' + '{{d.cancelButton.text}}' +
'</button>' + '</button>' +
'</div>' + '</div>' +
'</div>' + '</div>' +
@ -39,7 +85,10 @@ import {extend} from '../../util/util';
}) })
class ActionSheetCmp { class ActionSheetCmp {
constructor(params: NavParams, renderer: Renderer) { constructor(
private _viewCtrl: ViewController,
params: NavParams, renderer: Renderer
) {
this.d = params.data; this.d = params.data;
if (this.d.cssClass) { if (this.d.cssClass) {
@ -47,120 +96,63 @@ class ActionSheetCmp {
} }
} }
cancel() { click(button) {
this.d.cancel && this.d.cancel(); let shouldDismiss = true;
return this.close();
}
destructive() { if (button.handler) {
if (this.d.destructiveButtonClicked()) { // a handler has been provided, execute it
return this.close(); if (button.handler() === false) {
// if the return value of the handler is false then do not dismiss
shouldDismiss = false;
} }
} }
buttonClicked(index) { if (shouldDismiss) {
if (this.d.buttonClicked(index)) { this.dismiss();
return this.close();
}
} }
} }
/** dismiss() {
* @name ActionSheet this._viewCtrl.dismiss(this);
* @description
* The Action Sheet is a slide-up pane that lets the user choose from a set of options. Dangerous options are made obvious.
* There are easy ways to cancel out of the action sheet, such as tapping the backdrop or even hitting escape on the keyboard for desktop testing.
*
* @usage
* ```ts
* openMenu() {
*
* this.actionSheet.open({
* buttons: [
* { text: 'Share This' },
* { text: 'Move' }
* ],
* destructiveText: 'Delete',
* titleText: 'Modify your album',
* cancelText: 'Cancel',
* cancel: function() {
* console.log('Canceled');
* },
* destructiveButtonClicked: () => {
* console.log('Destructive clicked');
* },
* buttonClicked: function(index) {
* console.log('Button clicked', index);
* if(index == 1) { return false; }
* return true;
* }
*
* }).then(actionSheetRef => {
* this.actionSheetRef = actionSheetRef;
* });
*
* }
* ```
*
* @demo /docs/v2/demos/action-sheet/
* @see {@link /docs/v2/components#action-sheets ActionSheet Component Docs}
*/
@Injectable()
export class ActionSheet {
constructor(config: Config) {
//this.ctrl = ctrl;
this.config = config;
} }
/** onPageLoaded() {
* Create and open a new Action Sheet. This is the // normalize the data
* public API, and most often you will only use ActionSheet.open() let buttons = [];
*
* @param {Object} [opts={}] An object containing optional settings.
* - `[Object]` `buttons` Which buttons to show. Each button is an object with a `text` field.
* - `{string}` `titleText` The title to show on the action sheet.
* - `{string=}` `cancelText` the text for a 'cancel' button on the action sheet.
* - `{string=}` `destructiveText` The text for a 'danger' on the action sheet.
* - `{function=}` `cancel` Called if the cancel button is pressed, the backdrop is tapped or
* the hardware back button is pressed.
* - `{function=}` `buttonClicked` Called when one of the non-destructive buttons is clicked,
* with the index of the button that was clicked and the button object. Return true to close
* the action sheet, or false to keep it opened.
* - `{function=}` `destructiveButtonClicked` Called when the destructive button is clicked.
* Return true to close the action sheet, or false to keep it opened.
* - `{String}` `enterAnimation` The class used to animate an actionSheet that is entering.
* - `{String}` `leaveAnimation` The class used to animate an actionSheet that is leaving.
* @return {Promise} Promise that resolves when the action sheet is open.
*/
open(opts={}) {
opts = extend({
pageType: OVERLAY_TYPE,
enterAnimation: this.config.get('actionSheetEnter'),
leaveAnimation: this.config.get('actionSheetLeave'),
cancelIcon: this.config.get('actionSheetCancelIcon'),
destructiveIcon: this.config.get('actionSheetDestructiveIcon')
}, opts);
return this.ctrl.open(ActionSheetCmp, opts, opts); this.d.buttons.forEach(button => {
if (typeof button === 'string') {
button = { text: button };
} }
/** if (button.style === 'cancel') {
* Retrieves an actionSheet instance. this.d.cancelButton = button;
*
* @param {String} [handle] The handle used to open the instance to be retrieved. } else {
* @returns {ActionSheet} An actionSheet instance. if (button.style === 'destructive') {
*/ button.cssClass = (button.cssClass + ' ' || '') + 'action-sheet-destructive';
get(handle) {
if (handle) {
return this.ctrl.getByHandle(handle);
} }
return this.ctrl.getByType(OVERLAY_TYPE); buttons.push(button);
}
});
this.d.buttons = buttons;
let self = this;
self.keyUp = function(ev) {
if (ev.keyCode === 27) {
console.debug('actionsheet escape');
self.dismiss();
}
};
document.addEventListener('keyup', this.keyUp);
} }
onPageDidLeave() {
document.removeEventListener('keyup', this.keyUp);
}
} }
const OVERLAY_TYPE = 'action-sheet';

View File

@ -1,41 +1,66 @@
import {App, ActionSheet} from 'ionic/ionic'; import {App, Page, ActionSheet, NavController} from 'ionic/ionic';
@Page({
templateUrl: 'main.html'
})
class E2EPage {
constructor(nav: NavController) {
this.nav = nav;
}
openActionSheet(ev) {
this.result = '';
let actionSheet = ActionSheet.create({
buttons: [
{
text: 'Cancel',
style: 'cancel', // will always sort to be on the bottom
handler: () => {
console.log('cancel this clicked');
this.result = 'Canceled';
}
},
{
text: 'Archive',
handler: () => {
console.log('Archive clicked');
this.result = 'Archived';
}
},
{
text: 'No close',
handler: () => {
console.log('do not close clicked');
// returning false does not allow the actionsheet to be closed
return false;
}
},
{
text: 'Destructive',
style: 'destructive',
handler: () => {
console.log('Destructive clicked');
this.result = 'Destructive';
}
}
]
});
this.nav.present(actionSheet);
}
}
@App({ @App({
templateUrl: 'main.html' template: '<ion-nav [root]="root"></ion-nav>'
}) })
class E2EApp { class E2EApp {
constructor() {
constructor(actionSheet: ActionSheet) { this.root = E2EPage;
this.actionSheet = actionSheet;
} }
openActionSheet() {
this.actionSheet.open({
buttons: [
{ text: 'Share This' },
{ text: 'Move' }
],
destructiveText: 'Delete',
titleText: 'Modify your album',
cancelText: 'Cancel',
cancel: function() {
console.log('Canceled');
},
destructiveButtonClicked: () => {
console.log('Destructive clicked');
},
buttonClicked: function(index) {
console.log('Button clicked', index);
if(index == 1) { return false; }
return true;
}
}).then(actionSheetRef => {
this.actionSheetRef = actionSheetRef;
});
}
} }

View File

@ -1,4 +1,12 @@
<ion-navbar *navbar>
<ion-title>Action Sheet</ion-title>
</ion-navbar>
<ion-content padding> <ion-content padding>
<button class="e2eOpenActionSheet" (click)="openActionSheet()">Open Action Sheet</button> <button class="e2eOpenActionSheet" (click)="openActionSheet()">Open Action Sheet</button>
<pre>
Result: {{result}}
</pre>
</ion-content> </ion-content>

View File

@ -1,57 +1,50 @@
import {Component, ElementRef, Renderer} from 'angular2/core'; import {Component, ElementRef, Renderer} from 'angular2/core';
import {NgClass, NgIf, NgFor, FORM_DIRECTIVES} from 'angular2/common'; import {NgClass, NgIf, NgFor, FORM_DIRECTIVES} from 'angular2/common';
import {NavController, NavParams} from '../nav/nav-controller'; import {NavParams} from '../nav/nav-controller';
import {ViewController} from '../nav/view-controller'; import {ViewController} from '../nav/view-controller';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
import {Button} from '../button/button'; import {Button} from '../button/button';
import {extend, isDefined} from '../../util/util'; import {isDefined} from '../../util/util';
export class Alert extends ViewController { export class Alert extends ViewController {
constructor(opts={}) { constructor(data={}) {
super(AlertCmp, opts); data.inputs = data.inputs || [];
data.buttons = data.buttons || [];
this.data.inputs = this.data.inputs || []; super(AlertCmp, data);
this.data.buttons = this.data.buttons || []; this.viewType = 'alert';
} }
getTransitionName(direction) { getTransitionName(direction) {
let key = (direction === 'back' ? 'alertLeave' : 'alertEnter'); let key = (direction === 'back' ? 'alertLeave' : 'alertEnter');
return this._nav.config.get(key); return this._nav && this._nav.config.get(key);
} }
setTitle(title) { setTitle(title) {
this.data.title = title; this._data.title = title;
} }
setSubTitle(subTitle) { setSubTitle(subTitle) {
this.data.subTitle = subTitle; this._data.subTitle = subTitle;
} }
setBody(body) { setBody(body) {
this.data.body = body; this._data.body = body;
} }
addInput(input) { addInput(input) {
this.data.inputs.push(input); this._data.inputs.push(input);
} }
addButton(button) { addButton(button) {
this.data.buttons.push(button); this._data.buttons.push(button);
} }
onDismiss(handler) { static create(data={}) {
this.data.onDismiss = handler; return new Alert(data);
}
dismiss() {
this._nav.dismiss(this);
}
static create(opts={}) {
return new Alert(opts);
} }
} }
@ -102,9 +95,10 @@ class AlertCmp {
let shouldDismiss = true; let shouldDismiss = true;
if (button.handler) { if (button.handler) {
// a handler has been provided, run it // a handler has been provided, execute it
// pass the handler the values from the inputs
if (button.handler(this.getValues()) === false) { if (button.handler(this.getValues()) === false) {
// if the return value is a false then do not close // if the return value of the handler is false then do not dismiss
shouldDismiss = false; shouldDismiss = false;
} }
} }
@ -126,7 +120,7 @@ class AlertCmp {
return values; return values;
} }
onPageWillEnter() { onPageLoaded() {
// normalize the data // normalize the data
this.d.buttons = this.d.buttons.map(button => { this.d.buttons = this.d.buttons.map(button => {
if (typeof button === 'string') { if (typeof button === 'string') {
@ -137,9 +131,9 @@ class AlertCmp {
this.d.inputs = this.d.inputs.map((input, index) => { this.d.inputs = this.d.inputs.map((input, index) => {
return { return {
name: input.name || index, name: isDefined(input.name) ? input.name : index,
label: input.label, label: input.label,
placeholder: input.placeholder || '', placeholder: isDefined(input.placeholder) ? input.placeholder : '',
type: input.type || 'text', type: input.type || 'text',
value: isDefined(input.value) ? input.value : '' value: isDefined(input.value) ? input.value : ''
} }
@ -173,7 +167,6 @@ class AlertCmp {
} }
onPageDidLeave() { onPageDidLeave() {
this.d.onDismiss && this.d.onDismiss(this.getValues());
document.removeEventListener('keyup', this.keyUp); document.removeEventListener('keyup', this.keyUp);
} }
} }

View File

@ -8,7 +8,6 @@ class E2EPage {
constructor(nav: NavController) { constructor(nav: NavController) {
this.nav = nav; this.nav = nav;
this.testAlertOpen = false;
this.testConfirmOpen = false; this.testConfirmOpen = false;
this.testPromptOpen = false; this.testPromptOpen = false;
this.testConfirmResult = ''; this.testConfirmResult = '';
@ -19,15 +18,10 @@ class E2EPage {
let alert = Alert.create({ let alert = Alert.create({
title: 'Alert!', title: 'Alert!',
subTitle: 'Subtitle!!!', subTitle: 'Subtitle!!!',
buttons: ['Ok'], buttons: ['Ok']
onDismiss: () => {
this.testAlertOpen = false;
}
}); });
this.nav.present(alert); this.nav.present(alert);
this.testAlertOpen = true;
} }
doConfirm() { doConfirm() {
@ -39,6 +33,7 @@ class E2EPage {
handler: () => { handler: () => {
console.log('Confirm Cancel'); console.log('Confirm Cancel');
this.testConfirmResult = 'Cancel'; this.testConfirmResult = 'Cancel';
this.testConfirmOpen = false;
} }
}); });
alert.addButton({ alert.addButton({
@ -46,11 +41,8 @@ class E2EPage {
handler: () => { handler: () => {
console.log('Confirm Ok'); console.log('Confirm Ok');
this.testConfirmResult = 'Ok'; this.testConfirmResult = 'Ok';
}
});
alert.onDismiss(data => {
this.testConfirmOpen = false; this.testConfirmOpen = false;
}
}); });
this.nav.present(alert).then(() => { this.nav.present(alert).then(() => {
@ -87,12 +79,9 @@ class E2EPage {
text: 'Ok', text: 'Ok',
handler: data => { handler: data => {
console.log('Prompt data:', data); console.log('Prompt data:', data);
}
});
alert.onDismiss(data => {
this.testPromptOpen = false; this.testPromptOpen = false;
this.testPromptResult = data; this.testPromptResult = data;
}
}); });
this.nav.present(alert).then(() => { this.nav.present(alert).then(() => {

View File

@ -10,7 +10,6 @@
<button class="e2eOpenPrompt" (click)="doPrompt()">Prompt</button> <button class="e2eOpenPrompt" (click)="doPrompt()">Prompt</button>
<pre> <pre>
Alert Opened: {{testAlertOpen}}
Confirm Opened: {{testConfirmOpen}} Confirm Opened: {{testConfirmOpen}}
Confirm Result: {{testConfirmResult}} Confirm Result: {{testConfirmResult}}
Prompt Opened: {{testPromptOpen}} Prompt Opened: {{testPromptOpen}}

View File

@ -1,35 +1,53 @@
import {Injectable, Type} from 'angular2/core'; import {NavController, NavParams} from '../nav/nav-controller';
import {ViewController} from '../nav/view-controller';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
import {extend} from '../../util';
/** /**
* @name Modal * @name Modal
* @description * @description
* The Modal is a content pane that can go over the user's current page. * The Modal is a content pane that can go over the user's current page.
* Usually used for making a choice or editing an item. A modal can be opened * Usually it is used for making a choice or editing an item. A modal uses the
* similar to how {@link /docs/v2/api/components/nav/NavController/#push NavController.push} works, * `NavController` to "present" itself in the root nav stack. It is added to the
* where it is passed a Page component, along with optional Page params, * stack similar to how
* and options for presenting the modal. * {@link /docs/v2/api/components/nav/NavController/#push NavController.push}
* works, where it is passed a Page component, along with optional Page params
*
* When a modal (or any other overlay such as an alert or actionsheet) is
* "presented" to a nav controller, the overlay is added to the app's root nav.
* After the modal has been presented, from within the component instance The
* modal can later be closed or "dimsissed" by using the ViewController's
* `dismiss` method. Additinoally, you can dismiss any overlay by using `pop`
* on the root nav controller.
*
* A modal can also emit data, which is useful when it is used to add or edit
* data. For example, a profile page could slide up in a modal, and on submit,
* the submit button could emit the updated profile data, then dismiss the modal.
* From that point, anything which is subscribed to the modal's `data` event
* would receive the modal's data.
* *
* @usage * @usage
* ```ts * ```ts
* import {Modal, NavController} from 'ionic/ionic';
*
* class MyApp { * class MyApp {
* *
* constructor(modal: Modal) { * constructor(nav: NavController) {
* this.modal = modal; * this.nav = nav;
* } * }
* *
* openContactModal() { * presentContactModal() {
* this.modal.open(ContactUs); * let contactModal = Modal.create(ContactUs);
* this.nav.present(contactModal);
* } * }
* *
* openProfileModal() { * presentProfileModal() {
* this.modal.open(Profile, { userId: 8675309 }, { * let profileModal = Modal.create(Profile, { userId: 8675309 });
* enterAnimation: 'my-fade-in', * this.nav.present(profileModal, {
* leaveAnimation: 'my-fade-out', * animation: 'my-fade-in'
* handle: 'profile-modal' * });
* profileModal.data.subscribe(data => {
* console.log(data);
* }); * });
* } * }
* *
@ -38,64 +56,24 @@ import {extend} from '../../util';
* @demo /docs/v2/demos/modal/ * @demo /docs/v2/demos/modal/
* @see {@link /docs/v2/components#modals Modal Component Docs} * @see {@link /docs/v2/components#modals Modal Component Docs}
*/ */
@Injectable() export class Modal extends ViewController {
export class Modal {
constructor(config: Config) { constructor(componentType, data={}) {
//this.ctrl = ctrl; super(componentType, data);
this.config = config; this.viewType = 'modal';
} }
/** getTransitionName(direction) {
* Opens a new modal using the page component is was pass as the first let key = (direction === 'back' ? 'modalLeave' : 'modalEnter');
* argument. This is similar to how NavController's `push` method works. return this._nav && this._nav.config.get(key);
* Currently you must have `<ion-overlay>` in the `@App` component's template
* for the modal to work correctly. (This is something that will
* be hopefully be removed in the near future.)
*
* @param pageComponent The Page component to load in the modal.
* @param {Object} [params={}] Optional data which can be passed to the page
* component, which can be read from the constructor's `NavParams`.
* @param {Object} [opts={}] Additional options for this one modal instance of.
* Options include `enterAnimation` and `leaveAnimation`, which
* allows customization of which animation to use.
* @returns {Promise} Returns a promise which resolves when the modal has
* loaded and its entering animation has completed. The resolved promise's
* value is the instance of the newly created modal.
*/
open(pageComponent: Type, params={}, opts={}) {
opts = extend({
pageType: OVERLAY_TYPE,
enterAnimation: this.config.get('modalEnter'),
leaveAnimation: this.config.get('modalLeave'),
}, opts);
return this.ctrl.open(pageComponent, params, opts);
} }
/** static create(componentType, data={}) {
* Get the instance of a modal. This is usually helpful to getting ahold of a return new Modal(componentType, data);
* certain modal, from anywhere within the app, and closing it. By calling
* just `get()` without a `handle` argument, it'll return the active modal
* on top (it is possible to have multipe modals opened at the same time).
* If getting just the active modal isn't enough, when creating
* a modal, it's options can be given a `handle`, which is simply a string-based
* name for the modal instance. You can later get a reference to that modal's
* instance by calling this method with the same handle name.
* @param [handle] Optional string name given in the modal's options when it was opened.
* @returns Returns the instance of the modal if it is found, otherwise `null`.
*/
get(handle) {
if (handle) {
return this.ctrl.getByHandle(handle);
}
return this.ctrl.getByType(OVERLAY_TYPE);
} }
} }
const OVERLAY_TYPE = 'modal';
/** /**
* Animations for modals * Animations for modals
@ -108,6 +86,13 @@ class ModalSlideIn extends Animation {
.duration(400) .duration(400)
.fromTo('translateY', '100%', '0%') .fromTo('translateY', '100%', '0%')
.before.addClass('show-page'); .before.addClass('show-page');
if (enteringView.hasNavbar()) {
// entering page has a navbar
let enteringNavBar = new Animation(enteringView.navbarRef());
enteringNavBar.before.addClass('show-navbar');
this.add(enteringNavBar);
}
} }
} }
Animation.register('modal-slide-in', ModalSlideIn); Animation.register('modal-slide-in', ModalSlideIn);
@ -134,6 +119,13 @@ class ModalMDSlideIn extends Animation {
.fromTo('translateY', '40px', '0px') .fromTo('translateY', '40px', '0px')
.fadeIn() .fadeIn()
.before.addClass('show-page'); .before.addClass('show-page');
if (enteringView.hasNavbar()) {
// entering page has a navbar
let enteringNavBar = new Animation(enteringView.navbarRef());
enteringNavBar.before.addClass('show-navbar');
this.add(enteringNavBar);
}
} }
} }
Animation.register('modal-md-slide-in', ModalMDSlideIn); Animation.register('modal-md-slide-in', ModalMDSlideIn);

View File

@ -1,15 +1,14 @@
import {App, Page, Config, Platform} from 'ionic/ionic'; import {App, Page, Config, Platform} from 'ionic/ionic';
import {Modal, ActionSheet, NavController, NavParams, Animation} from 'ionic/ionic'; import {Modal, ActionSheet, NavController, NavParams, Animation, ViewController} from 'ionic/ionic';
@App({ @Page({
templateUrl: 'main.html' templateUrl: 'main.html'
}) })
class E2EApp { class E2EPage {
constructor(modal: Modal, config: Config, platform: Platform) {
this.modal = modal;
constructor(nav: NavController, config: Config, platform: Platform) {
this.nav = nav;
console.log('platforms', platform.platforms()); console.log('platforms', platform.platforms());
console.log('mode', config.get('mode')); console.log('mode', config.get('mode'));
@ -29,32 +28,29 @@ class E2EApp {
}); });
} }
openModal() { presentModal() {
this.modal.open(ModalPassParams, { userId: 3141209 }); let modal = Modal.create(ModalPassData, { userId: 8675309 });
} this.nav.present(modal);
openToolbarModal() { modal.data.subscribe(data => {
this.modal.open(ToolbarModal).then(modalRef => { console.log('data', data);
// modal has finished opening
// modalRef is a reference to the modal instance
modalRef.onClose = (modalData) => {
// somehow modalRef.close(modalData) was called w/ modalData
console.log('modalRef.onClose', modalData)
}
}); });
} }
openModalChildNav() { presentModalChildNav() {
this.modal.open(ContactUs, null, { let modal = Modal.create(ContactUs);
handle: 'my-awesome-modal' this.nav.present(modal);
});
} }
openModalCustomAnimation() { presentToolbarModal() {
this.modal.open(ContactUs, null, { let modal = Modal.create(ToolbarModal);
handle: 'my-awesome-modal', this.nav.present(modal);
enterAnimation: 'my-fade-in', }
leaveAnimation: 'my-fade-out'
presentModalCustomAnimation() {
let modal = Modal.create(ContactUs);
this.nav.present(modal, {
animation: 'my-fade-in'
}); });
} }
} }
@ -62,44 +58,65 @@ class E2EApp {
@Page({ @Page({
template: ` template: `
<h3>Pass Params</h3> <ion-navbar *navbar>
<p>User Id: {{userId}}</p> <ion-title>Data in/out</ion-title>
<p><button (click)="close()">Close Modal</button></p> </ion-navbar>
<ion-content>
<ion-list>
<ion-input>
<ion-label>UserId</ion-label>
<input type="number" [(ngModel)]="data.userId">
</ion-input>
<ion-input>
<ion-label>Name</ion-label>
<input type="text" [(ngModel)]="data.name">
</ion-input>
</ion-list>
<button full (click)="submit()">Submit</button>
</ion-content>
` `
}) })
class ModalPassParams { class ModalPassData {
constructor(private modal: Modal, params: NavParams) { constructor(params: NavParams, viewCtrl: ViewController) {
this.userId = params.get('userId'); this.data = {
userId: params.get('userId'),
name: 'Jenny'
};
this.viewCtrl = viewCtrl;
}
submit() {
this.viewCtrl.data.emit(this.data);
this.viewCtrl.dismiss();
} }
} }
@Page({ @Page({
template: ` template: `
<ion-toolbar> <ion-toolbar primary>
<ion-title>Modals</ion-title> <ion-title>Toolbar 1</ion-title>
</ion-toolbar> </ion-toolbar>
<ion-toolbar primary> <ion-toolbar>
<ion-title>Another toolbar</ion-title> <ion-title>Toolbar 2</ion-title>
</ion-toolbar> </ion-toolbar>
<ion-content padding> <ion-content padding>
<button block danger (click)="closeModal()" class="e2eCloseToolbarModal"> <button block danger (click)="dismiss()" class="e2eCloseToolbarModal">
<icon close></icon> Dismission Modal
Close Modal
</button> </button>
</ion-content> </ion-content>
` `
}) })
class ToolbarModal { class ToolbarModal {
constructor(private modal: Modal) {}
closeModal() { constructor(viewCtrl: ViewController) {
this.close({ this.viewCtrl = viewCtrl;
adel: 'hello', }
lionel: 'hello'
}); dismiss() {
this.viewCtrl.dismiss();
} }
} }
@ -142,7 +159,7 @@ class ContactUs {
<ion-navbar *navbar> <ion-navbar *navbar>
<ion-title>First Page Header</ion-title> <ion-title>First Page Header</ion-title>
<ion-buttons start> <ion-buttons start>
<button class="e2eCloseMenu" (click)="closeModal()">Close</button> <button class="e2eCloseMenu" (click)="dismiss()">Close</button>
</ion-buttons> </ion-buttons>
</ion-navbar> </ion-navbar>
<ion-content padding> <ion-content padding>
@ -152,23 +169,14 @@ class ContactUs {
<p> <p>
<button (click)="openActionSheet()">Open Action Sheet</button> <button (click)="openActionSheet()">Open Action Sheet</button>
</p> </p>
<p>
<button (click)="closeByHandeModal()">Close By Handle</button>
</p>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f> <f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f> <f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
</ion-content> </ion-content>
` `
}) })
class ModalFirstPage { class ModalFirstPage {
constructor( constructor(nav: NavController) {
nav: NavController,
modal: Modal,
actionSheet: ActionSheet
) {
this.nav = nav; this.nav = nav;
this.modal = modal;
this.actionSheet = actionSheet;
} }
push() { push() {
@ -179,12 +187,8 @@ class ModalFirstPage {
this.nav.push(page, params, opts); this.nav.push(page, params, opts);
} }
closeModal() { dismiss() {
this.modal.get().close(); this.nav.rootNav.pop();
}
closeByHandeModal() {
this.modal.get('my-awesome-modal').close();
} }
openActionSheet() { openActionSheet() {
@ -239,6 +243,16 @@ class ModalSecondPage {
} }
@App({
template: '<ion-nav [root]="root"></ion-nav>'
})
class E2EApp {
constructor() {
this.root = E2EPage;
}
}
class FadeIn extends Animation { class FadeIn extends Animation {
constructor(enteringView, leavingView) { constructor(enteringView, leavingView) {
super(enteringView.pageRef()); super(enteringView.pageRef());

View File

@ -1,15 +1,19 @@
<ion-navbar *navbar>
<ion-title>Modals</ion-title>
</ion-navbar>
<ion-content padding> <ion-content padding>
<p> <p>
<button class="e2eOpenModal" (click)="openModalChildNav()">Open modal w/ child ion-nav</button> <button (click)="presentModal()">Present modal, pass params</button>
</p> </p>
<p> <p>
<button (click)="openModal()">Open modal, pass params</button> <button class="e2eOpenModal" (click)="presentModalChildNav()">Present modal w/ child ion-nav</button>
</p> </p>
<p> <p>
<button class="e2eOpenToolbarModal" (click)="openToolbarModal()">Open modal w/ toolbar</button> <button class="e2eOpenToolbarModal" (click)="presentToolbarModal()">Present modal w/ toolbar</button>
</p> </p>
<p> <p>
<button (click)="openModalCustomAnimation()">Modal: Custom Animation</button> <button (click)="presentModalCustomAnimation()">Modal: Custom Animation</button>
</p> </p>
</ion-content> </ion-content>

View File

@ -268,8 +268,6 @@ export class NavController extends Ion {
// create a new ViewController // create a new ViewController
let enteringView = new ViewController(componentType, params); let enteringView = new ViewController(componentType, params);
enteringView.setNav(this); enteringView.setNav(this);
enteringView.pageType = opts.pageType;
enteringView.handle = opts.handle || null;
// default the direction to "forward" // default the direction to "forward"
opts.direction = opts.direction || 'forward'; opts.direction = opts.direction || 'forward';
@ -293,7 +291,9 @@ export class NavController extends Ion {
} }
present(enteringView, opts={}) { present(enteringView, opts={}) {
enteringView.setNav(this); let rootNav = this.rootNav;
enteringView.setNav(rootNav);
let resolve; let resolve;
let promise = new Promise(res => { resolve = res; }); let promise = new Promise(res => { resolve = res; });
@ -303,19 +303,24 @@ export class NavController extends Ion {
opts.direction = 'forward'; opts.direction = 'forward';
if (!opts.animation) { if (!opts.animation) {
opts.animation = enteringView.getTransitionName(opts.direction); opts.animation = enteringView.getTransitionName('forward');
} }
enteringView.setLeavingOpts({
keyboardClose: false,
skipCache: true,
direction: 'back',
animation: enteringView.getTransitionName('back')
});
// the active view is going to be the leaving one (if one exists) // the active view is going to be the leaving one (if one exists)
let leavingView = this.getActive() || new ViewController(); let leavingView = rootNav.getActive() || new ViewController();
leavingView.shouldCache = (isBoolean(opts.cacheLeavingView) ? opts.cacheLeavingView : true); leavingView.shouldCache = (isBoolean(opts.cacheLeavingView) ? opts.cacheLeavingView : true);
leavingView.shouldDestroy = !leavingView.shouldCache; leavingView.shouldDestroy = !leavingView.shouldCache;
if (leavingView.shouldDestroy) { if (leavingView.shouldDestroy) {
leavingView.willUnload(); leavingView.willUnload();
} }
let rootNav = this.rootNav;
// add the view to the stack // add the view to the stack
rootNav._add(enteringView); rootNav._add(enteringView);
@ -325,20 +330,6 @@ export class NavController extends Ion {
return promise; return promise;
} }
dismiss(leavingView, opts={}) {
opts.skipCache = true;
opts.direction ='back';
if (!opts.animation) {
opts.animation = leavingView.getTransitionName(opts.direction);
}
let rootNav = this.rootNav;
let index = rootNav.indexOf(leavingView);
return rootNav.remove(index, opts);
}
/** /**
* If you wanted to navigate back from a current view, you can use the back-button or programatically call `pop()` * If you wanted to navigate back from a current view, you can use the back-button or programatically call `pop()`
* Similar to `push()`, you can pass animation options. * Similar to `push()`, you can pass animation options.
@ -854,6 +845,7 @@ export class NavController extends Ion {
let transAnimation = Animation.createTransition(enteringView, let transAnimation = Animation.createTransition(enteringView,
leavingView, leavingView,
opts); opts);
if (opts.animate === false) { if (opts.animate === false) {
// force it to not animate the elements, just apply the "to" styles // force it to not animate the elements, just apply the "to" styles
transAnimation.clearDuration(); transAnimation.clearDuration();
@ -867,8 +859,8 @@ export class NavController extends Ion {
this.app.setEnabled(enableApp, duration); this.app.setEnabled(enableApp, duration);
this.setTransitioning(!enableApp, duration); this.setTransitioning(!enableApp, duration);
if (opts.pageType) { if (enteringView.viewType) {
transAnimation.before.addClass(opts.pageType); transAnimation.before.addClass(enteringView.viewType);
} }
wtfEndTimeRange(wtfScope); wtfEndTimeRange(wtfScope);
@ -1401,34 +1393,6 @@ export class NavController extends Ion {
return null; return null;
} }
/**
* @private
* @param {Handle} The handle of the page you want to get
* @returns {Component} Returns the component that matches the handle given
*/
getByHandle(handle) {
for (let i = 0, ii = this._views.length; i < ii; i++) {
if (this._views[i].handle === handle) {
return this._views[i];
}
}
return null;
}
/**
* @private
* @param {TODO} pageType TODO
* @returns {TODO} TODO
*/
getByType(pageType) {
for (let i = 0, ii = this._views.length; i < ii; i++) {
if (this._views[i].pageType === pageType) {
return this._views[i];
}
}
return null;
}
/** /**
* @private * @private
* @param {TODO} view TODO * @param {TODO} view TODO

View File

@ -1,3 +1,4 @@
import {Output, EventEmitter} from 'angular2/core';
import {NavParams} from './nav-controller'; import {NavParams} from './nav-controller';
/** /**
@ -16,16 +17,20 @@ import {NavParams} from './nav-controller';
* ``` * ```
*/ */
export class ViewController { export class ViewController {
@Output() data: EventEmitter<any> = new EventEmitter();
constructor(componentType, data={}) { constructor(componentType, data={}) {
this.componentType = componentType; this.componentType = componentType;
this.data = data; this._data = data;
this.instance = {}; this.instance = {};
this.state = 0; this.state = 0;
this._destroys = []; this._destroys = [];
this._loaded = false; this._loaded = false;
this._outputData = null;
this.shouldDestroy = false; this.shouldDestroy = false;
this.shouldCache = false; this.shouldCache = false;
this.viewType = '';
this._leavingOpts = null;
} }
setNav(navCtrl) { setNav(navCtrl) {
@ -37,7 +42,15 @@ export class ViewController {
} }
getNavParams() { getNavParams() {
return new NavParams(this.data); return new NavParams(this._data);
}
dismiss() {
return this._nav.remove(this._nav.indexOf(this), this._leavingOpts);
}
setLeavingOpts(opts) {
this._leavingOpts = opts;
} }
/** /**