diff --git a/packages/core/src/components/action-sheet/action-sheet.tsx b/packages/core/src/components/action-sheet/action-sheet.tsx index ada9df7fcb..7166508f79 100644 --- a/packages/core/src/components/action-sheet/action-sheet.tsx +++ b/packages/core/src/components/action-sheet/action-sheet.tsx @@ -131,7 +131,7 @@ export class ActionSheet implements OverlayInterface { @Listen('ionBackdropTap') protected onBackdropTap() { - this.dismiss(null, BACKDROP); + this.dismiss(null, BACKDROP).catch(); } /** diff --git a/packages/core/src/components/alert/alert.tsx b/packages/core/src/components/alert/alert.tsx index eaa3386c63..800b77b1db 100644 --- a/packages/core/src/components/alert/alert.tsx +++ b/packages/core/src/components/alert/alert.tsx @@ -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); diff --git a/packages/core/src/components/loading/loading.tsx b/packages/core/src/components/loading/loading.tsx index a3d0b4d96c..f45eaf1405 100644 --- a/packages/core/src/components/loading/loading.tsx +++ b/packages/core/src/components/loading/loading.tsx @@ -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(); }); } diff --git a/packages/core/src/components/modal/modal.tsx b/packages/core/src/components/modal/modal.tsx index 182e400442..af50d63092 100644 --- a/packages/core/src/components/modal/modal.tsx +++ b/packages/core/src/components/modal/modal.tsx @@ -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}`); diff --git a/packages/core/src/components/picker/picker.tsx b/packages/core/src/components/picker/picker.tsx index ddc343ad2d..13faa3e11e 100644 --- a/packages/core/src/components/picker/picker.tsx +++ b/packages/core/src/components/picker/picker.tsx @@ -111,6 +111,47 @@ export class Picker implements OverlayInterface { */ @Event() ionPickerDidUnload: EventEmitter; + + 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 }; } diff --git a/packages/core/src/components/popover/popover.tsx b/packages/core/src/components/popover/popover.tsx index d25de9cbf2..65031ff59b 100644 --- a/packages/core/src/components/popover/popover.tsx +++ b/packages/core/src/components/popover/popover.tsx @@ -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() { diff --git a/packages/core/src/components/toast/toast.tsx b/packages/core/src/components/toast/toast.tsx index 23be932909..1afa318639 100644 --- a/packages/core/src/components/toast/toast.tsx +++ b/packages/core/src/components/toast/toast.tsx @@ -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() {