fix(overlay): fix overlay indexing

This commit is contained in:
Manu Mtz.-Almeida
2018-02-23 00:49:45 +01:00
parent e9e7c39920
commit 111f526d05
28 changed files with 266 additions and 339 deletions

View File

@ -118,6 +118,7 @@ declare global {
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
overlayId?: number;
subTitle?: string; subTitle?: string;
title?: string; title?: string;
translucent?: boolean; translucent?: boolean;
@ -188,6 +189,7 @@ declare global {
inputs?: AlertInput[]; inputs?: AlertInput[];
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
message?: string; message?: string;
overlayId?: number;
subTitle?: string; subTitle?: string;
title?: string; title?: string;
translucent?: boolean; translucent?: boolean;
@ -1672,6 +1674,7 @@ declare global {
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
overlayId?: number;
showBackdrop?: boolean; showBackdrop?: boolean;
spinner?: string; spinner?: string;
translucent?: boolean; translucent?: boolean;
@ -1842,6 +1845,7 @@ declare global {
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
mode?: 'ios' | 'md'; mode?: 'ios' | 'md';
overlayId?: number;
showBackdrop?: boolean; showBackdrop?: boolean;
willAnimate?: boolean; willAnimate?: boolean;
} }
@ -2131,6 +2135,7 @@ declare global {
enableBackdropDismiss?: boolean; enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
overlayId?: number;
showBackdrop?: boolean; showBackdrop?: boolean;
willAnimate?: boolean; willAnimate?: boolean;
} }
@ -2232,6 +2237,7 @@ declare global {
ev?: Event; ev?: Event;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
mode?: 'ios' | 'md'; mode?: 'ios' | 'md';
overlayId?: number;
showBackdrop?: boolean; showBackdrop?: boolean;
translucent?: boolean; translucent?: boolean;
willAnimate?: boolean; willAnimate?: boolean;
@ -3510,6 +3516,7 @@ declare global {
enterAnimation?: AnimationBuilder; enterAnimation?: AnimationBuilder;
leaveAnimation?: AnimationBuilder; leaveAnimation?: AnimationBuilder;
message?: string; message?: string;
overlayId?: number;
position?: string; position?: string;
showCloseButton?: boolean; showCloseButton?: boolean;
translucent?: boolean; translucent?: boolean;

View File

@ -1,29 +1,31 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { ActionSheetEvent, ActionSheetOptions, OverlayController } from '../../index'; import { ActionSheetEvent, ActionSheetOptions, OverlayController } from '../../index';
import { createOverlay, getTopOverlay, dismissOverlay, removeLastOverlay } from '../../utils/overlays';
let ids = 0;
const actionSheets = new Map<number, HTMLIonActionSheetElement>();
@Component({ @Component({
tag: 'ion-action-sheet-controller' tag: 'ion-action-sheet-controller'
}) })
export class ActionSheetController implements OverlayController { export class ActionSheetController implements OverlayController {
private actionSheets = new Map<number, HTMLIonActionSheetElement>();
@Listen('body:ionActionSheetWillPresent') @Listen('body:ionActionSheetWillPresent')
protected actionSheetWillPresent(ev: ActionSheetEvent) { protected actionSheetWillPresent(ev: ActionSheetEvent) {
actionSheets.set(ev.target.actionSheetId, ev.target); this.actionSheets.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionActionSheetWillDismiss, body:ionActionSheetDidUnload') @Listen('body:ionActionSheetWillDismiss')
@Listen('body:ionActionSheetDidUnload')
protected actionSheetWillDismiss(ev: ActionSheetEvent) { protected actionSheetWillDismiss(ev: ActionSheetEvent) {
actionSheets.delete(ev.target.actionSheetId); this.actionSheets.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastActionSheet(); removeLastOverlay(this.actionSheets);
} }
/* /*
@ -31,34 +33,15 @@ export class ActionSheetController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: ActionSheetOptions): Promise<HTMLIonActionSheetElement> { create(opts?: ActionSheetOptions): Promise<HTMLIonActionSheetElement> {
// create ionic's wrapping ion-actionSheet component return createOverlay('ion-action-sheet', opts);
const actionSheetElement = document.createElement('ion-action-sheet');
// give this actionSheet a unique id
actionSheetElement.actionSheetId = ids++;
// convert the passed in actionSheet options into props
// that get passed down into the new actionSheet
Object.assign(actionSheetElement, opts);
// append the actionSheet element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(actionSheetElement);
return actionSheetElement.componentOnReady();
} }
/* /*
* Dismiss the open action sheet overlay. * Dismiss the open action sheet overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, actionSheetId = -1) { dismiss(data?: any, role?: string, actionSheetId = -1) {
actionSheetId = actionSheetId >= 0 ? actionSheetId : getHighestId(); return dismissOverlay(data, role, this.actionSheets, actionSheetId);
const actionSheet = actionSheets.get(actionSheetId);
if (!actionSheet) {
return Promise.reject('action-sheet does not exist');
}
return actionSheet.dismiss(data, role);
} }
/* /*
@ -66,21 +49,6 @@ export class ActionSheetController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return actionSheets.get(getHighestId()); return getTopOverlay(this.actionSheets);
} }
} }
function getHighestId() {
let minimum = -1;
actionSheets.forEach((_actionSheet: HTMLIonActionSheetElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastActionSheet() {
const toRemove = actionSheets.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -76,6 +76,8 @@ ion-action-sheet {
flex-shrink: 2; flex-shrink: 2;
pointer-events: all; pointer-events: all;
overscroll-behavior: contain;
} }
.action-sheet-group-cancel { .action-sheet-group-cancel {

View File

@ -3,6 +3,7 @@ import { Animation, AnimationBuilder, AnimationController, Config, DomController
import { domControllerAsync, isDef, playAnimationAsync } from '../../utils/helpers'; import { domControllerAsync, isDef, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses, getClassMap } from '../../utils/theme'; import { createThemedClasses, getClassMap } from '../../utils/theme';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
@ -20,10 +21,10 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'action-sheet' theme: 'action-sheet'
} }
}) })
export class ActionSheet { export class ActionSheet implements OverlayInterface {
mode: string; mode: string;
color: string; color: string;
actionSheetId: number;
private animation: Animation | null = null; private animation: Animation | null = null;
@ -32,6 +33,7 @@ export class ActionSheet {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
/** /**
* Animation to use when the action sheet is presented. * Animation to use when the action sheet is presented.
@ -128,7 +130,7 @@ export class ActionSheet {
@Listen('ionBackdropTap') @Listen('ionBackdropTap')
protected onBackdropTap() { protected onBackdropTap() {
this.dismiss(); this.dismiss(null, BACKDROP);
} }
/** /**
@ -138,7 +140,7 @@ export class ActionSheet {
present() { present() {
this.ionActionSheetWillPresent.emit(); this.ionActionSheetWillPresent.emit();
this.el.style.zIndex = `${20000 + this.actionSheetId}`; this.el.style.zIndex = `${20000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('actionSheetEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('actionSheetEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);

View File

@ -96,6 +96,11 @@ Animation to use when the action sheet is presented.
Animation to use when the action sheet is dismissed. Animation to use when the action sheet is dismissed.
#### overlayId
number
#### subTitle #### subTitle
string string
@ -162,6 +167,11 @@ Animation to use when the action sheet is presented.
Animation to use when the action sheet is dismissed. Animation to use when the action sheet is dismissed.
#### overlay-id
number
#### sub-title #### sub-title
string string

View File

@ -1,27 +1,28 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { AlertEvent, AlertOptions, OverlayController } from '../../index'; import { AlertEvent, AlertOptions, OverlayController } from '../../index';
import { createOverlay, removeLastOverlay, dismissOverlay, getTopOverlay } from '../../utils/overlays';
let ids = 0;
const alerts = new Map<number, HTMLIonAlertElement>();
@Component({ @Component({
tag: 'ion-alert-controller' tag: 'ion-alert-controller'
}) })
export class AlertController implements OverlayController { export class AlertController implements OverlayController {
private alerts = new Map<number, HTMLIonAlertElement>();
@Listen('body:ionAlertWillPresent') @Listen('body:ionAlertWillPresent')
protected alertWillPresent(ev: AlertEvent) { protected alertWillPresent(ev: AlertEvent) {
alerts.set(ev.target.alertId, ev.target); this.alerts.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionAlertWillDismiss, body:ionAlertDidUnload') @Listen('body:ionAlertWillDismiss')
@Listen('body:ionAlertDidUnload')
protected alertWillDismiss(ev: AlertEvent) { protected alertWillDismiss(ev: AlertEvent) {
alerts.delete(ev.target.alertId); this.alerts.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastAlert(); removeLastOverlay(this.alerts);
} }
/* /*
@ -29,21 +30,7 @@ export class AlertController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: AlertOptions): Promise<HTMLIonAlertElement> { create(opts?: AlertOptions): Promise<HTMLIonAlertElement> {
// create ionic's wrapping ion-alert component return createOverlay('ion-alert', opts);
const alertElement = document.createElement('ion-alert');
// give this alert a unique id
alertElement.alertId = ids++;
// convert the passed in alert options into props
// that get passed down into the new alert
Object.assign(alertElement, opts);
// append the alert element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(alertElement);
return alertElement.componentOnReady();
} }
/* /*
@ -51,12 +38,7 @@ export class AlertController implements OverlayController {
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, alertId = -1) { dismiss(data?: any, role?: any, alertId = -1) {
alertId = alertId >= 0 ? alertId : getHighestId(); return dismissOverlay(data, role, this.alerts, alertId);
const alert = alerts.get(alertId);
if (!alert) {
return Promise.reject('alert does not exist');
}
return alert.dismiss(data, role);
} }
/* /*
@ -64,21 +46,6 @@ export class AlertController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return alerts.get(getHighestId()); return getTopOverlay(this.alerts);
} }
} }
function getHighestId() {
let minimum = -1;
alerts.forEach((_alert: HTMLIonAlertElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastAlert() {
const toRemove = alerts.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -108,6 +108,7 @@
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
} }
.alert-ios .alert-tappable { .alert-ios .alert-tappable {

View File

@ -1,9 +1,8 @@
import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core'; import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index'; import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers'; import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { BACKDROP } from '../../utils/overlay-constants';
import { createThemedClasses, getClassMap } from '../../utils/theme'; import { createThemedClasses, getClassMap } from '../../utils/theme';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
@ -21,8 +20,7 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'alert' theme: 'alert'
} }
}) })
export class Alert { export class Alert implements OverlayInterface {
alertId: number;
mode: string; mode: string;
color: string; color: string;
@ -36,6 +34,7 @@ export class Alert {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
/** /**
* Animation to use when the alert is presented. * Animation to use when the alert is presented.
@ -147,7 +146,7 @@ export class Alert {
present() { present() {
this.ionAlertWillPresent.emit(); this.ionAlertWillPresent.emit();
this.el.style.zIndex = `${20000 + this.alertId}`; this.el.style.zIndex = `${20000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('alertEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('alertEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
@ -347,14 +346,14 @@ export class Alert {
...themedClasses, ...themedClasses,
...getClassMap(this.cssClass) ...getClassMap(this.cssClass)
}, },
id: this.alertId id: this.overlayId
}; };
} }
render() { render() {
const hdrId = `${this.alertId}-hdr`; const hdrId = `alert-${this.overlayId}-hdr`;
const subHdrId = `${this.alertId}-sub-hdr`; const subHdrId = `alert-${this.overlayId}-sub-hdr`;
const msgId = `${this.alertId}-msg`; const msgId = `alert-${this.overlayId}-msg`;
if (this.title || !this.subTitle) { if (this.title || !this.subTitle) {
this.hdrId = hdrId; this.hdrId = hdrId;
@ -385,7 +384,7 @@ export class Alert {
label: i.label, label: i.label,
checked: !!i.checked, checked: !!i.checked,
disabled: !!i.disabled, disabled: !!i.disabled,
id: i.id ? i.id : `alert-input-${this.alertId}-${index}`, id: i.id ? i.id : `alert-input-${this.overlayId}-${index}`,
handler: i.handler ? i.handler : null, handler: i.handler ? i.handler : null,
min: i.min ? i.min : null, min: i.min ? i.min : null,
max: i.max ? i.max : null max: i.max ? i.max : null

View File

@ -90,6 +90,11 @@ string
The main message to be displayed in the alert. The main message to be displayed in the alert.
#### overlayId
number
#### subTitle #### subTitle
string string
@ -170,6 +175,11 @@ string
The main message to be displayed in the alert. The main message to be displayed in the alert.
#### overlay-id
number
#### sub-title #### sub-title
string string

View File

@ -54,6 +54,7 @@ export class Backdrop {
hostData() { hostData() {
return { return {
tabindex: '-1',
class: { class: {
'backdrop-hide': !this.visible, 'backdrop-hide': !this.visible,
'backdrop-no-tappable': !this.tappable, 'backdrop-no-tappable': !this.tappable,

View File

@ -1,27 +1,28 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { LoadingEvent, LoadingOptions, OverlayController } from '../../index'; import { LoadingEvent, LoadingOptions, OverlayController } from '../../index';
import { createOverlay, dismissOverlay, getTopOverlay, removeLastOverlay } from '../../utils/overlays';
let ids = 0;
const loadings = new Map<number, HTMLIonLoadingElement>();
@Component({ @Component({
tag: 'ion-loading-controller' tag: 'ion-loading-controller'
}) })
export class LoadingController implements OverlayController { export class LoadingController implements OverlayController {
private loadings = new Map<number, HTMLIonLoadingElement>();
@Listen('body:ionLoadingWillPresent') @Listen('body:ionLoadingWillPresent')
protected loadingWillPresent(ev: LoadingEvent) { protected loadingWillPresent(ev: LoadingEvent) {
loadings.set(ev.target.loadingId, ev.target); this.loadings.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionLoadingWillDismiss, body:ionLoadingDidUnload') @Listen('body:ionLoadingWillDismiss, body:ionLoadingDidUnload')
protected loadingWillDismiss(ev: LoadingEvent) { protected loadingWillDismiss(ev: LoadingEvent) {
loadings.delete(ev.target.loadingId); this.loadings.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastLoading(); removeLastOverlay(this.loadings);
} }
/* /*
@ -29,31 +30,15 @@ export class LoadingController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: LoadingOptions): Promise<HTMLIonLoadingElement> { create(opts?: LoadingOptions): Promise<HTMLIonLoadingElement> {
// create ionic's wrapping ion-loading component return createOverlay('ion-loading', opts);
const loadingElement = document.createElement('ion-loading');
// give this loading a unique id
loadingElement.loadingId = ids++;
// convert the passed in loading options into props
// that get passed down into the new loading
Object.assign(loadingElement, opts);
// append the loading element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(loadingElement);
return loadingElement.componentOnReady();
} }
/* /*
* Dismiss the open loading overlay. * Dismiss the open loading overlay.
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, loadingId = -1) { dismiss(data?: any, role?: string, loadingId = -1) {
loadingId = loadingId >= 0 ? loadingId : getHighestId(); return dismissOverlay(data, role, this.loadings, loadingId);
const loading = loadings.get(loadingId);
return loading.dismiss(data, role);
} }
/* /*
@ -61,21 +46,6 @@ export class LoadingController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return loadings.get(getHighestId()); return getTopOverlay(this.loadings);
} }
} }
function getHighestId() {
let minimum = -1;
loadings.forEach((_loading: HTMLIonLoadingElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastLoading() {
const toRemove = loadings.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -7,6 +7,7 @@ import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
import mdEnterAnimation from './animations/md.enter'; import mdEnterAnimation from './animations/md.enter';
import mdLeaveAnimation from './animations/md.leave'; import mdLeaveAnimation from './animations/md.leave';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
@Component({ @Component({
tag: 'ion-loading', tag: 'ion-loading',
@ -18,10 +19,10 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'loading' theme: 'loading'
} }
}) })
export class Loading {
export class Loading implements OverlayInterface {
color: string; color: string;
mode: string; mode: string;
loadingId: number;
private animation: Animation; private animation: Animation;
private durationTimeout: any; private durationTimeout: any;
@ -31,6 +32,7 @@ export class Loading {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
/** /**
* Animation to use when the loading indicator is presented. * Animation to use when the loading indicator is presented.
@ -153,7 +155,7 @@ export class Loading {
@Listen('ionBackdropTap') @Listen('ionBackdropTap')
protected onBackdropTap() { protected onBackdropTap() {
this.dismiss(); this.dismiss(null, BACKDROP);
} }
/** /**
@ -163,7 +165,7 @@ export class Loading {
present() { present() {
this.ionLoadingWillPresent.emit(); this.ionLoadingWillPresent.emit();
this.el.style.zIndex = `${20000 + this.loadingId}`; this.el.style.zIndex = `${20000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('loadingEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('loadingEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);

View File

@ -83,6 +83,11 @@ Animation to use when the loading indicator is presented.
Animation to use when the loading indicator is dismissed. Animation to use when the loading indicator is dismissed.
#### overlayId
number
#### showBackdrop #### showBackdrop
boolean boolean
@ -164,6 +169,11 @@ Animation to use when the loading indicator is presented.
Animation to use when the loading indicator is dismissed. Animation to use when the loading indicator is dismissed.
#### overlay-id
number
#### show-backdrop #### show-backdrop
boolean boolean

View File

@ -1,27 +1,28 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { ModalEvent, ModalOptions, OverlayController } from '../../index'; import { ModalEvent, ModalOptions, OverlayController } from '../../index';
import { createOverlay, dismissOverlay, getTopOverlay, removeLastOverlay } from '../../utils/overlays';
let ids = 0;
const modals = new Map<number, HTMLIonModalElement>();
@Component({ @Component({
tag: 'ion-modal-controller' tag: 'ion-modal-controller'
}) })
export class ModalController implements OverlayController { export class ModalController implements OverlayController {
private modals = new Map<number, HTMLIonModalElement>();
@Listen('body:ionModalWillPresent') @Listen('body:ionModalWillPresent')
protected modalWillPresent(ev: ModalEvent) { protected modalWillPresent(ev: ModalEvent) {
modals.set(ev.target.modalId, ev.target); this.modals.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionModalWillDismiss, body:ionModalDidUnload') @Listen('body:ionModalWillDismiss, body:ionModalDidUnload')
protected modalWillDismiss(ev: ModalEvent) { protected modalWillDismiss(ev: ModalEvent) {
modals.delete(ev.target.modalId); this.modals.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastModal(); removeLastOverlay(this.modals);
} }
/* /*
@ -29,21 +30,7 @@ export class ModalController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: ModalOptions): Promise<HTMLIonModalElement> { create(opts?: ModalOptions): Promise<HTMLIonModalElement> {
// create ionic's wrapping ion-modal component return createOverlay('ion-modal', opts);
const modalElement = document.createElement('ion-modal');
// give this modal a unique id
modalElement.modalId = ids++;
// convert the passed in modal options into props
// that get passed down into the new modal
Object.assign(modalElement, opts);
// append the modal element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(modalElement);
return modalElement.componentOnReady();
} }
/* /*
@ -51,12 +38,7 @@ export class ModalController implements OverlayController {
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, modalId = -1) { dismiss(data?: any, role?: any, modalId = -1) {
modalId = modalId >= 0 ? modalId : getHighestId(); return dismissOverlay(data, role, this.modals, modalId);
const modal = modals.get(modalId);
if (!modal) {
return Promise.reject('modal does not exist');
}
return modal.dismiss(data, role);
} }
/* /*
@ -64,21 +46,6 @@ export class ModalController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return modals.get(getHighestId()); return getTopOverlay(this.modals);
} }
} }
function getHighestId() {
let minimum = -1;
modals.forEach((_modal: HTMLIonModalElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastModal() {
const toRemove = modals.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -4,6 +4,7 @@ import { Animation, AnimationBuilder, AnimationController, Config, DomController
import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate'; import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers'; import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme'; import { createThemedClasses } from '../../utils/theme';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
@ -21,8 +22,7 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'modal' theme: 'modal'
} }
}) })
export class Modal { export class Modal implements OverlayInterface {
modalId: number;
private animation: Animation; private animation: Animation;
private usersComponentElement: HTMLElement; private usersComponentElement: HTMLElement;
@ -32,7 +32,7 @@ export class Modal {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
@Prop({ mutable: true }) delegate: FrameworkDelegate; @Prop({ mutable: true }) delegate: FrameworkDelegate;
/** /**
@ -138,10 +138,7 @@ export class Modal {
@Listen('ionBackdropTap') @Listen('ionBackdropTap')
protected onBackdropTap() { protected onBackdropTap() {
// const opts: NavOptions = { this.dismiss(null, BACKDROP);
// minClickBlockDuration: 400
// };
this.dismiss();
} }
/** /**
@ -156,7 +153,7 @@ export class Modal {
this.ionModalWillPresent.emit(); this.ionModalWillPresent.emit();
this.el.style.zIndex = `${20000 + this.modalId}`; this.el.style.zIndex = `${20000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('modalEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);

View File

@ -114,6 +114,11 @@ Possible values are: `"ios"` or `"md"`.
For more information, see [Platform Styles](/docs/theming/platform-specific-styles). For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
#### overlayId
number
#### showBackdrop #### showBackdrop
boolean boolean
@ -196,6 +201,11 @@ Possible values are: `"ios"` or `"md"`.
For more information, see [Platform Styles](/docs/theming/platform-specific-styles). For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
#### overlay-id
number
#### show-backdrop #### show-backdrop
boolean boolean

View File

@ -1,27 +1,28 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { OverlayController, PickerEvent, PickerOptions } from '../../index'; import { OverlayController, PickerEvent, PickerOptions } from '../../index';
import { createOverlay, getTopOverlay, removeLastOverlay, dismissOverlay } from '../../utils/overlays';
let ids = 0;
const pickers = new Map<number, HTMLIonPickerElement>();
@Component({ @Component({
tag: 'ion-picker-controller' tag: 'ion-picker-controller'
}) })
export class PickerController implements OverlayController { export class PickerController implements OverlayController {
private pickers = new Map<number, HTMLIonPickerElement>();
@Listen('body:ionPickerWillPresent') @Listen('body:ionPickerWillPresent')
protected pickerWillPresent(ev: PickerEvent) { protected pickerWillPresent(ev: PickerEvent) {
pickers.set(ev.target.pickerId, ev.target); this.pickers.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionPickerWillDismiss, body:ionPickerDidUnload') @Listen('body:ionPickerWillDismiss, body:ionPickerDidUnload')
protected pickerWillDismiss(ev: PickerEvent) { protected pickerWillDismiss(ev: PickerEvent) {
pickers.delete(ev.target.pickerId); this.pickers.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastPicker(); removeLastOverlay(this.pickers);
} }
/* /*
@ -29,21 +30,7 @@ export class PickerController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: PickerOptions): Promise<HTMLIonPickerElement> { create(opts?: PickerOptions): Promise<HTMLIonPickerElement> {
// create ionic's wrapping ion-picker component return createOverlay('ion-picker', opts);
const pickerElement = document.createElement('ion-picker');
// give this picker a unique id
pickerElement.pickerId = ids++;
// convert the passed in picker options into props
// that get passed down into the new picker
Object.assign(pickerElement, opts);
// append the picker element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(pickerElement);
return pickerElement.componentOnReady();
} }
/* /*
@ -51,12 +38,7 @@ export class PickerController implements OverlayController {
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, pickerId = -1) { dismiss(data?: any, role?: any, pickerId = -1) {
pickerId = pickerId >= 0 ? pickerId : getHighestId(); return dismissOverlay(data, role, this.pickers, pickerId);
const picker = pickers.get(pickerId);
if (!picker) {
return Promise.reject('picker does not exist');
}
return picker.dismiss(data, role);
} }
/* /*
@ -64,21 +46,6 @@ export class PickerController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return pickers.get(getHighestId()); return getTopOverlay(this.pickers);
} }
} }
function getHighestId() {
let minimum = -1;
pickers.forEach((_picker: HTMLIonPickerElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastPicker() {
const toRemove = pickers.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -1,10 +1,12 @@
import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core'; import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index'; import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers'; import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { getClassMap } from '../../utils/theme';
import { OverlayInterface } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
import { getClassMap } from '../../utils/theme';
@Component({ @Component({
tag: 'ion-picker', tag: 'ion-picker',
@ -16,13 +18,12 @@ import { getClassMap } from '../../utils/theme';
theme: 'picker' theme: 'picker'
} }
}) })
export class Picker { export class Picker implements OverlayInterface {
private animation: Animation; private animation: Animation;
private durationTimeout: any; private durationTimeout: any;
private mode: string; private mode: string;
pickerId: number;
@Element() private el: HTMLElement; @Element() private el: HTMLElement;
@State() private showSpinner: boolean = null; @State() private showSpinner: boolean = null;
@ -31,6 +32,7 @@ export class Picker {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
/** /**
* Animation to use when the picker is presented. * Animation to use when the picker is presented.
@ -120,7 +122,7 @@ export class Picker {
this.ionPickerWillPresent.emit(); this.ionPickerWillPresent.emit();
this.el.style.zIndex = `${20000 + this.pickerId}`; this.el.style.zIndex = `${20000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('pickerEnter', iosEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('pickerEnter', iosEnterAnimation);

View File

@ -59,6 +59,11 @@ Animation to use when the picker is presented.
Animation to use when the picker is dismissed. Animation to use when the picker is dismissed.
#### overlayId
number
#### showBackdrop #### showBackdrop
boolean boolean
@ -125,6 +130,11 @@ Animation to use when the picker is presented.
Animation to use when the picker is dismissed. Animation to use when the picker is dismissed.
#### overlay-id
number
#### show-backdrop #### show-backdrop
boolean boolean

View File

@ -1,27 +1,27 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { OverlayController, PopoverEvent, PopoverOptions } from '../../index'; import { OverlayController, PopoverEvent, PopoverOptions } from '../../index';
import { createOverlay, dismissOverlay, getTopOverlay, removeLastOverlay } from '../../utils/overlays';
let ids = 0;
const popovers = new Map<number, HTMLIonPopoverElement>();
@Component({ @Component({
tag: 'ion-popover-controller' tag: 'ion-popover-controller'
}) })
export class PopoverController implements OverlayController { export class PopoverController implements OverlayController {
private popovers = new Map<number, HTMLIonPopoverElement>();
@Listen('body:ionPopoverWillPresent') @Listen('body:ionPopoverWillPresent')
protected popoverWillPresent(ev: PopoverEvent) { protected popoverWillPresent(ev: PopoverEvent) {
popovers.set(ev.target.popoverId, ev.target); this.popovers.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload') @Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload')
protected popoverWillDismiss(ev: PopoverEvent) { protected popoverWillDismiss(ev: PopoverEvent) {
popovers.delete(ev.target.popoverId); this.popovers.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastPopover(); removeLastOverlay(this.popovers);
} }
/* /*
@ -29,21 +29,7 @@ export class PopoverController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> { create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> {
// create ionic's wrapping ion-popover component return createOverlay('ion-popover', opts);
const popoverElement = document.createElement('ion-popover');
// give this popover a unique id
popoverElement.popoverId = ids++;
// convert the passed in popover options into props
// that get passed down into the new popover
Object.assign(popoverElement, opts);
// append the popover element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(popoverElement);
return popoverElement.componentOnReady();
} }
/* /*
@ -51,12 +37,7 @@ export class PopoverController implements OverlayController {
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, popoverId = -1) { dismiss(data?: any, role?: any, popoverId = -1) {
popoverId = popoverId >= 0 ? popoverId : getHighestId(); return dismissOverlay(data, role, this.popovers, popoverId);
const popover = popovers.get(popoverId);
if (!popover) {
return Promise.reject('popover does not exist');
}
return popover.dismiss(data, role);
} }
/* /*
@ -64,21 +45,6 @@ export class PopoverController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return popovers.get(getHighestId()); return getTopOverlay(this.popovers);
} }
} }
function getHighestId() {
let minimum = -1;
popovers.forEach((_popover: HTMLIonPopoverElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastPopover() {
const toRemove = popovers.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -4,6 +4,7 @@ import { Animation, AnimationBuilder, AnimationController, Config, DomController
import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate'; import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers'; import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme'; import { createThemedClasses } from '../../utils/theme';
import { OverlayInterface, BACKDROP } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
@ -20,8 +21,7 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'popover' theme: 'popover'
} }
}) })
export class Popover { export class Popover implements OverlayInterface {
popoverId: number;
private animation: Animation; private animation: Animation;
private usersComponentElement: HTMLElement; private usersComponentElement: HTMLElement;
@ -32,6 +32,7 @@ export class Popover {
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop({ mutable: true }) delegate: FrameworkDelegate; @Prop({ mutable: true }) delegate: FrameworkDelegate;
@Prop() overlayId: number;
/** /**
* The color to use from your Sass `$colors` map. * The color to use from your Sass `$colors` map.
@ -150,7 +151,7 @@ export class Popover {
@Listen('ionBackdropTap') @Listen('ionBackdropTap')
protected onBackdropTap() { protected onBackdropTap() {
this.dismiss(); this.dismiss(null, BACKDROP);
} }
/** /**
@ -164,7 +165,7 @@ export class Popover {
} }
this.ionPopoverWillPresent.emit(); this.ionPopoverWillPresent.emit();
this.el.style.zIndex = `${10000 + this.popoverId}`; this.el.style.zIndex = `${10000 + this.overlayId}`;
// get the user's animation fn if one was provided // get the user's animation fn if one was provided
const animationBuilder = this.enterAnimation || this.config.get('popoverEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); const animationBuilder = this.enterAnimation || this.config.get('popoverEnter', this.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);

View File

@ -101,6 +101,11 @@ Possible values are: `"ios"` or `"md"`.
For more information, see [Platform Styles](/docs/theming/platform-specific-styles). For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
#### overlayId
number
#### showBackdrop #### showBackdrop
boolean boolean
@ -197,6 +202,11 @@ Possible values are: `"ios"` or `"md"`.
For more information, see [Platform Styles](/docs/theming/platform-specific-styles). For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
#### overlay-id
number
#### show-backdrop #### show-backdrop
boolean boolean

View File

@ -1,27 +1,28 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { OverlayController, ToastEvent, ToastOptions } from '../../index'; import { OverlayController, ToastEvent, ToastOptions } from '../../index';
import { createOverlay, dismissOverlay, getTopOverlay, removeLastOverlay } from '../../utils/overlays';
let ids = 0;
const toasts = new Map<number, HTMLIonToastElement>();
@Component({ @Component({
tag: 'ion-toast-controller' tag: 'ion-toast-controller'
}) })
export class ToastController implements OverlayController { export class ToastController implements OverlayController {
private toasts = new Map<number, HTMLIonToastElement>();
@Listen('body:ionToastWillPresent') @Listen('body:ionToastWillPresent')
protected toastWillPresent(ev: ToastEvent) { protected toastWillPresent(ev: ToastEvent) {
toasts.set(ev.target.toastId, ev.target); this.toasts.set(ev.target.overlayId, ev.target);
} }
@Listen('body:ionToastWillDismiss, body:ionToastDidUnload') @Listen('body:ionToastWillDismiss, body:ionToastDidUnload')
protected toastWillDismiss(ev: ToastEvent) { protected toastWillDismiss(ev: ToastEvent) {
toasts.delete(ev.target.toastId); this.toasts.delete(ev.target.overlayId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastToast(); removeLastOverlay(this.toasts);
} }
/* /*
@ -29,21 +30,7 @@ export class ToastController implements OverlayController {
*/ */
@Method() @Method()
create(opts?: ToastOptions): Promise<HTMLIonToastElement> { create(opts?: ToastOptions): Promise<HTMLIonToastElement> {
// create ionic's wrapping ion-toast component return createOverlay('ion-toast', opts);
const toastElement = document.createElement('ion-toast');
// give this toast a unique id
toastElement.toastId = ids++;
// convert the passed in toast options into props
// that get passed down into the new toast
Object.assign(toastElement, opts);
// append the toast element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(toastElement);
return toastElement.componentOnReady();
} }
/* /*
@ -51,12 +38,7 @@ export class ToastController implements OverlayController {
*/ */
@Method() @Method()
dismiss(data?: any, role?: any, toastId = -1) { dismiss(data?: any, role?: any, toastId = -1) {
toastId = toastId >= 0 ? toastId : getHighestId(); return dismissOverlay(data, role, this.toasts, toastId);
const toast = toasts.get(toastId);
if (!toast) {
return Promise.reject('toast does not exist');
}
return toast.dismiss(data, role);
} }
/* /*
@ -64,21 +46,6 @@ export class ToastController implements OverlayController {
*/ */
@Method() @Method()
getTop() { getTop() {
return toasts.get(getHighestId()); return getTopOverlay(this.toasts);
} }
} }
function getHighestId() {
let minimum = -1;
toasts.forEach((_toast: HTMLIonToastElement, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
function removeLastToast() {
const toRemove = toasts.get(getHighestId());
return toRemove ? toRemove.dismiss() : Promise.resolve();
}

View File

@ -94,6 +94,11 @@ string
Message to be shown in the toast. Message to be shown in the toast.
#### overlayId
number
#### position #### position
string string
@ -175,6 +180,11 @@ string
Message to be shown in the toast. Message to be shown in the toast.
#### overlay-id
number
#### position #### position
string string

View File

@ -1,8 +1,9 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, CssClassMap, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index'; import { Animation, AnimationBuilder, AnimationController, Config, CssClassMap, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses, getClassMap } from '../../utils/theme'; import { createThemedClasses, getClassMap } from '../../utils/theme';
import { OverlayInterface } from '../../utils/overlays';
import iosEnterAnimation from './animations/ios.enter'; import iosEnterAnimation from './animations/ios.enter';
import iosLeaveAnimation from './animations/ios.leave'; import iosLeaveAnimation from './animations/ios.leave';
@ -20,10 +21,9 @@ import mdLeaveAnimation from './animations/md.leave';
theme: 'toast' theme: 'toast'
} }
}) })
export class Toast { export class Toast implements OverlayInterface {
private animation: Animation | null;
toastId: number; private animation: Animation | null;
@Element() private el: HTMLElement; @Element() private el: HTMLElement;
@ -33,6 +33,7 @@ export class Toast {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController; @Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config; @Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController; @Prop({ context: 'dom' }) dom: DomController;
@Prop() overlayId: number;
/** /**
* Animation to use when the toast is presented. * Animation to use when the toast is presented.

View File

@ -50,6 +50,16 @@ export function assert(bool: boolean, msg: string) {
} }
} }
export function autoFocus(containerEl: HTMLElement): HTMLElement {
const focusableEls = containerEl.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]');
if (focusableEls.length > 0) {
const el = focusableEls[0] as HTMLInputElement;
el.focus();
return el;
}
return null;
}
export function toDashCase(str: string) { export function toDashCase(str: string) {
return str.replace(/([A-Z])/g, (g) => '-' + g[0].toLowerCase()); return str.replace(/([A-Z])/g, (g) => '-' + g[0].toLowerCase());
} }

View File

@ -1,2 +0,0 @@
export const BACKDROP = 'backdrop';

View File

@ -0,0 +1,62 @@
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;
export function createOverlay<T extends HTMLIonOverlayElement>
(tagName: string, opts: any): Promise<T> {
// create ionic's wrapping ion-alert component
const element = document.createElement(tagName) as T;
// give this alert a unique id
element.overlayId = lastId++;
// convert the passed in alert options into props
// that get passed down into the new alert
Object.assign(element, opts);
// append the alert element to the document body
const appRoot = document.querySelector('ion-app') || document.body;
appRoot.appendChild(element);
return element.componentOnReady();
}
export function dismissOverlay(data: any, role: any, overlays: OverlayMap, id: number): Promise<void> {
id = id >= 0 ? id : getHighestId(overlays);
const overlay = overlays.get(id);
if (!overlay) {
return Promise.reject('overlay does not exist');
}
return overlay.dismiss(data, role);
}
export function getTopOverlay(overlays: Map<number, any>) {
return overlays.get(getHighestId(overlays));
}
export function getHighestId(overlays: Map<number, any>) {
let minimum = -1;
overlays.forEach((_, id: number) => {
if (id > minimum) {
minimum = id;
}
});
return minimum;
}
export function removeLastOverlay(overlays: OverlayMap) {
const toRemove = getTopOverlay(overlays);
return toRemove ? toRemove.dismiss() : Promise.resolve();
}