From 2bde55421d548cedecf73784a299c353c5a9bd84 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Mon, 5 Feb 2018 22:20:48 -0600 Subject: [PATCH] refactor(app): add cordova-platform to ion-app, add back-button support, get api working --- packages/core/src/components.d.ts | 30 +++ .../action-sheet-controller.tsx | 8 +- .../action-sheet-controller/readme.md | 3 + .../alert-controller/alert-controller.tsx | 8 +- .../src/components/alert-controller/readme.md | 3 + packages/core/src/components/app/app.tsx | 228 ++++++++++++++---- packages/core/src/components/app/readme.md | 18 +- .../cordova-platform.ios.scss | 0 .../cordova-platform/cordova-platform.md.scss | 0 .../cordova-platform/cordova-platform.tsx | 84 +++++++ .../src/components/cordova-platform/readme.md | 16 ++ .../loading-controller/loading-controller.tsx | 8 +- .../components/loading-controller/readme.md | 3 + .../modal-controller/modal-controller.tsx | 9 +- .../src/components/modal-controller/readme.md | 3 + .../core/src/components/nav/nav-interfaces.ts | 20 +- packages/core/src/components/nav/nav-utils.ts | 6 +- packages/core/src/components/nav/nav.tsx | 14 +- packages/core/src/components/nav/readme.md | 9 +- .../src/components/nav/view-controller.ts | 2 + .../picker-controller/picker-controller.tsx | 8 +- .../components/picker-controller/readme.md | 3 + .../popover-controller/popover-controller.tsx | 8 +- .../components/popover-controller/readme.md | 3 + .../src/components/toast-controller/readme.md | 3 + .../toast-controller/toast-controller.tsx | 8 +- packages/core/src/index.d.ts | 9 +- packages/core/src/utils/helpers.ts | 2 +- packages/core/stencil.config.js | 1 + 29 files changed, 423 insertions(+), 94 deletions(-) create mode 100644 packages/core/src/components/cordova-platform/cordova-platform.ios.scss create mode 100644 packages/core/src/components/cordova-platform/cordova-platform.md.scss create mode 100644 packages/core/src/components/cordova-platform/cordova-platform.tsx create mode 100644 packages/core/src/components/cordova-platform/readme.md diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index b57ad7ce7b..62c84ed460 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -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 { Datetime as IonDatetime } from './components/datetime/datetime'; diff --git a/packages/core/src/components/action-sheet-controller/action-sheet-controller.tsx b/packages/core/src/components/action-sheet-controller/action-sheet-controller.tsx index ded5113541..5562055009 100644 --- a/packages/core/src/components/action-sheet-controller/action-sheet-controller.tsx +++ b/packages/core/src/components/action-sheet-controller/action-sheet-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { ActionSheetEvent, ActionSheetOptions } from '../../index'; +import { ActionSheetEvent, ActionSheetOptions, OverlayController } from '../../index'; let ids = 0; const actionSheets = new Map(); @@ -7,7 +7,7 @@ const actionSheets = new Map(); @Component({ tag: 'ion-action-sheet-controller' }) -export class ActionSheetController { +export class ActionSheetController implements OverlayController { @Method() create(opts?: ActionSheetOptions): Promise { @@ -38,6 +38,10 @@ export class ActionSheetController { return actionSheet.dismiss(data, role); } + @Method() + getTop() { + return actionSheets.get(getHighestId()); + } @Listen('body:ionActionSheetWillPresent') protected actionSheetWillPresent(ev: ActionSheetEvent) { diff --git a/packages/core/src/components/action-sheet-controller/readme.md b/packages/core/src/components/action-sheet-controller/readme.md index b99cae7e0e..0f17d607c0 100644 --- a/packages/core/src/components/action-sheet-controller/readme.md +++ b/packages/core/src/components/action-sheet-controller/readme.md @@ -36,6 +36,9 @@ #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/alert-controller/alert-controller.tsx b/packages/core/src/components/alert-controller/alert-controller.tsx index a8ec009336..78ebd812b5 100644 --- a/packages/core/src/components/alert-controller/alert-controller.tsx +++ b/packages/core/src/components/alert-controller/alert-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { AlertEvent, AlertOptions } from '../../index'; +import { AlertEvent, AlertOptions, OverlayController } from '../../index'; let ids = 0; const alerts = new Map(); @@ -7,7 +7,7 @@ const alerts = new Map(); @Component({ tag: 'ion-alert-controller' }) -export class AlertController { +export class AlertController implements OverlayController { @Method() create(opts?: AlertOptions): Promise { @@ -38,6 +38,10 @@ export class AlertController { return alert.dismiss(data, role); } + @Method() + getTop() { + return alerts.get(getHighestId()); + } @Listen('body:ionAlertWillPresent') protected alertWillPresent(ev: AlertEvent) { diff --git a/packages/core/src/components/alert-controller/readme.md b/packages/core/src/components/alert-controller/readme.md index 37656c9a25..314873ddf4 100644 --- a/packages/core/src/components/alert-controller/readme.md +++ b/packages/core/src/components/alert-controller/readme.md @@ -50,6 +50,9 @@ building the form within a modal instead. #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/app/app.tsx b/packages/core/src/components/app/app.tsx index 67d6c0d87e..282a7473c1 100644 --- a/packages/core/src/components/app/app.tsx +++ b/packages/core/src/components/app/app.tsx @@ -1,11 +1,12 @@ -import { Component, Element, Listen, Method, Prop, State } from '@stencil/core'; -import { Config, NavContainer, NavEvent } from '../../index'; -import { isReady } from '../../utils/helpers'; +import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core'; +import { Config, NavEvent, OverlayController, PublicNav, PublicViewController } from '../../index'; + +import { getOrAppendElement } from '../../utils/helpers'; +import { isCordova } from '../../global/platform-utils'; const rootNavs = new Map(); const ACTIVE_SCROLLING_TIME = 100; - @Component({ tag: 'ion-app', styleUrls: { @@ -18,10 +19,10 @@ const ACTIVE_SCROLLING_TIME = 100; }) export class App { - private didScroll = false; private scrollTime = 0; @Element() element: HTMLElement; + @Event() exitApp: EventEmitter; @State() modeCode: string; @State() hoverCSS = false; @@ -44,16 +45,17 @@ export class App { * Returns an array of top level Navs */ @Method() - getRootNavs(): NavContainer[] { - /*const navs: NavContainer[] = []; - rootNavs.forEach((rootNav: NavContainer) => { + getRootNavs(): PublicNav[] { + const navs: PublicNav[] = []; + rootNavs.forEach((rootNav: PublicNav) => { navs.push(rootNav); }); return navs; - */ - return []; } + /** + * Returns whether the application is enabled or not + */ @Method() isEnabled(): boolean { return true; @@ -79,45 +81,66 @@ export class App { @Method() setScrolling() { this.scrollTime = Date.now() + ACTIVE_SCROLLING_TIME; - this.didScroll = true; } @Method() - getActiveNavs(_rootNavId?: number): NavContainer[] { - /*const portal = portals.get(PORTAL_MODAL); - 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 []; + getTopNavs(rootNavId = -1): PublicNav[] { + return getTopNavsImpl(rootNavId); } - @Method() getNavByIdOrName(_nameOrId: number | string): any { - /*const navs = Array.from(rootNavs.values()); + @Method() + getNavByIdOrName(nameOrId: number | string): PublicNav { + const navs = Array.from(rootNavs.values()); for (const navContainer of navs) { const match = getNavByIdOrNameImpl(navContainer, nameOrId); if (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; } @@ -133,17 +156,37 @@ export class App { render() { const isDevice = true; return [ + isCordova() && , isDevice && , isDevice && , - // ]; } } +export function getTopNavsImpl(rootNavId = -1) { + if (!rootNavs.size) { + return []; + } -export function findTopNavs(nav: NavContainer): NavContainer[] { - let containers: NavContainer[] = []; + if (rootNavId !== -1) { + 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(); if (!childNavs || !childNavs.length) { containers.push(nav); @@ -156,11 +199,11 @@ export function findTopNavs(nav: NavContainer): NavContainer[] { return containers; } -export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): NavContainer { - if (nav.id === id || nav.name === id) { +export function getNavByIdOrNameImpl(nav: PublicNav, id: number | string): PublicNav { + if (nav.navId === id || nav.name === id) { return nav; } - for (const child of nav.getAllChildNavs()) { + for (const child of nav.getChildNavs()) { const tmp = getNavByIdOrNameImpl(child, id); if (tmp) { return tmp; @@ -169,12 +212,95 @@ export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): Na return null; } -export function handleBackButtonClick(): Promise { - // if there is a menu controller dom element, hydrate it, otherwise move on - // TODO ensure ion-menu-controller is the name - const menuControllerElement = document.querySelector('ion-menu-controller'); // TODO - use menu controller types - const promise = menuControllerElement ? isReady(menuControllerElement) : Promise.resolve(); - return promise.then(() => { - // TODO check if the menu is open, close it if so +export function getHydratedController(tagName: string): Promise { + const controller = getOrAppendElement(tagName); + return (controller as any).componentOnReady(); +} + +export function checkIfMenuIsOpen(): Promise { + return getHydratedController('ion-menu-controller').then((menuController: HTMLIonMenuControllerElement) => { + if (menuController.isOpen()) { + return menuController.close().then(() => { + return true; + }); + } + return false; }); } + +export function checkIfNotModalOverlayIsOpen(): Promise { + const promises: Promise[] = []; + 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 { + 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 { + return getHydratedController('ion-modal-controller').then((modalController: HTMLIonModalControllerElement) => { + if (modalController.getTop()) { + return modalController.dismiss().then(() => { + return true; + }); + } + return false; + }); +} + +export function popEligibleView(): Promise { + 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 { +} diff --git a/packages/core/src/components/app/readme.md b/packages/core/src/components/app/readme.md index 866b9dfbf6..ac07c07a8d 100644 --- a/packages/core/src/components/app/readme.md +++ b/packages/core/src/components/app/readme.md @@ -5,9 +5,17 @@ +## Events + +#### exitApp + + ## Methods -#### getActiveNavs() +#### appPaused() + + +#### appResume() #### getNavByIdOrName() @@ -18,8 +26,16 @@ Returns an array of top level Navs +#### getTopNavs() + + +#### hardwareBackButtonPressed() + + #### isEnabled() +Returns whether the application is enabled or not + #### isScrolling() diff --git a/packages/core/src/components/cordova-platform/cordova-platform.ios.scss b/packages/core/src/components/cordova-platform/cordova-platform.ios.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/components/cordova-platform/cordova-platform.md.scss b/packages/core/src/components/cordova-platform/cordova-platform.md.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/components/cordova-platform/cordova-platform.tsx b/packages/core/src/components/cordova-platform/cordova-platform.tsx new file mode 100644 index 0000000000..a6e6fdafab --- /dev/null +++ b/packages/core/src/components/cordova-platform/cordova-platform.tsx @@ -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 { + if (isReady) { + return Promise.resolve(); + } + return new Promise((resolve) => { + readyQueue.push(resolve); + }); +} + +export function processReadyQueue() { + for (const resolve of readyQueue) { + resolve(); + } + readyQueue = []; +} diff --git a/packages/core/src/components/cordova-platform/readme.md b/packages/core/src/components/cordova-platform/readme.md new file mode 100644 index 0000000000..0fe91ac3f3 --- /dev/null +++ b/packages/core/src/components/cordova-platform/readme.md @@ -0,0 +1,16 @@ +# ion-cordova-platform + + + + + + +## Methods + +#### ready() + + + +---------------------------------------------- + +*Built by [StencilJS](https://stenciljs.com/)* diff --git a/packages/core/src/components/loading-controller/loading-controller.tsx b/packages/core/src/components/loading-controller/loading-controller.tsx index 4b8af48d22..563519e3e1 100644 --- a/packages/core/src/components/loading-controller/loading-controller.tsx +++ b/packages/core/src/components/loading-controller/loading-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { LoadingEvent, LoadingOptions } from '../../index'; +import { LoadingEvent, LoadingOptions, OverlayController } from '../../index'; let ids = 0; const loadings = new Map(); @@ -7,7 +7,7 @@ const loadings = new Map(); @Component({ tag: 'ion-loading-controller' }) -export class LoadingController { +export class LoadingController implements OverlayController { @Method() create(opts?: LoadingOptions): Promise { @@ -35,6 +35,10 @@ export class LoadingController { return loading.dismiss(data, role); } + @Method() + getTop() { + return loadings.get(getHighestId()); + } @Listen('body:ionLoadingWillPresent') protected loadingWillPresent(ev: LoadingEvent) { diff --git a/packages/core/src/components/loading-controller/readme.md b/packages/core/src/components/loading-controller/readme.md index 739f900661..bfe97aa325 100644 --- a/packages/core/src/components/loading-controller/readme.md +++ b/packages/core/src/components/loading-controller/readme.md @@ -44,6 +44,9 @@ in the `usage` section below. #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/modal-controller/modal-controller.tsx b/packages/core/src/components/modal-controller/modal-controller.tsx index 12a2adceba..28ec71d395 100644 --- a/packages/core/src/components/modal-controller/modal-controller.tsx +++ b/packages/core/src/components/modal-controller/modal-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { ModalEvent, ModalOptions } from '../../index'; +import { ModalEvent, ModalOptions, OverlayController } from '../../index'; let ids = 0; const modals = new Map(); @@ -7,7 +7,7 @@ const modals = new Map(); @Component({ tag: 'ion-modal-controller' }) -export class ModalController { +export class ModalController implements OverlayController { @Method() create(opts?: ModalOptions): Promise { @@ -38,6 +38,10 @@ export class ModalController { return modal.dismiss(data, role); } + @Method() + getTop() { + return modals.get(getHighestId()); + } @Listen('body:ionModalWillPresent') protected modalWillPresent(ev: ModalEvent) { @@ -50,7 +54,6 @@ export class ModalController { modals.delete(ev.target.modalId); } - @Listen('body:keyup.escape') protected escapeKeyUp() { removeLastModal(); diff --git a/packages/core/src/components/modal-controller/readme.md b/packages/core/src/components/modal-controller/readme.md index cdaaa28515..5b3d5685ce 100644 --- a/packages/core/src/components/modal-controller/readme.md +++ b/packages/core/src/components/modal-controller/readme.md @@ -13,6 +13,9 @@ #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/nav/nav-interfaces.ts b/packages/core/src/components/nav/nav-interfaces.ts index 2ee0bf0a87..a2369945e8 100644 --- a/packages/core/src/components/nav/nav-interfaces.ts +++ b/packages/core/src/components/nav/nav-interfaces.ts @@ -4,11 +4,11 @@ import { Animation, AnimationOptions, FrameworkDelegate, + FrameworkMountingData, Nav, NavOptions, PublicViewController, ViewController, - FrameworkMountingData } from '../../index'; export interface PublicNav { @@ -27,20 +27,15 @@ export interface PublicNav { getPrevious(view?: PublicViewController): PublicViewController; canGoBack(): boolean; canSwipeBack(): boolean; - getFirstView(): PublicViewController; + first(): PublicViewController; + last(): PublicViewController; getChildNavs(): PublicNav[]; + getViews(): PublicViewController[]; - element?: HTMLElement; -} - -export interface NavContainer { - id?: number; + navId?: number; name?: string; - parent?: Nav; - getChildNavs?(): NavContainer[]; - getAllChildNavs?(): NavContainer[]; - getType?(): string; - getSecondaryIdentifier?(): string; + element?: HTMLElement; + parent?: PublicNav; } export interface NavOptions { @@ -110,4 +105,5 @@ export interface PublicViewController { component?: any; instance?: any; element?: HTMLElement; + timestamp?: number; } diff --git a/packages/core/src/components/nav/nav-utils.ts b/packages/core/src/components/nav/nav-utils.ts index a65d1ecf3d..e5fa65a43d 100644 --- a/packages/core/src/components/nav/nav-utils.ts +++ b/packages/core/src/components/nav/nav-utils.ts @@ -159,7 +159,11 @@ export function canSwipeBack(_nav: Nav) { } 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[] { diff --git a/packages/core/src/components/nav/nav.tsx b/packages/core/src/components/nav/nav.tsx index 91dc53d6f7..58f37777a0 100644 --- a/packages/core/src/components/nav/nav.tsx +++ b/packages/core/src/components/nav/nav.tsx @@ -6,7 +6,6 @@ import { ComponentDataPair, Config, FrameworkDelegate, - NavContainer, NavOptions, NavResult, NavState, @@ -29,6 +28,7 @@ import { getActiveImpl, getFirstView, getHydratedTransition, + getLastView, getNextNavId, getNextTransitionId, getParentTransitionId, @@ -62,7 +62,7 @@ const urlMap = new Map(); tag: 'ion-nav', styleUrl: 'nav.scss' }) -export class Nav implements PublicNav, NavContainer { +export class Nav implements PublicNav { @Element() element: HTMLElement; @Event() navInit: EventEmitter; @@ -97,7 +97,6 @@ export class Nav implements PublicNav, NavContainer { componentWillLoad() { this.routes = Array.from(this.element.querySelectorAll('ion-route')) .map(child => child.getRoute()); - //this.useRouter = false; // this.config.getBoolean('useRouter', false); } componentDidLoad() { @@ -193,10 +192,15 @@ export class Nav implements PublicNav, NavContainer { } @Method() - getFirstView(): PublicViewController { + first(): PublicViewController { return getFirstView(this); } + @Method() + last(): PublicViewController { + return getLastView(this); + } + @Method() getState(): NavState { assert(this.useRouter, 'routing is disabled'); @@ -340,7 +344,7 @@ export function canGoBackImpl(nav: Nav) { } 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 event.target.setParent(potentialParent); if (!potentialParent.childNavs) { diff --git a/packages/core/src/components/nav/readme.md b/packages/core/src/components/nav/readme.md index 16f9a038e7..190b387e44 100644 --- a/packages/core/src/components/nav/readme.md +++ b/packages/core/src/components/nav/readme.md @@ -78,15 +78,15 @@ boolean #### clearTransitionInfoForUrl() +#### first() + + #### getActive() #### getChildNavs() -#### getFirstView() - - #### getId() @@ -114,6 +114,9 @@ boolean #### isTransitioning() +#### last() + + #### pop() diff --git a/packages/core/src/components/nav/view-controller.ts b/packages/core/src/components/nav/view-controller.ts index 6c8562c444..e587f70702 100644 --- a/packages/core/src/components/nav/view-controller.ts +++ b/packages/core/src/components/nav/view-controller.ts @@ -14,6 +14,7 @@ export class ViewController implements PublicViewController { overlay: boolean; zIndex: number; dismissProxy: any; + timestamp: number; onDidDismiss: (data: any, role: string) => void; @@ -159,6 +160,7 @@ export function didLoadImpl(viewController: ViewController) { } export function initializeNewViewController(viewController: ViewController, data: any) { + viewController.timestamp = Date.now(); viewController.state = STATE_NEW; viewController.data = data || {}; } diff --git a/packages/core/src/components/picker-controller/picker-controller.tsx b/packages/core/src/components/picker-controller/picker-controller.tsx index ec18347422..d7a6581da3 100644 --- a/packages/core/src/components/picker-controller/picker-controller.tsx +++ b/packages/core/src/components/picker-controller/picker-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { PickerEvent, PickerOptions } from '../../index'; +import { PickerEvent, PickerOptions, OverlayController } from '../../index'; let ids = 0; const pickers = new Map(); @@ -7,7 +7,7 @@ const pickers = new Map(); @Component({ tag: 'ion-picker-controller' }) -export class PickerController { +export class PickerController implements OverlayController { @Method() create(opts?: PickerOptions): Promise { @@ -38,6 +38,10 @@ export class PickerController { return picker.dismiss(data, role); } + @Method() + getTop() { + return pickers.get(getHighestId()); + } @Listen('body:ionPickerWillPresent') protected pickerWillPresent(ev: PickerEvent) { diff --git a/packages/core/src/components/picker-controller/readme.md b/packages/core/src/components/picker-controller/readme.md index 97e2d17d96..1ab112daa2 100644 --- a/packages/core/src/components/picker-controller/readme.md +++ b/packages/core/src/components/picker-controller/readme.md @@ -13,6 +13,9 @@ #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/popover-controller/popover-controller.tsx b/packages/core/src/components/popover-controller/popover-controller.tsx index 422c9d5c92..b16e01cd18 100644 --- a/packages/core/src/components/popover-controller/popover-controller.tsx +++ b/packages/core/src/components/popover-controller/popover-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { PopoverEvent, PopoverOptions } from '../../index'; +import { OverlayController, PopoverEvent, PopoverOptions } from '../../index'; let ids = 0; const popovers = new Map(); @@ -7,7 +7,7 @@ const popovers = new Map(); @Component({ tag: 'ion-popover-controller' }) -export class PopoverController { +export class PopoverController implements OverlayController { @Method() create(opts?: PopoverOptions): Promise { @@ -38,6 +38,10 @@ export class PopoverController { return popover.dismiss(data, role); } + @Method() + getTop() { + return popovers.get(getHighestId()); + } @Listen('body:ionPopoverWillPresent') protected popoverWillPresent(ev: PopoverEvent) { diff --git a/packages/core/src/components/popover-controller/readme.md b/packages/core/src/components/popover-controller/readme.md index 32bc84d31f..c7b2695554 100644 --- a/packages/core/src/components/popover-controller/readme.md +++ b/packages/core/src/components/popover-controller/readme.md @@ -32,6 +32,9 @@ view. See the [usage](#usage) section for an example of passing this event. #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/toast-controller/readme.md b/packages/core/src/components/toast-controller/readme.md index eed478d707..77af48c90e 100644 --- a/packages/core/src/components/toast-controller/readme.md +++ b/packages/core/src/components/toast-controller/readme.md @@ -13,6 +13,9 @@ #### dismiss() +#### getTop() + + ---------------------------------------------- diff --git a/packages/core/src/components/toast-controller/toast-controller.tsx b/packages/core/src/components/toast-controller/toast-controller.tsx index ca4cf81d3d..ade37093af 100644 --- a/packages/core/src/components/toast-controller/toast-controller.tsx +++ b/packages/core/src/components/toast-controller/toast-controller.tsx @@ -1,5 +1,5 @@ import { Component, Listen, Method } from '@stencil/core'; -import { ToastEvent, ToastOptions } from '../../index'; +import { OverlayController, ToastEvent, ToastOptions } from '../../index'; let ids = 0; const toasts = new Map(); @@ -7,7 +7,7 @@ const toasts = new Map(); @Component({ tag: 'ion-toast-controller' }) -export class ToastController { +export class ToastController implements OverlayController { @Method() create(opts?: ToastOptions): Promise { @@ -38,6 +38,10 @@ export class ToastController { return toast.dismiss(data, role); } + @Method() + getTop() { + return toasts.get(getHighestId()); + } @Listen('body:ionToastWillPresent') protected toastWillPresent(ev: ToastEvent) { diff --git a/packages/core/src/index.d.ts b/packages/core/src/index.d.ts index 857e24ad51..4999b1e04e 100644 --- a/packages/core/src/index.d.ts +++ b/packages/core/src/index.d.ts @@ -145,10 +145,6 @@ export interface OverlayDismissEventDetail { role?: string; } -export interface OverlayController { - create(): HTMLElement; -} - export interface FrameworkDelegate { attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise; removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise; @@ -160,7 +156,6 @@ export interface FrameworkMountingData { data: any; } - declare global { namespace JSXElements { @@ -186,3 +181,7 @@ declare global { } } } + +export interface OverlayController { + getTop(): HTMLElement; +} diff --git a/packages/core/src/utils/helpers.ts b/packages/core/src/utils/helpers.ts index 3a2135922a..69c1ad7ea7 100644 --- a/packages/core/src/utils/helpers.ts +++ b/packages/core/src/utils/helpers.ts @@ -302,7 +302,7 @@ export function debounce(func: Function, wait = 0) { export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null { for (let i = 0; i < element.children.length; i++) { if (element.children[i].tagName.toLowerCase() === 'ion-nav') { - return element.children[i] as HTMLIonNavElement; + return element.children[i] as any as HTMLIonNavElement; } } return null; diff --git a/packages/core/stencil.config.js b/packages/core/stencil.config.js index 7ee90fb562..a761769be0 100644 --- a/packages/core/stencil.config.js +++ b/packages/core/stencil.config.js @@ -42,6 +42,7 @@ exports.config = { { components: ['ion-toggle'] }, { components: ['ion-toast', 'ion-toast-controller'] }, { components: ['ion-tap-click', 'ion-status-tap'] }, + { components: ['ion-cordova-platform'] }, ], collections: [ 'ionicons'