123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960 |
- /*!
- * (C) Ionic http://ionicframework.com - MIT License
- */
- import { proxyCustomElement, HTMLElement, createEvent, h, Build } from '@stencil/core/internal/client';
- import { g as getTimeGivenProgression } from './cubic-bezier.js';
- import { n as assert, s as shallowEqualStringMap } from './helpers.js';
- import { a as printIonWarning, c as config } from './index4.js';
- import { l as lifecycle, t as transition, s as setPageHidden, d as LIFECYCLE_WILL_UNLOAD, b as LIFECYCLE_WILL_LEAVE, c as LIFECYCLE_DID_LEAVE } from './index2.js';
- import { b as getIonMode } from './ionic-global.js';
- import { a as attachComponent } from './framework-delegate.js';
- const VIEW_STATE_NEW = 1;
- const VIEW_STATE_ATTACHED = 2;
- const VIEW_STATE_DESTROYED = 3;
- // TODO(FW-2832): types
- class ViewController {
- constructor(component, params) {
- this.component = component;
- this.params = params;
- this.state = VIEW_STATE_NEW;
- }
- async init(container) {
- this.state = VIEW_STATE_ATTACHED;
- if (!this.element) {
- const component = this.component;
- this.element = await attachComponent(this.delegate, container, component, ['ion-page', 'ion-page-invisible'], this.params);
- }
- }
- /**
- * DOM WRITE
- */
- _destroy() {
- assert(this.state !== VIEW_STATE_DESTROYED, 'view state must be ATTACHED');
- const element = this.element;
- if (element) {
- if (this.delegate) {
- this.delegate.removeViewFromDom(element.parentElement, element);
- }
- else {
- element.remove();
- }
- }
- this.nav = undefined;
- this.state = VIEW_STATE_DESTROYED;
- }
- }
- const matches = (view, id, params) => {
- if (!view) {
- return false;
- }
- if (view.component !== id) {
- return false;
- }
- return shallowEqualStringMap(view.params, params);
- };
- const convertToView = (page, params) => {
- if (!page) {
- return null;
- }
- if (page instanceof ViewController) {
- return page;
- }
- return new ViewController(page, params);
- };
- const convertToViews = (pages) => {
- return pages
- .map((page) => {
- if (page instanceof ViewController) {
- return page;
- }
- if ('component' in page) {
- return convertToView(page.component, page.componentProps === null ? undefined : page.componentProps);
- }
- return convertToView(page, undefined);
- })
- .filter((v) => v !== null);
- };
- const navCss = ":host{left:0;right:0;top:0;bottom:0;position:absolute;contain:layout size style;z-index:0}";
- const IonNavStyle0 = navCss;
- const Nav = /*@__PURE__*/ proxyCustomElement(class Nav extends HTMLElement {
- constructor() {
- super();
- this.__registerHost();
- this.__attachShadow();
- this.ionNavWillLoad = createEvent(this, "ionNavWillLoad", 7);
- this.ionNavWillChange = createEvent(this, "ionNavWillChange", 3);
- this.ionNavDidChange = createEvent(this, "ionNavDidChange", 3);
- this.transInstr = [];
- this.gestureOrAnimationInProgress = false;
- this.useRouter = false;
- this.isTransitioning = false;
- this.destroyed = false;
- this.views = [];
- this.didLoad = false;
- this.delegate = undefined;
- this.swipeGesture = undefined;
- this.animated = true;
- this.animation = undefined;
- this.rootParams = undefined;
- this.root = undefined;
- }
- swipeGestureChanged() {
- if (this.gesture) {
- this.gesture.enable(this.swipeGesture === true);
- }
- }
- rootChanged() {
- const isDev = Build.isDev;
- if (this.root === undefined) {
- return;
- }
- if (this.didLoad === false) {
- /**
- * If the component has not loaded yet, we can skip setting up the root component.
- * It will be called when `componentDidLoad` fires.
- */
- return;
- }
- if (!this.useRouter) {
- if (this.root !== undefined) {
- this.setRoot(this.root, this.rootParams);
- }
- }
- else if (isDev) {
- printIonWarning('[ion-nav] - A root attribute is not supported when using ion-router.', this.el);
- }
- }
- componentWillLoad() {
- this.useRouter = document.querySelector('ion-router') !== null && this.el.closest('[no-router]') === null;
- if (this.swipeGesture === undefined) {
- const mode = getIonMode(this);
- this.swipeGesture = config.getBoolean('swipeBackEnabled', mode === 'ios');
- }
- this.ionNavWillLoad.emit();
- }
- async componentDidLoad() {
- // We want to set this flag before any watch callbacks are manually called
- this.didLoad = true;
- this.rootChanged();
- this.gesture = (await import('./swipe-back.js')).createSwipeBackGesture(this.el, this.canStart.bind(this), this.onStart.bind(this), this.onMove.bind(this), this.onEnd.bind(this));
- this.swipeGestureChanged();
- }
- connectedCallback() {
- this.destroyed = false;
- }
- disconnectedCallback() {
- for (const view of this.views) {
- lifecycle(view.element, LIFECYCLE_WILL_UNLOAD);
- view._destroy();
- }
- // Release swipe back gesture and transition.
- if (this.gesture) {
- this.gesture.destroy();
- this.gesture = undefined;
- }
- this.transInstr.length = 0;
- this.views.length = 0;
- this.destroyed = true;
- }
- /**
- * Push a new component onto the current navigation stack. Pass any additional
- * information along as an object. This additional information is accessible
- * through NavParams.
- *
- * @param component The component to push onto the navigation stack.
- * @param componentProps Any properties of the component.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- push(component, componentProps, opts, done) {
- return this.insert(-1, component, componentProps, opts, done);
- }
- /**
- * Inserts a component into the navigation stack at the specified index.
- * This is useful to add a component at any point in the navigation stack.
- *
- * @param insertIndex The index to insert the component at in the stack.
- * @param component The component to insert into the navigation stack.
- * @param componentProps Any properties of the component.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- insert(insertIndex, component, componentProps, opts, done) {
- return this.insertPages(insertIndex, [{ component, componentProps }], opts, done);
- }
- /**
- * Inserts an array of components into the navigation stack at the specified index.
- * The last component in the array will become instantiated as a view, and animate
- * in to become the active view.
- *
- * @param insertIndex The index to insert the components at in the stack.
- * @param insertComponents The components to insert into the navigation stack.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- insertPages(insertIndex, insertComponents, opts, done) {
- return this.queueTrns({
- insertStart: insertIndex,
- insertViews: insertComponents,
- opts,
- }, done);
- }
- /**
- * Pop a component off of the navigation stack. Navigates back from the current
- * component.
- *
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- pop(opts, done) {
- return this.removeIndex(-1, 1, opts, done);
- }
- /**
- * Pop to a specific index in the navigation stack.
- *
- * @param indexOrViewCtrl The index or view controller to pop to.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- popTo(indexOrViewCtrl, opts, done) {
- const ti = {
- removeStart: -1,
- removeCount: -1,
- opts,
- };
- if (typeof indexOrViewCtrl === 'object' && indexOrViewCtrl.component) {
- ti.removeView = indexOrViewCtrl;
- ti.removeStart = 1;
- }
- else if (typeof indexOrViewCtrl === 'number') {
- ti.removeStart = indexOrViewCtrl + 1;
- }
- return this.queueTrns(ti, done);
- }
- /**
- * Navigate back to the root of the stack, no matter how far back that is.
- *
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- popToRoot(opts, done) {
- return this.removeIndex(1, -1, opts, done);
- }
- /**
- * Removes a component from the navigation stack at the specified index.
- *
- * @param startIndex The number to begin removal at.
- * @param removeCount The number of components to remove.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- removeIndex(startIndex, removeCount = 1, opts, done) {
- return this.queueTrns({
- removeStart: startIndex,
- removeCount,
- opts,
- }, done);
- }
- /**
- * Set the root for the current navigation stack to a component.
- *
- * @param component The component to set as the root of the navigation stack.
- * @param componentProps Any properties of the component.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- setRoot(component, componentProps, opts, done) {
- return this.setPages([{ component, componentProps }], opts, done);
- }
- /**
- * Set the views of the current navigation stack and navigate to the last view.
- * By default animations are disabled, but they can be enabled by passing options
- * to the navigation controller. Navigation parameters can also be passed to the
- * individual pages in the array.
- *
- * @param views The list of views to set as the navigation stack.
- * @param opts The navigation options.
- * @param done The transition complete function.
- */
- setPages(views, opts, done) {
- opts !== null && opts !== void 0 ? opts : (opts = {});
- // if animation wasn't set to true then default it to NOT animate
- if (opts.animated !== true) {
- opts.animated = false;
- }
- return this.queueTrns({
- insertStart: 0,
- insertViews: views,
- removeStart: 0,
- removeCount: -1,
- opts,
- }, done);
- }
- /**
- * Called by the router to update the view.
- *
- * @param id The component tag.
- * @param params The component params.
- * @param direction A direction hint.
- * @param animation an AnimationBuilder.
- *
- * @return the status.
- * @internal
- */
- setRouteId(id, params, direction, animation) {
- const active = this.getActiveSync();
- if (matches(active, id, params)) {
- return Promise.resolve({
- changed: false,
- element: active.element,
- });
- }
- let resolve;
- const promise = new Promise((r) => (resolve = r));
- let finish;
- const commonOpts = {
- updateURL: false,
- viewIsReady: (enteringEl) => {
- let mark;
- const p = new Promise((r) => (mark = r));
- resolve({
- changed: true,
- element: enteringEl,
- markVisible: async () => {
- mark();
- await finish;
- },
- });
- return p;
- },
- };
- if (direction === 'root') {
- finish = this.setRoot(id, params, commonOpts);
- }
- else {
- // Look for a view matching the target in the view stack.
- const viewController = this.views.find((v) => matches(v, id, params));
- if (viewController) {
- finish = this.popTo(viewController, Object.assign(Object.assign({}, commonOpts), { direction: 'back', animationBuilder: animation }));
- }
- else if (direction === 'forward') {
- finish = this.push(id, params, Object.assign(Object.assign({}, commonOpts), { animationBuilder: animation }));
- }
- else if (direction === 'back') {
- finish = this.setRoot(id, params, Object.assign(Object.assign({}, commonOpts), { direction: 'back', animated: true, animationBuilder: animation }));
- }
- }
- return promise;
- }
- /**
- * Called by <ion-router> to retrieve the current component.
- *
- * @internal
- */
- async getRouteId() {
- const active = this.getActiveSync();
- if (active) {
- return {
- id: active.element.tagName,
- params: active.params,
- element: active.element,
- };
- }
- return undefined;
- }
- /**
- * Get the active view.
- */
- async getActive() {
- return this.getActiveSync();
- }
- /**
- * Get the view at the specified index.
- *
- * @param index The index of the view.
- */
- async getByIndex(index) {
- return this.views[index];
- }
- /**
- * Returns `true` if the current view can go back.
- *
- * @param view The view to check.
- */
- async canGoBack(view) {
- return this.canGoBackSync(view);
- }
- /**
- * Get the previous view.
- *
- * @param view The view to get.
- */
- async getPrevious(view) {
- return this.getPreviousSync(view);
- }
- /**
- * Returns the number of views in the stack.
- */
- async getLength() {
- return Promise.resolve(this.views.length);
- }
- getActiveSync() {
- return this.views[this.views.length - 1];
- }
- canGoBackSync(view = this.getActiveSync()) {
- return !!(view && this.getPreviousSync(view));
- }
- getPreviousSync(view = this.getActiveSync()) {
- if (!view) {
- return undefined;
- }
- const views = this.views;
- const index = views.indexOf(view);
- return index > 0 ? views[index - 1] : undefined;
- }
- /**
- * Adds a navigation stack change to the queue and schedules it to run.
- *
- * @returns Whether the transition succeeds.
- */
- async queueTrns(ti, done) {
- var _a, _b;
- if (this.isTransitioning && ((_a = ti.opts) === null || _a === void 0 ? void 0 : _a.skipIfBusy)) {
- return false;
- }
- const promise = new Promise((resolve, reject) => {
- ti.resolve = resolve;
- ti.reject = reject;
- });
- ti.done = done;
- /**
- * If using router, check to see if navigation hooks
- * will allow us to perform this transition. This
- * is required in order for hooks to work with
- * the ion-back-button or swipe to go back.
- */
- if (ti.opts && ti.opts.updateURL !== false && this.useRouter) {
- const router = document.querySelector('ion-router');
- if (router) {
- const canTransition = await router.canTransition();
- if (canTransition === false) {
- return false;
- }
- if (typeof canTransition === 'string') {
- router.push(canTransition, ti.opts.direction || 'back');
- return false;
- }
- }
- }
- // Normalize empty
- if (((_b = ti.insertViews) === null || _b === void 0 ? void 0 : _b.length) === 0) {
- ti.insertViews = undefined;
- }
- // Enqueue transition instruction
- this.transInstr.push(ti);
- // if there isn't a transition already happening
- // then this will kick off this transition
- this.nextTrns();
- return promise;
- }
- success(result, ti) {
- if (this.destroyed) {
- this.fireError('nav controller was destroyed', ti);
- return;
- }
- if (ti.done) {
- ti.done(result.hasCompleted, result.requiresTransition, result.enteringView, result.leavingView, result.direction);
- }
- ti.resolve(result.hasCompleted);
- if (ti.opts.updateURL !== false && this.useRouter) {
- const router = document.querySelector('ion-router');
- if (router) {
- const direction = result.direction === 'back' ? 'back' : 'forward';
- router.navChanged(direction);
- }
- }
- }
- failed(rejectReason, ti) {
- if (this.destroyed) {
- this.fireError('nav controller was destroyed', ti);
- return;
- }
- this.transInstr.length = 0;
- this.fireError(rejectReason, ti);
- }
- fireError(rejectReason, ti) {
- if (ti.done) {
- ti.done(false, false, rejectReason);
- }
- if (ti.reject && !this.destroyed) {
- ti.reject(rejectReason);
- }
- else {
- ti.resolve(false);
- }
- }
- /**
- * Consumes the next transition in the queue.
- *
- * @returns whether the transition is executed.
- */
- nextTrns() {
- // this is the framework's bread 'n butta function
- // only one transition is allowed at any given time
- if (this.isTransitioning) {
- return false;
- }
- // there is no transition happening right now, executes the next instructions.
- const ti = this.transInstr.shift();
- if (!ti) {
- return false;
- }
- this.runTransition(ti);
- return true;
- }
- /** Executes all the transition instruction from the queue. */
- async runTransition(ti) {
- try {
- // set that this nav is actively transitioning
- this.ionNavWillChange.emit();
- this.isTransitioning = true;
- this.prepareTI(ti);
- const leavingView = this.getActiveSync();
- const enteringView = this.getEnteringView(ti, leavingView);
- if (!leavingView && !enteringView) {
- throw new Error('no views in the stack to be removed');
- }
- if (enteringView && enteringView.state === VIEW_STATE_NEW) {
- await enteringView.init(this.el);
- }
- this.postViewInit(enteringView, leavingView, ti);
- // Needs transition?
- const requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
- if (requiresTransition && ti.opts && leavingView) {
- const isBackDirection = ti.opts.direction === 'back';
- /**
- * If heading back, use the entering page's animation
- * unless otherwise specified by the developer.
- */
- if (isBackDirection) {
- ti.opts.animationBuilder = ti.opts.animationBuilder || (enteringView === null || enteringView === void 0 ? void 0 : enteringView.animationBuilder);
- }
- leavingView.animationBuilder = ti.opts.animationBuilder;
- }
- let result;
- if (requiresTransition) {
- result = await this.transition(enteringView, leavingView, ti);
- }
- else {
- // transition is not required, so we are already done!
- // they're inserting/removing the views somewhere in the middle or
- // beginning, so visually nothing needs to animate/transition
- // resolve immediately because there's no animation that's happening
- result = {
- hasCompleted: true,
- requiresTransition: false,
- };
- }
- this.success(result, ti);
- this.ionNavDidChange.emit();
- }
- catch (rejectReason) {
- this.failed(rejectReason, ti);
- }
- this.isTransitioning = false;
- this.nextTrns();
- }
- prepareTI(ti) {
- var _a, _b;
- var _c;
- const viewsLength = this.views.length;
- (_a = ti.opts) !== null && _a !== void 0 ? _a : (ti.opts = {});
- (_b = (_c = ti.opts).delegate) !== null && _b !== void 0 ? _b : (_c.delegate = this.delegate);
- if (ti.removeView !== undefined) {
- assert(ti.removeStart !== undefined, 'removeView needs removeStart');
- assert(ti.removeCount !== undefined, 'removeView needs removeCount');
- const index = this.views.indexOf(ti.removeView);
- if (index < 0) {
- throw new Error('removeView was not found');
- }
- ti.removeStart += index;
- }
- if (ti.removeStart !== undefined) {
- if (ti.removeStart < 0) {
- ti.removeStart = viewsLength - 1;
- }
- if (ti.removeCount < 0) {
- ti.removeCount = viewsLength - ti.removeStart;
- }
- ti.leavingRequiresTransition = ti.removeCount > 0 && ti.removeStart + ti.removeCount === viewsLength;
- }
- if (ti.insertViews) {
- // allow -1 to be passed in to auto push it on the end
- // and clean up the index if it's larger then the size of the stack
- if (ti.insertStart < 0 || ti.insertStart > viewsLength) {
- ti.insertStart = viewsLength;
- }
- ti.enteringRequiresTransition = ti.insertStart === viewsLength;
- }
- const insertViews = ti.insertViews;
- if (!insertViews) {
- return;
- }
- assert(insertViews.length > 0, 'length can not be zero');
- const viewControllers = convertToViews(insertViews);
- if (viewControllers.length === 0) {
- throw new Error('invalid views to insert');
- }
- // Check all the inserted view are correct
- for (const view of viewControllers) {
- view.delegate = ti.opts.delegate;
- const nav = view.nav;
- if (nav && nav !== this) {
- throw new Error('inserted view was already inserted');
- }
- if (view.state === VIEW_STATE_DESTROYED) {
- throw new Error('inserted view was already destroyed');
- }
- }
- ti.insertViews = viewControllers;
- }
- /**
- * Returns the view that will be entered considering the transition instructions.
- *
- * @param ti The instructions.
- * @param leavingView The view being left or undefined if none.
- *
- * @returns The view that will be entered, undefined if none.
- */
- getEnteringView(ti, leavingView) {
- // The last inserted view will be entered when view are inserted.
- const insertViews = ti.insertViews;
- if (insertViews !== undefined) {
- return insertViews[insertViews.length - 1];
- }
- // When views are deleted, we will enter the last view that is not removed and not the view being left.
- const removeStart = ti.removeStart;
- if (removeStart !== undefined) {
- const views = this.views;
- const removeEnd = removeStart + ti.removeCount;
- for (let i = views.length - 1; i >= 0; i--) {
- const view = views[i];
- if ((i < removeStart || i >= removeEnd) && view !== leavingView) {
- return view;
- }
- }
- }
- return undefined;
- }
- /**
- * Adds and Removes the views from the navigation stack.
- *
- * @param enteringView The view being entered.
- * @param leavingView The view being left.
- * @param ti The instructions.
- */
- postViewInit(enteringView, leavingView, ti) {
- var _a, _b, _c;
- assert(leavingView || enteringView, 'Both leavingView and enteringView are null');
- assert(ti.resolve, 'resolve must be valid');
- assert(ti.reject, 'reject must be valid');
- // Compute the views to remove.
- const opts = ti.opts;
- const { insertViews, removeStart, removeCount } = ti;
- /** Records the view to destroy */
- let destroyQueue;
- // there are views to remove
- if (removeStart !== undefined && removeCount !== undefined) {
- assert(removeStart >= 0, 'removeStart can not be negative');
- assert(removeCount >= 0, 'removeCount can not be negative');
- destroyQueue = [];
- for (let i = removeStart; i < removeStart + removeCount; i++) {
- const view = this.views[i];
- if (view !== undefined && view !== enteringView && view !== leavingView) {
- destroyQueue.push(view);
- }
- }
- // default the direction to "back"
- (_a = opts.direction) !== null && _a !== void 0 ? _a : (opts.direction = 'back');
- }
- const finalNumViews = this.views.length + ((_b = insertViews === null || insertViews === void 0 ? void 0 : insertViews.length) !== null && _b !== void 0 ? _b : 0) - (removeCount !== null && removeCount !== void 0 ? removeCount : 0);
- assert(finalNumViews >= 0, 'final balance can not be negative');
- if (finalNumViews === 0) {
- printIonWarning(`[ion-nav] - You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, this, this.el);
- throw new Error('navigation stack needs at least one root page');
- }
- // At this point the transition can not be rejected, any throw should be an error
- // Insert the new views in the stack.
- if (insertViews) {
- // add the views to the
- let insertIndex = ti.insertStart;
- for (const view of insertViews) {
- this.insertViewAt(view, insertIndex);
- insertIndex++;
- }
- if (ti.enteringRequiresTransition) {
- // default to forward if not already set
- (_c = opts.direction) !== null && _c !== void 0 ? _c : (opts.direction = 'forward');
- }
- }
- // if the views to be removed are in the beginning or middle
- // and there is not a view that needs to visually transition out
- // then just destroy them and don't transition anything
- // batch all of lifecycles together
- // let's make sure, callbacks are zoned
- if (destroyQueue && destroyQueue.length > 0) {
- for (const view of destroyQueue) {
- lifecycle(view.element, LIFECYCLE_WILL_LEAVE);
- lifecycle(view.element, LIFECYCLE_DID_LEAVE);
- lifecycle(view.element, LIFECYCLE_WILL_UNLOAD);
- }
- // once all lifecycle events has been delivered, we can safely detroy the views
- for (const view of destroyQueue) {
- this.destroyView(view);
- }
- }
- }
- async transition(enteringView, leavingView, ti) {
- // 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 opts = ti.opts;
- const progressCallback = opts.progressAnimation
- ? (ani) => {
- /**
- * Because this progress callback is called asynchronously
- * it is possible for the gesture to start and end before
- * the animation is ever set. In that scenario, we should
- * immediately call progressEnd so that the transition promise
- * resolves and the gesture does not get locked up.
- */
- if (ani !== undefined && !this.gestureOrAnimationInProgress) {
- this.gestureOrAnimationInProgress = true;
- ani.onFinish(() => {
- this.gestureOrAnimationInProgress = false;
- }, { oneTimeCallback: true });
- /**
- * Playing animation to beginning
- * with a duration of 0 prevents
- * any flickering when the animation
- * is later cleaned up.
- */
- ani.progressEnd(0, 0, 0);
- }
- else {
- this.sbAni = ani;
- }
- }
- : undefined;
- const mode = getIonMode(this);
- const enteringEl = enteringView.element;
- // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
- const leavingEl = leavingView && leavingView.element;
- const animationOpts = Object.assign(Object.assign({ mode, showGoBack: this.canGoBackSync(enteringView), baseEl: this.el, progressCallback, animated: this.animated && config.getBoolean('animated', true), enteringEl,
- leavingEl }, opts), { animationBuilder: opts.animationBuilder || this.animation || config.get('navAnimation') });
- const { hasCompleted } = await transition(animationOpts);
- return this.transitionFinish(hasCompleted, enteringView, leavingView, opts);
- }
- transitionFinish(hasCompleted, enteringView, leavingView, opts) {
- /**
- * If the transition did not complete, the leavingView will still be the active
- * view on the stack. Otherwise unmount all the views after the enteringView.
- */
- const activeView = hasCompleted ? enteringView : leavingView;
- if (activeView) {
- this.unmountInactiveViews(activeView);
- }
- return {
- hasCompleted,
- requiresTransition: true,
- enteringView,
- leavingView,
- direction: opts.direction,
- };
- }
- /**
- * Inserts a view at the specified index.
- *
- * When the view already is in the stack it will be moved to the new position.
- *
- * @param view The view to insert.
- * @param index The index where to insert the view.
- */
- insertViewAt(view, index) {
- const views = this.views;
- const existingIndex = views.indexOf(view);
- if (existingIndex > -1) {
- assert(view.nav === this, 'view is not part of the nav');
- // The view already in the stack, removes it.
- views.splice(existingIndex, 1);
- // and add it back at the requested index.
- views.splice(index, 0, view);
- }
- else {
- assert(!view.nav, 'nav is used');
- // this is a new view to add to the stack
- // create the new entering view
- view.nav = this;
- views.splice(index, 0, view);
- }
- }
- /**
- * Removes a view from the stack.
- *
- * @param view The view to remove.
- */
- removeView(view) {
- assert(view.state === VIEW_STATE_ATTACHED || view.state === VIEW_STATE_DESTROYED, 'view state should be loaded or destroyed');
- const views = this.views;
- const index = views.indexOf(view);
- assert(index > -1, 'view must be part of the stack');
- if (index >= 0) {
- views.splice(index, 1);
- }
- }
- destroyView(view) {
- view._destroy();
- this.removeView(view);
- }
- /**
- * Unmounts all inactive views after the specified active view.
- *
- * DOM WRITE
- *
- * @param activeView The view that is actively visible in the stack. Used to calculate which views to unmount.
- */
- unmountInactiveViews(activeView) {
- // ok, cleanup time!! Destroy all of the views that are
- // INACTIVE and come after the active view
- // only do this if the views exist, though
- if (this.destroyed) {
- return;
- }
- const views = this.views;
- const activeViewIndex = views.indexOf(activeView);
- for (let i = views.length - 1; i >= 0; i--) {
- const view = views[i];
- /**
- * When inserting multiple views via insertPages
- * the last page will be transitioned to, but the
- * others will not be. As a result, a DOM element
- * will only be created for the last page inserted.
- * As a result, it is possible to have views in the
- * stack that do not have `view.element` yet.
- */
- const element = view.element;
- if (element) {
- if (i > activeViewIndex) {
- // this view comes after the active view
- // let's unload it
- lifecycle(element, LIFECYCLE_WILL_UNLOAD);
- 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
- setPageHidden(element, true);
- }
- }
- }
- }
- canStart() {
- return (!this.gestureOrAnimationInProgress &&
- !!this.swipeGesture &&
- !this.isTransitioning &&
- this.transInstr.length === 0 &&
- this.canGoBackSync());
- }
- onStart() {
- this.gestureOrAnimationInProgress = true;
- this.pop({ direction: 'back', progressAnimation: true });
- }
- onMove(stepValue) {
- if (this.sbAni) {
- this.sbAni.progressStep(stepValue);
- }
- }
- onEnd(shouldComplete, stepValue, dur) {
- if (this.sbAni) {
- this.sbAni.onFinish(() => {
- this.gestureOrAnimationInProgress = false;
- }, { oneTimeCallback: true });
- // Account for rounding errors in JS
- let newStepValue = shouldComplete ? -0.001 : 0.001;
- /**
- * Animation will be reversed here, so need to
- * reverse the easing curve as well
- *
- * Additionally, we need to account for the time relative
- * to the new easing curve, as `stepValue` is going to be given
- * in terms of a linear curve.
- */
- if (!shouldComplete) {
- this.sbAni.easing('cubic-bezier(1, 0, 0.68, 0.28)');
- newStepValue += getTimeGivenProgression([0, 0], [1, 0], [0.68, 0.28], [1, 1], stepValue)[0];
- }
- else {
- newStepValue += getTimeGivenProgression([0, 0], [0.32, 0.72], [0, 1], [1, 1], stepValue)[0];
- }
- this.sbAni.progressEnd(shouldComplete ? 1 : 0, newStepValue, dur);
- }
- else {
- this.gestureOrAnimationInProgress = false;
- }
- }
- render() {
- return h("slot", { key: '188d0abd6c047d235380f07aac81223b757010e8' });
- }
- get el() { return this; }
- static get watchers() { return {
- "swipeGesture": ["swipeGestureChanged"],
- "root": ["rootChanged"]
- }; }
- static get style() { return IonNavStyle0; }
- }, [1, "ion-nav", {
- "delegate": [16],
- "swipeGesture": [1028, "swipe-gesture"],
- "animated": [4],
- "animation": [16],
- "rootParams": [16],
- "root": [1],
- "push": [64],
- "insert": [64],
- "insertPages": [64],
- "pop": [64],
- "popTo": [64],
- "popToRoot": [64],
- "removeIndex": [64],
- "setRoot": [64],
- "setPages": [64],
- "setRouteId": [64],
- "getRouteId": [64],
- "getActive": [64],
- "getByIndex": [64],
- "canGoBack": [64],
- "getPrevious": [64],
- "getLength": [64]
- }, undefined, {
- "swipeGesture": ["swipeGestureChanged"],
- "root": ["rootChanged"]
- }]);
- function defineCustomElement$1() {
- if (typeof customElements === "undefined") {
- return;
- }
- const components = ["ion-nav"];
- components.forEach(tagName => { switch (tagName) {
- case "ion-nav":
- if (!customElements.get(tagName)) {
- customElements.define(tagName, Nav);
- }
- break;
- } });
- }
- const IonNav = Nav;
- const defineCustomElement = defineCustomElement$1;
- export { IonNav, defineCustomElement };
|