fix(angular): fix overlays

This commit is contained in:
Manu Mtz.-Almeida
2018-03-13 19:21:26 +01:00
parent de22ecae82
commit cc4fecc1be
12 changed files with 167 additions and 759 deletions

View File

@ -13,15 +13,16 @@ export { VirtualHeader } from './directives/virtual-header';
export { VirtualFooter } from './directives/virtual-footer'; export { VirtualFooter } from './directives/virtual-footer';
/* Providers */ /* Providers */
export { ActionSheetController, ActionSheetProxy } from './providers/action-sheet-controller'; export { ActionSheetController } from './providers/action-sheet-controller';
export { AlertController, AlertProxy } from './providers/alert-controller'; export { AlertController } from './providers/alert-controller';
export { Events } from './providers/events'; 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 { 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 { Platform } from './providers/platform';
export { PopoverController, PopoverProxy } from './providers/popover-controller'; export { PopoverController } from './providers/popover-controller';
export { ToastController, ToastProxy } from './providers/toast-controller'; export { ToastController } from './providers/toast-controller';
export * from './types/interfaces'; export * from './types/interfaces';

View File

@ -31,6 +31,7 @@ import { Events, setupProvideEvents } from './providers/events';
import { LoadingController } from './providers/loading-controller'; import { LoadingController } from './providers/loading-controller';
import { MenuController } from './providers/menu-controller'; import { MenuController } from './providers/menu-controller';
import { ModalController } from './providers/modal-controller'; import { ModalController } from './providers/modal-controller';
import { PickerController } from './providers/picker-controller';
import { Platform } from './providers/platform'; import { Platform } from './providers/platform';
import { PopoverController } from './providers/popover-controller'; import { PopoverController } from './providers/popover-controller';
import { ToastController } from './providers/toast-controller'; import { ToastController } from './providers/toast-controller';
@ -72,7 +73,7 @@ import { ToastController } from './providers/toast-controller';
], ],
providers: [ providers: [
ModalController, ModalController,
PopoverController PopoverController,
], ],
schemas: [ schemas: [
CUSTOM_ELEMENTS_SCHEMA CUSTOM_ELEMENTS_SCHEMA
@ -87,6 +88,7 @@ export class IonicAngularModule {
ActionSheetController, ActionSheetController,
Events, Events,
LoadingController, LoadingController,
PickerController,
MenuController, MenuController,
Platform, Platform,
ToastController, ToastController,

View File

@ -1,104 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActionSheetDismissEvent, ActionSheetOptions } from '@ionic/core'; import { ActionSheetOptions } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
import { ensureElementInBody, hydrateElement } from '../util/util';
let actionSheetId = 0;
@Injectable() @Injectable()
export class ActionSheetController { export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
create(opts?: ActionSheetOptions): ActionSheetProxy { constructor() {
return getActionSheetProxy(opts); 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<any> {
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<any> {
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<HTMLIonActionSheetElement> {
const element = ensureElementInBody('ion-action-sheet-controller') as HTMLIonActionSheetControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface ActionSheetProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -1,104 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AlertDismissEvent, AlertOptions } from '@ionic/core'; import { AlertOptions } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
import { ensureElementInBody, hydrateElement } from '../util/util';
let alertId = 0;
@Injectable() @Injectable()
export class AlertController { export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
create(opts?: AlertOptions): AlertProxy { constructor() {
return getAlertProxy(opts); 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<any> {
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<any> {
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<HTMLIonAlertElement> {
const element = ensureElementInBody('ion-alert-controller') as HTMLIonAlertControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface AlertProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -1,104 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { LoadingDismissEvent, LoadingOptions } from '@ionic/core'; import { LoadingOptions } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
import { ensureElementInBody, hydrateElement } from '../util/util';
let loadingId = 0;
@Injectable() @Injectable()
export class LoadingController { export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
create(opts?: LoadingOptions): LoadingProxy { constructor() {
return getLoadingProxy(opts); 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<any> {
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<any> {
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<HTMLIonLoadingElement> {
const element = ensureElementInBody('ion-loading-controller') as HTMLIonLoadingControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface LoadingProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -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 { export class MenuController {
/**
constructor() { * Programatically open the Menu.
element = ensureElementInBody('ion-menu-controller') as HTMLIonMenuControllerElement; * @param {string} [menuId] Optionally get the menu by its id, or side.
} * @return {Promise} returns a promise when the menu is fully opened
*/
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<HTMLIonMenuElement> {
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<HTMLIonMenuElement> {
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<HTMLIonMenuElement[]> {
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<HTMLIonMenuElement> {
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<boolean> {
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<boolean> {
return element.componentOnReady().then(() => {
return element.isOpen(menuId);
});
}
open(menuId?: string): Promise<boolean> { open(menuId?: string): Promise<boolean> {
return element.componentOnReady().then(() => { return proxyMethod(CTRL, 'open', menuId);
return element.open(menuId);
});
} }
swipeEnable(shouldEnable: boolean, menuId?: string) {
if (element && element.swipeEnable) { /**
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`
// IDK, this is not a good place to be in * is given then it'll close that exact menu.
return null; * @param {string} [menuId] Optionally get the menu by its id, or side.
} * @return {Promise} returns a promise when the menu is fully closed
*/
swipeEnableAsync(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> { close(menuId?: string): Promise<boolean> {
return element.componentOnReady().then(() => { return proxyMethod(CTRL, 'close', menuId);
return element.swipeEnable(shouldEnable, 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<boolean> { toggle(menuId?: string): Promise<boolean> {
return element.componentOnReady().then(() => { return proxyMethod(CTRL, 'toggle', menuId);
return element.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<HTMLIonMenuElement> {
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<HTMLIonMenuElement> {
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<boolean> {
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<boolean> {
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<HTMLIonMenuElement> {
return proxyMethod(CTRL, 'get', menuId);
}
/**
* @return {Menu} Returns the instance of the menu already opened, otherwise `null`.
*/
getOpen(): Promise<HTMLIonMenuElement> {
return proxyMethod(CTRL, 'getOpen');
}
/**
* @return {Array<HTMLIonMenuElement>} Returns an array of all menu instances.
*/
getMenus(): Promise<HTMLIonMenuElement[]> {
return proxyMethod(CTRL, 'getMenus');
} }
} }

View File

@ -1,121 +1,10 @@
import { import { Injectable } from '@angular/core';
Injectable, import { ModalOptions } from '@ionic/core';
} from '@angular/core'; import { OverlayBaseController } from '../util/overlay';
import {
ModalDismissEvent,
ModalOptions
} from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let modalId = 0;
@Injectable() @Injectable()
export class ModalController { export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
constructor() {
create(opts?: ModalOptions): ModalProxy { super('ion-modal-controller');
return getModalProxy(opts);
} }
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<any> {
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<any> {
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<HTMLIonModalElement> {
const element = ensureElementInBody('ion-modal-controller') as HTMLIonModalControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface ModalProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -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<PickerOptions, HTMLIonPickerElement> {
constructor() {
super('ion-picker-controller');
}
}

View File

@ -1,120 +1,10 @@
import { import { Injectable } from '@angular/core';
Injectable, import { PopoverOptions } from '@ionic/core';
} from '@angular/core'; import { OverlayBaseController } from '../util/overlay';
import {
PopoverDismissEvent,
PopoverOptions
} from '@ionic/core';
import { ensureElementInBody, hydrateElement } from '../util/util';
let popoverId = 0;
@Injectable() @Injectable()
export class PopoverController { export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
constructor() {
create(opts?: PopoverOptions): PopoverProxy { super('ion-popover-controller');
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 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<any> {
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<any> {
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<HTMLIonPopoverElement> {
const element = ensureElementInBody('ion-popover-controller') as HTMLIonPopoverControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface PopoverProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -1,104 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ToastDismissEvent, ToastOptions } from '@ionic/core'; import { ToastOptions } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
import { ensureElementInBody, hydrateElement } from '../util/util';
let toastId = 0;
@Injectable() @Injectable()
export class ToastController { export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
create(opts?: ToastOptions): ToastProxy { constructor() {
return getToastProxy(opts); 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<any> {
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<any> {
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<HTMLIonToastElement> {
const element = ensureElementInBody('ion-toast-controller') as HTMLIonToastControllerElement;
return hydrateElement(element).then(() => {
return element.create(opts);
});
}
export interface ToastProxy {
present(): Promise<void>;
dismiss(): Promise<void>;
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';

View File

@ -0,0 +1,15 @@
import { proxyMethod } from '../util/util';
export class OverlayBaseController<Opts, Overlay> {
constructor(private ctrl: string) {}
create(opts?: Opts): Promise<Overlay> {
return proxyMethod(this.ctrl, 'create', opts);
}
dismiss(data?: any, role?: string, id = -1): Promise<void> {
return proxyMethod(this.ctrl, 'dismiss', data, role, id);
}
}

View File

@ -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) { export function ensureElementInBody(elementName: string) {
let element = getElement(elementName); let element = document.querySelector(elementName);
if (!element) { if (!element) {
element = document.createElement(elementName); element = document.createElement(elementName);
document.body.appendChild(element); document.body.appendChild(element);
} }
return element; return element as HTMLStencilElement;
} }
export function removeAllNodeChildren(element: HTMLElement) { export function removeAllNodeChildren(element: HTMLElement) {