fix(overlays): typescript is our friend

By using typescript generics we are able to detect at
compiler time that the overlay options conform with the
overlay implementation.
This commit is contained in:
Manu Mtz.-Almeida
2018-02-23 10:54:28 +01:00
parent 111f526d05
commit ae77a64b37
10 changed files with 44 additions and 35 deletions

View File

@ -48,7 +48,7 @@ export class ActionSheetController implements OverlayController {
* Get the most recently opened action sheet overlay. * Get the most recently opened action sheet overlay.
*/ */
@Method() @Method()
getTop() { getTop(): HTMLIonActionSheetElement {
return getTopOverlay(this.actionSheets); return getTopOverlay(this.actionSheets);
} }
} }

View File

@ -37,7 +37,7 @@ export class AlertController implements OverlayController {
* Dismiss the open alert overlay. * Dismiss the open alert overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, alertId = -1) { dismiss(data?: any, role?: string, alertId = -1) {
return dismissOverlay(data, role, this.alerts, alertId); return dismissOverlay(data, role, this.alerts, alertId);
} }
@ -45,7 +45,7 @@ export class AlertController implements OverlayController {
* Get the most recently opened alert overlay. * Get the most recently opened alert overlay.
*/ */
@Method() @Method()
getTop() { getTop(): HTMLIonAlertElement {
return getTopOverlay(this.alerts); return getTopOverlay(this.alerts);
} }
} }

View File

@ -37,7 +37,7 @@ export class ModalController implements OverlayController {
* Dismiss the open modal overlay. * Dismiss the open modal overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, modalId = -1) { dismiss(data?: any, role?: string, modalId = -1) {
return dismissOverlay(data, role, this.modals, modalId); return dismissOverlay(data, role, this.modals, modalId);
} }

View File

@ -250,7 +250,7 @@ export interface ModalOptions {
showBackdrop?: boolean; showBackdrop?: boolean;
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
exitAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
cssClass?: string; cssClass?: string;
delegate?: FrameworkDelegate; delegate?: FrameworkDelegate;
} }

View File

@ -37,7 +37,7 @@ export class PickerController implements OverlayController {
* Dismiss the open picker overlay. * Dismiss the open picker overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, pickerId = -1) { dismiss(data?: any, role?: string, pickerId = -1) {
return dismissOverlay(data, role, this.pickers, pickerId); return dismissOverlay(data, role, this.pickers, pickerId);
} }
@ -45,7 +45,7 @@ export class PickerController implements OverlayController {
* Get the most recently opened picker overlay. * Get the most recently opened picker overlay.
*/ */
@Method() @Method()
getTop() { getTop(): HTMLIonPickerElement {
return getTopOverlay(this.pickers); return getTopOverlay(this.pickers);
} }
} }

View File

@ -36,7 +36,7 @@ export class PopoverController implements OverlayController {
* Dismiss the open popover overlay. * Dismiss the open popover overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, popoverId = -1) { dismiss(data?: any, role?: string, popoverId = -1) {
return dismissOverlay(data, role, this.popovers, popoverId); return dismissOverlay(data, role, this.popovers, popoverId);
} }
@ -44,7 +44,7 @@ export class PopoverController implements OverlayController {
* Get the most recently opened popover overlay. * Get the most recently opened popover overlay.
*/ */
@Method() @Method()
getTop() { getTop(): HTMLIonPopoverElement {
return getTopOverlay(this.popovers); return getTopOverlay(this.popovers);
} }
} }

View File

@ -270,7 +270,7 @@ export interface PopoverOptions {
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
translucent?: boolean; translucent?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leavenimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
cssClass?: string; cssClass?: string;
ev: Event; ev: Event;
delegate?: FrameworkDelegate; delegate?: FrameworkDelegate;

View File

@ -37,7 +37,7 @@ export class ToastController implements OverlayController {
* Dismiss the open toast overlay. * Dismiss the open toast overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, toastId = -1) { dismiss(data?: any, role?: string, toastId = -1) {
return dismissOverlay(data, role, this.toasts, toastId); return dismissOverlay(data, role, this.toasts, toastId);
} }
@ -45,7 +45,7 @@ export class ToastController implements OverlayController {
* Get the most recently opened toast overlay. * Get the most recently opened toast overlay.
*/ */
@Method() @Method()
getTop() { getTop(): HTMLIonToastElement {
return getTopOverlay(this.toasts); return getTopOverlay(this.toasts);
} }
} }

View File

@ -257,7 +257,7 @@ export interface ToastOptions {
position?: string; position?: string;
translucent?: boolean; translucent?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
exitAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
} }
export interface ToastEvent extends CustomEvent { export interface ToastEvent extends CustomEvent {

View File

@ -1,28 +1,23 @@
export const BACKDROP = 'backdrop';
export interface OverlayInterface {
overlayId: number;
present(): Promise<void>;
dismiss(data?: any, role?: string): Promise<void>;
}
export interface HTMLIonOverlayElement extends HTMLStencilElement, OverlayInterface {}
export type OverlayMap = Map<number, HTMLIonOverlayElement>;
let lastId = 1; let lastId = 1;
export function createOverlay<T extends HTMLIonOverlayElement> /**
(tagName: string, opts: any): Promise<T> { * Make all properties in T readonly
*/
export type PropDescriptions<K extends string> = {
[P in K]: any;
}
export function createOverlay<T extends HTMLIonOverlayElement & PropDescriptions<keyof B>, B>
(tagName: string, opts: B): Promise<T> {
// create ionic's wrapping ion-alert component // create ionic's wrapping ion-alert component
const element = document.createElement(tagName) as T; const element = document.createElement(tagName) as T;
// give this alert a unique id // give this alert a unique id
element.overlayId = lastId++; element.overlayId = lastId++;
// convert the passed in alert options into props // convert the passed in overlay options into props
// that get passed down into the new alert // that get passed down into the new alert
Object.assign(element, opts); Object.assign(element, opts);
@ -33,7 +28,7 @@ export function createOverlay<T extends HTMLIonOverlayElement>
return element.componentOnReady(); return element.componentOnReady();
} }
export function dismissOverlay(data: any, role: any, overlays: OverlayMap, id: number): Promise<void> { export function dismissOverlay(data: any, role: string, overlays: OverlayMap, id: number): Promise<void> {
id = id >= 0 ? id : getHighestId(overlays); id = id >= 0 ? id : getHighestId(overlays);
const overlay = overlays.get(id); const overlay = overlays.get(id);
if (!overlay) { if (!overlay) {
@ -42,13 +37,13 @@ export function dismissOverlay(data: any, role: any, overlays: OverlayMap, id: n
return overlay.dismiss(data, role); return overlay.dismiss(data, role);
} }
export function getTopOverlay(overlays: Map<number, any>) { export function getTopOverlay<T extends HTMLIonOverlayElement>(overlays: OverlayMap): T {
return overlays.get(getHighestId(overlays)); return overlays.get(getHighestId(overlays)) as T;
} }
export function getHighestId(overlays: Map<number, any>) { export function getHighestId(overlays: OverlayMap) {
let minimum = -1; let minimum = -1;
overlays.forEach((_, id: number) => { overlays.forEach((_, id) => {
if (id > minimum) { if (id > minimum) {
minimum = id; minimum = id;
} }
@ -57,6 +52,20 @@ export function getHighestId(overlays: Map<number, any>) {
} }
export function removeLastOverlay(overlays: OverlayMap) { export function removeLastOverlay(overlays: OverlayMap) {
const toRemove = getTopOverlay(overlays); const toRemove = getTopOverlay(overlays) as any;
return toRemove ? toRemove.dismiss() : Promise.resolve(); return toRemove ? toRemove.dismiss() : Promise.resolve();
} }
export interface OverlayInterface {
overlayId: number;
present(): Promise<void>;
dismiss(data?: any, role?: string): Promise<void>;
}
export interface HTMLIonOverlayElement extends HTMLStencilElement, OverlayInterface {}
export type OverlayMap = Map<number, HTMLIonOverlayElement>;
export const BACKDROP = 'backdrop';