mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 16:16:41 +08:00
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:
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1240,4 +1240,7 @@ export class Animator {
|
||||
return (this._hasTweenEffect && this._hasDur && this._elementTotal ? this._elements[0] : null);
|
||||
}
|
||||
|
||||
create() {
|
||||
return new Animator();
|
||||
}
|
||||
}
|
||||
|
||||
123
packages/core/src/components/nav-controller/nav-controller.tsx
Normal file
123
packages/core/src/components/nav-controller/nav-controller.tsx
Normal 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;
|
||||
})
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user