From cc4fecc1bef104837a636b43d349a3ca0e9629f5 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 13 Mar 2018 19:21:26 +0100 Subject: [PATCH] fix(angular): fix overlays --- angular/src/index.ts | 13 +- angular/src/module.ts | 4 +- .../src/providers/action-sheet-controller.ts | 104 +-------- angular/src/providers/alert-controller.ts | 104 +-------- angular/src/providers/loading-controller.ts | 104 +-------- angular/src/providers/menu-controller.ts | 209 ++++++++---------- angular/src/providers/modal-controller.ts | 123 +---------- angular/src/providers/picker-controller.ts | 10 + angular/src/providers/popover-controller.ts | 122 +--------- angular/src/providers/toast-controller.ts | 104 +-------- angular/src/util/overlay.ts | 15 ++ angular/src/util/util.ts | 14 +- 12 files changed, 167 insertions(+), 759 deletions(-) create mode 100644 angular/src/providers/picker-controller.ts create mode 100644 angular/src/util/overlay.ts diff --git a/angular/src/index.ts b/angular/src/index.ts index d880a04f28..7b790c5d68 100644 --- a/angular/src/index.ts +++ b/angular/src/index.ts @@ -13,15 +13,16 @@ export { VirtualHeader } from './directives/virtual-header'; export { VirtualFooter } from './directives/virtual-footer'; /* Providers */ -export { ActionSheetController, ActionSheetProxy } from './providers/action-sheet-controller'; -export { AlertController, AlertProxy } from './providers/alert-controller'; +export { ActionSheetController } from './providers/action-sheet-controller'; +export { AlertController } from './providers/alert-controller'; export { Events } from './providers/events'; -export { LoadingController, LoadingProxy } from './providers/loading-controller'; +export { LoadingController } from './providers/loading-controller'; export { MenuController } from './providers/menu-controller'; -export { ModalController, ModalProxy } from './providers/modal-controller'; +export { PickerController } from './providers/picker-controller'; +export { ModalController } from './providers/modal-controller'; export { Platform } from './providers/platform'; -export { PopoverController, PopoverProxy } from './providers/popover-controller'; -export { ToastController, ToastProxy } from './providers/toast-controller'; +export { PopoverController } from './providers/popover-controller'; +export { ToastController } from './providers/toast-controller'; export * from './types/interfaces'; diff --git a/angular/src/module.ts b/angular/src/module.ts index 5b81265d00..bb5d862465 100644 --- a/angular/src/module.ts +++ b/angular/src/module.ts @@ -31,6 +31,7 @@ import { Events, setupProvideEvents } from './providers/events'; import { LoadingController } from './providers/loading-controller'; import { MenuController } from './providers/menu-controller'; import { ModalController } from './providers/modal-controller'; +import { PickerController } from './providers/picker-controller'; import { Platform } from './providers/platform'; import { PopoverController } from './providers/popover-controller'; import { ToastController } from './providers/toast-controller'; @@ -72,7 +73,7 @@ import { ToastController } from './providers/toast-controller'; ], providers: [ ModalController, - PopoverController + PopoverController, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA @@ -87,6 +88,7 @@ export class IonicAngularModule { ActionSheetController, Events, LoadingController, + PickerController, MenuController, Platform, ToastController, diff --git a/angular/src/providers/action-sheet-controller.ts b/angular/src/providers/action-sheet-controller.ts index 35acb75160..12eb565898 100644 --- a/angular/src/providers/action-sheet-controller.ts +++ b/angular/src/providers/action-sheet-controller.ts @@ -1,104 +1,10 @@ import { Injectable } from '@angular/core'; -import { ActionSheetDismissEvent, ActionSheetOptions } from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let actionSheetId = 0; +import { ActionSheetOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class ActionSheetController { - create(opts?: ActionSheetOptions): ActionSheetProxy { - return getActionSheetProxy(opts); +export class ActionSheetController extends OverlayBaseController { + constructor() { + super('ion-action-sheet-controller'); } } - -export function getActionSheetProxy(opts: ActionSheetOptions) { - return { - id: actionSheetId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as ActionSheetProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as ActionSheetProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(actionSheetProxy: ActionSheetProxyInternal): Promise { - actionSheetProxy.state = PRESENTING; - return loadOverlay(actionSheetProxy.opts).then((actionSheetElement: HTMLIonActionSheetElement) => { - actionSheetProxy.element = actionSheetElement; - - const onDidDismissHandler = (event: ActionSheetDismissEvent) => { - actionSheetElement.removeEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler); - if (actionSheetProxy.onDidDismissHandler) { - actionSheetProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: ActionSheetDismissEvent) => { - actionSheetElement.removeEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler); - if (actionSheetProxy.onWillDismissHandler) { - actionSheetProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - actionSheetElement.addEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler); - actionSheetElement.addEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (actionSheetProxy.state === PRESENTING) { - return actionSheetElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(actionSheetProxy: ActionSheetProxyInternal): Promise { - actionSheetProxy.state = DISMISSING; - if (actionSheetProxy.element) { - if (actionSheetProxy.state === DISMISSING) { - return actionSheetProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: ActionSheetOptions): Promise { - const element = ensureElementInBody('ion-action-sheet-controller') as HTMLIonActionSheetControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface ActionSheetProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface ActionSheetProxyInternal extends ActionSheetProxy { - id: number; - opts: ActionSheetOptions; - state: number; - element: HTMLIonActionSheetElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_ACTION_SHEET_DID_DISMISS_EVENT = 'ionActionSheetDidDismiss'; -const ION_ACTION_SHEET_WILL_DISMISS_EVENT = 'ionActionSheetWillDismiss'; diff --git a/angular/src/providers/alert-controller.ts b/angular/src/providers/alert-controller.ts index 18156d7bd5..0abdbd7190 100644 --- a/angular/src/providers/alert-controller.ts +++ b/angular/src/providers/alert-controller.ts @@ -1,104 +1,10 @@ import { Injectable } from '@angular/core'; -import { AlertDismissEvent, AlertOptions } from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let alertId = 0; +import { AlertOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class AlertController { - create(opts?: AlertOptions): AlertProxy { - return getAlertProxy(opts); +export class AlertController extends OverlayBaseController { + constructor() { + super('ion-alert-controller'); } } - -export function getAlertProxy(opts: AlertOptions) { - return { - id: alertId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as AlertProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as AlertProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(alertProxy: AlertProxyInternal): Promise { - alertProxy.state = PRESENTING; - return loadOverlay(alertProxy.opts).then((alertElement: HTMLIonAlertElement) => { - alertProxy.element = alertElement; - - const onDidDismissHandler = (event: AlertDismissEvent) => { - alertElement.removeEventListener(ION_ALERT_DID_DISMISS_EVENT, onDidDismissHandler); - if (alertProxy.onDidDismissHandler) { - alertProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: AlertDismissEvent) => { - alertElement.removeEventListener(ION_ALERT_WILL_DISMISS_EVENT, onWillDismissHandler); - if (alertProxy.onWillDismissHandler) { - alertProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - alertElement.addEventListener(ION_ALERT_DID_DISMISS_EVENT, onDidDismissHandler); - alertElement.addEventListener(ION_ALERT_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (alertProxy.state === PRESENTING) { - return alertElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(alertProxy: AlertProxyInternal): Promise { - alertProxy.state = DISMISSING; - if (alertProxy.element) { - if (alertProxy.state === DISMISSING) { - return alertProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: AlertOptions): Promise { - const element = ensureElementInBody('ion-alert-controller') as HTMLIonAlertControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface AlertProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface AlertProxyInternal extends AlertProxy { - id: number; - opts: AlertOptions; - state: number; - element: HTMLIonAlertElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_ALERT_DID_DISMISS_EVENT = 'ionAlertDidDismiss'; -const ION_ALERT_WILL_DISMISS_EVENT = 'ionAlertWillDismiss'; diff --git a/angular/src/providers/loading-controller.ts b/angular/src/providers/loading-controller.ts index 5b2735460d..4130606dc8 100644 --- a/angular/src/providers/loading-controller.ts +++ b/angular/src/providers/loading-controller.ts @@ -1,104 +1,10 @@ import { Injectable } from '@angular/core'; -import { LoadingDismissEvent, LoadingOptions } from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let loadingId = 0; +import { LoadingOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class LoadingController { - create(opts?: LoadingOptions): LoadingProxy { - return getLoadingProxy(opts); +export class LoadingController extends OverlayBaseController { + constructor() { + super('ion-loading-controller'); } } - -export function getLoadingProxy(opts: LoadingOptions) { - return { - id: loadingId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as LoadingProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as LoadingProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(loadingProxy: LoadingProxyInternal): Promise { - loadingProxy.state = PRESENTING; - return loadOverlay(loadingProxy.opts).then((loadingElement: HTMLIonLoadingElement) => { - loadingProxy.element = loadingElement; - - const onDidDismissHandler = (event: LoadingDismissEvent) => { - loadingElement.removeEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler); - if (loadingProxy.onDidDismissHandler) { - loadingProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: LoadingDismissEvent) => { - loadingElement.removeEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler); - if (loadingProxy.onWillDismissHandler) { - loadingProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - loadingElement.addEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler); - loadingElement.addEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (loadingProxy.state === PRESENTING) { - return loadingElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(loadingProxy: LoadingProxyInternal): Promise { - loadingProxy.state = DISMISSING; - if (loadingProxy.element) { - if (loadingProxy.state === DISMISSING) { - return loadingProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: LoadingOptions): Promise { - const element = ensureElementInBody('ion-loading-controller') as HTMLIonLoadingControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface LoadingProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface LoadingProxyInternal extends LoadingProxy { - id: number; - opts: LoadingOptions; - state: number; - element: HTMLIonLoadingElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_LOADING_DID_DISMISS_EVENT = 'ionLoadingDidDismiss'; -const ION_LOADING_WILL_DISMISS_EVENT = 'ionLoadingWillDismiss'; diff --git a/angular/src/providers/menu-controller.ts b/angular/src/providers/menu-controller.ts index da0a1365ff..6a17b4b4aa 100644 --- a/angular/src/providers/menu-controller.ts +++ b/angular/src/providers/menu-controller.ts @@ -1,127 +1,104 @@ -import { ensureElementInBody } from '../util/util'; +import { Injectable } from '@angular/core'; +import { proxyMethod } from '../util/util'; -let element: HTMLIonMenuControllerElement; +const CTRL = 'ion-menu-controller'; +@Injectable() export class MenuController { - - constructor() { - element = ensureElementInBody('ion-menu-controller') as HTMLIonMenuControllerElement; - } - - close(menuId?: string) { - return element.componentOnReady().then(() => { - return element.close(menuId); - }); - } - - // maintain legacy sync api - enable(enabled: boolean, menuId?: string) { - if (element && element.enable) { - return element.enable(enabled, menuId); - } - // IDK, this is not a good place to be in - return null; - } - - enableAsync(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.enable(true, menuId); - }); - } - - get(menuId?: string) { - if (element && element.get) { - return element.get(menuId); - } - // IDK, this is not a good place to be in - return null; - } - - getAsync(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.get(menuId); - }); - } - - getMenus() { - if (element && element.getMenus) { - return element.getMenus(); - } - // IDK, this is not a good place to be in - return []; - } - - getMenusAsync(): Promise { - return element.componentOnReady().then(() => { - return element.getMenus(); - }); - } - - getOpen() { - if (element && element.getOpen) { - return element.getOpen(); - } - // IDK, this is not a good place to be in - return null; - } - - getOpenAsync(): Promise { - return element.componentOnReady().then(() => { - return element.getOpen(); - }); - } - - isEnabled(menuId?: string) { - if (element && element.isEnabled) { - return element.isEnabled(menuId); - } - // IDK, this is not a good place to be in - return false; - } - - isEnabledAsync(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.isEnabled(menuId); - }); - } - - isOpen(menuId?: string) { - if (element && element.isOpen) { - return element.isOpen(menuId); - } - // IDK, this is not a good place to be in - return false; - } - - isOpenAsync(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.isOpen(menuId); - }); - } - + /** + * Programatically open the Menu. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {Promise} returns a promise when the menu is fully opened + */ open(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.open(menuId); - }); + return proxyMethod(CTRL, 'open', menuId); } - swipeEnable(shouldEnable: boolean, menuId?: string) { - if (element && element.swipeEnable) { - return element.swipeEnable(shouldEnable, menuId); - } - // IDK, this is not a good place to be in - return null; - } - - swipeEnableAsync(shouldEnable: boolean, menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.swipeEnable(shouldEnable, menuId); - }); + + /** + * Programatically close the Menu. If no `menuId` is given as the first + * argument then it'll close any menu which is open. If a `menuId` + * is given then it'll close that exact menu. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {Promise} returns a promise when the menu is fully closed + */ + close(menuId?: string): Promise { + return proxyMethod(CTRL, 'close', menuId); } + /** + * Toggle the menu. If it's closed, it will open, and if opened, it + * will close. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {Promise} returns a promise when the menu has been toggled + */ toggle(menuId?: string): Promise { - return element.componentOnReady().then(() => { - return element.toggle(menuId); - }); + return proxyMethod(CTRL, 'toggle', menuId); + } + + /** + * Used to enable or disable a menu. For example, there could be multiple + * left menus, but only one of them should be able to be opened at the same + * time. If there are multiple menus on the same side, then enabling one menu + * will also automatically disable all the others that are on the same side. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining. + */ + enable(shouldEnable: boolean, menuId?: string): Promise { + return proxyMethod(CTRL, 'enable', shouldEnable, menuId); + } + + /** + * Used to enable or disable the ability to swipe open the menu. + * @param {boolean} shouldEnable True if it should be swipe-able, false if not. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining. + */ + swipeEnable(shouldEnable: boolean, menuId?: string): Promise { + return proxyMethod(CTRL, 'swipeEnable', shouldEnable, menuId); + } + + /** + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {boolean} Returns true if the specified menu is currently open, otherwise false. + * If the menuId is not specified, it returns true if ANY menu is currenly open. + */ + isOpen(menuId?: string): Promise { + return proxyMethod(CTRL, 'isOpen', menuId); + } + + /** + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {boolean} Returns true if the menu is currently enabled, otherwise false. + */ + isEnabled(menuId?: string): Promise { + return proxyMethod(CTRL, 'isEnabled', menuId); + } + + /** + * Used to get a menu instance. If a `menuId` is not provided then it'll + * return the first menu found. If a `menuId` is `left` or `right`, then + * it'll return the enabled menu on that side. Otherwise, if a `menuId` is + * provided, then it'll try to find the menu using the menu's `id` + * property. If a menu is not found then it'll return `null`. + * @param {string} [menuId] Optionally get the menu by its id, or side. + * @return {HTMLIonMenuElement} Returns the instance of the menu if found, otherwise `null`. + */ + get(menuId?: string): Promise { + return proxyMethod(CTRL, 'get', menuId); + } + + /** + * @return {Menu} Returns the instance of the menu already opened, otherwise `null`. + */ + getOpen(): Promise { + return proxyMethod(CTRL, 'getOpen'); + } + + /** + * @return {Array} Returns an array of all menu instances. + */ + getMenus(): Promise { + return proxyMethod(CTRL, 'getMenus'); } } diff --git a/angular/src/providers/modal-controller.ts b/angular/src/providers/modal-controller.ts index 204bd7e413..ba656e31af 100644 --- a/angular/src/providers/modal-controller.ts +++ b/angular/src/providers/modal-controller.ts @@ -1,121 +1,10 @@ -import { - Injectable, -} from '@angular/core'; - -import { - ModalDismissEvent, - ModalOptions -} from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let modalId = 0; +import { Injectable } from '@angular/core'; +import { ModalOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class ModalController { - - create(opts?: ModalOptions): ModalProxy { - return getModalProxy(opts); +export class ModalController extends OverlayBaseController { + constructor() { + super('ion-modal-controller'); } - - dismiss(data?: any, role?: string, id?: number) { - const modalController = document.querySelector('ion-modal-controller'); - return modalController.componentOnReady().then(() => { - return modalController.dismiss(data, role, id); - }); - } - } - -export function getModalProxy(opts: ModalOptions) { - return { - id: modalId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as ModalProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as ModalProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(modalProxy: ModalProxyInternal): Promise { - modalProxy.state = PRESENTING; - return loadOverlay(modalProxy.opts).then((modalElement: HTMLIonModalElement) => { - Object.assign(modalElement, modalProxy.opts); - modalProxy.element = modalElement; - - const onDidDismissHandler = (event: ModalDismissEvent) => { - modalElement.removeEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler); - if (modalProxy.onDidDismissHandler) { - modalProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: ModalDismissEvent) => { - modalElement.removeEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler); - if (modalProxy.onWillDismissHandler) { - modalProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - modalElement.addEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler); - modalElement.addEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (modalProxy.state === PRESENTING) { - return modalElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(modalProxy: ModalProxyInternal): Promise { - modalProxy.state = DISMISSING; - if (modalProxy.element) { - if (modalProxy.state === DISMISSING) { - return modalProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: ModalOptions): Promise { - const element = ensureElementInBody('ion-modal-controller') as HTMLIonModalControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface ModalProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface ModalProxyInternal extends ModalProxy { - id: number; - opts: ModalOptions; - state: number; - element: HTMLIonModalElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_MODAL_DID_DISMISS_EVENT = 'ionModalDidDismiss'; -const ION_MODAL_WILL_DISMISS_EVENT = 'ionModalWillDismiss'; - diff --git a/angular/src/providers/picker-controller.ts b/angular/src/providers/picker-controller.ts new file mode 100644 index 0000000000..482a9bd9ac --- /dev/null +++ b/angular/src/providers/picker-controller.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@angular/core'; +import { PickerOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; + +@Injectable() +export class PickerController extends OverlayBaseController { + constructor() { + super('ion-picker-controller'); + } +} diff --git a/angular/src/providers/popover-controller.ts b/angular/src/providers/popover-controller.ts index fe0488dbe7..221d58243b 100644 --- a/angular/src/providers/popover-controller.ts +++ b/angular/src/providers/popover-controller.ts @@ -1,120 +1,10 @@ -import { - Injectable, -} from '@angular/core'; - -import { - PopoverDismissEvent, - PopoverOptions -} from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let popoverId = 0; +import { Injectable } from '@angular/core'; +import { PopoverOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class PopoverController { - - create(opts?: PopoverOptions): PopoverProxy { - return getPopoverProxy(opts); - } - - dismiss(data?: any, role?: string, id?: number) { - const popoverController = document.querySelector('ion-popover-controller'); - return popoverController.componentOnReady().then(() => { - return popoverController.dismiss(data, role, id); - }); +export class PopoverController extends OverlayBaseController { + constructor() { + super('ion-popover-controller'); } } - -export function getPopoverProxy(opts: PopoverOptions) { - return { - id: popoverId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as PopoverProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as PopoverProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(popoverProxy: PopoverProxyInternal): Promise { - popoverProxy.state = PRESENTING; - return loadOverlay(popoverProxy.opts).then((popoverElement: HTMLIonPopoverElement) => { - Object.assign(popoverElement, popoverProxy.opts); - popoverProxy.element = popoverElement; - - const onDidDismissHandler = (event: PopoverDismissEvent) => { - popoverElement.removeEventListener(ION_POPOVER_DID_DISMISS_EVENT, onDidDismissHandler); - if (popoverProxy.onDidDismissHandler) { - popoverProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: PopoverDismissEvent) => { - popoverElement.removeEventListener(ION_POPOVER_WILL_DISMISS_EVENT, onWillDismissHandler); - if (popoverProxy.onWillDismissHandler) { - popoverProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - popoverElement.addEventListener(ION_POPOVER_DID_DISMISS_EVENT, onDidDismissHandler); - popoverElement.addEventListener(ION_POPOVER_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (popoverProxy.state === PRESENTING) { - return popoverElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(popoverProxy: PopoverProxyInternal): Promise { - popoverProxy.state = DISMISSING; - if (popoverProxy.element) { - if (popoverProxy.state === DISMISSING) { - return popoverProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: PopoverOptions): Promise { - const element = ensureElementInBody('ion-popover-controller') as HTMLIonPopoverControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface PopoverProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface PopoverProxyInternal extends PopoverProxy { - id: number; - opts: PopoverOptions; - state: number; - element: HTMLIonPopoverElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_POPOVER_DID_DISMISS_EVENT = 'ionPopoverDidDismiss'; -const ION_POPOVER_WILL_DISMISS_EVENT = 'ionPopoverWillDismiss'; - diff --git a/angular/src/providers/toast-controller.ts b/angular/src/providers/toast-controller.ts index 3c3368ba65..fa2e461767 100644 --- a/angular/src/providers/toast-controller.ts +++ b/angular/src/providers/toast-controller.ts @@ -1,104 +1,10 @@ import { Injectable } from '@angular/core'; -import { ToastDismissEvent, ToastOptions } from '@ionic/core'; - -import { ensureElementInBody, hydrateElement } from '../util/util'; - -let toastId = 0; +import { ToastOptions } from '@ionic/core'; +import { OverlayBaseController } from '../util/overlay'; @Injectable() -export class ToastController { - create(opts?: ToastOptions): ToastProxy { - return getToastProxy(opts); +export class ToastController extends OverlayBaseController { + constructor() { + super('ion-popover-controller'); } } - -export function getToastProxy(opts: ToastOptions) { - return { - id: toastId++, - state: PRESENTING, - opts: opts, - present: function() { return present(this); }, - dismiss: function() { return dismiss(this); }, - onDidDismiss: function(callback: (data: any, role: string) => void) { - (this as ToastProxyInternal).onDidDismissHandler = callback; - }, - onWillDismiss: function(callback: (data: any, role: string) => void) { - (this as ToastProxyInternal).onWillDismissHandler = callback; - }, - }; -} - -export function present(toastProxy: ToastProxyInternal): Promise { - toastProxy.state = PRESENTING; - return loadOverlay(toastProxy.opts).then((toastElement: HTMLIonToastElement) => { - toastProxy.element = toastElement; - - const onDidDismissHandler = (event: ToastDismissEvent) => { - toastElement.removeEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler); - if (toastProxy.onDidDismissHandler) { - toastProxy.onDidDismissHandler(event.detail.data, event.detail.role); - } - }; - - const onWillDismissHandler = (event: ToastDismissEvent) => { - toastElement.removeEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler); - if (toastProxy.onWillDismissHandler) { - toastProxy.onWillDismissHandler(event.detail.data, event.detail.role); - } - }; - - toastElement.addEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler); - toastElement.addEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler); - - if (toastProxy.state === PRESENTING) { - return toastElement.present(); - } - - // we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state - // attribute before it could async load and present itself. - // with that in mind, just return null to make the TS compiler happy - return null; - }); -} - -export function dismiss(toastProxy: ToastProxyInternal): Promise { - toastProxy.state = DISMISSING; - if (toastProxy.element) { - if (toastProxy.state === DISMISSING) { - return toastProxy.element.dismiss(); - } - } - // either we're not in the dismissing state - // or we're calling this before the element is created - // so just return a resolved promise - return Promise.resolve(); -} - -export function loadOverlay(opts: ToastOptions): Promise { - const element = ensureElementInBody('ion-toast-controller') as HTMLIonToastControllerElement; - return hydrateElement(element).then(() => { - return element.create(opts); - }); -} - -export interface ToastProxy { - present(): Promise; - dismiss(): Promise; - onDidDismiss(callback: (data: any, role: string) => void): void; - onWillDismiss(callback: (data: any, role: string) => void): void; -} - -export interface ToastProxyInternal extends ToastProxy { - id: number; - opts: ToastOptions; - state: number; - element: HTMLIonToastElement; - onDidDismissHandler?: (data: any, role: string) => void; - onWillDismissHandler?: (data: any, role: string) => void; -} - -export const PRESENTING = 1; -export const DISMISSING = 2; - -const ION_TOAST_DID_DISMISS_EVENT = 'ionToastDidDismiss'; -const ION_TOAST_WILL_DISMISS_EVENT = 'ionToastWillDismiss'; diff --git a/angular/src/util/overlay.ts b/angular/src/util/overlay.ts new file mode 100644 index 0000000000..e4e24d6b3c --- /dev/null +++ b/angular/src/util/overlay.ts @@ -0,0 +1,15 @@ + +import { proxyMethod } from '../util/util'; + + +export class OverlayBaseController { + constructor(private ctrl: string) {} + + create(opts?: Opts): Promise { + return proxyMethod(this.ctrl, 'create', opts); + } + + dismiss(data?: any, role?: string, id = -1): Promise { + return proxyMethod(this.ctrl, 'dismiss', data, role, id); + } +} diff --git a/angular/src/util/util.ts b/angular/src/util/util.ts index 5b64f0a1cd..3d5e5adbcc 100644 --- a/angular/src/util/util.ts +++ b/angular/src/util/util.ts @@ -1,19 +1,19 @@ -export function hydrateElement(element: any) { - return element.componentOnReady(); -} -export function getElement(elementName: string) { - return document.querySelector(elementName); + +export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) { + const controller = ensureElementInBody(ctrlName); + return controller.componentOnReady() + .then(() => (controller as any)[methodName].apply(args)); } export function ensureElementInBody(elementName: string) { - let element = getElement(elementName); + let element = document.querySelector(elementName); if (!element) { element = document.createElement(elementName); document.body.appendChild(element); } - return element; + return element as HTMLStencilElement; } export function removeAllNodeChildren(element: HTMLElement) {