mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +08:00
refactor(nav): transitions
This commit is contained in:
1
core/src/components.d.ts
vendored
1
core/src/components.d.ts
vendored
@ -2007,6 +2007,7 @@ declare global {
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonNavAttributes extends HTMLAttributes {
|
||||
animated?: boolean;
|
||||
root?: any;
|
||||
rootParams?: any;
|
||||
swipeBackEnabled?: boolean;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { ViewController } from '../..';
|
||||
|
||||
export interface AnimationController {
|
||||
create(animationBuilder?: AnimationBuilder, baseEl?: any, opts?: any): Promise<Animation>;
|
||||
@ -48,19 +47,6 @@ export interface AnimationBuilder {
|
||||
(Animation: Animation, baseEl?: HTMLElement, opts?: any): Promise<Animation>;
|
||||
}
|
||||
|
||||
|
||||
export interface AnimationOptions {
|
||||
animation?: string;
|
||||
duration?: number;
|
||||
easing?: string;
|
||||
direction?: string;
|
||||
isRTL?: boolean;
|
||||
ev?: any;
|
||||
enteringView: ViewController;
|
||||
leavingView: ViewController;
|
||||
nav: HTMLIonNavElement;
|
||||
}
|
||||
|
||||
export interface PlayOptions {
|
||||
duration?: number;
|
||||
promise?: boolean;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AnimationOptions, EffectProperty, EffectState, PlayOptions } from './animation-interface';
|
||||
import { EffectProperty, EffectState, PlayOptions } from './animation-interface';
|
||||
import { CSS_PROP, CSS_VALUE_REGEX, DURATION_MIN, TRANSITION_END_FALLBACK_PADDING_MS } from './constants';
|
||||
import { transitionEnd } from './transition-end';
|
||||
|
||||
@ -54,7 +54,6 @@ export class Animator {
|
||||
private _destroyed = false;
|
||||
|
||||
parent: Animator|undefined;
|
||||
opts: AnimationOptions;
|
||||
hasChildren = false;
|
||||
isPlaying = false;
|
||||
hasCompleted = false;
|
||||
|
@ -170,11 +170,6 @@ ion-app,
|
||||
contain: layout size style;
|
||||
}
|
||||
|
||||
.hide-page {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
// Misc
|
||||
// --------------------------------------------------
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.back-button.can-back-back,
|
||||
.can-go-back > ion-header .back-button,
|
||||
.back-button.show-back-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -35,15 +35,6 @@ export class BackButton {
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
'show-back-button': !!this.defaultHref
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private onClick(ev: Event) {
|
||||
const nav = this.el.closest('ion-nav');
|
||||
if (nav && nav.canGoBack()) {
|
||||
@ -54,6 +45,14 @@ export class BackButton {
|
||||
}
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
'show-back-button': !!this.defaultHref
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const backButtonIcon = this.icon || this.config.get('backButtonIcon', 'arrow-back');
|
||||
const backButtonText = this.text || this.config.get('backButtonText', this.mode === 'ios' ? 'Back' : '');
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Animation, AnimationOptions } from '../../../index';
|
||||
import { isDef } from '../../../utils/helpers';
|
||||
import { Animation } from '../../../index';
|
||||
import { AnimationOptions } from '../transition';
|
||||
|
||||
const DURATION = 500;
|
||||
const EASING = 'cubic-bezier(0.36,0.66,0.04,1)';
|
||||
@ -8,27 +8,29 @@ const TRANSFORM = 'transform';
|
||||
const TRANSLATEX = 'translateX';
|
||||
const CENTER = '0%';
|
||||
const OFF_OPACITY = 0.8;
|
||||
const SHOW_BACK_BTN_CSS = 'can-back-back';
|
||||
|
||||
export default function iosTransitionAnimation(Animation: Animation, _: HTMLElement, opts: AnimationOptions): Promise<Animation> {
|
||||
export default function iosTransitionAnimation(Animation: Animation, navEl: HTMLElement, opts: AnimationOptions): Promise<Animation> {
|
||||
|
||||
const isRTL = opts.isRTL;
|
||||
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
||||
const OFF_LEFT = isRTL ? '31%' : '-31%';
|
||||
|
||||
const enteringEl = opts.enteringView ? opts.enteringView.element : undefined;
|
||||
const leavingEl = opts.leavingView ? opts.leavingView.element : undefined;
|
||||
const nav = opts.nav;
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
|
||||
const rootTransition = new Animation();
|
||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : DURATION);
|
||||
rootTransition.easing(isDef(opts.easing) ? opts.easing : EASING);
|
||||
rootTransition.addElement(enteringEl);
|
||||
rootTransition.beforeRemoveClass('hide-page');
|
||||
rootTransition
|
||||
.addElement(enteringEl)
|
||||
.duration(opts.duration || DURATION)
|
||||
.easing(opts.easing || EASING)
|
||||
.beforeRemoveClass('hide-page');
|
||||
|
||||
if (leavingEl && nav) {
|
||||
if (leavingEl && navEl) {
|
||||
const navDecor = new Animation();
|
||||
navDecor.addElement(nav.el).duringAddClass('show-decor');
|
||||
navDecor
|
||||
.addElement(navEl)
|
||||
.duringAddClass('show-decor');
|
||||
|
||||
rootTransition.add(navDecor);
|
||||
}
|
||||
|
||||
@ -90,10 +92,8 @@ export default function iosTransitionAnimation(Animation: Animation, _: HTMLElem
|
||||
if (backDirection) {
|
||||
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
||||
|
||||
if (opts.enteringView.enableBack()) {
|
||||
// back direction, entering page has a back button
|
||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
||||
}
|
||||
// back direction, entering page has a back button
|
||||
enteringBackButton.fromTo(OPACITY, 0.01, 1, true);
|
||||
} else {
|
||||
// entering toolbar, forward direction
|
||||
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||
@ -102,21 +102,15 @@ export default function iosTransitionAnimation(Animation: Animation, _: HTMLElem
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(OPACITY, 0.01, 1, true);
|
||||
|
||||
if (opts.enteringView.enableBack()) {
|
||||
// forward direction, entering page has a back button
|
||||
enteringBackButton
|
||||
.beforeAddClass(SHOW_BACK_BTN_CSS)
|
||||
.fromTo(OPACITY, 0.01, 1, true);
|
||||
// forward direction, entering page has a back button
|
||||
enteringBackButton.fromTo(OPACITY, 0.01, 1, true);
|
||||
|
||||
const enteringBackBtnText = new Animation();
|
||||
enteringBackBtnText
|
||||
.addElement(enteringToolBarEle.querySelector('ion-back-button .button-text'))
|
||||
.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
||||
|
||||
const enteringBackBtnText = new Animation();
|
||||
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('ion-back-button .button-text'));
|
||||
|
||||
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
||||
enteringToolBar.add(enteringBackBtnText);
|
||||
} else {
|
||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||
}
|
||||
enteringToolBar.add(enteringBackBtnText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +1,61 @@
|
||||
import { Animation, AnimationOptions } from '../../../index';
|
||||
import { isDef } from '../../../utils/helpers';
|
||||
import { Animation } from '../../../index';
|
||||
import { AnimationOptions } from '../transition';
|
||||
|
||||
const TRANSLATEY = 'translateY';
|
||||
const OFF_BOTTOM = '40px';
|
||||
const CENTER = '0px';
|
||||
const SHOW_BACK_BTN_CSS = 'can-back-back';
|
||||
|
||||
export default function mdTransitionAnimation(Animation: Animation, _: HTMLElement, opts: AnimationOptions): Promise<Animation> {
|
||||
|
||||
const enteringEl = opts.enteringView ? opts.enteringView.element : undefined;
|
||||
const leavingEl = opts.leavingView ? opts.leavingView.element : undefined;
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
const ionPageElement = getIonPageElement(enteringEl);
|
||||
|
||||
const rootTransition = new Animation();
|
||||
rootTransition.addElement(ionPageElement);
|
||||
rootTransition.beforeRemoveClass('hide-page');
|
||||
rootTransition
|
||||
.addElement(ionPageElement)
|
||||
.beforeRemoveClass('hide-page');
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
if (enteringEl) {
|
||||
|
||||
// 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 {
|
||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : 280).easing('cubic-bezier(0.36,0.66,0.04,1)');
|
||||
|
||||
rootTransition
|
||||
.fromTo(TRANSLATEY, OFF_BOTTOM, CENTER, true)
|
||||
.fromTo('opacity', 0.01, 1, true);
|
||||
.duration(opts.duration || 200)
|
||||
.easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
|
||||
} else {
|
||||
rootTransition
|
||||
.duration(opts.duration || 280)
|
||||
.easing('cubic-bezier(0.36,0.66,0.04,1)')
|
||||
.fromTo(TRANSLATEY, OFF_BOTTOM, CENTER, true)
|
||||
.fromTo('opacity', 0.01, 1, true);
|
||||
}
|
||||
|
||||
// Animate toolbar if it's there
|
||||
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
|
||||
if (enteringToolbarEle) {
|
||||
|
||||
const enteringToolBar = new Animation();
|
||||
enteringToolBar.addElement(enteringToolbarEle);
|
||||
rootTransition.add(enteringToolBar);
|
||||
|
||||
const enteringBackButton = new Animation();
|
||||
enteringBackButton.addElement(enteringToolbarEle.querySelector('ion-back-button'));
|
||||
rootTransition.add(enteringBackButton);
|
||||
if (opts.enteringView.enableBack()) {
|
||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS);
|
||||
} else {
|
||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup leaving view
|
||||
if (leavingEl && backDirection) {
|
||||
// leaving content
|
||||
rootTransition.duration(opts.duration || 200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
rootTransition
|
||||
.duration(opts.duration || 200)
|
||||
.easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||
|
||||
const leavingPage = new Animation();
|
||||
leavingPage.addElement(getIonPageElement(leavingEl));
|
||||
rootTransition.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fromTo('opacity', 1, 0));
|
||||
leavingPage
|
||||
.addElement(getIonPageElement(leavingEl))
|
||||
.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM)
|
||||
.fromTo('opacity', 1, 0);
|
||||
|
||||
rootTransition.add(leavingPage);
|
||||
}
|
||||
|
||||
return Promise.resolve(rootTransition);
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { ViewController, isViewController } from './view-controller';
|
||||
import { NavControllerBase } from './nav';
|
||||
import { Transition } from './transition';
|
||||
import { FrameworkDelegate } from '../..';
|
||||
import { Animation, FrameworkDelegate } from '../..';
|
||||
|
||||
export function convertToView(page: any, params: any): ViewController {
|
||||
if (!page) {
|
||||
@ -26,32 +24,12 @@ export function convertToViews(pages: any[]): ViewController[] {
|
||||
.filter(v => v !== null);
|
||||
}
|
||||
|
||||
export function setZIndex(nav: NavControllerBase, enteringView: ViewController, leavingView: ViewController, direction: string) {
|
||||
if (enteringView) {
|
||||
|
||||
leavingView = leavingView || nav.getPrevious(enteringView);
|
||||
|
||||
if (leavingView && isPresent(leavingView._zIndex)) {
|
||||
if (direction === NavDirection.back) {
|
||||
enteringView._setZIndex(leavingView._zIndex - 1);
|
||||
|
||||
} else {
|
||||
enteringView._setZIndex(leavingView._zIndex + 1);
|
||||
}
|
||||
|
||||
} else {
|
||||
enteringView._setZIndex(INIT_ZINDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isPresent(val: any): val is any {
|
||||
return val !== undefined && val !== null;
|
||||
}
|
||||
|
||||
export const enum ViewState {
|
||||
New = 1,
|
||||
Initialized,
|
||||
Attached,
|
||||
Destroyed
|
||||
}
|
||||
@ -61,15 +39,13 @@ export const enum NavDirection {
|
||||
forward = 'forward'
|
||||
}
|
||||
|
||||
export const INIT_ZINDEX = 100;
|
||||
|
||||
export type NavParams = {[key: string]: any};
|
||||
|
||||
export interface NavResult {
|
||||
hasCompleted: boolean;
|
||||
requiresTransition: boolean;
|
||||
enteringName?: string;
|
||||
leavingName?: string;
|
||||
enteringView?: ViewController;
|
||||
leavingView?: ViewController;
|
||||
direction?: string;
|
||||
}
|
||||
|
||||
@ -96,11 +72,11 @@ export interface TransitionResolveFn {
|
||||
}
|
||||
|
||||
export interface TransitionRejectFn {
|
||||
(rejectReason: any, transition?: Transition): void;
|
||||
(rejectReason: any, transition?: Animation): void;
|
||||
}
|
||||
|
||||
export interface TransitionDoneFn {
|
||||
(hasCompleted: boolean, requiresTransition: boolean, enteringName?: string, leavingName?: string, direction?: string): void;
|
||||
(hasCompleted: boolean, requiresTransition: boolean, enteringView?: ViewController, leavingView?: ViewController, direction?: string): void;
|
||||
}
|
||||
|
||||
export interface TransitionInstruction {
|
||||
|
@ -6,16 +6,19 @@ ion-nav {
|
||||
@include position(0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
z-index: $z-index-page-container;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
contain: layout size style;
|
||||
}
|
||||
|
||||
.hide-page {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nav-decor {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Build, Component, Element, Event, EventEmitter, Method, Prop, Watch } from '@stencil/core';
|
||||
import {
|
||||
INIT_ZINDEX,
|
||||
NavDirection,
|
||||
NavOptions,
|
||||
NavParams,
|
||||
@ -10,21 +9,16 @@ import {
|
||||
ViewState,
|
||||
convertToViews,
|
||||
isPresent,
|
||||
setZIndex
|
||||
} from './nav-util';
|
||||
|
||||
import { ViewController, isViewController } from './view-controller';
|
||||
import { AnimationOptions, Config, DomController, GestureDetail, NavOutlet } from '../..';
|
||||
import { Animation, Config, DomController, GestureDetail, NavOutlet } from '../..';
|
||||
import { RouteID, RouteWrite } from '../router/utils/interfaces';
|
||||
import { assert } from '../../utils/helpers';
|
||||
|
||||
import { TransitionController } from './transition-controller';
|
||||
import { Transition } from './transition';
|
||||
|
||||
import iosTransitionAnimation from './animations/ios.transition';
|
||||
import mdTransitionAnimation from './animations/md.transition';
|
||||
|
||||
const TrnsCtrl = new TransitionController();
|
||||
import { AnimationOptions, ViewLifecycle, lifecycle, transition } from './transition';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-nav',
|
||||
@ -36,13 +30,12 @@ export class NavControllerBase implements NavOutlet {
|
||||
private _ids = -1;
|
||||
private _init = false;
|
||||
private _queue: TransitionInstruction[] = [];
|
||||
private _sbTrns: Transition;
|
||||
private _sbTrns: Animation;
|
||||
private useRouter = false;
|
||||
isTransitioning = false;
|
||||
private _destroyed = false;
|
||||
|
||||
_views: ViewController[] = [];
|
||||
_trnsId: number = null;
|
||||
|
||||
id: string;
|
||||
name: string;
|
||||
@ -56,7 +49,8 @@ export class NavControllerBase implements NavOutlet {
|
||||
@Prop({context: 'config'}) config: Config;
|
||||
|
||||
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: HTMLIonAnimationControllerElement;
|
||||
@Prop({mutable: true}) swipeBackEnabled: boolean;
|
||||
@Prop({ mutable: true }) swipeBackEnabled: boolean;
|
||||
@Prop({ mutable: true }) animated: boolean;
|
||||
@Prop() rootParams: any;
|
||||
@Prop() root: any;
|
||||
@Watch('root')
|
||||
@ -73,14 +67,17 @@ export class NavControllerBase implements NavOutlet {
|
||||
@Event() ionNavChanged: EventEmitter;
|
||||
|
||||
componentWillLoad() {
|
||||
this.id = 'n' + (++ctrlIds);
|
||||
this.useRouter = !!document.querySelector('ion-router') && !this.el.closest('[no-router]');
|
||||
if (this.swipeBackEnabled === undefined) {
|
||||
this.swipeBackEnabled = this.mode === 'ios' && this.config.getBoolean('swipeBackEnabled', true);
|
||||
this.swipeBackEnabled = this.config.getBoolean('swipeBackEnabled', this.mode === 'ios');
|
||||
}
|
||||
if (this.animated === undefined) {
|
||||
this.animated = this.config.getBoolean('animate', true);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
this.id = 'n' + (++ctrlIds);
|
||||
this.rootChanged();
|
||||
}
|
||||
|
||||
@ -89,7 +86,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
push(page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
push(page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
insertStart: -1,
|
||||
insertViews: [{ page: page, params: params }],
|
||||
@ -98,7 +95,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
insert(insertIndex: number, page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
insert(insertIndex: number, page: any, params?: NavParams, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
insertStart: insertIndex,
|
||||
insertViews: [{ page: page, params: params }],
|
||||
@ -107,7 +104,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
insertStart: insertIndex,
|
||||
insertViews: insertPages,
|
||||
@ -116,7 +113,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
pop(opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
pop(opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
removeStart: -1,
|
||||
removeCount: 1,
|
||||
@ -125,7 +122,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
const config: TransitionInstruction = {
|
||||
removeStart: -1,
|
||||
removeCount: -1,
|
||||
@ -141,7 +138,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
popToRoot(opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
popToRoot(opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
removeStart: 1,
|
||||
removeCount: -1,
|
||||
@ -150,8 +147,8 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
popAll(): Promise<any[]> {
|
||||
const promises: any[] = [];
|
||||
popAll(): Promise<boolean[]> {
|
||||
const promises: Promise<boolean>[] = [];
|
||||
for (let i = this._views.length - 1; i >= 0; i--) {
|
||||
promises.push(this.pop(null));
|
||||
}
|
||||
@ -159,7 +156,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
removeIndex(startIndex: number, removeCount = 1, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
removeIndex(startIndex: number, removeCount = 1, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
removeStart: startIndex,
|
||||
removeCount: removeCount,
|
||||
@ -168,7 +165,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
removeView(viewController: ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
removeView(viewController: ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this._queueTrns({
|
||||
removeView: viewController,
|
||||
removeStart: 0,
|
||||
@ -178,12 +175,12 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
return this.setPages([{ page: pageOrViewCtrl, params: params }], opts, done);
|
||||
}
|
||||
|
||||
@Method()
|
||||
setPages(pages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
setPages(pages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<boolean> {
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
@ -264,12 +261,10 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
canGoBack(): boolean {
|
||||
const activeView = this.getActive();
|
||||
return !!(activeView && activeView.enableBack());
|
||||
canGoBack(view = this.getActive()): boolean {
|
||||
return !!(view && this.getPrevious(view));
|
||||
}
|
||||
|
||||
|
||||
@Method()
|
||||
getActive(): ViewController {
|
||||
return this._views[this._views.length - 1];
|
||||
@ -281,11 +276,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
@Method()
|
||||
getPrevious(view?: ViewController): ViewController {
|
||||
// returns the view controller which is before the given view controller.
|
||||
if (!view) {
|
||||
view = this.getActive();
|
||||
}
|
||||
getPrevious(view = this.getActive()): ViewController {
|
||||
const views = this._views;
|
||||
const index = views.indexOf(view);
|
||||
return (index > 0) ? views[index - 1] : null;
|
||||
@ -301,12 +292,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
*/
|
||||
@Method()
|
||||
getViewById(id: string): ViewController {
|
||||
for (const vc of this._views) {
|
||||
if (vc && vc.id === id) {
|
||||
return vc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return this._views.find(vc => vc.id === id);
|
||||
}
|
||||
|
||||
indexOf(viewController: ViewController) {
|
||||
@ -355,7 +341,6 @@ export class NavControllerBase implements NavOutlet {
|
||||
return;
|
||||
}
|
||||
this._init = true;
|
||||
this._trnsId = null;
|
||||
|
||||
// ensure we're not transitioning here
|
||||
this.isTransitioning = false;
|
||||
@ -373,8 +358,8 @@ export class NavControllerBase implements NavOutlet {
|
||||
ti.done(
|
||||
result.hasCompleted,
|
||||
result.requiresTransition,
|
||||
result.enteringName,
|
||||
result.leavingName,
|
||||
result.enteringView,
|
||||
result.leavingView,
|
||||
result.direction
|
||||
);
|
||||
}
|
||||
@ -386,7 +371,6 @@ export class NavControllerBase implements NavOutlet {
|
||||
this._fireError('nav controller was destroyed', ti);
|
||||
return;
|
||||
}
|
||||
this._trnsId = null;
|
||||
this._queue.length = 0;
|
||||
|
||||
// let's see if there's another to kick off
|
||||
@ -422,50 +406,36 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
// set that this nav is actively transitioning
|
||||
let enteringView: ViewController;
|
||||
let leavingView: ViewController;
|
||||
this.isTransitioning = true;
|
||||
|
||||
this._startTI(ti)
|
||||
.then(() => {
|
||||
this._prepareViewControllers(ti);
|
||||
leavingView = this.getActive();
|
||||
enteringView = this._getEnteringView(ti, leavingView);
|
||||
try {
|
||||
this._prepareTI(ti);
|
||||
const leavingView = this.getActive();
|
||||
const enteringView = this._getEnteringView(ti, leavingView);
|
||||
|
||||
if (!leavingView && !enteringView) {
|
||||
throw new Error('no views in the stack to be removed');
|
||||
}
|
||||
if (!leavingView && !enteringView) {
|
||||
throw new Error('no views in the stack to be removed');
|
||||
}
|
||||
|
||||
// Needs transition?
|
||||
ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
|
||||
// Needs transition?
|
||||
ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
|
||||
|
||||
if (enteringView && enteringView._state === ViewState.New) {
|
||||
this._viewInit(enteringView);
|
||||
}
|
||||
})
|
||||
.then(() => this._postViewInit(enteringView, leavingView, ti))
|
||||
.then(() => this._transition(enteringView, leavingView, ti))
|
||||
.then((result) => this._success(result, ti))
|
||||
.catch((rejectReason) => this._failed(rejectReason, ti));
|
||||
if (enteringView && enteringView._state === ViewState.New) {
|
||||
enteringView.init(this.el);
|
||||
}
|
||||
this._postViewInit(enteringView, leavingView, ti);
|
||||
|
||||
this._transition(enteringView, leavingView, ti)
|
||||
.then((result) => this._success(result, ti))
|
||||
.catch((rejectReason) => this._failed(rejectReason, ti));
|
||||
|
||||
} catch (rejectReason) {
|
||||
this._failed(rejectReason, ti);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _waitUntilReady(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction) {
|
||||
const promises = [];
|
||||
if (enteringView) {
|
||||
promises.push(isReady(enteringView.element));
|
||||
}
|
||||
if (leavingView) {
|
||||
promises.push(isReady(leavingView.element));
|
||||
}
|
||||
const promise = Promise.all(promises);
|
||||
if (ti.opts.viewIsReady) {
|
||||
return promise.then(ti.opts.viewIsReady);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _startTI(ti: TransitionInstruction): Promise<void> {
|
||||
private _prepareTI(ti: TransitionInstruction) {
|
||||
const viewsLength = this._views.length;
|
||||
|
||||
if (isPresent(ti.removeView)) {
|
||||
@ -474,7 +444,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
|
||||
const index = this._views.indexOf(ti.removeView);
|
||||
if (index < 0) {
|
||||
return Promise.reject('removeView was not found');
|
||||
throw new Error('removeView was not found');
|
||||
}
|
||||
ti.removeStart += index;
|
||||
}
|
||||
@ -496,11 +466,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
ti.enteringRequiresTransition = (ti.insertStart === viewsLength);
|
||||
}
|
||||
this.isTransitioning = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private _prepareViewControllers(ti: TransitionInstruction) {
|
||||
const insertViews = ti.insertViews;
|
||||
if (!insertViews) {
|
||||
return;
|
||||
@ -553,7 +519,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
assert(ti.resolve, 'resolve must be valid');
|
||||
assert(ti.reject, 'reject must be valid');
|
||||
|
||||
const opts = ti.opts || {};
|
||||
const opts = ti.opts = ti.opts || {};
|
||||
const insertViews = ti.insertViews;
|
||||
const removeStart = ti.removeStart;
|
||||
const removeCount = ti.removeCount;
|
||||
@ -612,9 +578,9 @@ export class NavControllerBase implements NavOutlet {
|
||||
if (destroyQueue && destroyQueue.length > 0) {
|
||||
for (let i = 0; i < destroyQueue.length; i++) {
|
||||
const view = destroyQueue[i];
|
||||
view._willLeave(true);
|
||||
view._didLeave();
|
||||
view._willUnload();
|
||||
lifecycle(view.element, ViewLifecycle.WillLeave);
|
||||
lifecycle(view.element, ViewLifecycle.DidLeave);
|
||||
lifecycle(view.element, ViewLifecycle.WillUnload);
|
||||
}
|
||||
|
||||
// once all lifecycle events has been delivered, we can safely detroy the views
|
||||
@ -629,35 +595,6 @@ export class NavControllerBase implements NavOutlet {
|
||||
? (leavingView || enteringView).getTransitionName(opts.direction)
|
||||
: (enteringView || leavingView).getTransitionName(opts.direction);
|
||||
}
|
||||
ti.opts = opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM WRITE
|
||||
*/
|
||||
private _viewInit(enteringView: ViewController) {
|
||||
assert(enteringView, 'enteringView must be non null');
|
||||
assert(enteringView._state === ViewState.New, 'enteringView state must be NEW');
|
||||
|
||||
enteringView._state = ViewState.Initialized;
|
||||
enteringView.init();
|
||||
}
|
||||
|
||||
private _viewAttachToDOM(view: ViewController) {
|
||||
assert(view._state === ViewState.Initialized, 'view state must be INITIALIZED');
|
||||
|
||||
// fire willLoad before change detection runs
|
||||
view._willLoad();
|
||||
|
||||
// render the component ref instance to the DOM
|
||||
// ******** DOM WRITE ****************
|
||||
this.el.appendChild(view.element);
|
||||
|
||||
view._state = ViewState.Attached;
|
||||
|
||||
// successfully finished loading the entering view
|
||||
// fire off the "didLoad" lifecycle events
|
||||
view._didLoad();
|
||||
}
|
||||
|
||||
private _transition(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction): Promise<NavResult> {
|
||||
@ -671,200 +608,70 @@ export class NavControllerBase implements NavOutlet {
|
||||
requiresTransition: false
|
||||
});
|
||||
}
|
||||
|
||||
const opts = ti.opts;
|
||||
|
||||
// figure out if this transition is the root one or a
|
||||
// child of a parent nav that has the root transition
|
||||
this._trnsId = TrnsCtrl.getRootTrnsId(this);
|
||||
if (this._trnsId === null) {
|
||||
// this is the root transition, meaning all child navs and their views
|
||||
// should be added as a child transition to this one
|
||||
this._trnsId = TrnsCtrl.nextId();
|
||||
if (this._sbTrns) {
|
||||
this._sbTrns.destroy();
|
||||
this._sbTrns = null;
|
||||
}
|
||||
|
||||
// create the transition options
|
||||
const animationOpts: AnimationOptions = {
|
||||
animation: opts.animation,
|
||||
direction: opts.direction,
|
||||
duration: (opts.animate === false ? 0 : opts.duration),
|
||||
easing: opts.easing,
|
||||
isRTL: document.dir === 'rtl',
|
||||
ev: opts.ev,
|
||||
enteringView: enteringView,
|
||||
leavingView: leavingView,
|
||||
nav: this as any,
|
||||
};
|
||||
|
||||
const animation = this.mode === 'ios' ? iosTransitionAnimation : mdTransitionAnimation;
|
||||
|
||||
const transition = new Transition(
|
||||
this.animationCtrl,
|
||||
animation,
|
||||
enteringView,
|
||||
leavingView,
|
||||
animationOpts
|
||||
);
|
||||
TrnsCtrl.register(this._trnsId, transition);
|
||||
|
||||
// ensure any swipeback transitions are cleared out
|
||||
this._sbTrns && this._sbTrns.destroy();
|
||||
this._sbTrns = null;
|
||||
|
||||
// swipe to go back root transition
|
||||
if (!transition.parent && opts.progressAnimation) {
|
||||
this._sbTrns = transition;
|
||||
}
|
||||
|
||||
// transition start has to be registered before attaching the view to the DOM!
|
||||
const promise = new Promise<void>(resolve => transition.registerStart(resolve))
|
||||
.then(() => this._waitUntilReady(enteringView, leavingView, ti))
|
||||
.then(() => this._transitionInit(transition, enteringView, leavingView, opts))
|
||||
.then(() => this._transitionStart(transition, enteringView, leavingView, opts));
|
||||
|
||||
if (enteringView && (enteringView._state === ViewState.Initialized)) {
|
||||
// render the entering component in the DOM
|
||||
// this would also render new child navs/views
|
||||
// which may have their very own async canEnter/Leave tests
|
||||
// ******** DOM WRITE ****************
|
||||
this._viewAttachToDOM(enteringView);
|
||||
}
|
||||
|
||||
|
||||
// if (!transition.hasChildren) {
|
||||
// lowest level transition, so kick it off and let it bubble up to start all of them
|
||||
transition.start();
|
||||
// }
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
private _transitionInit(transition: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions): Promise<void> {
|
||||
assert(this.isTransitioning, 'isTransitioning has to be true');
|
||||
|
||||
this._trnsId = null;
|
||||
|
||||
// set the correct zIndex for the entering and leaving views
|
||||
// ******** DOM WRITE ****************
|
||||
setZIndex(this, enteringView, leavingView, opts.direction);
|
||||
|
||||
// always ensure the entering view is viewable
|
||||
// ******** DOM WRITE ****************
|
||||
enteringView && enteringView._domShow(true);
|
||||
|
||||
// always ensure the leaving view is viewable
|
||||
// ******** DOM WRITE ****************
|
||||
leavingView && leavingView._domShow(true);
|
||||
|
||||
// initialize the transition
|
||||
return transition.init();
|
||||
}
|
||||
|
||||
private _transitionStart(transition: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions): Promise<NavResult> {
|
||||
assert(this.isTransitioning, 'isTransitioning has to be true');
|
||||
|
||||
// we should animate (duration > 0) if the pushed page is not the first one (startup)
|
||||
// or if it is a portal (modal, actionsheet, etc.)
|
||||
const shouldNotAnimate = !this._init && this._views.length === 1;
|
||||
const canNotAnimate = !this.config.getBoolean('animate', true);
|
||||
if (shouldNotAnimate || canNotAnimate) {
|
||||
opts.animate = false;
|
||||
}
|
||||
const shouldAnimate = this.animated && this._init && this._views.length > 1;
|
||||
|
||||
if (opts.animate === false) {
|
||||
// if it was somehow set to not animation, then make the duration zero
|
||||
transition.ani.duration(0);
|
||||
}
|
||||
const animationBuilder = (shouldAnimate)
|
||||
? this.mode === 'ios' ? iosTransitionAnimation : mdTransitionAnimation
|
||||
: undefined;
|
||||
|
||||
// create a callback that needs to run within zone
|
||||
// that will fire off the willEnter/Leave lifecycle events at the right time
|
||||
transition.ani.beforeAddRead(this._viewsWillLifecycles.bind(this, enteringView, leavingView));
|
||||
const progressAnimation = ti.opts.progressAnimation
|
||||
? (animation: Animation) => this._sbTrns = animation
|
||||
: undefined;
|
||||
|
||||
// create a callback for when the animation is done
|
||||
const promise = new Promise(resolve => {
|
||||
transition.ani.onFinish(resolve);
|
||||
});
|
||||
const opts = ti.opts;
|
||||
const enteringEl = enteringView && enteringView.element;
|
||||
const leavingEl = leavingView && leavingView.element;
|
||||
const animationOpts: AnimationOptions = {
|
||||
animationCtrl: this.animationCtrl,
|
||||
animationBuilder: animationBuilder,
|
||||
animation: undefined,
|
||||
|
||||
if (transition.ani.isRoot()) {
|
||||
// cool, let's do this, start the transition
|
||||
if (opts.progressAnimation) {
|
||||
// this is a swipe to go back, just get the transition progress ready
|
||||
// kick off the swipe animation start
|
||||
transition.ani.progressStart();
|
||||
direction: opts.direction as NavDirection,
|
||||
duration: opts.duration,
|
||||
easing: opts.easing,
|
||||
viewIsReady: opts.viewIsReady,
|
||||
|
||||
} else {
|
||||
// only the top level transition should actually start "play"
|
||||
// kick it off and let it play through
|
||||
// ******** DOM WRITE ****************
|
||||
transition.ani.play();
|
||||
}
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
return this._transitionFinish(transition, opts);
|
||||
});
|
||||
showGoBack: this.canGoBack(enteringView),
|
||||
isRTL: document.dir === 'rtl',
|
||||
progressAnimation,
|
||||
baseEl: this.el,
|
||||
enteringEl,
|
||||
leavingEl
|
||||
};
|
||||
return transition(animationOpts)
|
||||
.then(trns => this._transitionFinish(trns, enteringView, leavingView, ti.opts));
|
||||
}
|
||||
|
||||
private _transitionFinish(transition: Transition, opts: NavOptions): NavResult {
|
||||
private _transitionFinish(transition: Animation, enteringView: ViewController, leavingView: ViewController, opts: NavOptions): NavResult {
|
||||
|
||||
const hasCompleted = transition.ani.hasCompleted;
|
||||
const enteringView = transition.enteringView;
|
||||
const leavingView = transition.leavingView;
|
||||
|
||||
// mainly for testing
|
||||
let enteringName: string;
|
||||
let leavingName: string;
|
||||
const hasCompleted = transition ? transition.hasCompleted : true;
|
||||
|
||||
if (hasCompleted) {
|
||||
// transition has completed (went from 0 to 1)
|
||||
if (enteringView) {
|
||||
enteringName = enteringView.name;
|
||||
enteringView._didEnter();
|
||||
}
|
||||
|
||||
if (leavingView) {
|
||||
leavingName = leavingView.name;
|
||||
leavingView._didLeave();
|
||||
}
|
||||
|
||||
this._cleanup(enteringView);
|
||||
} else {
|
||||
// If transition does not complete, we have to cleanup anyway, because
|
||||
// previous pages in the stack are not hidden probably.
|
||||
this._cleanup(leavingView);
|
||||
}
|
||||
|
||||
if (transition.ani.isRoot()) {
|
||||
// this is the root transition
|
||||
// it's safe to destroy this transition
|
||||
TrnsCtrl.destroy(transition.trnsId);
|
||||
|
||||
// it's safe to enable the app again
|
||||
// mark ourselves as not transitioning - `deepLinker navchange` requires this
|
||||
// TODO - probably could be resolved in a better way
|
||||
this.isTransitioning = false;
|
||||
}
|
||||
// this is the root transition
|
||||
// it's safe to destroy this transition
|
||||
transition && transition.destroy();
|
||||
|
||||
return {
|
||||
hasCompleted: hasCompleted,
|
||||
requiresTransition: true,
|
||||
enteringName: enteringName,
|
||||
leavingName: leavingName,
|
||||
enteringView,
|
||||
leavingView,
|
||||
direction: opts.direction
|
||||
};
|
||||
}
|
||||
|
||||
private _viewsWillLifecycles(enteringView: ViewController, leavingView: ViewController) {
|
||||
if (enteringView || leavingView) {
|
||||
// Here, the order is important. WillLeave must be called before WillEnter.
|
||||
if (leavingView) {
|
||||
const willUnload = enteringView ? leavingView.index > enteringView.index : true;
|
||||
leavingView._willLeave(willUnload);
|
||||
}
|
||||
enteringView && enteringView._willEnter();
|
||||
}
|
||||
}
|
||||
|
||||
private _insertViewAt(view: ViewController, index: number) {
|
||||
const existingIndex = this._views.indexOf(view);
|
||||
if (existingIndex > -1) {
|
||||
@ -915,52 +722,30 @@ export class NavControllerBase implements NavOutlet {
|
||||
if (!this._destroyed) {
|
||||
const activeViewIndex = this._views.indexOf(activeView);
|
||||
const views = this._views;
|
||||
let reorderZIndexes = false;
|
||||
let view: ViewController;
|
||||
let i: number;
|
||||
|
||||
for (i = views.length - 1; i >= 0; i--) {
|
||||
view = views[i];
|
||||
for (let i = views.length - 1; i >= 0; i--) {
|
||||
const view = views[i];
|
||||
if (i > activeViewIndex) {
|
||||
// this view comes after the active view
|
||||
// let's unload it
|
||||
view._willUnload();
|
||||
lifecycle(view.element, ViewLifecycle.WillUnload);
|
||||
this._destroyView(view);
|
||||
|
||||
} else if (i < activeViewIndex) {
|
||||
// this view comes before the active view
|
||||
// and it is not a portal then ensure it is hidden
|
||||
view._domShow(false);
|
||||
}
|
||||
if (view._zIndex <= 0) {
|
||||
reorderZIndexes = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reorderZIndexes) {
|
||||
for (i = 0; i < views.length; i++) {
|
||||
view = views[i];
|
||||
// ******** DOM WRITE ****************
|
||||
view._setZIndex(view._zIndex + INIT_ZINDEX + 1);
|
||||
view.element.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// registerChildNav(container: NavigationContainer) {
|
||||
// this._children.push(container);
|
||||
// }
|
||||
|
||||
// unregisterChildNav(nav: any) {
|
||||
// this._children = this._children.filter(child => child !== nav);
|
||||
// }
|
||||
|
||||
destroy() {
|
||||
const views = this._views;
|
||||
let view: ViewController;
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
view = views[i];
|
||||
view._willUnload();
|
||||
lifecycle(view.element, ViewLifecycle.WillUnload);
|
||||
view._destroy();
|
||||
}
|
||||
|
||||
@ -1006,7 +791,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
const delta = detail.deltaX;
|
||||
const stepValue = delta / window.innerWidth;
|
||||
// set the transition animation's progress
|
||||
this._sbTrns.ani.progressStep(stepValue);
|
||||
this._sbTrns.progressStep(stepValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1029,7 +814,7 @@ export class NavControllerBase implements NavOutlet {
|
||||
realDur = Math.min(dur, 300);
|
||||
}
|
||||
|
||||
this._sbTrns.ani.progressEnd(shouldComplete, stepValue, realDur);
|
||||
this._sbTrns.progressEnd(shouldComplete, stepValue, realDur);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1066,14 +851,3 @@ export class NavControllerBase implements NavOutlet {
|
||||
}
|
||||
|
||||
let ctrlIds = -1;
|
||||
|
||||
function isReady(el: HTMLElement): Promise<any> {
|
||||
if (!el) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if ((el as any).componentOnReady) {
|
||||
return (el as any).componentOnReady();
|
||||
} else {
|
||||
return Promise.all(Array.from(el.children).map(isReady));
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
## Properties
|
||||
|
||||
#### animated
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### root
|
||||
|
||||
any
|
||||
@ -24,6 +29,11 @@ boolean
|
||||
|
||||
## Attributes
|
||||
|
||||
#### animated
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### root
|
||||
|
||||
any
|
||||
|
@ -9,59 +9,56 @@
|
||||
class PageOne extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Page One</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page One</h1>
|
||||
<ion-nav-push component="page-two">
|
||||
<ion-button class="next">Go to Page Two</ion-button>
|
||||
</ion-nav-push>
|
||||
</ion-content>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Page One</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page One</h1>
|
||||
<ion-nav-push component="page-two">
|
||||
<ion-button class="next">Go to Page Two</ion-button>
|
||||
</ion-nav-push>
|
||||
</ion-content>
|
||||
`;
|
||||
}
|
||||
}
|
||||
class PageTwo extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button text="Page One"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Two</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page Two</h1>
|
||||
<div>
|
||||
<ion-nav-push component="page-three">
|
||||
<ion-button class="next">Go to Page Two</ion-button>
|
||||
</ion-nav-push>
|
||||
</div>
|
||||
</ion-content>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button text="Page One"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Two</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page Two</h1>
|
||||
<div>
|
||||
<ion-nav-push component="page-three">
|
||||
<ion-button class="next">Go to Page Two</ion-button>
|
||||
</ion-nav-push>
|
||||
</div>
|
||||
</ion-content>
|
||||
`;
|
||||
}
|
||||
}
|
||||
class PageThree extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button text="Page Two"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Three</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page Three</h1>
|
||||
</ion-content>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button text="Page Two"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Page Three</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page Three</h1>
|
||||
</ion-content>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -21,21 +21,23 @@ describe('NavController', () => {
|
||||
const pop3Done = jest.fn();
|
||||
|
||||
// Push 1
|
||||
await nav.push(mockView(MockView1), null, {animate: false}, push1Done);
|
||||
const view1 = mockView(MockView1);
|
||||
await nav.push(view1, null, {animate: false}, push1Done);
|
||||
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(push1Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, NavDirection.forward
|
||||
hasCompleted, requiresTransition, view1, undefined, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
|
||||
// Push 2
|
||||
await nav.push(mockView(MockView2), null, {animate: false}, push2Done);
|
||||
const view2 = mockView(MockView2);
|
||||
await nav.push(view2, null, {animate: false}, push2Done);
|
||||
|
||||
expect(push2Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view2, view1, NavDirection.forward
|
||||
);
|
||||
|
||||
expect(nav.length()).toEqual(2);
|
||||
@ -43,10 +45,11 @@ describe('NavController', () => {
|
||||
expect(nav.getByIndex(1).component).toEqual(MockView2);
|
||||
|
||||
// Push 3
|
||||
await nav.push(mockView(MockView3), null, {animate: false}, push3Done);
|
||||
const view3 = mockView(MockView3);
|
||||
await nav.push(view3, null, {animate: false}, push3Done);
|
||||
|
||||
expect(push3Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view2', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view3, view2, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(3);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -54,9 +57,10 @@ describe('NavController', () => {
|
||||
expect(nav.getByIndex(2).component).toEqual(MockView3);
|
||||
|
||||
// Push 4
|
||||
await nav.push(mockView(MockView4), null, {animate: false}, push4Done);
|
||||
const view4 = mockView(MockView4);
|
||||
await nav.push(view4, null, {animate: false}, push4Done);
|
||||
expect(push4Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view4, view3, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(4);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -67,7 +71,7 @@ describe('NavController', () => {
|
||||
// Pop 1
|
||||
await nav.pop({animate: false}, pop1Done);
|
||||
expect(pop1Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view4', NavDirection.back
|
||||
hasCompleted, requiresTransition, view3, view4, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(3);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -77,7 +81,7 @@ describe('NavController', () => {
|
||||
// Pop 2
|
||||
await nav.pop({animate: false}, pop2Done);
|
||||
expect(pop2Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
hasCompleted, requiresTransition, view2, view3, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -86,7 +90,7 @@ describe('NavController', () => {
|
||||
// Pop 3
|
||||
await nav.pop({animate: false}, pop3Done);
|
||||
expect(pop3Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', NavDirection.back
|
||||
hasCompleted, requiresTransition, view1, view2, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -98,28 +102,30 @@ describe('NavController', () => {
|
||||
|
||||
it('should push a component as the first view', async () => {
|
||||
|
||||
await nav.push(mockView(MockView1), null, null, trnsDone);
|
||||
const view1 = mockView(MockView1);
|
||||
await nav.push(view1, null, null, trnsDone);
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, NavDirection.forward
|
||||
hasCompleted, requiresTransition, view1, undefined, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
expect(nav.isTransitioning).toEqual(false);
|
||||
|
||||
|
||||
}, 10000);
|
||||
|
||||
it('should push a component as the second view at the end', async () => {
|
||||
mockViews(nav, [mockView(MockView1)]);
|
||||
const view1 = mockView(MockView1);
|
||||
mockViews(nav, [view1]);
|
||||
|
||||
await nav.push(mockView(MockView2), null, null, trnsDone);
|
||||
const view2 = mockView(MockView2);
|
||||
await nav.push(view2, null, null, trnsDone);
|
||||
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view2, view1, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -139,7 +145,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.push(view2, null, null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -148,7 +153,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -160,7 +164,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view', 'mock-view', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view2, view1, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
|
||||
@ -188,7 +192,6 @@ describe('NavController', () => {
|
||||
mockViews(nav, [mockView(MockView1), mockView(MockView2), mockView(MockView3)]);
|
||||
|
||||
await nav.insert(0, view4, null, opts, trnsDone);
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -210,13 +213,16 @@ describe('NavController', () => {
|
||||
|
||||
it('should insert at the end when given -1', async () => {
|
||||
const opts: NavOptions = {};
|
||||
mockViews(nav, [mockView(MockView1)]);
|
||||
const view1 = mockView(MockView1);
|
||||
|
||||
await nav.insert(-1, mockView(MockView2), null, opts, trnsDone);
|
||||
mockViews(nav, [view1]);
|
||||
|
||||
const view2 = mockView(MockView2);
|
||||
await nav.insert(-1, view2, null, opts, trnsDone);
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view2, view1, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav._views[nav._views.length - 1].component).toEqual(MockView2);
|
||||
@ -224,13 +230,15 @@ describe('NavController', () => {
|
||||
}, 10000);
|
||||
|
||||
it('should insert at the end when given a number greater than actual length', async () => {
|
||||
mockViews(nav, [mockView(MockView1)]);
|
||||
const view1 = mockView(MockView1);
|
||||
mockViews(nav, [view1]);
|
||||
|
||||
await nav.insert(9999, mockView(MockView2), null, null, trnsDone);
|
||||
const view2 = mockView(MockView2);
|
||||
await nav.insert(9999, view2, null, null, trnsDone);
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
hasCompleted, requiresTransition, view2, view1, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav._views[nav._views.length - 1].component).toEqual(MockView2);
|
||||
@ -307,10 +315,15 @@ describe('NavController', () => {
|
||||
it('should insert all pages in the middle', async () => {
|
||||
const view4 = mockView(MockView4);
|
||||
const instance4 = spyOnLifecycles(view4);
|
||||
mockViews(nav, [mockView(MockView1), mockView(MockView2), mockView(MockView3)]);
|
||||
|
||||
await nav.insertPages(1, [view4, mockView(MockView5)], null, trnsDone);
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
const view1 = mockView(MockView1);
|
||||
const view2 = mockView(MockView2);
|
||||
const view3 = mockView(MockView3);
|
||||
|
||||
mockViews(nav, [view1, view2, view3]);
|
||||
|
||||
const view5 = mockView(MockView5);
|
||||
await nav.insertPages(1, [view4, view5], null, trnsDone);
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -366,7 +379,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.pop(null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -375,7 +387,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -387,7 +398,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', NavDirection.back
|
||||
hasCompleted, requiresTransition, view1, view2, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -411,7 +422,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
hasCompleted, requiresTransition, view2, view3, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -432,7 +443,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', NavDirection.back
|
||||
hasCompleted, requiresTransition, view2, view4, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -455,7 +466,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.popTo(0, null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -464,7 +474,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -473,7 +482,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -482,7 +490,6 @@ describe('NavController', () => {
|
||||
expect(instance3.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -494,7 +501,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', NavDirection.back
|
||||
hasCompleted, requiresTransition, view1, view4, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -519,7 +526,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.popToRoot(null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -528,7 +534,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -537,7 +542,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -546,7 +550,6 @@ describe('NavController', () => {
|
||||
expect(instance3.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -558,7 +561,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', NavDirection.back
|
||||
hasCompleted, requiresTransition, view1, view4, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -600,7 +603,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.removeIndex(0, 3, null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -609,7 +611,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -618,7 +619,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -627,7 +627,6 @@ describe('NavController', () => {
|
||||
expect(instance3.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -663,7 +662,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.removeIndex(2, 2, null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -672,7 +670,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -681,7 +678,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -690,7 +686,6 @@ describe('NavController', () => {
|
||||
expect(instance3.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -699,7 +694,6 @@ describe('NavController', () => {
|
||||
expect(instance4.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance5.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance5.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance5.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance5.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -735,7 +729,6 @@ describe('NavController', () => {
|
||||
|
||||
await nav.removeIndex(2, 2, null, trnsDone);
|
||||
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -744,7 +737,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -753,7 +745,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -762,7 +753,6 @@ describe('NavController', () => {
|
||||
expect(instance3.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance4.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance4.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance4.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -774,7 +764,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', NavDirection.back
|
||||
hasCompleted, requiresTransition, view2, view4, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -798,7 +788,6 @@ describe('NavController', () => {
|
||||
const instance3 = spyOnLifecycles(view3);
|
||||
|
||||
await nav.setRoot(view3, null, null, trnsDone);
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -807,7 +796,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -816,7 +804,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -847,7 +834,6 @@ describe('NavController', () => {
|
||||
const instance3 = spyOnLifecycles(view3);
|
||||
|
||||
await nav.setRoot(view2, null, null, trnsDone);
|
||||
expect(instance1.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -856,7 +842,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -865,7 +850,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -877,7 +861,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
hasCompleted, requiresTransition, view2, view3, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView2);
|
||||
@ -895,7 +879,6 @@ describe('NavController', () => {
|
||||
const instance3 = spyOnLifecycles(view3);
|
||||
|
||||
await nav.setRoot(view1, null, null, trnsDone);
|
||||
expect(instance1.ionViewDidLoad).toHaveBeenCalled();
|
||||
// expect(instance1.ionViewCanEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillEnter).toHaveBeenCalled();
|
||||
expect(instance1.ionViewDidEnter).toHaveBeenCalled();
|
||||
@ -904,7 +887,6 @@ describe('NavController', () => {
|
||||
expect(instance1.ionViewDidLeave).not.toHaveBeenCalled();
|
||||
expect(instance1.ionViewWillUnload).not.toHaveBeenCalled();
|
||||
|
||||
expect(instance2.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance2.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance2.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -913,7 +895,6 @@ describe('NavController', () => {
|
||||
expect(instance2.ionViewDidLeave).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
|
||||
expect(instance3.ionViewDidLoad).not.toHaveBeenCalled();
|
||||
// expect(instance3.ionViewCanEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillEnter).not.toHaveBeenCalled();
|
||||
expect(instance3.ionViewDidEnter).not.toHaveBeenCalled();
|
||||
@ -925,7 +906,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view3', NavDirection.back
|
||||
hasCompleted, requiresTransition, view1, view3, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@ -944,7 +925,8 @@ describe('NavController', () => {
|
||||
const instance2 = spyOnLifecycles(view2);
|
||||
const instance3 = spyOnLifecycles(view3);
|
||||
|
||||
await nav.setRoot(mockView(MockView4), null, null, trnsDone);
|
||||
const view4 = mockView(MockView4);
|
||||
await nav.setRoot(view4, null, null, trnsDone);
|
||||
expect(instance1.ionViewWillUnload).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
expect(instance3.ionViewWillUnload).toHaveBeenCalled();
|
||||
@ -952,7 +934,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', NavDirection.back
|
||||
hasCompleted, requiresTransition, view4, view3, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView4);
|
||||
@ -971,10 +953,13 @@ describe('NavController', () => {
|
||||
const instance1 = spyOnLifecycles(view1);
|
||||
const instance2 = spyOnLifecycles(view2);
|
||||
|
||||
const view4 = mockView(MockView4);
|
||||
const view5 = mockView(MockView5);
|
||||
|
||||
await nav.setPages([{
|
||||
page: mockView(MockView4)
|
||||
page: view4
|
||||
}, {
|
||||
page: mockView(MockView5)
|
||||
page: view5
|
||||
}], null, trnsDone);
|
||||
expect(instance1.ionViewWillUnload).toHaveBeenCalled();
|
||||
expect(instance2.ionViewWillUnload).toHaveBeenCalled();
|
||||
@ -982,7 +967,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view5', 'mock-view2', NavDirection.back
|
||||
hasCompleted, requiresTransition, view5, view2, NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView4);
|
||||
@ -1038,9 +1023,6 @@ describe('NavController', () => {
|
||||
function spyOnLifecycles(view: ViewController) {
|
||||
const element = view.element as any;
|
||||
Object.assign(element, {
|
||||
ionViewDidLoad: () => {
|
||||
return;
|
||||
},
|
||||
ionViewWillEnter: () => {
|
||||
return;
|
||||
},
|
||||
@ -1060,7 +1042,6 @@ describe('NavController', () => {
|
||||
|
||||
|
||||
const instance = {
|
||||
ionViewDidLoad: jest.spyOn(element, 'ionViewDidLoad'),
|
||||
ionViewWillEnter: jest.spyOn(element, 'ionViewWillEnter'),
|
||||
ionViewDidEnter: jest.spyOn(element, 'ionViewDidEnter'),
|
||||
ionViewWillLeave: jest.spyOn(element, 'ionViewWillLeave'),
|
||||
@ -1068,7 +1049,6 @@ describe('NavController', () => {
|
||||
ionViewWillUnload: jest.spyOn(element, 'ionViewWillUnload'),
|
||||
};
|
||||
|
||||
element.addEventListener('ionViewDidLoad', element.ionViewDidLoad);
|
||||
element.addEventListener('ionViewWillEnter', element.ionViewWillEnter);
|
||||
element.addEventListener('ionViewDidEnter', element.ionViewDidEnter);
|
||||
element.addEventListener('ionViewWillLeave', element.ionViewWillLeave);
|
||||
@ -1093,17 +1073,23 @@ const MockView4 = 'mock-view4';
|
||||
const MockView5 = 'mock-view5';
|
||||
const dom = mockDocument();
|
||||
|
||||
|
||||
const win = global as any;
|
||||
if (!win.CustomEvent) {
|
||||
win.CustomEvent = function(name: string, params: any) {
|
||||
console.log('"hkljhlkhljkhljk');
|
||||
const event = dom.createEvent('CustomEvent');
|
||||
event.initCustomEvent(name, false, false, params.detail);
|
||||
return event;
|
||||
};
|
||||
}
|
||||
|
||||
function mockView(component ?: any, data ?: any) {
|
||||
if (!component) {
|
||||
component = MockView;
|
||||
}
|
||||
|
||||
const view = new ViewController(component, data);
|
||||
view._lifecycle = function(lifecycle: string) {
|
||||
const event = dom.createEvent('CustomEvent');
|
||||
event.initCustomEvent(`ionView${lifecycle}`, false, false, null);
|
||||
this.element.dispatchEvent(event);
|
||||
};
|
||||
view.element = mockElement(component) as HTMLElement;
|
||||
return view;
|
||||
}
|
||||
@ -1128,7 +1114,7 @@ function mockNavController(): NavControllerBase {
|
||||
? mockElement(enteringView.component) as HTMLElement
|
||||
: enteringView.element = enteringView.component as HTMLElement;
|
||||
}
|
||||
enteringView._state = ViewState.Initialized;
|
||||
enteringView._state = ViewState.Attached;
|
||||
};
|
||||
return nav;
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
import { isPresent } from './nav-util';
|
||||
import { Transition } from './transition';
|
||||
import { NavControllerBase } from './nav';
|
||||
|
||||
export class TransitionController {
|
||||
private _ids = 0;
|
||||
private _trns = new Map<number, Transition>();
|
||||
|
||||
getRootTrnsId(nav: NavControllerBase): number {
|
||||
nav = nav.parent;
|
||||
while (nav) {
|
||||
if (isPresent(nav._trnsId)) {
|
||||
return nav._trnsId;
|
||||
}
|
||||
nav = nav.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
nextId() {
|
||||
return this._ids++;
|
||||
}
|
||||
|
||||
register(trnsId: number, trns: Transition) {
|
||||
trns.trnsId = trnsId;
|
||||
|
||||
const parent = this._trns.get(trnsId);
|
||||
if (!parent) {
|
||||
// we haven't created the root transition yet
|
||||
this._trns.set(trnsId, trns);
|
||||
|
||||
} else {
|
||||
// we already have a root transition created
|
||||
// add this new transition as a child to the root
|
||||
parent.parent = trns;
|
||||
}
|
||||
}
|
||||
|
||||
destroy(trnsId: number) {
|
||||
const trans = this._trns.get(trnsId);
|
||||
if (trans) {
|
||||
trans.destroy();
|
||||
this._trns.delete(trnsId);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +1,183 @@
|
||||
import { ViewController } from './view-controller';
|
||||
import { NavDirection } from './nav-util';
|
||||
import { Animation, AnimationBuilder } from '../..';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*
|
||||
* - play
|
||||
* - Add before classes - DOM WRITE
|
||||
* - Remove before classes - DOM WRITE
|
||||
* - Add before inline styles - DOM WRITE
|
||||
* - set inline FROM styles - DOM WRITE
|
||||
* - RAF
|
||||
* - read toolbar dimensions - DOM READ
|
||||
* - write content top/bottom padding - DOM WRITE
|
||||
* - set css transition duration/easing - DOM WRITE
|
||||
* - RAF
|
||||
* - set inline TO styles - DOM WRITE
|
||||
*/
|
||||
export class Transition {
|
||||
_trnsStart: Function;
|
||||
export function transition(opts: AnimationOptions): Promise<Animation|undefined> {
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
|
||||
trnsId: number;
|
||||
ani: Animation;
|
||||
parent: Transition;
|
||||
setZIndex(enteringEl, leavingEl, opts.direction);
|
||||
showPages(enteringEl, leavingEl);
|
||||
showGoBack(enteringEl, opts.showGoBack);
|
||||
|
||||
constructor(
|
||||
private animationCtrl: HTMLIonAnimationControllerElement,
|
||||
private builder: AnimationBuilder,
|
||||
public enteringView: ViewController,
|
||||
public leavingView: ViewController,
|
||||
private opts: any
|
||||
) {}
|
||||
|
||||
registerStart(trnsStart: Function) {
|
||||
this._trnsStart = trnsStart;
|
||||
// fast path for no animation
|
||||
if (!opts.animationBuilder && !opts.animation) {
|
||||
return noAnimation(opts);
|
||||
}
|
||||
|
||||
init() {
|
||||
return this.animationCtrl.create(this.builder, null, this.opts).then((ani) => {
|
||||
this.ani = ani;
|
||||
// transition path
|
||||
return waitDeepReady(opts)
|
||||
.then(() => fireWillEvents(enteringEl, leavingEl))
|
||||
.then(() => createTransition(opts))
|
||||
.then((transition) => playTransition(transition, opts))
|
||||
.then((transition) => {
|
||||
if (transition.hasCompleted) {
|
||||
fireDidEvents(enteringEl, leavingEl);
|
||||
}
|
||||
return transition;
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
this._trnsStart && this._trnsStart();
|
||||
this._trnsStart = null;
|
||||
|
||||
// bubble up start
|
||||
this.parent && this.parent.start();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ani && this.ani.destroy();
|
||||
this.ani = this._trnsStart = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function notifyViewReady(viewIsReady: undefined | (() => Promise<any>)) {
|
||||
if (viewIsReady) {
|
||||
return viewIsReady();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function noAnimation(opts: AnimationOptions) {
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
|
||||
enteringEl && enteringEl.classList.remove('hide-page');
|
||||
leavingEl && leavingEl.classList.remove('hide-page');
|
||||
|
||||
return waitShallowReady(opts).then(() => {
|
||||
fireWillEvents(enteringEl, leavingEl);
|
||||
fireDidEvents(enteringEl, leavingEl);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function waitDeepReady(opts: AnimationOptions) {
|
||||
return Promise.all([
|
||||
deepReady(opts.enteringEl),
|
||||
deepReady(opts.leavingEl)
|
||||
]).then(() => notifyViewReady(opts.viewIsReady));
|
||||
}
|
||||
|
||||
function waitShallowReady(opts: AnimationOptions) {
|
||||
return Promise.all([
|
||||
shallowReady(opts.enteringEl),
|
||||
shallowReady(opts.leavingEl)
|
||||
]).then(() => notifyViewReady(opts.viewIsReady));
|
||||
}
|
||||
|
||||
function showPages(enteringEl: HTMLElement, leavingEl: HTMLElement) {
|
||||
if (enteringEl) {
|
||||
enteringEl.hidden = false;
|
||||
}
|
||||
if (leavingEl) {
|
||||
leavingEl.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
function showGoBack(enteringEl: HTMLElement, goBack: boolean) {
|
||||
if (enteringEl) {
|
||||
if (goBack) {
|
||||
enteringEl.classList.add('can-go-back');
|
||||
} else {
|
||||
enteringEl.classList.remove('can-go-back');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createTransition(opts: AnimationOptions) {
|
||||
if (opts.animation) {
|
||||
return opts.animation;
|
||||
}
|
||||
return opts.animationCtrl.create(opts.animationBuilder, opts.baseEl, opts);
|
||||
}
|
||||
|
||||
function playTransition(transition: Animation, opts: AnimationOptions): Promise<Animation> {
|
||||
const progressAnimation = opts.progressAnimation;
|
||||
const promise = new Promise<Animation>(resolve => transition.onFinish(resolve));
|
||||
|
||||
// cool, let's do this, start the transition
|
||||
if (progressAnimation) {
|
||||
// this is a swipe to go back, just get the transition progress ready
|
||||
// kick off the swipe animation start
|
||||
transition.progressStart();
|
||||
progressAnimation(transition);
|
||||
|
||||
} else {
|
||||
// only the top level transition should actually start "play"
|
||||
// kick it off and let it play through
|
||||
// ******** DOM WRITE ****************
|
||||
transition.play();
|
||||
}
|
||||
// create a callback for when the animation is done
|
||||
return promise;
|
||||
}
|
||||
|
||||
function fireWillEvents(enteringEl: HTMLElement, leavingEl: HTMLElement) {
|
||||
lifecycle(leavingEl, ViewLifecycle.WillLeave);
|
||||
lifecycle(enteringEl, ViewLifecycle.WillEnter);
|
||||
}
|
||||
|
||||
function fireDidEvents(enteringEl: HTMLElement, leavingEl: HTMLElement) {
|
||||
lifecycle(enteringEl, ViewLifecycle.DidEnter);
|
||||
lifecycle(leavingEl, ViewLifecycle.DidLeave);
|
||||
}
|
||||
|
||||
function setZIndex(enteringEl: HTMLElement, leavingEl: HTMLElement, direction: NavDirection) {
|
||||
if (enteringEl) {
|
||||
enteringEl.style.zIndex = (direction === NavDirection.back)
|
||||
? '99'
|
||||
: '101';
|
||||
}
|
||||
if (leavingEl) {
|
||||
leavingEl.style.zIndex = '100';
|
||||
}
|
||||
}
|
||||
|
||||
export function lifecycle(el: HTMLElement, lifecycle: ViewLifecycle) {
|
||||
if (el) {
|
||||
const event = new CustomEvent(lifecycle, {
|
||||
bubbles: false,
|
||||
cancelable: false
|
||||
});
|
||||
el.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function shallowReady(el: HTMLElement): Promise<any> {
|
||||
if (el && (el as any).componentOnReady) {
|
||||
return (el as any).componentOnReady();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function deepReady(el: HTMLElement): Promise<any> {
|
||||
if (!el) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if ((el as any).componentOnReady) {
|
||||
return (el as any).componentOnReady();
|
||||
} else {
|
||||
return Promise.all(Array.from(el.children).map(deepReady));
|
||||
}
|
||||
}
|
||||
|
||||
export enum ViewLifecycle {
|
||||
WillEnter = 'ionViewWillEnter',
|
||||
DidEnter = 'ionViewDidEnter',
|
||||
WillLeave = 'ionViewWillLeave',
|
||||
DidLeave = 'ionViewDidLeave',
|
||||
WillUnload = 'ionViewWillUnload'
|
||||
}
|
||||
|
||||
export interface AnimationOptions {
|
||||
animationCtrl: HTMLIonAnimationControllerElement;
|
||||
animationBuilder: AnimationBuilder;
|
||||
animation: Animation|undefined;
|
||||
direction: NavDirection;
|
||||
duration: number|undefined;
|
||||
easing: string|undefined;
|
||||
isRTL: boolean;
|
||||
showGoBack: boolean;
|
||||
viewIsReady: undefined | (() => Promise<any>);
|
||||
progressAnimation?: Function;
|
||||
enteringEl: HTMLElement;
|
||||
leavingEl: HTMLElement;
|
||||
baseEl: HTMLElement;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
import { NavOptions, ViewState } from './nav-util';
|
||||
import { NavControllerBase } from './nav';
|
||||
import { assert } from '../../utils/helpers';
|
||||
import { FrameworkDelegate } from '../..';
|
||||
|
||||
/**
|
||||
* @name ViewController
|
||||
@ -23,45 +24,48 @@ import { assert } from '../../utils/helpers';
|
||||
export class ViewController {
|
||||
|
||||
private _cntDir: any;
|
||||
private _isHidden = false;
|
||||
private _leavingOpts: NavOptions;
|
||||
private _detached: boolean;
|
||||
|
||||
_nav: NavControllerBase;
|
||||
_zIndex: number;
|
||||
_state: ViewState = ViewState.New;
|
||||
|
||||
/** @hidden */
|
||||
id: string;
|
||||
|
||||
/** @hidden */
|
||||
isOverlay = false;
|
||||
|
||||
element: HTMLElement;
|
||||
/** @hidden */
|
||||
// @Output() private _emitter: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
public component: any,
|
||||
public data?: any
|
||||
public data: any,
|
||||
private delegate?: FrameworkDelegate,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
init() {
|
||||
if (this.element) {
|
||||
return;
|
||||
}
|
||||
init(container: HTMLElement) {
|
||||
this._state = ViewState.Attached;
|
||||
|
||||
const component = this.component;
|
||||
this.element = (typeof component === 'string')
|
||||
if (this.delegate) {
|
||||
return this.delegate.attachViewToDom(container, component, this.data, ['ion-page']).then(el => {
|
||||
this.element = el;
|
||||
});
|
||||
}
|
||||
const element = (this.element)
|
||||
? this.element
|
||||
: typeof component === 'string'
|
||||
? document.createElement(component)
|
||||
: component;
|
||||
|
||||
this.element.classList.add('ion-page');
|
||||
element.classList.add('ion-page');
|
||||
element.classList.add('hide-page');
|
||||
|
||||
if (this.data) {
|
||||
Object.assign(this.element, this.data);
|
||||
Object.assign(element, this.data);
|
||||
}
|
||||
container.appendChild(element);
|
||||
this.element = element;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
_setNav(navCtrl: NavControllerBase) {
|
||||
@ -89,164 +93,6 @@ export class ViewController {
|
||||
this._leavingOpts = opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if you can go back in the navigation stack.
|
||||
* @returns {boolean} Returns if it's possible to go back from this Page.
|
||||
*/
|
||||
enableBack(): boolean {
|
||||
// update if it's possible to go back from this nav item
|
||||
if (!this._nav) {
|
||||
return false;
|
||||
}
|
||||
// the previous view may exist, but if it's about to be destroyed
|
||||
// it shouldn't be able to go back to
|
||||
const previousItem = this._nav.getPrevious(this);
|
||||
return !!(previousItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
get name(): string {
|
||||
const component = this.component;
|
||||
if (typeof component === 'string') {
|
||||
return component;
|
||||
}
|
||||
if (component.tagName) {
|
||||
return component.tagName;
|
||||
}
|
||||
return this.element ? this.element.tagName : 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* DOM WRITE
|
||||
*/
|
||||
_domShow(shouldShow: boolean) {
|
||||
// using hidden element attribute to display:none and not render views
|
||||
// doing checks to make sure we only update the DOM when actually needed
|
||||
// if it should render, then the hidden attribute should not be on the element
|
||||
if (this.element && shouldShow === this._isHidden) {
|
||||
this._isHidden = !shouldShow;
|
||||
|
||||
// ******** DOM WRITE ****************
|
||||
if (shouldShow) {
|
||||
this.element.removeAttribute('hidden');
|
||||
} else {
|
||||
this.element.setAttribute('hidden', '');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getZIndex(): number {
|
||||
return this._zIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* DOM WRITE
|
||||
*/
|
||||
_setZIndex(zIndex: number) {
|
||||
if (zIndex !== this._zIndex) {
|
||||
this._zIndex = zIndex;
|
||||
const pageEl = this.element;
|
||||
if (pageEl) {
|
||||
const el = pageEl as HTMLElement;
|
||||
// ******** DOM WRITE ****************
|
||||
el.style.zIndex = zIndex + '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view has loaded. This event only happens once per view will be created.
|
||||
* This event is fired before the component and his children have been initialized.
|
||||
*/
|
||||
_willLoad() {
|
||||
assert(this._state === ViewState.Initialized, 'view state must be INITIALIZED');
|
||||
this._lifecycle('WillLoad');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view has loaded. This event only happens once per view being
|
||||
* created. If a view leaves but is cached, then this will not
|
||||
* fire again on a subsequent viewing. This method is a good place
|
||||
* to put your setup code for the view; however, it is not the
|
||||
* recommended method to use when a view becomes active.
|
||||
*/
|
||||
_didLoad() {
|
||||
assert(this._state === ViewState.Attached, 'view state must be ATTACHED');
|
||||
this._lifecycle('DidLoad');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view is about to enter and become the active view.
|
||||
*/
|
||||
_willEnter() {
|
||||
assert(this._state === ViewState.Attached, 'view state must be ATTACHED');
|
||||
|
||||
if (this._detached) {
|
||||
// ensure this has been re-attached to the change detector
|
||||
// TODO
|
||||
// this._cmp.changeDetectorRef.reattach();
|
||||
this._detached = false;
|
||||
}
|
||||
|
||||
// this.willEnter.emit(null);
|
||||
this._lifecycle('WillEnter');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view has fully entered and is now the active view. This
|
||||
* will fire, whether it was the first load or loaded from the cache.
|
||||
*/
|
||||
_didEnter() {
|
||||
assert(this._state === ViewState.Attached, 'view state must be ATTACHED');
|
||||
|
||||
this._lifecycle('DidEnter');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view is about to leave and no longer be the active view.
|
||||
*/
|
||||
_willLeave(_willUnload: boolean) {
|
||||
this._lifecycle('WillLeave');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* The view has finished leaving and is no longer the active view. This
|
||||
* will fire, whether it is cached or unloaded.
|
||||
*/
|
||||
_didLeave() {
|
||||
this._lifecycle('DidLeave');
|
||||
|
||||
// when this is not the active page
|
||||
// we no longer need to detect changes
|
||||
if (!this._detached) {
|
||||
// TODO
|
||||
// this._cmp.changeDetectorRef.detach();
|
||||
this._detached = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_willUnload() {
|
||||
this._lifecycle('WillUnload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* DOM WRITE
|
||||
@ -270,39 +116,8 @@ export class ViewController {
|
||||
get index(): number {
|
||||
return (this._nav ? this._nav.indexOf(this) : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_lifecycleTest(_lifecycle: string): Promise<any> {
|
||||
// const instance = this.instance;
|
||||
// const methodName = 'ionViewCan' + lifecycle;
|
||||
// if (instance && instance[methodName]) {
|
||||
// try {
|
||||
// const result = instance[methodName]();
|
||||
// if (result instanceof Promise) {
|
||||
// return result;
|
||||
// } else {
|
||||
// // Any value but explitic false, should be true
|
||||
// return Promise.resolve(result !== false);
|
||||
// }
|
||||
|
||||
// } catch (e) {
|
||||
// return Promise.reject(`${this.name} ${methodName} error: ${e.message}`);
|
||||
// }
|
||||
// }
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
_lifecycle(lifecycle: string) {
|
||||
const event = new CustomEvent(`ionView${lifecycle}`, {
|
||||
bubbles: false,
|
||||
cancelable: false
|
||||
});
|
||||
this.element.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
export function isViewController(viewCtrl: any): viewCtrl is ViewController {
|
||||
return !!(viewCtrl && (<ViewController>viewCtrl)._didLoad && (<ViewController>viewCtrl)._willUnload);
|
||||
return viewCtrl instanceof ViewController;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ export function writeNavState(root: HTMLElement, chain: RouteChain|null, index:
|
||||
}
|
||||
return node.componentOnReady()
|
||||
.then(() => node.setRouteId(route.id, route.params, direction))
|
||||
.catch(() => ({changed: false, markVisible: undefined}))
|
||||
.then(result => {
|
||||
if (result.changed) {
|
||||
direction = 0;
|
||||
|
1
core/src/index.d.ts
vendored
1
core/src/index.d.ts
vendored
@ -8,7 +8,6 @@ export {
|
||||
Animation,
|
||||
AnimationBuilder,
|
||||
AnimationController,
|
||||
AnimationOptions
|
||||
} from './components/animation-controller/animation-interface';
|
||||
export { App } from './components/app/app';
|
||||
export { Avatar } from './components/avatar/avatar';
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
export interface FrameworkDelegate {
|
||||
attachViewToDom(container: any, component: any, propsOrDataObj?: any, cssClasses?: string[]): Promise<any>;
|
||||
attachViewToDom(container: any, component: any, propsOrDataObj?: any, cssClasses?: string[]): Promise<HTMLElement>;
|
||||
removeViewFromDom(container: any, component: any): Promise<void>;
|
||||
}
|
||||
|
Reference in New Issue
Block a user