fix(overlays): missing events + playAnimation

This commit is contained in:
Manu Mtz.-Almeida
2018-02-23 19:41:03 +01:00
parent d26afdfea0
commit 11b65c245b
7 changed files with 205 additions and 258 deletions

View File

@ -131,7 +131,7 @@ export class ActionSheet implements OverlayInterface {
@Listen('ionBackdropTap')
protected onBackdropTap() {
this.dismiss(null, BACKDROP);
this.dismiss(null, BACKDROP).catch();
}
/**

View File

@ -1,6 +1,6 @@
import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { domControllerAsync, playAnimationAsync, autoFocus } from '../../utils/helpers';
import { createThemedClasses, getClassMap } from '../../utils/theme';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
@ -137,7 +137,7 @@ export class Alert implements OverlayInterface {
@Listen('ionBackdropTap')
protected onBackdropTap() {
this.dismiss(null, BACKDROP);
this.dismiss(null, BACKDROP).catch();
}
/**
@ -158,10 +158,7 @@ export class Alert implements OverlayInterface {
// build the animation and kick it off
return this.playAnimation(animationBuilder).then(() => {
const firstInput = this.el.querySelector('[tabindex]') as HTMLElement;
if (firstInput) {
firstInput.focus();
}
autoFocus(this.el);
this.ionAlertDidPresent.emit();
});
}
@ -181,6 +178,8 @@ export class Alert implements OverlayInterface {
const animationBuilder = this.leaveAnimation || this.config.get('alertLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
return this.playAnimation(animationBuilder).then(() => {
this.ionAlertDidDismiss.emit({data, role});
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
@ -274,7 +273,6 @@ export class Alert implements OverlayInterface {
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
// if the duration is 0, it won't actually animate I don't think
animation.duration(0);
}
return playAnimationAsync(animation);

View File

@ -129,19 +129,6 @@ export class Loading implements OverlayInterface {
this.ionLoadingDidLoad.emit();
}
componentDidEnter() {
// blur the currently active element
const activeElement: any = document.activeElement;
activeElement && activeElement.blur && activeElement.blur();
// If there is a duration, dismiss after that amount of time
if (typeof this.duration === 'number' && this.duration > 10) {
this.durationTimeout = setTimeout(() => this.dismiss(), this.duration);
}
this.ionLoadingDidPresent.emit();
}
componentDidUnload() {
this.ionLoadingDidUnload.emit();
}
@ -156,7 +143,7 @@ export class Loading implements OverlayInterface {
@Listen('ionBackdropTap')
protected onBackdropTap() {
this.dismiss(null, BACKDROP);
this.dismiss(null, BACKDROP).catch();
}
/**
@ -177,7 +164,15 @@ export class Loading implements OverlayInterface {
// build the animation and kick it off
return this.playAnimation(animationBuilder).then(() => {
this.componentDidEnter();
// blur the currently active element
const activeElement: any = document.activeElement;
activeElement && activeElement.blur && activeElement.blur();
// If there is a duration, dismiss after that amount of time
if (typeof this.duration === 'number' && this.duration > 10) {
this.durationTimeout = setTimeout(() => this.dismiss(), this.duration);
}
this.ionLoadingDidPresent.emit();
});
}

View File

@ -139,7 +139,7 @@ export class Modal implements OverlayInterface {
@Listen('ionBackdropTap')
protected onBackdropTap() {
this.dismiss(null, BACKDROP);
this.dismiss(null, BACKDROP).catch();
}
/**
@ -151,12 +151,6 @@ export class Modal implements OverlayInterface {
return Promise.reject('overlay already presented');
}
this.presented = true;
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionModalWillPresent.emit();
this.el.style.zIndex = `${20000 + this.overlayId}`;
@ -177,20 +171,12 @@ export class Modal implements OverlayInterface {
// add the modal by default to the data being passed
this.data = this.data || {};
this.data.modal = this.el;
this.delegate.attachViewToDom(userComponentParent, this.component, this.data, cssClasses)
.then((mountingData) => {
this.usersComponentElement = mountingData.element;
});
return this.animationCtrl.create(animationBuilder, this.el)
.then(animation => {
this.animation = animation;
if (!this.willAnimate) this.animation = animation.duration(0);
return playAnimationAsync(animation);
})
.then((animation) => {
animation.destroy();
this.ionModalDidPresent.emit();
return this.delegate.attachViewToDom(userComponentParent, this.component, this.data, cssClasses)
.then((mountingData) => this.usersComponentElement = mountingData.element)
.then(() => this.playAnimation(animationBuilder))
.then(() => {
this.ionModalDidPresent.emit();
});
}
@ -203,10 +189,6 @@ export class Modal implements OverlayInterface {
return Promise.reject('overlay is not presented');
}
this.presented = false;
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionModalWillDismiss.emit({data, role});
if (!this.delegate) {
@ -216,20 +198,8 @@ export class Modal implements OverlayInterface {
// get the user's animation fn if one was provided
const animationBuilder = this.leaveAnimation || this.config.get('modalLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
return this.animationCtrl.create(animationBuilder, this.el)
.then(animation => {
this.animation = animation;
if (!this.willAnimate) {
this.animation = animation.duration(0);
}
return playAnimationAsync(animation);
})
.then((animation) => {
animation.destroy();
return this.playAnimation(animationBuilder).then(() => {
this.ionModalDidDismiss.emit({data, role});
})
.then(() => {
return domControllerAsync(this.dom.write, () => {
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);
this.delegate.removeViewFromDom(userComponentParent, this.usersComponentElement);
@ -238,6 +208,24 @@ export class Modal implements OverlayInterface {
});
}
private playAnimation(animationBuilder: AnimationBuilder) {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
animation.duration(0);
}
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.animation = null;
});
}
@Method()
getUserComponentContainer(): HTMLElement {
return this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);

View File

@ -111,6 +111,47 @@ export class Picker implements OverlayInterface {
*/
@Event() ionPickerDidUnload: EventEmitter<PickerEventDetail>;
componentDidLoad() {
if (!this.spinner) {
let defaultSpinner = 'lines';
if (this.mode === 'md') {
defaultSpinner = 'crescent';
}
this.spinner = this.config.get('pickerSpinner') || defaultSpinner;
}
if (this.showSpinner === null || this.showSpinner === undefined) {
this.showSpinner = !!(this.spinner && this.spinner !== 'hide');
}
this.ionPickerDidLoad.emit();
}
componentDidUnload() {
this.ionPickerDidUnload.emit();
}
@Listen('ionDismiss')
protected onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
@Listen('ionBackdropTap')
protected onBackdropTap() {
const cancelBtn = this.buttons.find(b => b.role === 'cancel');
if (cancelBtn) {
this.buttonClick(cancelBtn);
} else {
this.dismiss().catch();
}
}
/**
* Present the picker overlay after it has been created.
*/
@ -134,17 +175,16 @@ export class Picker implements OverlayInterface {
const animationBuilder = this.enterAnimation || this.config.get('pickerEnter', iosEnterAnimation);
// build the animation and kick it off
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
// if the duration is 0, it won't actually animate I don't think
// TODO - validate this
this.animation = animation.duration(0);
return this.playAnimation(animationBuilder).then(() => {
// blur the currently active element
const activeElement: any = document.activeElement;
activeElement && activeElement.blur && activeElement.blur();
// If there is a duration, dismiss after that amount of time
if (typeof this.duration === 'number' && this.duration > 10) {
this.durationTimeout = setTimeout(() => this.dismiss(), this.duration);
}
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.componentDidEnter();
this.ionPickerDidPresent.emit();
});
}
@ -159,122 +199,18 @@ export class Picker implements OverlayInterface {
this.presented = false;
clearTimeout(this.durationTimeout);
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionPickerWillDismiss.emit({
data,
role
});
this.ionPickerWillDismiss.emit({data, role});
const animationBuilder = this.leaveAnimation || this.config.get('pickerLeave', iosLeaveAnimation);
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return this.playAnimation(animationBuilder).then(() => {
this.ionPickerDidDismiss.emit({data, role});
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {
this.ionPickerDidDismiss.emit({
data,
role
});
});
}
componentDidLoad() {
if (!this.spinner) {
let defaultSpinner = 'lines';
if (this.mode === 'md') {
defaultSpinner = 'crescent';
}
this.spinner = this.config.get('pickerSpinner') || defaultSpinner;
}
if (this.showSpinner === null || this.showSpinner === undefined) {
this.showSpinner = !!(this.spinner && this.spinner !== 'hide');
}
this.ionPickerDidLoad.emit();
}
componentDidEnter() {
// blur the currently active element
const activeElement: any = document.activeElement;
activeElement && activeElement.blur && activeElement.blur();
// If there is a duration, dismiss after that amount of time
if (typeof this.duration === 'number' && this.duration > 10) {
this.durationTimeout = setTimeout(() => this.dismiss(), this.duration);
}
this.ionPickerDidPresent.emit();
}
componentDidUnload() {
this.ionPickerDidUnload.emit();
}
@Listen('ionDismiss')
protected onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
@Listen('ionBackdropTap')
protected onBackdropTap() {
const cancelBtn = this.buttons.find(b => b.role === 'cancel');
if (cancelBtn) {
this.buttonClick(cancelBtn);
} else {
this.dismiss();
}
}
buttonClick(button: PickerButton) {
// if (this.disabled) {
// return;
// }
// keep the time of the most recent button click
let shouldDismiss = true;
if (button.handler) {
// a handler has been provided, execute it
// pass the handler the values from the inputs
if (button.handler(this.getSelected()) === false) {
// if the return value of the handler is false then do not dismiss
shouldDismiss = false;
}
}
if (shouldDismiss) {
this.dismiss();
}
}
getSelected(): any {
const selected: {[k: string]: any} = {};
this.columns.forEach((col, index) => {
const selectedColumn = col.options[col.selectedIndex];
selected[col.name] = {
text: selectedColumn ? selectedColumn.text : null,
value: selectedColumn ? selectedColumn.value : null,
columnIndex: index,
};
});
return selected;
}
@Method()
addButton(button: any) {
this.buttons.push(button);
@ -295,11 +231,63 @@ export class Picker implements OverlayInterface {
return this.columns;
}
private playAnimation(animationBuilder: AnimationBuilder) {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
animation.duration(0);
}
return playAnimationAsync(animation);
}).then(animation => {
animation.destroy();
this.animation = null;
})
}
private buttonClick(button: PickerButton) {
// if (this.disabled) {
// return;
// }
// keep the time of the most recent button click
let shouldDismiss = true;
if (button.handler) {
// a handler has been provided, execute it
// pass the handler the values from the inputs
if (button.handler(this.getSelected()) === false) {
// if the return value of the handler is false then do not dismiss
shouldDismiss = false;
}
}
if (shouldDismiss) {
this.dismiss();
}
}
private getSelected(): any {
const selected: {[k: string]: any} = {};
this.columns.forEach((col, index) => {
const selectedColumn = col.options[col.selectedIndex];
selected[col.name] = {
text: selectedColumn ? selectedColumn.text : null,
value: selectedColumn ? selectedColumn.value : null,
columnIndex: index,
};
});
return selected;
}
render() {
// TODO: cssClass
const buttons = this.buttons
.map(b => {
const buttons = this.buttons.map(b => {
if (typeof b === 'string') {
b = { text: b };
}

View File

@ -134,10 +134,6 @@ export class Popover implements OverlayInterface {
this.ionPopoverDidLoad.emit();
}
componentDidEnter() {
this.ionPopoverDidPresent.emit();
}
componentDidUnload() {
this.ionPopoverDidUnload.emit();
}
@ -152,7 +148,7 @@ export class Popover implements OverlayInterface {
@Listen('ionBackdropTap')
protected onBackdropTap() {
this.dismiss(null, BACKDROP);
this.dismiss(null, BACKDROP).catch();
}
/**
@ -165,10 +161,6 @@ export class Popover implements OverlayInterface {
}
this.presented = true;
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionPopoverWillPresent.emit();
this.el.style.zIndex = `${10000 + this.overlayId}`;
@ -191,19 +183,11 @@ export class Popover implements OverlayInterface {
this.data.modal = this.el;
return this.delegate.attachViewToDom(userComponentParent, this.component, this.data, cssClasses)
.then((mountingData) => {
this.usersComponentElement = mountingData.element;
return domControllerAsync(this.dom.raf)
.then(() => this.animationCtrl.create(animationBuilder, this.el, this.ev));
})
.then((animation) => {
this.animation = animation;
if (!this.willAnimate) this.animation = animation.duration(0);
return playAnimationAsync(animation);
})
.then((animation) => {
animation.destroy();
this.componentDidEnter();
.then((mountingData) => this.usersComponentElement = mountingData.element)
.then(() => domControllerAsync(this.dom.raf))
.then(() => this.playAnimation(animationBuilder))
.then(() => {
this.ionPopoverDidPresent.emit();
});
}
@ -229,22 +213,33 @@ export class Popover implements OverlayInterface {
const animationBuilder = this.leaveAnimation || this.config.get('popoverLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
return this.animationCtrl.create(animationBuilder, this.el)
.then(animation => {
this.animation = animation;
return playAnimationAsync(animation);
})
.then((animation) => {
animation.destroy();
this.ionPopoverDidDismiss.emit({ data, role });
})
.then(() => {
return domControllerAsync(this.dom.write, () => {
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_POPOVER_CONTAINER_CLASS}`);
this.delegate.removeViewFromDom(userComponentParent, this.usersComponentElement);
this.el.parentNode.removeChild(this.el);
});
return this.playAnimation(animationBuilder).then(() => {
this.ionPopoverDidDismiss.emit({ data, role });
return domControllerAsync(this.dom.write, () => {
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_POPOVER_CONTAINER_CLASS}`);
this.delegate.removeViewFromDom(userComponentParent, this.usersComponentElement);
this.el.parentNode.removeChild(this.el);
});
});
}
private playAnimation(animationBuilder: AnimationBuilder) {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return this.animationCtrl.create(animationBuilder, this.el, this.ev).then((animation) => {
this.animation = animation;
if (!this.willAnimate) {
animation.duration(0);
}
return playAnimationAsync(animation);
}).then(animation => {
animation.destroy();
this.animation = null;
})
}
hostData() {

View File

@ -133,27 +133,17 @@ export class Toast implements OverlayInterface {
}
this.presented = true;
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionToastWillPresent.emit();
// get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('toastEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
// build the animation and kick it off
return this.animationCtrl.create(animationBuilder, this.el, this.position).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
// if the duration is 0, it won't actually animate I don't think
// TODO - validate this
this.animation = animation.duration(0);
return this.playAnimation(animationBuilder).then(() => {
this.ionToastDidPresent.emit();
if (this.duration) {
setTimeout(() => this.dismiss(), this.duration);
}
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.componentDidEnter();
});
}
@ -167,45 +157,38 @@ export class Toast implements OverlayInterface {
}
this.presented = false;
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionToastWillDismiss.emit({
data,
role
});
this.ionToastWillDismiss.emit({data, role});
const animationBuilder = this.leaveAnimation || this.config.get('toastLeave', this.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
return this.animationCtrl.create(animationBuilder, this.el, this.position).then(animation => {
this.animation = animation;
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.ionToastDidDismiss.emit({
data,
role
});
}).then(() => {
return this.playAnimation(animationBuilder).then(() => {
this.ionToastDidDismiss.emit({data, role});
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
});
}
componentDidLoad() {
this.ionToastDidLoad.emit();
playAnimation(animationBuilder: AnimationBuilder) {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return this.animationCtrl.create(animationBuilder, this.el, this.position).then(animation => {
this.animation = animation;
if (!this.willAnimate) {
animation.duration(0);
}
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
this.animation = null;
});
}
componentDidEnter() {
this.ionToastDidPresent.emit();
if (this.duration) {
setTimeout(() => {
this.dismiss();
}, this.duration);
}
componentDidLoad() {
this.ionToastDidLoad.emit();
}
componentDidUnload() {