refactor(app): add cordova-platform to ion-app, add back-button support, get api working

This commit is contained in:
Dan Bucholtz
2018-02-05 22:20:48 -06:00
parent 2f8a027e2f
commit 2bde55421d
29 changed files with 423 additions and 94 deletions

View File

@ -725,6 +725,36 @@ declare global {
} }
import {
CordovaPlatform as IonCordovaPlatform
} from './components/cordova-platform/cordova-platform';
declare global {
interface HTMLIonCordovaPlatformElement extends IonCordovaPlatform, HTMLElement {
}
var HTMLIonCordovaPlatformElement: {
prototype: HTMLIonCordovaPlatformElement;
new (): HTMLIonCordovaPlatformElement;
};
interface HTMLElementTagNameMap {
"ion-cordova-platform": HTMLIonCordovaPlatformElement;
}
interface ElementTagNameMap {
"ion-cordova-platform": HTMLIonCordovaPlatformElement;
}
namespace JSX {
interface IntrinsicElements {
"ion-cordova-platform": JSXElements.IonCordovaPlatformAttributes;
}
}
namespace JSXElements {
export interface IonCordovaPlatformAttributes extends HTMLAttributes {
}
}
}
import { import {
Datetime as IonDatetime Datetime as IonDatetime
} from './components/datetime/datetime'; } from './components/datetime/datetime';

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { ActionSheetEvent, ActionSheetOptions } from '../../index'; import { ActionSheetEvent, ActionSheetOptions, OverlayController } from '../../index';
let ids = 0; let ids = 0;
const actionSheets = new Map<number, HTMLIonActionSheetElement>(); const actionSheets = new Map<number, HTMLIonActionSheetElement>();
@ -7,7 +7,7 @@ const actionSheets = new Map<number, HTMLIonActionSheetElement>();
@Component({ @Component({
tag: 'ion-action-sheet-controller' tag: 'ion-action-sheet-controller'
}) })
export class ActionSheetController { export class ActionSheetController implements OverlayController {
@Method() @Method()
create(opts?: ActionSheetOptions): Promise<HTMLIonActionSheetElement> { create(opts?: ActionSheetOptions): Promise<HTMLIonActionSheetElement> {
@ -38,6 +38,10 @@ export class ActionSheetController {
return actionSheet.dismiss(data, role); return actionSheet.dismiss(data, role);
} }
@Method()
getTop() {
return actionSheets.get(getHighestId());
}
@Listen('body:ionActionSheetWillPresent') @Listen('body:ionActionSheetWillPresent')
protected actionSheetWillPresent(ev: ActionSheetEvent) { protected actionSheetWillPresent(ev: ActionSheetEvent) {

View File

@ -36,6 +36,9 @@
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { AlertEvent, AlertOptions } from '../../index'; import { AlertEvent, AlertOptions, OverlayController } from '../../index';
let ids = 0; let ids = 0;
const alerts = new Map<number, HTMLIonAlertElement>(); const alerts = new Map<number, HTMLIonAlertElement>();
@ -7,7 +7,7 @@ const alerts = new Map<number, HTMLIonAlertElement>();
@Component({ @Component({
tag: 'ion-alert-controller' tag: 'ion-alert-controller'
}) })
export class AlertController { export class AlertController implements OverlayController {
@Method() @Method()
create(opts?: AlertOptions): Promise<HTMLIonAlertElement> { create(opts?: AlertOptions): Promise<HTMLIonAlertElement> {
@ -38,6 +38,10 @@ export class AlertController {
return alert.dismiss(data, role); return alert.dismiss(data, role);
} }
@Method()
getTop() {
return alerts.get(getHighestId());
}
@Listen('body:ionAlertWillPresent') @Listen('body:ionAlertWillPresent')
protected alertWillPresent(ev: AlertEvent) { protected alertWillPresent(ev: AlertEvent) {

View File

@ -50,6 +50,9 @@ building the form within a modal instead.
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -1,11 +1,12 @@
import { Component, Element, Listen, Method, Prop, State } from '@stencil/core'; import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Config, NavContainer, NavEvent } from '../../index'; import { Config, NavEvent, OverlayController, PublicNav, PublicViewController } from '../../index';
import { isReady } from '../../utils/helpers';
import { getOrAppendElement } from '../../utils/helpers';
import { isCordova } from '../../global/platform-utils';
const rootNavs = new Map<number, HTMLIonNavElement>(); const rootNavs = new Map<number, HTMLIonNavElement>();
const ACTIVE_SCROLLING_TIME = 100; const ACTIVE_SCROLLING_TIME = 100;
@Component({ @Component({
tag: 'ion-app', tag: 'ion-app',
styleUrls: { styleUrls: {
@ -18,10 +19,10 @@ const ACTIVE_SCROLLING_TIME = 100;
}) })
export class App { export class App {
private didScroll = false;
private scrollTime = 0; private scrollTime = 0;
@Element() element: HTMLElement; @Element() element: HTMLElement;
@Event() exitApp: EventEmitter<ExitAppEventDetail>;
@State() modeCode: string; @State() modeCode: string;
@State() hoverCSS = false; @State() hoverCSS = false;
@ -44,16 +45,17 @@ export class App {
* Returns an array of top level Navs * Returns an array of top level Navs
*/ */
@Method() @Method()
getRootNavs(): NavContainer[] { getRootNavs(): PublicNav[] {
/*const navs: NavContainer[] = []; const navs: PublicNav[] = [];
rootNavs.forEach((rootNav: NavContainer) => { rootNavs.forEach((rootNav: PublicNav) => {
navs.push(rootNav); navs.push(rootNav);
}); });
return navs; return navs;
*/
return [];
} }
/**
* Returns whether the application is enabled or not
*/
@Method() @Method()
isEnabled(): boolean { isEnabled(): boolean {
return true; return true;
@ -79,45 +81,66 @@ export class App {
@Method() @Method()
setScrolling() { setScrolling() {
this.scrollTime = Date.now() + ACTIVE_SCROLLING_TIME; this.scrollTime = Date.now() + ACTIVE_SCROLLING_TIME;
this.didScroll = true;
} }
@Method() @Method()
getActiveNavs(_rootNavId?: number): NavContainer[] { getTopNavs(rootNavId = -1): PublicNav[] {
/*const portal = portals.get(PORTAL_MODAL); return getTopNavsImpl(rootNavId);
if (portal && portal.views && portal.views.length) {
return findTopNavs(portal);
}
*/
// TODO - figure out if a modal is open, don't use portal
/*if (!rootNavs.size) {
return [];
}
if (rootNavId) {
return findTopNavs(rootNavs.get(rootNavId));
}
if (rootNavs.size === 1) {
return findTopNavs(rootNavs.values().next().value);
}
// fallback to just using all root navs
let activeNavs: NavContainer[] = [];
rootNavs.forEach(nav => {
activeNavs = activeNavs.concat(findTopNavs(nav));
});
return activeNavs;
*/
return [];
} }
@Method() getNavByIdOrName(_nameOrId: number | string): any { @Method()
/*const navs = Array.from(rootNavs.values()); getNavByIdOrName(nameOrId: number | string): PublicNav {
const navs = Array.from(rootNavs.values());
for (const navContainer of navs) { for (const navContainer of navs) {
const match = getNavByIdOrNameImpl(navContainer, nameOrId); const match = getNavByIdOrNameImpl(navContainer, nameOrId);
if (match) { if (match) {
return match; return match;
} }
} }
*/ return null;
}
@Method()
hardwareBackButtonPressed() {
// check if menu exists and is open
return checkIfMenuIsOpen().then((done: boolean) => {
if (!done) {
// we need to check if there is an action-sheet, alert, loading, picker, popover or toast open
// if so, just return and don't do anything
// Why? I have no idea, but that is the existing behavior in Ionic 3
return checkIfNotModalOverlayIsOpen();
}
return done;
}).then((done: boolean) => {
if (!done) {
// if there's a modal open, close that instead
return closeModalIfOpen();
}
return done;
}).then((done: boolean) => {
// okay cool, it's time to pop a nav if possible
if (!done) {
return popEligibleView();
}
return done;
}).then((done: boolean) => {
if (!done) {
// okay, we didn't find a nav that we can pop, so we should just exit the app
// since each platform exits differently, just delegate it to the platform to
// figure out how to exit
return this.exitApp.emit();
}
return Promise.resolve();
});
}
@Method()
appResume(): void {
return null;
}
@Method()
appPaused(): void {
return null; return null;
} }
@ -133,17 +156,37 @@ export class App {
render() { render() {
const isDevice = true; const isDevice = true;
return [ return [
isCordova() && <ion-cordova-platform/>,
isDevice && <ion-tap-click />, isDevice && <ion-tap-click />,
isDevice && <ion-status-tap />, isDevice && <ion-status-tap />,
// <ion-router-controller></ion-router-controller>
<slot></slot> <slot></slot>
]; ];
} }
} }
export function getTopNavsImpl(rootNavId = -1) {
if (!rootNavs.size) {
return [];
}
export function findTopNavs(nav: NavContainer): NavContainer[] { if (rootNavId !== -1) {
let containers: NavContainer[] = []; return findTopNavs(rootNavs.get(rootNavId));
}
if (rootNavs.size === 1) {
return findTopNavs(rootNavs.values().next().value);
}
// fallback to just using all root navs
let activeNavs: PublicNav[] = [];
rootNavs.forEach(nav => {
activeNavs = activeNavs.concat(findTopNavs(nav));
});
return activeNavs;
}
export function findTopNavs(nav: PublicNav): PublicNav[] {
let containers: PublicNav[] = [];
const childNavs = nav.getChildNavs(); const childNavs = nav.getChildNavs();
if (!childNavs || !childNavs.length) { if (!childNavs || !childNavs.length) {
containers.push(nav); containers.push(nav);
@ -156,11 +199,11 @@ export function findTopNavs(nav: NavContainer): NavContainer[] {
return containers; return containers;
} }
export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): NavContainer { export function getNavByIdOrNameImpl(nav: PublicNav, id: number | string): PublicNav {
if (nav.id === id || nav.name === id) { if (nav.navId === id || nav.name === id) {
return nav; return nav;
} }
for (const child of nav.getAllChildNavs()) { for (const child of nav.getChildNavs()) {
const tmp = getNavByIdOrNameImpl(child, id); const tmp = getNavByIdOrNameImpl(child, id);
if (tmp) { if (tmp) {
return tmp; return tmp;
@ -169,12 +212,95 @@ export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): Na
return null; return null;
} }
export function handleBackButtonClick(): Promise<any> { export function getHydratedController(tagName: string): Promise<HTMLElement> {
// if there is a menu controller dom element, hydrate it, otherwise move on const controller = getOrAppendElement(tagName);
// TODO ensure ion-menu-controller is the name return (controller as any).componentOnReady();
const menuControllerElement = document.querySelector('ion-menu-controller'); // TODO - use menu controller types }
const promise = menuControllerElement ? isReady(menuControllerElement) : Promise.resolve();
return promise.then(() => { export function checkIfMenuIsOpen(): Promise<boolean> {
// TODO check if the menu is open, close it if so return getHydratedController('ion-menu-controller').then((menuController: HTMLIonMenuControllerElement) => {
if (menuController.isOpen()) {
return menuController.close().then(() => {
return true;
});
}
return false;
}); });
} }
export function checkIfNotModalOverlayIsOpen(): Promise<boolean> {
const promises: Promise<any>[] = [];
promises.push(checkIfOverlayExists('ion-action-sheet-controller'));
promises.push(checkIfOverlayExists('ion-alert-controller'));
promises.push(checkIfOverlayExists('ion-loading-controller'));
promises.push(checkIfOverlayExists('ion-picker-controller'));
promises.push(checkIfOverlayExists('ion-popover-controller'));
promises.push(checkIfOverlayExists('ion-toast-controller'));
return Promise.all(promises).then((results: boolean[]) => {
return results.every((value: boolean) => !!value);
});
}
export function checkIfOverlayExists(tagName: string): Promise<boolean> {
const overlayControllerElement = document.querySelector(tagName) as any as OverlayController;
if (!overlayControllerElement) {
return Promise.resolve(false);
}
return (overlayControllerElement as any).componentOnReady().then(() => {
return !!(overlayControllerElement.getTop());
});
}
export function closeModalIfOpen(): Promise<boolean> {
return getHydratedController('ion-modal-controller').then((modalController: HTMLIonModalControllerElement) => {
if (modalController.getTop()) {
return modalController.dismiss().then(() => {
return true;
});
}
return false;
});
}
export function popEligibleView(): Promise<boolean> {
let navToPop: PublicNav = null;
let mostRecentVC: PublicViewController = null;
rootNavs.forEach(nav => {
const topNavs = getTopNavsImpl(nav.navId);
const poppableNavs = topNavs.map(topNav => getPoppableNav(topNav)).filter(nav => !!nav).filter(nav => !!nav.last());
poppableNavs.forEach(poppable => {
const topViewController = poppable.last();
if (!mostRecentVC || topViewController.timestamp >= mostRecentVC.timestamp) {
mostRecentVC = topViewController;
navToPop = poppable;
}
});
});
if (navToPop) {
return navToPop.pop().then(() => {
return true;
});
}
return Promise.resolve(false);
}
export function getPoppableNav(nav: PublicNav): PublicNav {
if (!nav) {
return null;
}
// to be a poppable nav, a nav must a top view, plus a view that we can pop back to
if (nav.getViews.length > 1) {
return nav;
}
return getPoppableNav(nav.parent);
}
export interface ExitAppEvent extends CustomEvent {
target: HTMLIonAppElement;
detail: ExitAppEventDetail;
}
export interface ExitAppEventDetail {
}

View File

@ -5,9 +5,17 @@
<!-- Auto Generated Below --> <!-- Auto Generated Below -->
## Events
#### exitApp
## Methods ## Methods
#### getActiveNavs() #### appPaused()
#### appResume()
#### getNavByIdOrName() #### getNavByIdOrName()
@ -18,8 +26,16 @@
Returns an array of top level Navs Returns an array of top level Navs
#### getTopNavs()
#### hardwareBackButtonPressed()
#### isEnabled() #### isEnabled()
Returns whether the application is enabled or not
#### isScrolling() #### isScrolling()

View File

@ -0,0 +1,84 @@
import { Component, Listen, Method} from '@stencil/core';
let isReady = false;
let readyQueue: Function[] = [];
@Component({
tag: 'ion-cordova-platform',
styleUrls: {
ios: 'cordova-platform.ios.scss',
md: 'cordova-platform.md.scss'
},
host: {
theme: 'cordova-platform'
}
})
export class CordovaPlatform {
@Method()
ready() {
return readyImpl();
}
@Listen('document:deviceready')
deviceReadyHandler() {
isReady = true;
processReadyQueue();
}
@Listen('body:exitApp')
exitCordovaApp() {
// this is lifted directly from Ionic 3
((window.navigator as any).app as any).exitApp();
}
componentDidLoad() {
readyImpl().then(() => {
// okay cool, we've received the ready event, we need to listen for the other events now
document.addEventListener('backbutton', handleBackButton);
document.addEventListener('resume', handleResume);
document.addEventListener('pause', handlePause);
});
}
}
export function handleBackButton() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.hardwareBackButtonPressed();
});
}
export function handleResume() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.appResume();
});
}
export function handlePause() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.appPaused();
});
}
export function getHydratedApp() {
const app = document.querySelector('ion-app');
return (app as any).componentOnReady();
}
export function readyImpl(): Promise<any> {
if (isReady) {
return Promise.resolve();
}
return new Promise((resolve) => {
readyQueue.push(resolve);
});
}
export function processReadyQueue() {
for (const resolve of readyQueue) {
resolve();
}
readyQueue = [];
}

View File

@ -0,0 +1,16 @@
# ion-cordova-platform
<!-- Auto Generated Below -->
## Methods
#### ready()
----------------------------------------------
*Built by [StencilJS](https://stenciljs.com/)*

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { LoadingEvent, LoadingOptions } from '../../index'; import { LoadingEvent, LoadingOptions, OverlayController } from '../../index';
let ids = 0; let ids = 0;
const loadings = new Map<number, HTMLIonLoadingElement>(); const loadings = new Map<number, HTMLIonLoadingElement>();
@ -7,7 +7,7 @@ const loadings = new Map<number, HTMLIonLoadingElement>();
@Component({ @Component({
tag: 'ion-loading-controller' tag: 'ion-loading-controller'
}) })
export class LoadingController { export class LoadingController implements OverlayController {
@Method() @Method()
create(opts?: LoadingOptions): Promise<HTMLIonLoadingElement> { create(opts?: LoadingOptions): Promise<HTMLIonLoadingElement> {
@ -35,6 +35,10 @@ export class LoadingController {
return loading.dismiss(data, role); return loading.dismiss(data, role);
} }
@Method()
getTop() {
return loadings.get(getHighestId());
}
@Listen('body:ionLoadingWillPresent') @Listen('body:ionLoadingWillPresent')
protected loadingWillPresent(ev: LoadingEvent) { protected loadingWillPresent(ev: LoadingEvent) {

View File

@ -44,6 +44,9 @@ in the `usage` section below.
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { ModalEvent, ModalOptions } from '../../index'; import { ModalEvent, ModalOptions, OverlayController } from '../../index';
let ids = 0; let ids = 0;
const modals = new Map<number, HTMLIonModalElement>(); const modals = new Map<number, HTMLIonModalElement>();
@ -7,7 +7,7 @@ const modals = new Map<number, HTMLIonModalElement>();
@Component({ @Component({
tag: 'ion-modal-controller' tag: 'ion-modal-controller'
}) })
export class ModalController { export class ModalController implements OverlayController {
@Method() @Method()
create(opts?: ModalOptions): Promise<HTMLIonModalElement> { create(opts?: ModalOptions): Promise<HTMLIonModalElement> {
@ -38,6 +38,10 @@ export class ModalController {
return modal.dismiss(data, role); return modal.dismiss(data, role);
} }
@Method()
getTop() {
return modals.get(getHighestId());
}
@Listen('body:ionModalWillPresent') @Listen('body:ionModalWillPresent')
protected modalWillPresent(ev: ModalEvent) { protected modalWillPresent(ev: ModalEvent) {
@ -50,7 +54,6 @@ export class ModalController {
modals.delete(ev.target.modalId); modals.delete(ev.target.modalId);
} }
@Listen('body:keyup.escape') @Listen('body:keyup.escape')
protected escapeKeyUp() { protected escapeKeyUp() {
removeLastModal(); removeLastModal();

View File

@ -13,6 +13,9 @@
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -4,11 +4,11 @@ import {
Animation, Animation,
AnimationOptions, AnimationOptions,
FrameworkDelegate, FrameworkDelegate,
FrameworkMountingData,
Nav, Nav,
NavOptions, NavOptions,
PublicViewController, PublicViewController,
ViewController, ViewController,
FrameworkMountingData
} from '../../index'; } from '../../index';
export interface PublicNav { export interface PublicNav {
@ -27,20 +27,15 @@ export interface PublicNav {
getPrevious(view?: PublicViewController): PublicViewController; getPrevious(view?: PublicViewController): PublicViewController;
canGoBack(): boolean; canGoBack(): boolean;
canSwipeBack(): boolean; canSwipeBack(): boolean;
getFirstView(): PublicViewController; first(): PublicViewController;
last(): PublicViewController;
getChildNavs(): PublicNav[]; getChildNavs(): PublicNav[];
getViews(): PublicViewController[];
element?: HTMLElement; navId?: number;
}
export interface NavContainer {
id?: number;
name?: string; name?: string;
parent?: Nav; element?: HTMLElement;
getChildNavs?(): NavContainer[]; parent?: PublicNav;
getAllChildNavs?(): NavContainer[];
getType?(): string;
getSecondaryIdentifier?(): string;
} }
export interface NavOptions { export interface NavOptions {
@ -110,4 +105,5 @@ export interface PublicViewController {
component?: any; component?: any;
instance?: any; instance?: any;
element?: HTMLElement; element?: HTMLElement;
timestamp?: number;
} }

View File

@ -159,7 +159,11 @@ export function canSwipeBack(_nav: Nav) {
} }
export function getFirstView(nav: Nav): ViewController { export function getFirstView(nav: Nav): ViewController {
return nav.views && nav.views.length > 0 ? nav.views[0] : null; return nav.views && nav.views.length ? nav.views[0] : null;
}
export function getLastView(nav: Nav): ViewController {
return nav.views && nav.views.length ? nav.views[nav.views.length - 1] : null;
} }
export function getActiveChildNavs(nav: Nav): Nav[] { export function getActiveChildNavs(nav: Nav): Nav[] {

View File

@ -6,7 +6,6 @@ import {
ComponentDataPair, ComponentDataPair,
Config, Config,
FrameworkDelegate, FrameworkDelegate,
NavContainer,
NavOptions, NavOptions,
NavResult, NavResult,
NavState, NavState,
@ -29,6 +28,7 @@ import {
getActiveImpl, getActiveImpl,
getFirstView, getFirstView,
getHydratedTransition, getHydratedTransition,
getLastView,
getNextNavId, getNextNavId,
getNextTransitionId, getNextTransitionId,
getParentTransitionId, getParentTransitionId,
@ -62,7 +62,7 @@ const urlMap = new Map<string, TransitionInstruction>();
tag: 'ion-nav', tag: 'ion-nav',
styleUrl: 'nav.scss' styleUrl: 'nav.scss'
}) })
export class Nav implements PublicNav, NavContainer { export class Nav implements PublicNav {
@Element() element: HTMLElement; @Element() element: HTMLElement;
@Event() navInit: EventEmitter<NavEventDetail>; @Event() navInit: EventEmitter<NavEventDetail>;
@ -97,7 +97,6 @@ export class Nav implements PublicNav, NavContainer {
componentWillLoad() { componentWillLoad() {
this.routes = Array.from(this.element.querySelectorAll('ion-route')) this.routes = Array.from(this.element.querySelectorAll('ion-route'))
.map(child => child.getRoute()); .map(child => child.getRoute());
//this.useRouter = false; // this.config.getBoolean('useRouter', false);
} }
componentDidLoad() { componentDidLoad() {
@ -193,10 +192,15 @@ export class Nav implements PublicNav, NavContainer {
} }
@Method() @Method()
getFirstView(): PublicViewController { first(): PublicViewController {
return getFirstView(this); return getFirstView(this);
} }
@Method()
last(): PublicViewController {
return getLastView(this);
}
@Method() @Method()
getState(): NavState { getState(): NavState {
assert(this.useRouter, 'routing is disabled'); assert(this.useRouter, 'routing is disabled');
@ -340,7 +344,7 @@ export function canGoBackImpl(nav: Nav) {
} }
export function navInitializedImpl(potentialParent: Nav, event: NavEvent) { export function navInitializedImpl(potentialParent: Nav, event: NavEvent) {
if (potentialParent.element !== event.target) { if ((potentialParent.element as any as HTMLIonNavElement) !== event.target) {
// set the parent on the child nav that dispatched the event // set the parent on the child nav that dispatched the event
event.target.setParent(potentialParent); event.target.setParent(potentialParent);
if (!potentialParent.childNavs) { if (!potentialParent.childNavs) {

View File

@ -78,15 +78,15 @@ boolean
#### clearTransitionInfoForUrl() #### clearTransitionInfoForUrl()
#### first()
#### getActive() #### getActive()
#### getChildNavs() #### getChildNavs()
#### getFirstView()
#### getId() #### getId()
@ -114,6 +114,9 @@ boolean
#### isTransitioning() #### isTransitioning()
#### last()
#### pop() #### pop()

View File

@ -14,6 +14,7 @@ export class ViewController implements PublicViewController {
overlay: boolean; overlay: boolean;
zIndex: number; zIndex: number;
dismissProxy: any; dismissProxy: any;
timestamp: number;
onDidDismiss: (data: any, role: string) => void; onDidDismiss: (data: any, role: string) => void;
@ -159,6 +160,7 @@ export function didLoadImpl(viewController: ViewController) {
} }
export function initializeNewViewController(viewController: ViewController, data: any) { export function initializeNewViewController(viewController: ViewController, data: any) {
viewController.timestamp = Date.now();
viewController.state = STATE_NEW; viewController.state = STATE_NEW;
viewController.data = data || {}; viewController.data = data || {};
} }

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { PickerEvent, PickerOptions } from '../../index'; import { PickerEvent, PickerOptions, OverlayController } from '../../index';
let ids = 0; let ids = 0;
const pickers = new Map<number, HTMLIonPickerElement>(); const pickers = new Map<number, HTMLIonPickerElement>();
@ -7,7 +7,7 @@ const pickers = new Map<number, HTMLIonPickerElement>();
@Component({ @Component({
tag: 'ion-picker-controller' tag: 'ion-picker-controller'
}) })
export class PickerController { export class PickerController implements OverlayController {
@Method() @Method()
create(opts?: PickerOptions): Promise<HTMLIonPickerElement> { create(opts?: PickerOptions): Promise<HTMLIonPickerElement> {
@ -38,6 +38,10 @@ export class PickerController {
return picker.dismiss(data, role); return picker.dismiss(data, role);
} }
@Method()
getTop() {
return pickers.get(getHighestId());
}
@Listen('body:ionPickerWillPresent') @Listen('body:ionPickerWillPresent')
protected pickerWillPresent(ev: PickerEvent) { protected pickerWillPresent(ev: PickerEvent) {

View File

@ -13,6 +13,9 @@
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { PopoverEvent, PopoverOptions } from '../../index'; import { OverlayController, PopoverEvent, PopoverOptions } from '../../index';
let ids = 0; let ids = 0;
const popovers = new Map<number, HTMLIonPopoverElement>(); const popovers = new Map<number, HTMLIonPopoverElement>();
@ -7,7 +7,7 @@ const popovers = new Map<number, HTMLIonPopoverElement>();
@Component({ @Component({
tag: 'ion-popover-controller' tag: 'ion-popover-controller'
}) })
export class PopoverController { export class PopoverController implements OverlayController {
@Method() @Method()
create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> { create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> {
@ -38,6 +38,10 @@ export class PopoverController {
return popover.dismiss(data, role); return popover.dismiss(data, role);
} }
@Method()
getTop() {
return popovers.get(getHighestId());
}
@Listen('body:ionPopoverWillPresent') @Listen('body:ionPopoverWillPresent')
protected popoverWillPresent(ev: PopoverEvent) { protected popoverWillPresent(ev: PopoverEvent) {

View File

@ -32,6 +32,9 @@ view. See the [usage](#usage) section for an example of passing this event.
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -13,6 +13,9 @@
#### dismiss() #### dismiss()
#### getTop()
---------------------------------------------- ----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core'; import { Component, Listen, Method } from '@stencil/core';
import { ToastEvent, ToastOptions } from '../../index'; import { OverlayController, ToastEvent, ToastOptions } from '../../index';
let ids = 0; let ids = 0;
const toasts = new Map<number, HTMLIonToastElement>(); const toasts = new Map<number, HTMLIonToastElement>();
@ -7,7 +7,7 @@ const toasts = new Map<number, HTMLIonToastElement>();
@Component({ @Component({
tag: 'ion-toast-controller' tag: 'ion-toast-controller'
}) })
export class ToastController { export class ToastController implements OverlayController {
@Method() @Method()
create(opts?: ToastOptions): Promise<HTMLIonToastElement> { create(opts?: ToastOptions): Promise<HTMLIonToastElement> {
@ -38,6 +38,10 @@ export class ToastController {
return toast.dismiss(data, role); return toast.dismiss(data, role);
} }
@Method()
getTop() {
return toasts.get(getHighestId());
}
@Listen('body:ionToastWillPresent') @Listen('body:ionToastWillPresent')
protected toastWillPresent(ev: ToastEvent) { protected toastWillPresent(ev: ToastEvent) {

View File

@ -145,10 +145,6 @@ export interface OverlayDismissEventDetail {
role?: string; role?: string;
} }
export interface OverlayController {
create(): HTMLElement;
}
export interface FrameworkDelegate { export interface FrameworkDelegate {
attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise<FrameworkMountingData>; attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise<FrameworkMountingData>;
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>; removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>;
@ -160,7 +156,6 @@ export interface FrameworkMountingData {
data: any; data: any;
} }
declare global { declare global {
namespace JSXElements { namespace JSXElements {
@ -186,3 +181,7 @@ declare global {
} }
} }
} }
export interface OverlayController {
getTop(): HTMLElement;
}

View File

@ -302,7 +302,7 @@ export function debounce(func: Function, wait = 0) {
export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null { export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null {
for (let i = 0; i < element.children.length; i++) { for (let i = 0; i < element.children.length; i++) {
if (element.children[i].tagName.toLowerCase() === 'ion-nav') { if (element.children[i].tagName.toLowerCase() === 'ion-nav') {
return element.children[i] as HTMLIonNavElement; return element.children[i] as any as HTMLIonNavElement;
} }
} }
return null; return null;

View File

@ -42,6 +42,7 @@ exports.config = {
{ components: ['ion-toggle'] }, { components: ['ion-toggle'] },
{ components: ['ion-toast', 'ion-toast-controller'] }, { components: ['ion-toast', 'ion-toast-controller'] },
{ components: ['ion-tap-click', 'ion-status-tap'] }, { components: ['ion-tap-click', 'ion-status-tap'] },
{ components: ['ion-cordova-platform'] },
], ],
collections: [ collections: [
'ionicons' 'ionicons'