refactor(navigation): ion-nav and ion-nav-controller are separate components

* nav-nav-nav-nav-nav

* wipit

* holy async batman
This commit is contained in:
Dan Bucholtz
2017-08-24 13:31:42 -05:00
committed by GitHub
parent b4530b55dc
commit 2c6a4dcf34
18 changed files with 911 additions and 212 deletions

View File

@ -5,6 +5,7 @@ export interface AnimationController {
export interface Animation {
new (): Animation;
parent: Animation;
hasChildren: boolean;
addElement(elm: Node|Node[]|NodeList): Animation;
add(childAnimation: Animation): Animation;
duration(milliseconds: number): Animation;
@ -34,11 +35,14 @@ export interface Animation {
progressEnd(shouldComplete: boolean, currentStepValue: number, dur: number): void;
onFinish(callback: (animation?: Animation) => void, opts?: {oneTimeCallback?: boolean, clearExistingCallacks?: boolean}): Animation;
destroy(): void;
isRoot(): boolean;
create(): Animation;
hasCompleted: boolean;
}
export interface AnimationBuilder {
(Animation: Animation, baseElm?: HTMLElement, opts?: any): Animation;
(Animation: Animation, baseElm?: HTMLElement, opts?: any): Animation;
}

View File

@ -1240,4 +1240,7 @@ export class Animator {
return (this._hasTweenEffect && this._hasDur && this._elementTotal ? this._elements[0] : null);
}
create() {
return new Animator();
}
}

View File

@ -0,0 +1,123 @@
import { Component, Element, Method, Prop } from '@stencil/core';
import { AnimationController, Config } from '../..';
import { ComponentDataPair, FrameworkDelegate, Nav, NavController, NavOptions, ViewController } from '../../navigation/nav-interfaces';
import { isReady } from '../../utils/helpers';
import {
insert as insertImpl,
insertPages as insertPagesImpl,
pop as popImpl,
popTo as popToImpl,
popToRoot as popToRootImpl,
push as pushImpl,
remove as removeImpl,
removeView as removeViewImpl,
setPages as setPagesImpl,
setRoot as setRootImpl,
} from '../../navigation/nav-controller-functions';
let defaultDelegate: FrameworkDelegate = null;
@Component({
tag: 'ion-nav-controller',
})
export class NavControllerImpl implements NavController {
@Element() element: HTMLElement;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop() delegate: FrameworkDelegate;
constructor() {
}
@Method()
push(nav: Nav, component: any, data?: any, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return pushImpl(nav, delegate, component, data, opts);
});
}
@Method()
pop(nav: Nav, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return popImpl(nav, delegate, opts);
});
}
@Method()
setRoot(nav: Nav, component: any, data?: any, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return setRootImpl(nav, delegate, component, data, opts);
});
}
@Method()
insert(nav: Nav, insertIndex: number, page: any, params?: any, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return insertImpl(nav, delegate, insertIndex, page, params, opts);
});
}
@Method()
insertPages(nav: Nav, insertIndex: number, insertPages: any[], opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return insertPagesImpl(nav, delegate, insertIndex, insertPages, opts);
});
}
@Method()
popToRoot(nav: Nav, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return popToRootImpl(nav, delegate, opts);
});
}
@Method()
popTo(nav: Nav, indexOrViewCtrl: any, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return popToImpl(nav, delegate, indexOrViewCtrl, opts);
});
}
@Method()
remove(nav: Nav, startIndex: number, removeCount?: number, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return removeImpl(nav, delegate, startIndex, removeCount, opts);
});
}
@Method()
removeView(nav: Nav, viewController: ViewController, opts?: NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return removeViewImpl(nav, delegate, viewController, opts);
});
}
@Method()
setPages(nav: Nav, componentDataPairs: ComponentDataPair[], opts? : NavOptions): Promise<any> {
return getDelegate(this).then((delegate) => {
return setPagesImpl(nav, delegate, componentDataPairs, opts);
});
}
render() {
return <slot></slot>;
}
}
export function getDelegate(navController: NavController): Promise<FrameworkDelegate> {
if (navController.delegate) {
return Promise.resolve(navController.delegate);
}
// no delegate is set, so fall back to inserting the stencil-ion-nav-delegate
const element = document.createElement('stencil-ion-nav-delegate');
document.body.appendChild(element);
return isReady(element).then(() => {
defaultDelegate = element as any as FrameworkDelegate;
return defaultDelegate;
})
}

View File

@ -0,0 +1,30 @@
import { Component, Method } from '@stencil/core';
import { StencilElement } from '../..';
import { FrameworkDelegate, Nav, ViewController } from '../../navigation/nav-interfaces';
@Component({
tag: 'stencil-ion-nav-delegate'
})
export class StencilNavDelegate implements FrameworkDelegate {
@Method()
attachViewToDom(nav: Nav, enteringView: ViewController): Promise<any> {
return new Promise((resolve) => {
const usersElement = document.createElement(enteringView.component);
const ionPage = document.createElement('ion-page');
enteringView.element = ionPage;
ionPage.appendChild(usersElement);
nav.element.appendChild(ionPage);
(ionPage as StencilElement).componentOnReady(() => {
resolve();
});
});
}
@Method()
removeViewFromDom(nav: Nav, leavingView: ViewController): Promise<any> {
nav.element.removeChild(leavingView.element);
return Promise.resolve();
}
}

View File

@ -1,16 +1,18 @@
import { Component, Element, Method, Prop } from '@stencil/core';
import { FrameworkDelegate, NavController, NavOptions, ViewController } from '../../navigation/nav-interfaces';
import { getNextNavId, getViews, pop, push, setRoot } from '../../navigation/nav-controller-functions';
import { AnimationController, Config } from '../..';
import { ComponentDataPair, FrameworkDelegate, Nav, NavController, NavOptions, ViewController } from '../../navigation/nav-interfaces';
import { delegate as defaultStencilDelegate } from '../../navigation/stencil-framework-delegate';
import { getActiveImpl, getFirstView, getPreviousImpl, getViews, init } from '../../navigation/nav-utils';
import { isReady } from '../../utils/helpers';
@Component({
tag: 'ion-nav',
})
export class Nav implements NavController {
export class IonNav implements Nav {
@Element() element: HTMLElement;
id: number;
parent: Nav;
views: ViewController[];
transitioning?: boolean;
destroyed?: boolean;
@ -18,10 +20,13 @@ export class Nav implements NavController {
isViewInitialized?: boolean;
isPortal: boolean;
swipeToGoBackTransition: any; // TODO Transition
childNavs?: NavController[];
childNavs?: Nav[];
navController?: NavController;
@Prop() root: any;
@Prop() delegate: FrameworkDelegate;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
constructor() {
init(this);
@ -35,22 +40,79 @@ export class Nav implements NavController {
return getViews(this);
}
getParent(): NavController {
return null; // TODO
@Method()
push(component: any, data?: any, opts?: NavOptions) {
return pushImpl(this, component, data, opts);
}
@Method()
push(component: any, data?: any, opts: NavOptions = {}) {
return push(this, this.delegate || defaultStencilDelegate, component, data, opts);
pop(opts?: NavOptions) {
return popImpl(this, opts);
}
@Method()
pop(opts: NavOptions = {}) {
return pop(this, this.delegate || defaultStencilDelegate, opts);
setRoot(component: any, data?: any, opts?: NavOptions) {
return setRootImpl(this, component, data, opts);
}
setRoot(component: any, data?: any, opts: NavOptions = {}) {
return setRoot(this, this.delegate || defaultStencilDelegate, component, data, opts);
@Method()
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions) {
return insertImpl(this, insertIndex, page, params, opts);
}
@Method()
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions) {
return insertPagesImpl(this, insertIndex, insertPages, opts);
}
@Method()
popToRoot(opts?: NavOptions) {
return popToRootImpl(this, opts);
}
@Method()
popTo(indexOrViewCtrl: any, opts?: NavOptions) {
return popToImpl(this, indexOrViewCtrl, opts);
}
@Method()
remove(startIndex: number, removeCount?: number, opts?: NavOptions) {
return removeImpl(this, startIndex, removeCount, opts);
}
@Method()
removeView(viewController: ViewController, opts?: NavOptions) {
return removeViewImpl(this, viewController, opts);
}
@Method()
setPages(componentDataPairs: ComponentDataPair[], opts? : NavOptions) {
return setPagesImpl(this, componentDataPairs, opts);
}
@Method()
getActive(): ViewController {
return getActiveImpl(this);
}
@Method()
getPrevious(view?: ViewController): ViewController {
return getPreviousImpl(this, view);
}
@Method()
canGoBack(nav: Nav) {
return nav.views && nav.views.length > 0;
}
@Method()
canSwipeBack() {
return true; // TODO, implement this for real
}
@Method()
getFirstView() {
return getFirstView(this);
}
render() {
@ -58,7 +120,70 @@ export class Nav implements NavController {
}
}
export function init(nav: NavController) {
nav.id = getNextNavId();
nav.views = [];
export function pushImpl(nav: Nav, component: any, data: any, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.push(nav, component, data, opts);
});
}
export function popImpl(nav: Nav, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.pop(nav, opts);
});
}
export function setRootImpl(nav: Nav, component: any, data: any, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.setRoot(nav, component, data, opts);
});
}
export function insertImpl(nav: Nav, insertIndex: number, page: any, params: any, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.insert(nav, insertIndex, page, params, opts);
});
}
export function insertPagesImpl(nav: Nav, insertIndex: number, insertPages: any[], opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.insertPages(nav, insertIndex, insertPages, opts);
});
}
export function popToRootImpl(nav: Nav, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.popToRoot(nav, opts);
});
}
export function popToImpl(nav: Nav, indexOrViewCtrl: any, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.popTo(nav, indexOrViewCtrl, opts);
});
}
export function removeImpl(nav: Nav, startIndex: number, removeCount: number, opts: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.remove(nav, startIndex, removeCount, opts);
});
}
export function removeViewImpl(nav: Nav, viewController: ViewController, opts?: NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.removeView(nav, viewController, opts);
});
}
export function setPagesImpl(nav: Nav, componentDataPairs: ComponentDataPair[], opts? : NavOptions) {
return getNavController(nav).then(() => {
return nav.navController.setPages(nav, componentDataPairs, opts);
});
}
export function getNavController(nav: Nav): Promise<any> {
if (nav.navController) {
return Promise.resolve();
}
nav.navController = document.querySelector('ion-nav-controller') as any as NavController;
return isReady(nav.navController as any as HTMLElement);
}

View File

@ -7,6 +7,8 @@
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-nav-delegate></ion-nav-delegate>
<ion-nav-controller delegateType="stencil"></ion-nav-controller>
<ion-app>
<ion-nav root="page-one"></ion-nav>
</ion-app>

View File

@ -14,4 +14,12 @@ ion-page {
height: 100%;
contain: strict;
// do not show, but still render so we can get dimensions
opacity: 0;
}
ion-page.show-page {
// show the page now that it's ready
opacity: 1;
}