refactor(modal): update API for modal to match other overlays

This commit is contained in:
Dan Bucholtz
2017-12-02 00:37:19 -06:00
parent 0a7d827df7
commit 01c46b230f
3 changed files with 77 additions and 60 deletions

View File

@ -8,11 +8,11 @@ import { Modal, ModalEvent, ModalOptions } from '../../index';
export class ModalController { export class ModalController {
private ids = 0; private ids = 0;
private modalResolves: {[modalId: string]: Function} = {}; private modalResolves: {[modalId: string]: Function} = {};
private modals: Modal[] = []; private modals: HTMLIonModalElement[] = [];
@Method() @Method()
create(opts?: ModalOptions) { create(opts?: ModalOptions): Promise<HTMLIonModalElement> {
// create ionic's wrapping ion-modal component // create ionic's wrapping ion-modal component
const modal = document.createElement('ion-modal'); const modal = document.createElement('ion-modal');
@ -31,7 +31,7 @@ export class ModalController {
appRoot.appendChild(modal as any); appRoot.appendChild(modal as any);
// store the resolve function to be called later up when the modal loads // store the resolve function to be called later up when the modal loads
return new Promise<Modal>(resolve => { return new Promise<HTMLIonModalElement>(resolve => {
this.modalResolves[modal.modalId] = resolve; this.modalResolves[modal.modalId] = resolve;
}); });
} }
@ -39,7 +39,7 @@ export class ModalController {
@Listen('body:ionModalDidLoad') @Listen('body:ionModalDidLoad')
protected modalDidLoad(ev: ModalEvent) { protected modalDidLoad(ev: ModalEvent) {
const modal = ev.detail.modal; const modal = ev.target as HTMLIonModalElement;
const modalResolve = this.modalResolves[modal.modalId]; const modalResolve = this.modalResolves[modal.modalId];
if (modalResolve) { if (modalResolve) {
modalResolve(modal); modalResolve(modal);
@ -50,13 +50,13 @@ export class ModalController {
@Listen('body:ionModalWillPresent') @Listen('body:ionModalWillPresent')
protected modalWillPresent(ev: ModalEvent) { protected modalWillPresent(ev: ModalEvent) {
this.modals.push(ev.detail.modal); this.modals.push(ev.target as HTMLIonModalElement);
} }
@Listen('body:ionModalWillDismiss, body:ionModalDidUnload') @Listen('body:ionModalWillDismiss, body:ionModalDidUnload')
protected modalWillDismiss(ev: ModalEvent) { protected modalWillDismiss(ev: ModalEvent) {
const index = this.modals.indexOf(ev.detail.modal); const index = this.modals.indexOf(ev.target as HTMLIonModalElement);
if (index > -1) { if (index > -1) {
this.modals.splice(index, 1); this.modals.splice(index, 1);
} }

View File

@ -1,5 +1,13 @@
import { Component, Element, Event, EventEmitter, Listen, Prop } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config } from '../../index'; import {
Animation,
AnimationBuilder,
AnimationController,
Config,
OverlayDismissEvent,
OverlayDismissEventDetail
} from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme'; import { createThemedClasses } from '../../utils/theme';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
@ -23,32 +31,32 @@ export class Modal {
/** /**
* @output {ModalEvent} Emitted after the modal has loaded. * @output {ModalEvent} Emitted after the modal has loaded.
*/ */
@Event() ionModalDidLoad: EventEmitter; @Event() ionModalDidLoad: EventEmitter<ModalEventDetail>;
/** /**
* @output {ModalEvent} Emitted after the modal has presented. * @output {ModalEvent} Emitted after the modal has presented.
*/ */
@Event() ionModalDidPresent: EventEmitter; @Event() ionModalDidPresent: EventEmitter<ModalEventDetail>;
/** /**
* @output {ModalEvent} Emitted before the modal has presented. * @output {ModalEvent} Emitted before the modal has presented.
*/ */
@Event() ionModalWillPresent: EventEmitter; @Event() ionModalWillPresent: EventEmitter<ModalEventDetail>;
/** /**
* @output {ModalEvent} Emitted before the modal has dismissed. * @output {ModalEvent} Emitted before the modal has dismissed.
*/ */
@Event() ionModalWillDismiss: EventEmitter; @Event() ionModalWillDismiss: EventEmitter<ModalDismissEventDetail>;
/** /**
* @output {ModalEvent} Emitted after the modal has dismissed. * @output {ModalEvent} Emitted after the modal has dismissed.
*/ */
@Event() ionModalDidDismiss: EventEmitter; @Event() ionModalDidDismiss: EventEmitter<ModalDismissEventDetail>;
/** /**
* @output {ModalEvent} Emitted after the modal has unloaded. * @output {ModalEvent} Emitted after the modal has unloaded.
*/ */
@Event() ionModalDidUnload: EventEmitter; @Event() ionModalDidUnload: EventEmitter<ModalEventDetail>;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@ -64,64 +72,65 @@ export class Modal {
@Prop() enterAnimation: AnimationBuilder; @Prop() enterAnimation: AnimationBuilder;
@Prop() leaveAnimation: AnimationBuilder; @Prop() leaveAnimation: AnimationBuilder;
@Prop() animate: boolean;
private animation: Animation; private animation: Animation;
@Method()
present() { present() {
return new Promise<void>(resolve => {
this._present(resolve);
});
}
private _present(resolve: Function) {
if (this.animation) { if (this.animation) {
this.animation.destroy(); this.animation.destroy();
this.animation = null; this.animation = null;
} }
this.ionModalWillPresent.emit({ modal: this }); this.ionModalWillPresent.emit({ loading: this });
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
// build the animation and kick it off // build the animation and kick it off
this.animationCtrl.create(animationBuilder, this.el).then(animation => { // build the animation and kick it off
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation; this.animation = animation;
if (!this.animate) {
animation.onFinish((a: any) => { // if the duration is 0, it won't actually animate I don't think
a.destroy(); // TODO - validate this
this.ionModalDidPresent.emit({ modal: this }); this.animation = animation.duration(0);
resolve(); }
}).play(); return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.ionModalDidPresent.emit();
}); });
} }
dismiss() { @Method()
dismiss(data?: any, role?: string) {
if (this.animation) { if (this.animation) {
this.animation.destroy(); this.animation.destroy();
this.animation = null; this.animation = null;
} }
this.ionModalWillDismiss.emit({
return new Promise<void>(resolve => { data,
this.ionModalWillDismiss.emit({ modal: this }); role
});
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.leaveAnimation || this.config.get('modalLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation); const animationBuilder = this.leaveAnimation || this.config.get('modalLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
// build the animation and kick it off return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation; this.animation = animation;
return playAnimationAsync(animation);
animation.onFinish((a: any) => { }).then((animation) => {
a.destroy(); animation.destroy();
this.ionModalDidDismiss.emit({ modal: this }); return domControllerAsync(Context.dom.write, () => {
Context.dom.write(() => {
this.el.parentNode.removeChild(this.el); this.el.parentNode.removeChild(this.el);
}); });
resolve(); }).then(() => {
}).play(); this.ionModalDidDismiss.emit({
data,
role
}); });
}); });
} }
@ -195,10 +204,20 @@ export interface ModalOptions {
} }
export interface ModalEvent extends Event { export interface ModalEvent extends CustomEvent {
detail: { detail: ModalEventDetail;
modal: Modal; }
};
export interface ModalEventDetail {
}
export interface ModalDismissEventDetail extends OverlayDismissEventDetail {
// keep this just for the sake of static types and potential future extensions
}
export interface ModalDismissEvent extends OverlayDismissEvent {
// keep this just for the sake of static types and potential future extensions
} }
export { export {

View File

@ -27,15 +27,13 @@
</ion-app> </ion-app>
<script> <script>
const controller = document.querySelector('ion-modal-controller'); async function presentModal() {
function presentModal() { const modalController = document.querySelector('ion-modal-controller');
controller await modalController.componentOnReady();
.create({ const modalElement = await modalController.create({
component: 'page-one' component: 'page-one'
})
.then(modal => {
modal.present();
}); });
modalElement.present();
} }
</script> </script>
</body> </body>