mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
refactor(nav): implement nav integration with external router (angular to start)
This commit is contained in:
646
packages/core/package-lock.json
generated
646
packages/core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
2
packages/core/src/components.d.ts
vendored
2
packages/core/src/components.d.ts
vendored
@ -14,6 +14,7 @@ import {
|
||||
FrameworkDelegate,
|
||||
PickerColumn,
|
||||
PickerOptions,
|
||||
RouterDelegate,
|
||||
} from './index';
|
||||
import {
|
||||
AlertButton,
|
||||
@ -1837,6 +1838,7 @@ declare global {
|
||||
lazy?: boolean;
|
||||
mode?: string;
|
||||
root?: any;
|
||||
routerDelegate?: RouterDelegate;
|
||||
swipeBackEnabled?: boolean;
|
||||
useUrls?: boolean;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
import { Component, Element, Listen } from '@stencil/core';
|
||||
|
||||
import { NavResult } from '../..';
|
||||
@Component({
|
||||
tag: 'ion-nav-pop',
|
||||
})
|
||||
@ -8,12 +9,12 @@ export class NavPop {
|
||||
@Element() element: HTMLElement;
|
||||
|
||||
@Listen('child:click')
|
||||
pop() {
|
||||
pop(): Promise<NavResult> {
|
||||
const nav = this.element.closest('ion-nav') as HTMLIonNavElement;
|
||||
if (nav) {
|
||||
return nav.pop();
|
||||
}
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -36,6 +36,7 @@ export interface PublicNav {
|
||||
name?: string;
|
||||
element?: HTMLElement;
|
||||
parent?: PublicNav;
|
||||
initialized?: boolean;
|
||||
}
|
||||
|
||||
export interface NavOptions {
|
||||
@ -71,9 +72,9 @@ export interface TransitionInstruction {
|
||||
nav?: Nav;
|
||||
delegate?: FrameworkDelegate;
|
||||
animation?: Animation;
|
||||
escapeHatch?: any;
|
||||
escapeHatch?: EscapeHatch;
|
||||
method?: string;
|
||||
mountingData?: any;
|
||||
mountingData?: FrameworkMountingData;
|
||||
}
|
||||
|
||||
export interface NavResult {
|
||||
@ -86,6 +87,18 @@ export interface ComponentDataPair {
|
||||
data: any;
|
||||
}
|
||||
|
||||
export interface ExternalNavData {
|
||||
url: string;
|
||||
method: string;
|
||||
resolve: Function;
|
||||
reject: Function;
|
||||
}
|
||||
|
||||
export interface EscapeHatch {
|
||||
fromExternalRouter?: boolean;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface Transition extends Animation {
|
||||
enteringView?: ViewController;
|
||||
leavingView?: ViewController;
|
||||
|
||||
@ -29,22 +29,12 @@ export let VIEW_ID_START = 2000;
|
||||
let transitionIds = 0;
|
||||
const activeTransitions = new Map<number, any>();
|
||||
|
||||
let portalZindex = 9999;
|
||||
|
||||
export function isViewController(object: any): boolean {
|
||||
return !!(object && object.didLoad && object.willUnload);
|
||||
}
|
||||
|
||||
export function setZIndex(nav: Nav, enteringView: ViewController, leavingView: ViewController, direction: string) {
|
||||
if (enteringView) {
|
||||
if (nav.isPortal) {
|
||||
if (direction === DIRECTION_FORWARD) {
|
||||
// TODO - fix typing
|
||||
updateZIndex(enteringView, (nav as any).zIndexOffset + portalZindex);
|
||||
}
|
||||
portalZindex++;
|
||||
return;
|
||||
}
|
||||
|
||||
leavingView = leavingView || nav.getPrevious(enteringView) as ViewController;
|
||||
|
||||
|
||||
@ -5,12 +5,16 @@ import {
|
||||
AnimationOptions,
|
||||
ComponentDataPair,
|
||||
Config,
|
||||
EscapeHatch,
|
||||
ExternalNavData,
|
||||
FrameworkDelegate,
|
||||
NavOptions,
|
||||
NavOutlet,
|
||||
NavResult,
|
||||
PublicNav,
|
||||
PublicViewController,
|
||||
RouterDelegate,
|
||||
RouterEntries,
|
||||
Transition,
|
||||
TransitionInstruction,
|
||||
} from '../../index';
|
||||
@ -40,12 +44,14 @@ import {
|
||||
} from './nav-utils';
|
||||
|
||||
import { DomFrameworkDelegate } from '../../utils/dom-framework-delegate';
|
||||
import { DomRouterDelegate } from '../../utils/dom-router-delegate';
|
||||
|
||||
import {
|
||||
assert,
|
||||
focusOutActiveElement,
|
||||
isDef,
|
||||
isNumber,
|
||||
normalizeUrl,
|
||||
} from '../../utils/helpers';
|
||||
|
||||
|
||||
@ -53,8 +59,8 @@ import { buildIOSTransition } from './transitions/transition.ios';
|
||||
import { buildMdTransition } from './transitions/transition.md';
|
||||
import { GestureDetail } from '../gesture/gesture';
|
||||
|
||||
const queueMap = new Map<number, TransitionInstruction[]>();
|
||||
const urlMap = new Map<string, TransitionInstruction>();
|
||||
const transitionQueue = new Map<number, TransitionInstruction[]>();
|
||||
const allTransitionsCompleteHandlerQueue = new Map<number, Function[]>();
|
||||
|
||||
/* it is very important to keep this class in sync with ./nav-interface interface */
|
||||
@Component({
|
||||
@ -67,21 +73,23 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
@Event() navInit: EventEmitter<NavEventDetail>;
|
||||
@Event() ionNavChanged: EventEmitter<NavEventDetail>;
|
||||
|
||||
navId: number;
|
||||
init = false;
|
||||
parent: Nav;
|
||||
useRouter = false;
|
||||
navId = getNextNavId();
|
||||
routes: RouterEntries = [];
|
||||
parent: Nav = null;
|
||||
views: ViewController[] = [];
|
||||
transitioning?: boolean;
|
||||
destroyed?: boolean;
|
||||
transitionId?: number;
|
||||
isViewInitialized?: boolean;
|
||||
isPortal: boolean;
|
||||
transitioning = false;
|
||||
destroyed = false;
|
||||
transitionId = NOT_TRANSITIONING_TRANSITION_ID;
|
||||
initialized = false;
|
||||
sbTrns: any; // TODO Transition
|
||||
childNavs?: Nav[];
|
||||
urlExternalNavMap = new Map<string, ExternalNavData>();
|
||||
|
||||
@Prop() mode: string;
|
||||
@Prop() root: any;
|
||||
@Prop() delegate: FrameworkDelegate;
|
||||
@Prop() routerDelegate: RouterDelegate;
|
||||
@Prop() useUrls = false;
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
|
||||
@ -93,20 +101,15 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
if (this.init) {
|
||||
return;
|
||||
}
|
||||
this.init = true;
|
||||
if (!this.lazy) {
|
||||
componentDidLoadImpl(this);
|
||||
}
|
||||
return componentDidLoadImpl(this);
|
||||
}
|
||||
|
||||
@Watch('root')
|
||||
updateRootComponent(): any {
|
||||
if (this.init) {
|
||||
updateRootComponent(): Promise<NavResult> {
|
||||
if (this.initialized) {
|
||||
return this.setRoot(this.root);
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
@Method()
|
||||
@ -115,52 +118,52 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
push(component: any, data?: any, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
push(component: any, data?: any, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return pushImpl(this, component, data, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
pop(opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
pop(opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return popImpl(this, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
setRoot(component: any, data?: any, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
setRoot(component: any, data?: any, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return setRootImpl(this, component, data, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return insertImpl(this, insertIndex, page, params, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return insertPagesImpl(this, insertIndex, insertPages, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
popToRoot(opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
popToRoot(opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return popToRootImpl(this, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return popToImpl(this, indexOrViewCtrl, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
removeIndex(startIndex: number, removeCount?: number, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
removeIndex(startIndex: number, removeCount?: number, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return removeImpl(this, startIndex, removeCount, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
removeView(viewController: PublicViewController, opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
removeView(viewController: PublicViewController, opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return removeViewImpl(this, viewController, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@Method()
|
||||
setPages(componentDataPairs: ComponentDataPair[], opts?: NavOptions, escapeHatch: any = {}): Promise<any> {
|
||||
setPages(componentDataPairs: ComponentDataPair[], opts?: NavOptions, escapeHatch: EscapeHatch = getDefaultEscapeHatch()): Promise<NavResult> {
|
||||
return setPagesImpl(this, componentDataPairs, opts, escapeHatch);
|
||||
}
|
||||
|
||||
@ -190,10 +193,11 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
setRouteId(id: string, _: any = {}): Promise<void> {
|
||||
setRouteId(id: string, _: any = {}): Promise<any> {
|
||||
assert(this.useRouter, 'routing is disabled');
|
||||
const active = this.getActive();
|
||||
if (active && active.component === id) {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return this.setRoot(id);
|
||||
}
|
||||
@ -224,7 +228,7 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
|
||||
@Method()
|
||||
isTransitioning() {
|
||||
return !!this.transitionId;
|
||||
return this.transitionId >= 0;
|
||||
}
|
||||
|
||||
@Method()
|
||||
@ -238,13 +242,8 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
getTransitionInfoForUrl(url: string): TransitionInstruction {
|
||||
return urlMap.get(url);
|
||||
}
|
||||
|
||||
@Method()
|
||||
clearTransitionInfoForUrl(url: string) {
|
||||
urlMap.delete(url);
|
||||
onAllTransitionsComplete() {
|
||||
return allTransitionsCompleteImpl(this);
|
||||
}
|
||||
|
||||
canSwipeBack(): boolean {
|
||||
@ -330,48 +329,60 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
}
|
||||
|
||||
export function componentDidLoadImpl(nav: Nav) {
|
||||
if (nav.initialized) {
|
||||
return;
|
||||
}
|
||||
nav.initialized = true;
|
||||
nav.navInit.emit();
|
||||
if (nav.root) {
|
||||
nav.setRoot(nav.root);
|
||||
if (!nav.useRouter) {
|
||||
if (nav.root && !nav.lazy) {
|
||||
nav.setRoot(nav.root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function pushImpl(nav: Nav, component: any, data: any, opts: NavOptions, escapeHatch: any) {
|
||||
export async function pushImpl(nav: Nav, component: any, data: any, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return push(nav, nav.delegate, animation, component, data, opts, escapeHatch);
|
||||
return push(nav, nav.delegate, animation, component, data, opts, escapeHatch).then((navResult) => {
|
||||
return navResult;
|
||||
});
|
||||
}
|
||||
|
||||
export async function popImpl(nav: Nav, opts: NavOptions, escapeHatch: any) {
|
||||
export async function popImpl(nav: Nav, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return pop(nav, nav.delegate, animation, opts, escapeHatch);
|
||||
return pop(nav, nav.delegate, animation, opts, escapeHatch).then((navResult) => {
|
||||
return navResult;
|
||||
});
|
||||
}
|
||||
|
||||
export async function setRootImpl(nav: Nav, component: any, data: any, opts: NavOptions, escapeHatch: any) {
|
||||
export async function setRootImpl(nav: Nav, component: any, data: any, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return setRoot(nav, nav.delegate, animation, component, data, opts, escapeHatch);
|
||||
return setRoot(nav, nav.delegate, animation, component, data, opts, escapeHatch).then((navResult) => {
|
||||
return navResult;
|
||||
});
|
||||
}
|
||||
|
||||
export async function insertImpl(nav: Nav, insertIndex: number, page: any, params: any, opts: NavOptions, escapeHatch: any) {
|
||||
export async function insertImpl(nav: Nav, insertIndex: number, page: any, params: any, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return insert(nav, nav.delegate, animation, insertIndex, page, params, opts, escapeHatch);
|
||||
}
|
||||
|
||||
export async function insertPagesImpl(nav: Nav, insertIndex: number, pagesToInsert: any[], opts: NavOptions, escapeHatch: any) {
|
||||
export async function insertPagesImpl(nav: Nav, insertIndex: number, pagesToInsert: any[], opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return insertPages(nav, nav.delegate, animation, insertIndex, pagesToInsert, opts, escapeHatch);
|
||||
}
|
||||
|
||||
export async function popToRootImpl(nav: Nav, opts: NavOptions, escapeHatch: any) {
|
||||
export async function popToRootImpl(nav: Nav, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return popToRoot(nav, nav.delegate, animation, opts, escapeHatch);
|
||||
}
|
||||
|
||||
export async function popToImpl(nav: Nav, indexOrViewCtrl: any, opts: NavOptions, escapeHatch: any) {
|
||||
export async function popToImpl(nav: Nav, indexOrViewCtrl: any, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return popTo(nav, nav.delegate, animation, indexOrViewCtrl, opts, escapeHatch);
|
||||
}
|
||||
|
||||
export async function removeImpl(nav: Nav, startIndex: number, removeCount: number, opts: NavOptions, escapeHatch: any) {
|
||||
export async function removeImpl(nav: Nav, startIndex: number, removeCount: number, opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return remove(nav, nav.delegate, animation, startIndex, removeCount, opts, escapeHatch);
|
||||
}
|
||||
@ -381,9 +392,9 @@ export async function removeViewImpl(nav: Nav, viewController: PublicViewControl
|
||||
return removeView(nav, nav.delegate, animation, viewController as ViewController, opts, escapeHatch);
|
||||
}
|
||||
|
||||
export async function setPagesImpl(nav: Nav, componentDataPairs: ComponentDataPair[], opts: NavOptions, escapeHatch: any) {
|
||||
export async function setPagesImpl(nav: Nav, componentDataPairs: ComponentDataPair[], opts: NavOptions, escapeHatch: EscapeHatch) {
|
||||
const animation = await hydrateAnimationController(nav.animationCtrl);
|
||||
return setPages(nav, nav.delegate, animation, componentDataPairs, opts, escapeHatch);
|
||||
return setPages(nav, nav.delegate, animation, componentDataPairs, opts, escapeHatch, null);
|
||||
}
|
||||
|
||||
export function canGoBackImpl(nav: Nav) {
|
||||
@ -407,11 +418,10 @@ export function hydrateAnimationController(animationController: AnimationControl
|
||||
return animationController.create();
|
||||
}
|
||||
|
||||
|
||||
// public api
|
||||
|
||||
export function push(nav: Nav, delegate: FrameworkDelegate, animation: Animation, component: any, data: any, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function push(nav: Nav, delegate: FrameworkDelegate, animation: Animation, component: any, data: any, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: component,
|
||||
insertStart: -1,
|
||||
insertViews: [{component, data}],
|
||||
@ -421,12 +431,12 @@ export function push(nav: Nav, delegate: FrameworkDelegate, animation: Animation
|
||||
id: nav.navId,
|
||||
animation,
|
||||
escapeHatch,
|
||||
method: 'push'
|
||||
method: PUSH
|
||||
});
|
||||
}
|
||||
|
||||
export function insert(nav: Nav, delegate: FrameworkDelegate, animation: Animation, insertIndex: number, component: any, data: any, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function insert(nav: Nav, delegate: FrameworkDelegate, animation: Animation, insertIndex: number, component: any, data: any, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: component,
|
||||
insertStart: insertIndex,
|
||||
insertViews: [{ component, data }],
|
||||
@ -440,8 +450,8 @@ export function insert(nav: Nav, delegate: FrameworkDelegate, animation: Animati
|
||||
});
|
||||
}
|
||||
|
||||
export function insertPages(nav: Nav, delegate: FrameworkDelegate, animation: Animation, insertIndex: number, insertPages: any[], opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function insertPages(nav: Nav, delegate: FrameworkDelegate, animation: Animation, insertIndex: number, insertPages: any[], opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: null,
|
||||
insertStart: insertIndex,
|
||||
insertViews: insertPages,
|
||||
@ -455,8 +465,8 @@ export function insertPages(nav: Nav, delegate: FrameworkDelegate, animation: An
|
||||
});
|
||||
}
|
||||
|
||||
export function pop(nav: Nav, delegate: FrameworkDelegate, animation: Animation, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function pop(nav: Nav, delegate: FrameworkDelegate, animation: Animation, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: null,
|
||||
removeStart: -1,
|
||||
removeCount: 1,
|
||||
@ -466,12 +476,12 @@ export function pop(nav: Nav, delegate: FrameworkDelegate, animation: Animation,
|
||||
id: nav.navId,
|
||||
animation,
|
||||
escapeHatch,
|
||||
method: 'pop'
|
||||
method: POP
|
||||
});
|
||||
}
|
||||
|
||||
export function popToRoot(nav: Nav, delegate: FrameworkDelegate, animation: Animation, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function popToRoot(nav: Nav, delegate: FrameworkDelegate, animation: Animation, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: null,
|
||||
removeStart: 1,
|
||||
removeCount: -1,
|
||||
@ -485,7 +495,7 @@ export function popToRoot(nav: Nav, delegate: FrameworkDelegate, animation: Anim
|
||||
});
|
||||
}
|
||||
|
||||
export function popTo(nav: Nav, delegate: FrameworkDelegate, animation: Animation, indexOrViewCtrl: any, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
export function popTo(nav: Nav, delegate: FrameworkDelegate, animation: Animation, indexOrViewCtrl: any, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
const config: TransitionInstruction = {
|
||||
component: null,
|
||||
removeStart: -1,
|
||||
@ -504,11 +514,11 @@ export function popTo(nav: Nav, delegate: FrameworkDelegate, animation: Animatio
|
||||
} else if (isNumber(indexOrViewCtrl)) {
|
||||
config.removeStart = indexOrViewCtrl + 1;
|
||||
}
|
||||
return queueTransaction(config);
|
||||
return preprocessTransaction(config);
|
||||
}
|
||||
|
||||
export function remove(nav: Nav, delegate: FrameworkDelegate, animation: Animation, startIndex: number, removeCount = 1, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function remove(nav: Nav, delegate: FrameworkDelegate, animation: Animation, startIndex: number, removeCount = 1, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: null,
|
||||
removeStart: startIndex,
|
||||
removeCount: removeCount,
|
||||
@ -522,8 +532,8 @@ export function remove(nav: Nav, delegate: FrameworkDelegate, animation: Animati
|
||||
});
|
||||
}
|
||||
|
||||
export function removeView(nav: Nav, delegate: FrameworkDelegate, animation: Animation, viewController: ViewController, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return queueTransaction({
|
||||
export function removeView(nav: Nav, delegate: FrameworkDelegate, animation: Animation, viewController: ViewController, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return preprocessTransaction({
|
||||
component: null,
|
||||
removeView: viewController,
|
||||
removeStart: 0,
|
||||
@ -538,18 +548,18 @@ export function removeView(nav: Nav, delegate: FrameworkDelegate, animation: Ani
|
||||
});
|
||||
}
|
||||
|
||||
export function setRoot(nav: Nav, delegate: FrameworkDelegate, animation: Animation, component: any, data: any, opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
return setPages(nav, delegate, animation, [{ component, data }], opts, escapeHatch);
|
||||
export function setRoot(nav: Nav, delegate: FrameworkDelegate, animation: Animation, component: any, data: any, opts: NavOptions, escapeHatch: EscapeHatch): Promise<NavResult> {
|
||||
return setPages(nav, delegate, animation, [{ component, data }], opts, escapeHatch, SET_ROOT);
|
||||
}
|
||||
|
||||
export function setPages(nav: Nav, delegate: FrameworkDelegate, animation: Animation, componentDataPairs: ComponentDataPair[], opts: NavOptions, escapeHatch: any): Promise<any> {
|
||||
export function setPages(nav: Nav, delegate: FrameworkDelegate, animation: Animation, componentDataPairs: ComponentDataPair[], opts: NavOptions, escapeHatch: EscapeHatch, methodName: string): Promise<NavResult> {
|
||||
if (!isDef(opts)) {
|
||||
opts = {};
|
||||
}
|
||||
if (opts.animate !== true) {
|
||||
opts.animate = false;
|
||||
}
|
||||
return queueTransaction({
|
||||
return preprocessTransaction({
|
||||
component: componentDataPairs.length === 1 ? componentDataPairs[0].component : null,
|
||||
insertStart: 0,
|
||||
insertViews: componentDataPairs,
|
||||
@ -561,22 +571,49 @@ export function setPages(nav: Nav, delegate: FrameworkDelegate, animation: Anima
|
||||
id: nav.navId,
|
||||
animation,
|
||||
escapeHatch,
|
||||
method: 'setPages'
|
||||
method: methodName ? methodName : 'setPages'
|
||||
});
|
||||
}
|
||||
|
||||
// private api, exported for testing
|
||||
export function preprocessTransaction(ti: TransitionInstruction): Promise<NavResult> {
|
||||
if (isUrl(ti.component)) {
|
||||
if (ti.method === PUSH || ti.method === POP || ti.method === SET_ROOT) {
|
||||
return navigateToUrl(ti.nav, normalizeUrl(ti.component) as string, ti.method);
|
||||
} else {
|
||||
return Promise.reject(new Error('only push, pop, and setRoot methods support urls'));
|
||||
}
|
||||
}
|
||||
|
||||
export function queueOrNavigate(ti: TransitionInstruction): void | Promise<NavResult> {
|
||||
if (isComponentUrl(ti.component)) {
|
||||
urlMap.set(ti.component as string, ti);
|
||||
window.location.href = ti.component;
|
||||
const response = checkIfPopRedirectRequired(ti);
|
||||
if (response.required) {
|
||||
return navigateToUrl(ti.nav, response.url, POP);
|
||||
}
|
||||
|
||||
return queueTransaction(ti);
|
||||
}
|
||||
|
||||
export function isComponentUrl(component: any) {
|
||||
return component && typeof component === 'string' && component.charAt(0) === '/';
|
||||
export function isUrl(component: any): boolean {
|
||||
return typeof component === 'string' && component.charAt(0) === '/';
|
||||
}
|
||||
|
||||
export function navigateToUrl(nav: Nav, url: string, method: string): Promise<NavResult> {
|
||||
let routingPromise: Promise<any> = null;
|
||||
return new Promise((resolve, reject) => {
|
||||
nav.urlExternalNavMap.set(url, {
|
||||
url,
|
||||
method,
|
||||
resolve,
|
||||
reject
|
||||
});
|
||||
if (!nav.routerDelegate) {
|
||||
nav.routerDelegate = new DomRouterDelegate();
|
||||
}
|
||||
routingPromise = nav.routerDelegate.pushUrlState(url);
|
||||
}).then((navResult: NavResult) => {
|
||||
return routingPromise.then(() => {
|
||||
return navResult;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function queueTransaction(ti: TransitionInstruction): Promise<NavResult> {
|
||||
@ -617,16 +654,68 @@ export function nextTransaction(nav: Nav): Promise<any> {
|
||||
|
||||
const topTransaction = getTopTransaction(nav.navId);
|
||||
if (!topTransaction) {
|
||||
// cool, there are no transitions going for this nav
|
||||
processAllTransitionCompleteQueue(nav.navId);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return doNav(nav, topTransaction);
|
||||
}
|
||||
|
||||
export function checkIfPopRedirectRequired(ti: TransitionInstruction): IsRedirectRequired {
|
||||
if (ti.method === POP) {
|
||||
|
||||
if (ti.escapeHatch.fromExternalRouter) {
|
||||
// if the pop method is called from a router, that means the redirect already happened
|
||||
// so just do a normal pop because the url is in a good place. Basically, the router is telling us to
|
||||
// pop
|
||||
return {
|
||||
required: false
|
||||
};
|
||||
}
|
||||
|
||||
// check if we need to redirect to a url for the pop operation
|
||||
const popToIndex = ti.nav.views.length - 2;
|
||||
if (popToIndex >= 0) {
|
||||
const viewController = ti.nav.views[popToIndex];
|
||||
return {
|
||||
required: viewController.fromExternalRouter,
|
||||
url: viewController.url
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
required: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function processAllTransitionCompleteQueue(navId: number) {
|
||||
const queue = allTransitionsCompleteHandlerQueue.get(navId) || [];
|
||||
for (const callback of queue) {
|
||||
callback();
|
||||
}
|
||||
allTransitionsCompleteHandlerQueue.set(navId, []);
|
||||
}
|
||||
|
||||
export function allTransitionsCompleteImpl(nav: Nav) {
|
||||
return new Promise((resolve) => {
|
||||
const queue = transitionQueue.get(nav.navId) || [];
|
||||
if (queue.length) {
|
||||
// there are pending transitions, so queue it up and we'll be notified when it's done
|
||||
const handlers = allTransitionsCompleteHandlerQueue.get(nav.navId) || [];
|
||||
handlers.push(resolve);
|
||||
return allTransitionsCompleteHandlerQueue.set(nav.navId, handlers);
|
||||
}
|
||||
// there are no pending transitions, so just resolve right away
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function doNav(nav: Nav, ti: TransitionInstruction) {
|
||||
let enteringView: ViewController;
|
||||
let leavingView: ViewController;
|
||||
return initializeViewBeforeTransition(nav, ti).then(([_enteringView, _leavingView]) => {
|
||||
return initializeViewBeforeTransition(ti).then(([_enteringView, _leavingView]) => {
|
||||
enteringView = _enteringView;
|
||||
leavingView = _leavingView;
|
||||
return attachViewToDom(nav, enteringView, ti);
|
||||
@ -647,8 +736,8 @@ export function successfullyTransitioned(ti: TransitionInstruction) {
|
||||
return fireError(new Error('Queue is null, the nav must have been destroyed'), ti);
|
||||
}
|
||||
|
||||
ti.nav.isViewInitialized = true;
|
||||
ti.nav.transitionId = null;
|
||||
ti.nav.initialized = true;
|
||||
ti.nav.transitionId = NOT_TRANSITIONING_TRANSITION_ID;
|
||||
ti.nav.transitioning = false;
|
||||
|
||||
// TODO - check if it's a swipe back
|
||||
@ -739,7 +828,7 @@ export function loadViewAndTransition(nav: Nav, enteringView: ViewController, le
|
||||
|
||||
export function executeAsyncTransition(nav: Nav, transition: Transition, enteringView: ViewController, leavingView: ViewController, delegate: FrameworkDelegate, opts: NavOptions, configShouldAnimate: boolean): Promise<void> {
|
||||
assert(nav.transitioning, 'must be transitioning');
|
||||
nav.transitionId = null;
|
||||
nav.transitionId = NOT_TRANSITIONING_TRANSITION_ID;
|
||||
setZIndex(nav, enteringView, leavingView, opts.direction);
|
||||
|
||||
// always ensure the entering view is viewable
|
||||
@ -751,8 +840,8 @@ export function executeAsyncTransition(nav: Nav, transition: Transition, enterin
|
||||
// ******** DOM WRITE ****************
|
||||
leavingView && toggleHidden(leavingView.element, false);
|
||||
|
||||
const isFirstPage = !nav.isViewInitialized && nav.views.length === 1;
|
||||
const shouldNotAnimate = isFirstPage && !nav.isPortal;
|
||||
const isFirstPage = !nav.initialized && nav.views.length === 1;
|
||||
const shouldNotAnimate = isFirstPage;
|
||||
if (configShouldAnimate || shouldNotAnimate) {
|
||||
opts.animate = false;
|
||||
}
|
||||
@ -847,7 +936,7 @@ export function cleanUpView(nav: Nav, delegate: FrameworkDelegate, activeViewCon
|
||||
// this view comes after the active view
|
||||
inactiveViewController.willUnload();
|
||||
promises.push(destroyView(nav, delegate, inactiveViewController));
|
||||
} else if ( i < activeIndex && !nav.isPortal) {
|
||||
} else if ( i < activeIndex) {
|
||||
// this view comes before the active view
|
||||
// and it is not a portal then ensure it is hidden
|
||||
toggleHidden(inactiveViewController.element, true);
|
||||
@ -875,11 +964,11 @@ export function attachViewToDom(nav: Nav, enteringView: ViewController, ti: Tran
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function initializeViewBeforeTransition(nav: Nav, ti: TransitionInstruction): Promise<ViewController[]> {
|
||||
export function initializeViewBeforeTransition(ti: TransitionInstruction): Promise<ViewController[]> {
|
||||
let leavingView: ViewController = null;
|
||||
let enteringView: ViewController = null;
|
||||
return startTransaction(ti).then(() => {
|
||||
const viewControllers = convertComponentToViewController(nav, ti);
|
||||
const viewControllers = convertComponentToViewController(ti);
|
||||
ti.viewControllers = viewControllers;
|
||||
leavingView = ti.nav.getActive() as ViewController;
|
||||
enteringView = getEnteringView(ti, ti.nav, leavingView);
|
||||
@ -925,7 +1014,7 @@ export function updateNavStacks(enteringView: ViewController, leavingView: ViewC
|
||||
|
||||
const finalBalance = ti.nav.views.length + (ti.insertViews ? ti.insertViews.length : 0) - (ti.removeCount ? ti.removeCount : 0);
|
||||
assert(finalBalance >= 0, 'final balance can not be negative');
|
||||
if (finalBalance === 0 && !ti.nav.isPortal) {
|
||||
if (finalBalance === 0) {
|
||||
console.warn(`You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`);
|
||||
throw new Error('Navigation stack needs at least one root page');
|
||||
}
|
||||
@ -1001,7 +1090,7 @@ export function insertViewIntoNav(nav: Nav, view: ViewController, index: number)
|
||||
assert(view.nav === nav, 'view is not part of the nav');
|
||||
nav.views.splice(index, 0, nav.views.splice(existingIndex, 1)[0]);
|
||||
} else {
|
||||
assert(!view.nav || (nav.isPortal && view.nav === nav), 'nav is used');
|
||||
assert(!view.nav, 'nav is used');
|
||||
// this is a new view to add to the stack
|
||||
// create the new entering view
|
||||
view.nav = nav;
|
||||
@ -1117,17 +1206,18 @@ export function getEnteringView(ti: TransitionInstruction, nav: Nav, leavingView
|
||||
return null;
|
||||
}
|
||||
|
||||
export function convertViewsToViewControllers(pairs: ComponentDataPair[]): ViewController[] {
|
||||
export function convertViewsToViewControllers(pairs: ComponentDataPair[], escapeHatch: EscapeHatch): ViewController[] {
|
||||
return pairs.filter(pair => !!pair)
|
||||
.map(pair => {
|
||||
return new ViewController(pair.component, pair.data);
|
||||
const applyEscapeHatch = pair === pairs[pairs.length - 1];
|
||||
return new ViewController(pair.component, pair.data, applyEscapeHatch ? escapeHatch.fromExternalRouter : false, applyEscapeHatch ? escapeHatch.url : null);
|
||||
});
|
||||
}
|
||||
|
||||
export function convertComponentToViewController(_: Nav, ti: TransitionInstruction): ViewController[] {
|
||||
export function convertComponentToViewController(ti: TransitionInstruction): ViewController[] {
|
||||
if (ti.insertViews) {
|
||||
assert(ti.insertViews.length > 0, 'length can not be zero');
|
||||
const viewControllers = convertViewsToViewControllers(ti.insertViews);
|
||||
const viewControllers = convertViewsToViewControllers(ti.insertViews, ti.escapeHatch);
|
||||
assert(ti.insertViews.length === viewControllers.length, 'lengths does not match');
|
||||
if (viewControllers.length === 0) {
|
||||
throw new Error('No views to insert');
|
||||
@ -1147,17 +1237,17 @@ export function convertComponentToViewController(_: Nav, ti: TransitionInstructi
|
||||
}
|
||||
|
||||
export function addToQueue(ti: TransitionInstruction) {
|
||||
const list = queueMap.get(ti.id) || [];
|
||||
const list = transitionQueue.get(ti.id) || [];
|
||||
list.push(ti);
|
||||
queueMap.set(ti.id, list);
|
||||
transitionQueue.set(ti.id, list);
|
||||
}
|
||||
|
||||
export function getQueue(id: number) {
|
||||
return queueMap.get(id) || [];
|
||||
return transitionQueue.get(id) || [];
|
||||
}
|
||||
|
||||
export function resetQueue(id: number) {
|
||||
queueMap.set(id, []);
|
||||
transitionQueue.set(id, []);
|
||||
}
|
||||
|
||||
export function getTopTransaction(id: number) {
|
||||
@ -1167,7 +1257,7 @@ export function getTopTransaction(id: number) {
|
||||
}
|
||||
const tmp = queue.concat();
|
||||
const toReturn = tmp.shift();
|
||||
queueMap.set(id, tmp);
|
||||
transitionQueue.set(id, tmp);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@ -1177,6 +1267,7 @@ export function getDefaultTransition(config: Config) {
|
||||
|
||||
let viewIds = VIEW_ID_START;
|
||||
const DISABLE_APP_MINIMUM_DURATION = 64;
|
||||
const NOT_TRANSITIONING_TRANSITION_ID = -1;
|
||||
|
||||
export interface NavEvent extends CustomEvent {
|
||||
target: HTMLIonNavElement;
|
||||
@ -1186,3 +1277,18 @@ export interface NavEvent extends CustomEvent {
|
||||
export interface NavEventDetail {
|
||||
isPop?: boolean;
|
||||
}
|
||||
|
||||
export function getDefaultEscapeHatch(): EscapeHatch {
|
||||
return {
|
||||
fromExternalRouter: false,
|
||||
};
|
||||
}
|
||||
|
||||
export interface IsRedirectRequired {
|
||||
required: boolean;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export const POP = 'pop';
|
||||
export const PUSH = 'push';
|
||||
export const SET_ROOT = 'setRoot';
|
||||
|
||||
@ -27,6 +27,11 @@ string
|
||||
any
|
||||
|
||||
|
||||
#### routerDelegate
|
||||
|
||||
|
||||
|
||||
|
||||
#### swipeBackEnabled
|
||||
|
||||
boolean
|
||||
@ -59,6 +64,11 @@ string
|
||||
any
|
||||
|
||||
|
||||
#### router-delegate
|
||||
|
||||
|
||||
|
||||
|
||||
#### swipe-back-enabled
|
||||
|
||||
boolean
|
||||
@ -82,9 +92,6 @@ boolean
|
||||
#### canGoBack()
|
||||
|
||||
|
||||
#### clearTransitionInfoForUrl()
|
||||
|
||||
|
||||
#### first()
|
||||
|
||||
|
||||
@ -106,9 +113,6 @@ boolean
|
||||
#### getRouteId()
|
||||
|
||||
|
||||
#### getTransitionInfoForUrl()
|
||||
|
||||
|
||||
#### getViews()
|
||||
|
||||
|
||||
@ -124,6 +128,9 @@ boolean
|
||||
#### last()
|
||||
|
||||
|
||||
#### onAllTransitionsComplete()
|
||||
|
||||
|
||||
#### pop()
|
||||
|
||||
|
||||
|
||||
@ -14,10 +14,10 @@ const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||
export function buildIOSTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||
const componentReadyPromise: Promise<any>[] = [];
|
||||
// Let makes sure everything is hydrated and ready to animate
|
||||
if (enteringView) {
|
||||
if (enteringView && (enteringView.element as any).componentOnReady) {
|
||||
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
||||
}
|
||||
if (leavingView) {
|
||||
if (leavingView && (leavingView.element as any).componentOnReady) {
|
||||
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
||||
}
|
||||
|
||||
|
||||
@ -10,10 +10,10 @@ const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||
export function buildMdTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||
|
||||
const componentReadyPromise: Promise<any>[] = [];
|
||||
if (enteringView) {
|
||||
if (enteringView && (enteringView.element as any).componentOnReady) {
|
||||
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
||||
}
|
||||
if (leavingView) {
|
||||
if (leavingView && (leavingView.element as any).componentOnReady) {
|
||||
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
||||
}
|
||||
|
||||
@ -32,8 +32,7 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
||||
// animate the component itself
|
||||
if (backDirection) {
|
||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : 200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : 280).easing('cubic-bezier(0.36,0.66,0.04,1)');
|
||||
|
||||
rootTransition
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { FrameworkDelegate, Nav, PublicViewController } from '../../index';
|
||||
import { STATE_ATTACHED, STATE_DESTROYED, STATE_INITIALIZED, STATE_NEW } from './nav-utils';
|
||||
|
||||
import { assert } from '../../utils/helpers';
|
||||
import {
|
||||
assert,
|
||||
normalizeUrl
|
||||
} from '../../utils/helpers';
|
||||
|
||||
export class ViewController implements PublicViewController {
|
||||
|
||||
@ -15,13 +18,15 @@ export class ViewController implements PublicViewController {
|
||||
zIndex: number;
|
||||
dismissProxy: any;
|
||||
timestamp: number;
|
||||
fromExternalRouter: boolean;
|
||||
url: string;
|
||||
|
||||
|
||||
onDidDismiss: (data: any, role: string) => void;
|
||||
onWillDismiss: (data: any, role: string) => void;
|
||||
|
||||
constructor(public component: any, data?: any) {
|
||||
initializeNewViewController(this, data);
|
||||
constructor(public component: any, data: any, fromExternalRouter: boolean, url: string) {
|
||||
initializeNewViewController(this, data, fromExternalRouter, url);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,8 +164,10 @@ export function didLoadImpl(viewController: ViewController) {
|
||||
callLifeCycleFunction(viewController.instance, 'ionViewDidLoad');
|
||||
}
|
||||
|
||||
export function initializeNewViewController(viewController: ViewController, data: any) {
|
||||
export function initializeNewViewController(viewController: ViewController, data: any, fromExternalRouter: boolean, url: string) {
|
||||
viewController.timestamp = Date.now();
|
||||
viewController.state = STATE_NEW;
|
||||
viewController.data = data || {};
|
||||
viewController.fromExternalRouter = fromExternalRouter;
|
||||
viewController.url = url && normalizeUrl(url);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export interface NavOutlet {
|
||||
setRouteId(id: any, data?: any): Promise<void>;
|
||||
setRouteId(id: any, data?: any): Promise<any>;
|
||||
getRouteId(): string;
|
||||
getContentElement(): HTMLElement | null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core';
|
||||
|
||||
import { getNavAsChildIfExists } from '../../utils/helpers';
|
||||
import { FrameworkDelegate } from '../..';
|
||||
|
||||
@ -9,7 +8,7 @@ import { FrameworkDelegate } from '../..';
|
||||
})
|
||||
export class Tab {
|
||||
|
||||
private loaded = false;
|
||||
// private loaded = false;
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@State() init = false;
|
||||
@ -66,6 +65,7 @@ export class Tab {
|
||||
|
||||
@Prop({ mutable: true }) selected = false;
|
||||
|
||||
|
||||
@Watch('selected')
|
||||
selectedChanged(selected: boolean) {
|
||||
if (selected) {
|
||||
@ -81,7 +81,30 @@ export class Tab {
|
||||
@Method()
|
||||
setActive(active: boolean): Promise<any> {
|
||||
this.active = active;
|
||||
if (this.loaded || !active) {
|
||||
if (active) {
|
||||
const nav = getNavAsChildIfExists(this.el);
|
||||
if (nav) {
|
||||
// the tab's nav has been initialized externally
|
||||
if ((window as any).externalNavPromise) {
|
||||
return (window as any).externalNavPromise.then(() => {
|
||||
(window as any).externalNavPromise = null;
|
||||
});
|
||||
} else {
|
||||
// the tab's nav has not been initialized externally, so
|
||||
// check if we need to initiailize it
|
||||
return (nav as any).componentOnReady().then(() => {
|
||||
return nav.onAllTransitionsComplete();
|
||||
}).then(() => {
|
||||
if (!nav.getViews().length && !nav.isTransitioning() && !nav.initialized) {
|
||||
return nav.setRoot(nav.root);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (this.loaded || !active) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@ -97,6 +120,8 @@ export class Tab {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
return promise.then(() => this.fireChildren());
|
||||
*/
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@Method()
|
||||
@ -110,7 +135,7 @@ export class Tab {
|
||||
return null;
|
||||
}
|
||||
|
||||
private fireChildren() {
|
||||
/*private fireChildren() {
|
||||
const nav = getNavAsChildIfExists(this.el);
|
||||
if (nav && nav.getViews().length === 0 && nav.root) {
|
||||
// we need to initialize
|
||||
@ -119,6 +144,7 @@ export class Tab {
|
||||
// it's already been initialized if it exists
|
||||
return Promise.resolve();
|
||||
}
|
||||
*/
|
||||
|
||||
hostData() {
|
||||
const visible = this.active && this.selected;
|
||||
@ -127,7 +153,7 @@ export class Tab {
|
||||
'aria-labelledby': this.btnId,
|
||||
'role': 'tabpanel',
|
||||
class: {
|
||||
'show-tab': this.active
|
||||
'show-tab': visible
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -137,14 +163,15 @@ export class Tab {
|
||||
}
|
||||
}
|
||||
|
||||
function attachViewToDom(container: HTMLElement, cmp: string): Promise<any> {
|
||||
/*function attachViewToDom(container: HTMLElement, cmp: string): Promise<any> {
|
||||
const el = document.createElement(cmp) as HTMLStencilElement;
|
||||
container.appendChild(el);
|
||||
if (el.componentOnReady ) {
|
||||
if (el.componentOnReady) {
|
||||
return el.componentOnReady();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
*/
|
||||
|
||||
export interface TabEvent extends CustomEvent {
|
||||
detail: TabEventDetail;
|
||||
|
||||
@ -9,6 +9,7 @@ import { Config, NavEventDetail, NavOutlet } from '../../index';
|
||||
export class Tabs implements NavOutlet {
|
||||
private ids = -1;
|
||||
private tabsId: number = (++tabIds);
|
||||
initialized = false;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@ -70,8 +71,14 @@ export class Tabs implements NavOutlet {
|
||||
this.loadConfig('tabsLayout', 'icon-top');
|
||||
this.loadConfig('tabsHighlight', true);
|
||||
|
||||
this.initTabs();
|
||||
this.initSelect();
|
||||
return this.initTabs().then(() => {
|
||||
if (! (window as any).externalNav) {
|
||||
return this.initSelect();
|
||||
}
|
||||
return null;
|
||||
}).then(() => {
|
||||
this.initialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUnload() {
|
||||
@ -171,16 +178,19 @@ export class Tabs implements NavOutlet {
|
||||
|
||||
private initTabs() {
|
||||
const tabs = this.tabs = Array.from(this.el.querySelectorAll('ion-tab'));
|
||||
const tabPromises: Promise<any>[] = [];
|
||||
for (const tab of tabs) {
|
||||
const id = `t-${this.tabsId}-${++this.ids}`;
|
||||
tab.btnId = 'tab-' + id;
|
||||
tab.id = 'tabpanel-' + id;
|
||||
tabPromises.push((tab as any).componentOnReady());
|
||||
}
|
||||
return Promise.all(tabPromises);
|
||||
}
|
||||
|
||||
private initSelect() {
|
||||
if (document.querySelector('ion-router')) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
// find pre-selected tabs
|
||||
const selectedTab = this.tabs.find(t => t.selected) ||
|
||||
@ -192,10 +202,10 @@ export class Tabs implements NavOutlet {
|
||||
tab.selected = false;
|
||||
}
|
||||
}
|
||||
if (selectedTab) {
|
||||
selectedTab.setActive(true);
|
||||
}
|
||||
this.selectedTab = selectedTab;
|
||||
const promise = selectedTab ? selectedTab.setActive(true) : Promise.resolve();
|
||||
return promise.then(() => {
|
||||
this.selectedTab = selectedTab;
|
||||
});
|
||||
}
|
||||
|
||||
private loadConfig(attrKey: string, fallback: any) {
|
||||
|
||||
5
packages/core/src/index.d.ts
vendored
5
packages/core/src/index.d.ts
vendored
@ -146,6 +146,11 @@ export interface FrameworkDelegate {
|
||||
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>;
|
||||
}
|
||||
|
||||
export interface RouterDelegate {
|
||||
pushUrlState(urlSegment: string, stateObject?: any, title?: string): Promise<any>;
|
||||
popUrlState(): Promise<any>;
|
||||
}
|
||||
|
||||
export interface FrameworkMountingData {
|
||||
element: HTMLElement;
|
||||
component: any;
|
||||
|
||||
@ -28,12 +28,4 @@ export class DomFrameworkDelegate implements FrameworkDelegate {
|
||||
parentElement.removeChild(childElement);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
shouldDeferToRouter(_elementOrComponentToMount: any): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
routeToUrl(_elementOrComponentToMount: any): Promise<any> {
|
||||
return Promise.resolve('todo');
|
||||
}
|
||||
}
|
||||
|
||||
16
packages/core/src/utils/dom-router-delegate.ts
Normal file
16
packages/core/src/utils/dom-router-delegate.ts
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
import { RouterDelegate } from '../index';
|
||||
|
||||
export class DomRouterDelegate implements RouterDelegate {
|
||||
|
||||
pushUrlState(urlSegment: string, stateObject: any = null, title = ''): Promise<any> {
|
||||
history.pushState(stateObject, title, urlSegment);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
popUrlState() {
|
||||
history.back();
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
@ -303,3 +303,16 @@ export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|n
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeUrl(url: string) {
|
||||
url = url.trim();
|
||||
if (url.charAt(0) !== '/') {
|
||||
// ensure first char is a /
|
||||
url = '/' + url;
|
||||
}
|
||||
if (url.length > 1 && url.charAt(url.length - 1) === '/') {
|
||||
// ensure last char is not a /
|
||||
url = url.substr(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowUnreachableCode": false,
|
||||
"alwaysStrict": true,
|
||||
"declaration": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "react",
|
||||
|
||||
Reference in New Issue
Block a user