refactor(popover): Make api and events match other overlays

This commit is contained in:
Dan Bucholtz
2017-12-02 00:54:21 -06:00
parent 71ff57c536
commit 3f6b937cec
3 changed files with 78 additions and 61 deletions

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { Popover, PopoverEvent, PopoverOptions } from '../../index'; import { PopoverEvent, PopoverOptions } from '../../index';
@Component({ @Component({
tag: 'ion-popover-controller' tag: 'ion-popover-controller'
@ -7,14 +7,14 @@ import { Popover, PopoverEvent, PopoverOptions } from '../../index';
export class PopoverController { export class PopoverController {
private ids = 0; private ids = 0;
private popoverResolves: {[popoverId: string]: Function} = {}; private popoverResolves: {[popoverId: string]: Function} = {};
private popovers: Popover[] = []; private popovers: HTMLIonPopoverElement[] = [];
/** /**
* Create a popover component instance * Create a popover component instance
* @param opts Options when creating a new popover instance * @param opts Options when creating a new popover instance
*/ */
@Method() @Method()
create(opts?: PopoverOptions) { create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> {
// create ionic's wrapping ion-popover component // create ionic's wrapping ion-popover component
const popover = document.createElement('ion-popover'); const popover = document.createElement('ion-popover');
const id = this.ids++; const id = this.ids++;
@ -32,7 +32,7 @@ export class PopoverController {
appRoot.appendChild(popover as any); appRoot.appendChild(popover as any);
// store the resolve function to be called later up when the popover loads // store the resolve function to be called later up when the popover loads
return new Promise<Popover>(resolve => { return new Promise<HTMLIonPopoverElement>(resolve => {
this.popoverResolves[popover.popoverId] = resolve; this.popoverResolves[popover.popoverId] = resolve;
}); });
} }
@ -40,7 +40,7 @@ export class PopoverController {
@Listen('body:ionPopoverDidLoad') @Listen('body:ionPopoverDidLoad')
protected didLoad(ev: PopoverEvent) { protected didLoad(ev: PopoverEvent) {
const popover = ev.detail.popover; const popover = ev.target as HTMLIonPopoverElement;
const popoverResolve = this.popoverResolves[popover.popoverId]; const popoverResolve = this.popoverResolves[popover.popoverId];
if (popoverResolve) { if (popoverResolve) {
popoverResolve(popover); popoverResolve(popover);
@ -51,13 +51,13 @@ export class PopoverController {
@Listen('body:ionPopoverWillPresent') @Listen('body:ionPopoverWillPresent')
protected willPresent(ev: PopoverEvent) { protected willPresent(ev: PopoverEvent) {
this.popovers.push(ev.detail.popover); this.popovers.push(ev.target as HTMLIonPopoverElement);
} }
@Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload') @Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload')
protected willDismiss(ev: PopoverEvent) { protected willDismiss(ev: PopoverEvent) {
const index = this.popovers.indexOf(ev.detail.popover); const index = this.popovers.indexOf(ev.target as HTMLIonPopoverElement);
if (index > -1) { if (index > -1) {
this.popovers.splice(index, 1); this.popovers.splice(index, 1);
} }

View File

@ -1,6 +1,13 @@
import { Component, Element, Event, EventEmitter, Listen, Prop, State } 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';
@ -26,32 +33,32 @@ export class Popover {
/** /**
* @output {PopoverEvent} Emitted after the popover has loaded. * @output {PopoverEvent} Emitted after the popover has loaded.
*/ */
@Event() ionPopoverDidLoad: EventEmitter; @Event() ionPopoverDidLoad: EventEmitter<PopoverEventDetail>;
/** /**
* @output {PopoverEvent} Emitted after the popover has presented. * @output {PopoverEvent} Emitted after the popover has presented.
*/ */
@Event() ionPopoverDidPresent: EventEmitter; @Event() ionPopoverDidPresent: EventEmitter<PopoverEventDetail>;
/** /**
* @output {PopoverEvent} Emitted before the popover has presented. * @output {PopoverEvent} Emitted before the popover has presented.
*/ */
@Event() ionPopoverWillPresent: EventEmitter; @Event() ionPopoverWillPresent: EventEmitter<PopoverEventDetail>;
/** /**
* @output {PopoverEvent} Emitted before the popover has dismissed. * @output {PopoverEvent} Emitted before the popover has dismissed.
*/ */
@Event() ionPopoverWillDismiss: EventEmitter; @Event() ionPopoverWillDismiss: EventEmitter<PopoverDismissEventDetail>;
/** /**
* @output {PopoverEvent} Emitted after the popover has dismissed. * @output {PopoverEvent} Emitted after the popover has dismissed.
*/ */
@Event() ionPopoverDidDismiss: EventEmitter; @Event() ionPopoverDidDismiss: EventEmitter<PopoverDismissEventDetail>;
/** /**
* @output {PopoverEvent} Emitted after the popover has unloaded. * @output {PopoverEvent} Emitted after the popover has unloaded.
*/ */
@Event() ionPopoverDidUnload: EventEmitter; @Event() ionPopoverDidUnload: EventEmitter<PopoverEventDetail>;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@ -68,76 +75,76 @@ export class Popover {
@Prop() popoverId: string; @Prop() popoverId: string;
@Prop() showBackdrop: boolean = true; @Prop() showBackdrop: boolean = true;
@Prop() translucent: boolean = false; @Prop() translucent: boolean = false;
@Prop() animate: boolean;
@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.ionPopoverWillPresent.emit({ popover: this }); this.ionPopoverWillPresent.emit();
// 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('popoverEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('popoverEnter', 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, this.ev).then(animation => { 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.animation = animation.duration(0);
}
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.componentDidEnter(); this.componentDidEnter();
resolve();
}).play();
}); });
} }
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;
} }
return new Promise(resolve => {
this.ionPopoverWillDismiss.emit({ popover: this });
// get the user's animation fn if one was provided this.ionPopoverWillDismiss.emit({
data,
role
});
const animationBuilder = this.leaveAnimation || this.config.get('popoverLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation); const animationBuilder = this.leaveAnimation || this.config.get('popoverLeave', 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.ionPopoverDidDismiss.emit({ popover: this }); return domControllerAsync(Context.dom.write, () => {
Context.dom.write(() => {
this.el.parentNode.removeChild(this.el); this.el.parentNode.removeChild(this.el);
}); });
}).then(() => {
resolve(); this.ionPopoverDidDismiss.emit({
}).play(); data,
role
}); });
}); });
} }
componentDidLoad() { componentDidLoad() {
this.ionPopoverDidLoad.emit({ popover: this }); this.ionPopoverDidLoad.emit();
} }
componentDidEnter() { componentDidEnter() {
this.ionPopoverDidPresent.emit({ popover: this }); this.ionPopoverDidPresent.emit();
} }
componentDidUnload() { componentDidUnload() {
this.ionPopoverDidUnload.emit({ popover: this }); this.ionPopoverDidUnload.emit();
} }
@Listen('ionDismiss') @Listen('ionDismiss')
@ -205,10 +212,20 @@ export interface PopoverOptions {
ev: Event; ev: Event;
} }
export interface PopoverEvent { export interface PopoverEvent extends CustomEvent {
detail: { detail: PopoverEventDetail;
popover: Popover; }
};
export interface PopoverEventDetail {
}
export interface PopoverDismissEventDetail extends OverlayDismissEventDetail {
// keep this just for the sake of static types and potential future extensions
}
export interface PopoverDismissEvent extends OverlayDismissEvent {
// keep this just for the sake of static types and potential future extensions
} }
export const POPOVER_POSITION_PROPERTIES: any = { export const POPOVER_POSITION_PROPERTIES: any = {

View File

@ -44,11 +44,11 @@
</ion-page> </ion-page>
</ion-app> </ion-app>
<script> <script>
function presentPopover(opts) { async function presentPopover(opts) {
var popoverController = document.querySelector('ion-popover-controller'); const popoverController = document.querySelector('ion-popover-controller');
popoverController.create(opts).then(popover => { await popoverController.componentOnReady();
popover.present(); const popoverElement = await popoverController.create(opts);
}); return await popoverElement.present();
} }
class ProfilePage extends HTMLElement { class ProfilePage extends HTMLElement {