UNPKG

@ionic/core

Version:
516 lines (515 loc) • 20.1 kB
/*! * (C) Ionic http://ionicframework.com - MIT License */ import { h } from "@stencil/core"; import { getTimeGivenProgression } from "../../utils/animation/cubic-bezier"; import { attachComponent, detachComponent } from "../../utils/framework-delegate"; import { shallowEqualStringMap, hasLazyBuild } from "../../utils/helpers"; import { createLockController } from "../../utils/lock-controller"; import { printIonError } from "../../utils/logging/index"; import { transition } from "../../utils/transition/index"; import { config } from "../../global/config"; import { getIonMode } from "../../global/ionic-global"; export class RouterOutlet { constructor() { this.lockController = createLockController(); this.gestureOrAnimationInProgress = false; /** * The mode determines which platform styles to use. */ this.mode = getIonMode(this); /** * If `true`, the router-outlet should animate the transition of components. */ this.animated = true; } swipeHandlerChanged() { if (this.gesture) { this.gesture.enable(this.swipeHandler !== undefined); } } async connectedCallback() { const onStart = () => { this.gestureOrAnimationInProgress = true; if (this.swipeHandler) { this.swipeHandler.onStart(); } }; this.gesture = (await import('../../utils/gesture/swipe-back')).createSwipeBackGesture(this.el, () => !this.gestureOrAnimationInProgress && !!this.swipeHandler && this.swipeHandler.canStart(), () => onStart(), (step) => { var _a; return (_a = this.ani) === null || _a === void 0 ? void 0 : _a.progressStep(step); }, (shouldComplete, step, dur) => { if (this.ani) { this.ani.onFinish(() => { this.gestureOrAnimationInProgress = false; if (this.swipeHandler) { this.swipeHandler.onEnd(shouldComplete); } }, { 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.ani.easing('cubic-bezier(1, 0, 0.68, 0.28)'); newStepValue += getTimeGivenProgression([0, 0], [1, 0], [0.68, 0.28], [1, 1], step)[0]; } else { newStepValue += getTimeGivenProgression([0, 0], [0.32, 0.72], [0, 1], [1, 1], step)[0]; } this.ani.progressEnd(shouldComplete ? 1 : 0, newStepValue, dur); } else { this.gestureOrAnimationInProgress = false; } }); this.swipeHandlerChanged(); } componentWillLoad() { this.ionNavWillLoad.emit(); } disconnectedCallback() { if (this.gesture) { this.gesture.destroy(); this.gesture = undefined; } } /** @internal */ async commit(enteringEl, leavingEl, opts) { const unlock = await this.lockController.lock(); let changed = false; try { changed = await this.transition(enteringEl, leavingEl, opts); } catch (e) { printIonError('[ion-router-outlet] - Exception in commit:', e); } unlock(); return changed; } /** @internal */ async setRouteId(id, params, direction, animation) { const changed = await this.setRoot(id, params, { duration: direction === 'root' ? 0 : undefined, direction: direction === 'back' ? 'back' : 'forward', animationBuilder: animation, }); return { changed, element: this.activeEl, }; } /** @internal */ async getRouteId() { const active = this.activeEl; return active ? { id: active.tagName, element: active, params: this.activeParams, } : undefined; } async setRoot(component, params, opts) { if (this.activeComponent === component && shallowEqualStringMap(params, this.activeParams)) { return false; } // attach entering view to DOM const leavingEl = this.activeEl; const enteringEl = await attachComponent(this.delegate, this.el, component, ['ion-page', 'ion-page-invisible'], params); this.activeComponent = component; this.activeEl = enteringEl; this.activeParams = params; // commit animation await this.commit(enteringEl, leavingEl, opts); await detachComponent(this.delegate, leavingEl); return true; } async transition(enteringEl, leavingEl, opts = {}) { if (leavingEl === enteringEl) { return false; } // emit nav will change event this.ionNavWillChange.emit(); const { el, mode } = this; const animated = this.animated && config.getBoolean('animated', true); const animationBuilder = opts.animationBuilder || this.animation || config.get('navAnimation'); await transition(Object.assign(Object.assign({ mode, animated, enteringEl, leavingEl, baseEl: el, /** * We need to wait for all Stencil components * to be ready only when using the lazy * loaded bundle. */ deepWait: hasLazyBuild(el), 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; if (this.swipeHandler) { this.swipeHandler.onEnd(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.ani = ani; } } : undefined }, opts), { animationBuilder })); // emit nav changed event this.ionNavDidChange.emit(); return true; } render() { return h("slot", { key: '84b50f1155b0d780dff802ee13223287259fd525' }); } static get is() { return "ion-router-outlet"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["router-outlet.scss"] }; } static get styleUrls() { return { "$": ["router-outlet.css"] }; } static get properties() { return { "mode": { "type": "string", "attribute": "mode", "mutable": true, "complexType": { "original": "\"ios\" | \"md\"", "resolved": "\"ios\" | \"md\"", "references": { "Mode": { "location": "global", "id": "global::Mode" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "The mode determines which platform styles to use." }, "getter": false, "setter": false, "reflect": false, "defaultValue": "getIonMode(this)" }, "delegate": { "type": "unknown", "attribute": "delegate", "mutable": false, "complexType": { "original": "FrameworkDelegate", "resolved": "FrameworkDelegate | undefined", "references": { "FrameworkDelegate": { "location": "import", "path": "../../interface", "id": "src/interface.d.ts::FrameworkDelegate" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "getter": false, "setter": false }, "animated": { "type": "boolean", "attribute": "animated", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "If `true`, the router-outlet should animate the transition of components." }, "getter": false, "setter": false, "reflect": false, "defaultValue": "true" }, "animation": { "type": "unknown", "attribute": "animation", "mutable": false, "complexType": { "original": "AnimationBuilder", "resolved": "((baseEl: any, opts?: any) => Animation) | undefined", "references": { "AnimationBuilder": { "location": "import", "path": "../../interface", "id": "src/interface.d.ts::AnimationBuilder" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "This property allows to create custom transition using AnimationBuilder functions." }, "getter": false, "setter": false }, "swipeHandler": { "type": "unknown", "attribute": "swipe-handler", "mutable": false, "complexType": { "original": "SwipeGestureHandler", "resolved": "SwipeGestureHandler | undefined", "references": { "SwipeGestureHandler": { "location": "import", "path": "../nav/nav-interface", "id": "src/components/nav/nav-interface.ts::SwipeGestureHandler" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "getter": false, "setter": false } }; } static get events() { return [{ "method": "ionNavWillLoad", "name": "ionNavWillLoad", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "ionNavWillChange", "name": "ionNavWillChange", "bubbles": false, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "ionNavDidChange", "name": "ionNavDidChange", "bubbles": false, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "commit": { "complexType": { "signature": "(enteringEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: RouterOutletOptions) => Promise<boolean>", "parameters": [{ "name": "enteringEl", "type": "HTMLElement", "docs": "" }, { "name": "leavingEl", "type": "HTMLElement | undefined", "docs": "" }, { "name": "opts", "type": "RouterOutletOptions | undefined", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" }, "HTMLElement": { "location": "global", "id": "global::HTMLElement" }, "RouterOutletOptions": { "location": "import", "path": "../nav/nav-interface", "id": "src/components/nav/nav-interface.ts::RouterOutletOptions" } }, "return": "Promise<boolean>" }, "docs": { "text": "", "tags": [{ "name": "internal", "text": undefined }] } }, "setRouteId": { "complexType": { "signature": "(id: string, params: ComponentProps | undefined, direction: RouterDirection, animation?: AnimationBuilder) => Promise<RouteWrite>", "parameters": [{ "name": "id", "type": "string", "docs": "" }, { "name": "params", "type": "ComponentProps | undefined", "docs": "" }, { "name": "direction", "type": "\"root\" | \"back\" | \"forward\"", "docs": "" }, { "name": "animation", "type": "AnimationBuilder | undefined", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" }, "RouteWrite": { "location": "import", "path": "../router/utils/interface", "id": "src/components/router/utils/interface.ts::RouteWrite" }, "ComponentProps": { "location": "import", "path": "../../interface", "id": "src/interface.d.ts::ComponentProps" }, "RouterDirection": { "location": "import", "path": "../router/utils/interface", "id": "src/components/router/utils/interface.ts::RouterDirection" }, "AnimationBuilder": { "location": "import", "path": "../../interface", "id": "src/interface.d.ts::AnimationBuilder" } }, "return": "Promise<RouteWrite>" }, "docs": { "text": "", "tags": [{ "name": "internal", "text": undefined }] } }, "getRouteId": { "complexType": { "signature": "() => Promise<RouteID | undefined>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" }, "RouteID": { "location": "import", "path": "../router/utils/interface", "id": "src/components/router/utils/interface.ts::RouteID" } }, "return": "Promise<RouteID | undefined>" }, "docs": { "text": "", "tags": [{ "name": "internal", "text": undefined }] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "swipeHandler", "methodName": "swipeHandlerChanged" }]; } }