chore(vue-router): add eslint and prettier (#26636)

This commit is contained in:
Liam DeBeasi
2023-01-19 08:51:08 -05:00
committed by GitHub
parent dc27736bd5
commit 87d83bb21b
10 changed files with 4478 additions and 261 deletions

View File

@ -17,7 +17,7 @@ runs:
path: ./packages/vue path: ./packages/vue
filename: VueBuild.zip filename: VueBuild.zip
- name: Install Vue Router Dependencies - name: Install Vue Router Dependencies
run: npm install run: npm ci
shell: bash shell: bash
working-directory: ./packages/vue-router working-directory: ./packages/vue-router
- name: Sync - name: Sync

View File

@ -0,0 +1,26 @@
module.exports = {
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"@ionic/eslint-config/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/prefer-optional-chain": "off",
"@typescript-eslint/ban-types": "off"
}
};

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,10 @@
"description": "Vue Router integration for @ionic/vue", "description": "Vue Router integration for @ionic/vue",
"scripts": { "scripts": {
"test.spec": "jest", "test.spec": "jest",
"lint": "echo add linter", "eslint": "eslint src",
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx}\"",
"lint": "npm run eslint && npm run prettier -- --write --cache",
"lint.fix": "npm run eslint -- --fix && npm run prettier -- --write --cache",
"bundle": "rollup --config rollup.config.js", "bundle": "rollup --config rollup.config.js",
"build": "npm run clean && npm run compile && npm run bundle", "build": "npm run clean && npm run compile && npm run bundle",
"clean": "rimraf dist", "clean": "rimraf dist",
@ -46,10 +49,16 @@
"@ionic/vue": "^6.5.0" "@ionic/vue": "^6.5.0"
}, },
"devDependencies": { "devDependencies": {
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@types/jest": "^28.1.1", "@types/jest": "^28.1.1",
"@types/node": "^14.10.1", "@types/node": "^14.10.1",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"eslint": "^7.32.0",
"jest": "^28.1.1", "jest": "^28.1.1",
"jest-environment-jsdom": "^28.1.1", "jest-environment-jsdom": "^28.1.1",
"prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.32.1", "rollup": "^2.32.1",
"ts-jest": "^28.0.5", "ts-jest": "^28.0.5",

View File

@ -1,13 +1,14 @@
import { App } from 'vue'; import type { App } from "vue";
import { import {
createRouter as createVueRouter, createRouter as createVueRouter,
createWebHistory as createVueWebHistory, createWebHistory as createVueWebHistory,
createWebHashHistory as createVueWebHashHistory, createWebHashHistory as createVueWebHashHistory,
createMemoryHistory as createVueMemoryHistory createMemoryHistory as createVueMemoryHistory,
} from 'vue-router'; } from "vue-router";
import { createIonRouter } from './router';
import { createViewStacks } from './viewStacks'; import { createIonRouter } from "./router";
import { IonicVueRouterOptions } from './types'; import type { IonicVueRouterOptions } from "./types";
import { createViewStacks } from "./viewStacks";
export const createRouter = (opts: IonicVueRouterOptions) => { export const createRouter = (opts: IonicVueRouterOptions) => {
const routerOptions = { ...opts }; const routerOptions = { ...opts };
@ -19,8 +20,8 @@ export const createRouter = (opts: IonicVueRouterOptions) => {
const oldInstall = router.install.bind(router); const oldInstall = router.install.bind(router);
router.install = (app: App) => { router.install = (app: App) => {
app.provide('navManager', ionRouter); app.provide("navManager", ionRouter);
app.provide('viewStacks', viewStacks); app.provide("viewStacks", viewStacks);
oldInstall(app); oldInstall(app);
}; };
@ -29,8 +30,10 @@ export const createRouter = (opts: IonicVueRouterOptions) => {
router.isReady = () => oldIsReady(); router.isReady = () => oldIsReady();
return router; return router;
} };
export const createWebHistory = (base?: string) => createVueWebHistory(base); export const createWebHistory = (base?: string) => createVueWebHistory(base);
export const createWebHashHistory = (base?: string) => createVueWebHashHistory(base); export const createWebHashHistory = (base?: string) =>
export const createMemoryHistory = (base?: string) => createVueMemoryHistory(base); createVueWebHashHistory(base);
export const createMemoryHistory = (base?: string) =>
createVueMemoryHistory(base);

View File

@ -1,4 +1,4 @@
import { RouteInfo } from './types'; import type { RouteInfo } from "./types";
export const createLocationHistory = () => { export const createLocationHistory = () => {
const locationHistory: RouteInfo[] = []; const locationHistory: RouteInfo[] = [];
@ -14,20 +14,22 @@ export const createLocationHistory = () => {
break; break;
} }
if (routeInfo.routerDirection === 'root') { if (routeInfo.routerDirection === "root") {
clearHistory(); clearHistory();
addRoute(routeInfo); addRoute(routeInfo);
} }
} };
const update = (routeInfo: RouteInfo) => { const update = (routeInfo: RouteInfo) => {
const locationIndex = locationHistory.findIndex(x => x.id === routeInfo.id); const locationIndex = locationHistory.findIndex(
(x) => x.id === routeInfo.id
);
if (locationIndex > -1) { if (locationIndex > -1) {
locationHistory.splice(locationIndex, 1, routeInfo); locationHistory.splice(locationIndex, 1, routeInfo);
} }
const tabArray = tabsHistory[routeInfo.tab || '']; const tabArray = tabsHistory[routeInfo.tab || ""];
if (tabArray) { if (tabArray) {
const tabIndex = tabArray.findIndex(x => x.id === routeInfo.id); const tabIndex = tabArray.findIndex((x) => x.id === routeInfo.id);
if (tabIndex > -1) { if (tabIndex > -1) {
tabArray.splice(tabIndex, 1, routeInfo); tabArray.splice(tabIndex, 1, routeInfo);
} else { } else {
@ -36,7 +38,7 @@ export const createLocationHistory = () => {
} else if (routeInfo.tab) { } else if (routeInfo.tab) {
tabsHistory[routeInfo.tab] = [routeInfo]; tabsHistory[routeInfo.tab] = [routeInfo];
} }
} };
const pop = (routeInfo: RouteInfo) => { const pop = (routeInfo: RouteInfo) => {
const tabHistory = getTabsHistory(routeInfo.tab); const tabHistory = getTabsHistory(routeInfo.tab);
@ -61,19 +63,22 @@ export const createLocationHistory = () => {
// Replace with updated route // Replace with updated route
locationHistory.pop(); locationHistory.pop();
locationHistory.push(routeInfo); locationHistory.push(routeInfo);
} };
const addRoute = (routeInfo: RouteInfo) => { const addRoute = (routeInfo: RouteInfo) => {
const tabHistory = getTabsHistory(routeInfo.tab); const tabHistory = getTabsHistory(routeInfo.tab);
if (tabHistory) { if (tabHistory) {
// If the latest routeInfo is the same (going back and forth between tabs), replace it // If the latest routeInfo is the same (going back and forth between tabs), replace it
if (tabHistory[tabHistory.length - 1] && tabHistory[tabHistory.length - 1].id === routeInfo.id) { if (
tabHistory[tabHistory.length - 1] &&
tabHistory[tabHistory.length - 1].id === routeInfo.id
) {
tabHistory.pop(); tabHistory.pop();
} }
tabHistory.push(routeInfo); tabHistory.push(routeInfo);
} }
locationHistory.push(routeInfo); locationHistory.push(routeInfo);
} };
/** /**
* Wipes the location history arrays. * Wipes the location history arrays.
@ -90,17 +95,21 @@ export const createLocationHistory = () => {
* then there will not be any route index in * then there will not be any route index in
* tabs either. * tabs either.
*/ */
const existingRouteIndex = locationHistory.findIndex(r => r.position === position); const existingRouteIndex = locationHistory.findIndex(
(r) => r.position === position
);
if (existingRouteIndex === -1) return; if (existingRouteIndex === -1) return;
locationHistory.splice(existingRouteIndex); locationHistory.splice(existingRouteIndex);
const clearTabHistory = (tab: string) => { const clearTabHistory = (tab: string) => {
const existingTabRouteIndex = tabsHistory[tab].findIndex(r => r.position === position); const existingTabRouteIndex = tabsHistory[tab].findIndex(
(r) => r.position === position
);
if (existingTabRouteIndex === -1) return; if (existingTabRouteIndex === -1) return;
tabsHistory[tab].splice(existingTabRouteIndex); tabsHistory[tab].splice(existingTabRouteIndex);
} };
/** /**
* We also need to search the current tab * We also need to search the current tab
@ -112,19 +121,19 @@ export const createLocationHistory = () => {
const tabHistory = tabsHistory[tab]; const tabHistory = tabsHistory[tab];
if (tab && tabHistory) { if (tab && tabHistory) {
clearTabHistory(tab); clearTabHistory(tab);
/** /**
* If we are not clearing items after * If we are not clearing items after
* a tabs page, it is still possible * a tabs page, it is still possible
* that there are future tabs pages to clear. * that there are future tabs pages to clear.
* As a result, we need to search through * As a result, we need to search through
* all the tab stacks and remove views that appear * all the tab stacks and remove views that appear
* after the given routeInfo. * after the given routeInfo.
* *
* Example: /non-tabs-page --> /tabs/tab1 --> /non-tabs-page * Example: /non-tabs-page --> /tabs/tab1 --> /non-tabs-page
* (via router.go(-1)) --> /tabs/tab2. The /tabs/tab1 history * (via router.go(-1)) --> /tabs/tab2. The /tabs/tab1 history
* has been overwritten with /tabs/tab2. As a result, * has been overwritten with /tabs/tab2. As a result,
* the /tabs/tab1 route info in the Tab 1 stack should be removed. * the /tabs/tab1 route info in the Tab 1 stack should be removed.
*/ */
} else { } else {
for (const tab in tabsHistory) { for (const tab in tabsHistory) {
clearTabHistory(tab); clearTabHistory(tab);
@ -137,7 +146,7 @@ export const createLocationHistory = () => {
locationHistory.length = 0; locationHistory.length = 0;
} }
} };
const getTabsHistory = (tab: string): RouteInfo[] => { const getTabsHistory = (tab: string): RouteInfo[] => {
let history; let history;
if (tab) { if (tab) {
@ -148,7 +157,7 @@ export const createLocationHistory = () => {
} }
return history; return history;
} };
const size = () => locationHistory.length; const size = () => locationHistory.length;
@ -166,7 +175,7 @@ export const createLocationHistory = () => {
*/ */
const index = currentHistory - initialHistory; const index = currentHistory - initialHistory;
return locationHistory[index] || last(); return locationHistory[index] || last();
} };
const last = () => locationHistory[locationHistory.length - 1]; const last = () => locationHistory[locationHistory.length - 1];
/** /**
@ -177,9 +186,13 @@ export const createLocationHistory = () => {
* the length of locationHistory, but that only worked since we were pruning * the length of locationHistory, but that only worked since we were pruning
* the array. * the array.
*/ */
const canGoBack = (deep: number = 1, initialHistory: number, currentHistory: number) => { const canGoBack = (
deep = 1,
initialHistory: number,
currentHistory: number
) => {
return currentHistory - deep >= initialHistory; return currentHistory - deep >= initialHistory;
} };
const getFirstRouteInfoForTab = (tab: string): RouteInfo | undefined => { const getFirstRouteInfoForTab = (tab: string): RouteInfo | undefined => {
const tabHistory = getTabsHistory(tab); const tabHistory = getTabsHistory(tab);
@ -187,7 +200,7 @@ export const createLocationHistory = () => {
return tabHistory[0]; return tabHistory[0];
} }
return undefined; return undefined;
} };
const getCurrentRouteInfoForTab = (tab: string): RouteInfo | undefined => { const getCurrentRouteInfoForTab = (tab: string): RouteInfo | undefined => {
const tabHistory = getTabsHistory(tab); const tabHistory = getTabsHistory(tab);
@ -195,7 +208,7 @@ export const createLocationHistory = () => {
return tabHistory[tabHistory.length - 1]; return tabHistory[tabHistory.length - 1];
} }
return undefined; return undefined;
} };
/** /**
* Finds and returns the previous view based upon * Finds and returns the previous view based upon
@ -207,7 +220,10 @@ export const createLocationHistory = () => {
* do not modify the locationHistory stack so we would * do not modify the locationHistory stack so we would
* not update pushedByRoute anyways. * not update pushedByRoute anyways.
*/ */
const findLastLocation = (routeInfo: RouteInfo, delta: number = -1): RouteInfo | undefined => { const findLastLocation = (
routeInfo: RouteInfo,
delta = -1
): RouteInfo | undefined => {
const routeInfos = getTabsHistory(routeInfo.tab); const routeInfos = getTabsHistory(routeInfo.tab);
if (routeInfos) { if (routeInfos) {
if (delta < -1) { if (delta < -1) {
@ -236,7 +252,7 @@ export const createLocationHistory = () => {
} }
} }
return undefined; return undefined;
} };
return { return {
current, current,
@ -248,6 +264,6 @@ export const createLocationHistory = () => {
getFirstRouteInfoForTab, getFirstRouteInfoForTab,
getCurrentRouteInfoForTab, getCurrentRouteInfoForTab,
findLastLocation, findLastLocation,
clearHistory clearHistory,
} };
} };

View File

@ -1,27 +1,35 @@
import { import type { AnimationBuilder } from "@ionic/vue";
parseQuery, import type {
Router, Router,
RouteLocationNormalized, RouteLocationNormalized,
NavigationFailure, NavigationFailure,
RouteLocationRaw RouteLocationRaw,
} from 'vue-router'; } from "vue-router";
import { createLocationHistory } from './locationHistory'; import { parseQuery } from "vue-router";
import { generateId } from './utils';
import { import { createLocationHistory } from "./locationHistory";
import type {
ExternalNavigationOptions, ExternalNavigationOptions,
RouteInfo, RouteInfo,
RouteParams, RouteParams,
RouteAction, RouteAction,
RouteDirection, RouteDirection,
IonicVueRouterOptions, IonicVueRouterOptions,
NavigationInformation NavigationInformation,
} from './types'; } from "./types";
import { AnimationBuilder } from '@ionic/vue'; import { generateId } from "./utils";
// TODO(FW-2969): types // TODO(FW-2969): types
export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) => { export const createIonRouter = (
let currentNavigationInfo: NavigationInformation = { direction: undefined, action: undefined, delta: undefined }; opts: IonicVueRouterOptions,
router: Router
) => {
let currentNavigationInfo: NavigationInformation = {
direction: undefined,
action: undefined,
delta: undefined,
};
/** /**
* Ionic Vue should only react to navigation * Ionic Vue should only react to navigation
@ -32,27 +40,37 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* which is fired once navigation is confirmed * which is fired once navigation is confirmed
* and any user guards have run. * and any user guards have run.
*/ */
router.afterEach((to: RouteLocationNormalized, _: RouteLocationNormalized, failure?: NavigationFailure) => { router.afterEach(
if (failure) return; (
to: RouteLocationNormalized,
_: RouteLocationNormalized,
failure?: NavigationFailure
) => {
if (failure) return;
const { direction, action, delta } = currentNavigationInfo; const { direction, action, delta } = currentNavigationInfo;
/** /**
* When calling router.replace, we are not informed * When calling router.replace, we are not informed
* about the replace action in opts.history.listen * about the replace action in opts.history.listen
* but we can check to see if the latest routing action * but we can check to see if the latest routing action
* was a replace action by looking at the history state. * was a replace action by looking at the history state.
* We need to use opts.history rather than window.history * We need to use opts.history rather than window.history
* because window.history will be undefined when using SSR. * because window.history will be undefined when using SSR.
*/ */
currentHistoryPosition = opts.history.state.position as number; currentHistoryPosition = opts.history.state.position as number;
const replaceAction = opts.history.state.replaced ? 'replace' : undefined; const replaceAction = opts.history.state.replaced ? "replace" : undefined;
handleHistoryChange(to, action || replaceAction, direction, delta); handleHistoryChange(to, action || replaceAction, direction, delta);
currentNavigationInfo = { direction: undefined, action: undefined, delta: undefined }; currentNavigationInfo = {
}); direction: undefined,
action: undefined,
delta: undefined,
};
}
);
const locationHistory = createLocationHistory(); const locationHistory = createLocationHistory();
@ -68,15 +86,15 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
let currentRouteInfo: RouteInfo; let currentRouteInfo: RouteInfo;
let incomingRouteParams: RouteParams; let incomingRouteParams: RouteParams;
let historyChangeListeners: any[] = []; const historyChangeListeners: any[] = [];
if (typeof (document as any) !== 'undefined') { if (typeof (document as any) !== "undefined") {
document.addEventListener('ionBackButton', (ev: Event) => { document.addEventListener("ionBackButton", (ev: Event) => {
(ev as any).detail.register(0, (processNextHandler: () => void) => { (ev as any).detail.register(0, (processNextHandler: () => void) => {
opts.history.go(-1); opts.history.go(-1);
processNextHandler(); processNextHandler();
}); });
}) });
} }
opts.history.listen((_: any, _x: any, info: any) => { opts.history.listen((_: any, _x: any, info: any) => {
@ -99,38 +117,46 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* are considered "pop" actions, but when going forward * are considered "pop" actions, but when going forward
* we want to make sure the forward animation is used. * we want to make sure the forward animation is used.
*/ */
action: (info.type === 'pop' && info.delta >= 1) ? 'push' : info.type, action: info.type === "pop" && info.delta >= 1 ? "push" : info.type,
direction: info.direction === '' ? 'forward' : info.direction direction: info.direction === "" ? "forward" : info.direction,
}; };
}); });
const handleNavigateBack = (defaultHref?: string, routerAnimation?: AnimationBuilder) => { const handleNavigateBack = (
const routeInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition); defaultHref?: string,
routerAnimation?: AnimationBuilder
) => {
const routeInfo = locationHistory.current(
initialHistoryPosition,
currentHistoryPosition
);
if (routeInfo && routeInfo.pushedByRoute) { if (routeInfo && routeInfo.pushedByRoute) {
const prevInfo = locationHistory.findLastLocation(routeInfo); const prevInfo = locationHistory.findLastLocation(routeInfo);
if (prevInfo) { if (prevInfo) {
incomingRouteParams = { ...prevInfo, routerAction: 'pop', routerDirection: 'back', routerAnimation: routerAnimation || routeInfo.routerAnimation }; incomingRouteParams = {
...prevInfo,
routerAction: "pop",
routerDirection: "back",
routerAnimation: routerAnimation || routeInfo.routerAnimation,
};
if ( if (
routeInfo.lastPathname === routeInfo.pushedByRoute || routeInfo.lastPathname === routeInfo.pushedByRoute ||
( /**
/** * We need to exclude tab switches/tab
* We need to exclude tab switches/tab * context changes here because tabbed
* context changes here because tabbed * navigation is not linear, but router.back()
* navigation is not linear, but router.back() * will go back in a linear fashion.
* will go back in a linear fashion. */
*/ (prevInfo.pathname === routeInfo.pushedByRoute &&
prevInfo.pathname === routeInfo.pushedByRoute &&
/** /**
* Tab info can be undefined or '' (empty string) * Tab info can be undefined or '' (empty string)
* both are false-y values, so we can just use !. * both are false-y values, so we can just use !.
*/ */
!routeInfo.tab && !prevInfo.tab !routeInfo.tab &&
) !prevInfo.tab)
) { ) {
router.back(); router.back();
} else { } else {
/** /**
* When going back to a child page of a tab * When going back to a child page of a tab
* after being on another tab, we need to use * after being on another tab, we need to use
@ -160,22 +186,28 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
router.go(prevInfo.position - routeInfo.position); router.go(prevInfo.position - routeInfo.position);
} }
} else { } else {
handleNavigate(defaultHref, 'pop', 'back'); handleNavigate(defaultHref, "pop", "back");
} }
} else { } else {
handleNavigate(defaultHref, 'pop', 'back'); handleNavigate(defaultHref, "pop", "back");
} }
} };
const handleNavigate = (path: RouteLocationRaw, routerAction?: RouteAction, routerDirection?: RouteDirection, routerAnimation?: AnimationBuilder, tab?: string) => { const handleNavigate = (
path: RouteLocationRaw,
routerAction?: RouteAction,
routerDirection?: RouteDirection,
routerAnimation?: AnimationBuilder,
tab?: string
) => {
setIncomingRouteParams(routerAction, routerDirection, routerAnimation, tab); setIncomingRouteParams(routerAction, routerDirection, routerAnimation, tab);
if (routerAction === 'push') { if (routerAction === "push") {
router.push(path); router.push(path);
} else { } else {
router.replace(path); router.replace(path);
} }
} };
// TODO RouteLocationNormalized // TODO RouteLocationNormalized
const handleHistoryChange = ( const handleHistoryChange = (
@ -186,17 +218,22 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
) => { ) => {
let leavingLocationInfo: RouteInfo; let leavingLocationInfo: RouteInfo;
if (incomingRouteParams) { if (incomingRouteParams) {
/** /**
* If we are replacing the state of a route * If we are replacing the state of a route
* with another route, the "leaving" route * with another route, the "leaving" route
* is at the same position in location history * is at the same position in location history
* as where the replaced route will exist. * as where the replaced route will exist.
*/ */
if (incomingRouteParams.routerAction === 'replace') { if (incomingRouteParams.routerAction === "replace") {
leavingLocationInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition); leavingLocationInfo = locationHistory.current(
} else if (incomingRouteParams.routerAction === 'pop') { initialHistoryPosition,
leavingLocationInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition + 1); currentHistoryPosition
);
} else if (incomingRouteParams.routerAction === "pop") {
leavingLocationInfo = locationHistory.current(
initialHistoryPosition,
currentHistoryPosition + 1
);
/** /**
* If the Ionic Router action was "pop" * If the Ionic Router action was "pop"
@ -233,7 +270,7 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* the past history and you can no longer go * the past history and you can no longer go
* forward to /page2 or /page3. * forward to /page2 or /page3.
*/ */
if (action === 'replace') { if (action === "replace") {
locationHistory.clearHistory(); locationHistory.clearHistory();
} }
} else { } else {
@ -249,8 +286,14 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* so we can grab the previous item in history relative * so we can grab the previous item in history relative
* to where the history state currently is. * to where the history state currently is.
*/ */
const position = (incomingRouteParams.routerDirection === 'root') ? currentHistoryPosition : currentHistoryPosition - 1; const position =
leavingLocationInfo = locationHistory.current(initialHistoryPosition, position); incomingRouteParams.routerDirection === "root"
? currentHistoryPosition
: currentHistoryPosition - 1;
leavingLocationInfo = locationHistory.current(
initialHistoryPosition,
position
);
} }
} else { } else {
leavingLocationInfo = currentRouteInfo; leavingLocationInfo = currentRouteInfo;
@ -258,42 +301,49 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
if (!leavingLocationInfo) { if (!leavingLocationInfo) {
leavingLocationInfo = { leavingLocationInfo = {
pathname: '', pathname: "",
search: '' search: "",
} };
} }
const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search; const leavingUrl =
leavingLocationInfo.pathname + leavingLocationInfo.search;
if (leavingUrl !== location.fullPath) { if (leavingUrl !== location.fullPath) {
if (!incomingRouteParams) { if (!incomingRouteParams) {
if (action === 'replace') { if (action === "replace") {
incomingRouteParams = { incomingRouteParams = {
routerAction: 'replace', routerAction: "replace",
routerDirection: 'none' routerDirection: "none",
} };
} else if (action === 'pop') { } else if (action === "pop") {
const routeInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition - delta); const routeInfo = locationHistory.current(
initialHistoryPosition,
currentHistoryPosition - delta
);
if (routeInfo && routeInfo.pushedByRoute) { if (routeInfo && routeInfo.pushedByRoute) {
const prevRouteInfo = locationHistory.findLastLocation(routeInfo, delta); const prevRouteInfo = locationHistory.findLastLocation(
routeInfo,
delta
);
incomingRouteParams = { incomingRouteParams = {
...prevRouteInfo, ...prevRouteInfo,
routerAction: 'pop', routerAction: "pop",
routerDirection: 'back' routerDirection: "back",
}; };
} else { } else {
incomingRouteParams = { incomingRouteParams = {
routerAction: 'pop', routerAction: "pop",
routerDirection: 'none' routerDirection: "none",
} };
} }
} }
if (!incomingRouteParams) { if (!incomingRouteParams) {
incomingRouteParams = { incomingRouteParams = {
routerAction: 'push', routerAction: "push",
routerDirection: direction || 'forward' routerDirection: direction || "forward",
} };
} }
} }
@ -301,31 +351,39 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
if (incomingRouteParams?.id) { if (incomingRouteParams?.id) {
routeInfo = { routeInfo = {
...incomingRouteParams, ...incomingRouteParams,
lastPathname: leavingLocationInfo.pathname lastPathname: leavingLocationInfo.pathname,
} };
} else { } else {
const isPushed = incomingRouteParams.routerAction === 'push' && incomingRouteParams.routerDirection === 'forward'; const isPushed =
incomingRouteParams.routerAction === "push" &&
incomingRouteParams.routerDirection === "forward";
routeInfo = { routeInfo = {
id: generateId('routeInfo'), id: generateId("routeInfo"),
...incomingRouteParams, ...incomingRouteParams,
lastPathname: leavingLocationInfo.pathname, lastPathname: leavingLocationInfo.pathname,
pathname: location.path, pathname: location.path,
search: location.fullPath && location.fullPath.split('?')[1] || '', search: (location.fullPath && location.fullPath.split("?")[1]) || "",
params: location.params && location.params, params: location.params && location.params,
prevRouteLastPathname: leavingLocationInfo.lastPathname prevRouteLastPathname: leavingLocationInfo.lastPathname,
} };
if (isPushed) { if (isPushed) {
routeInfo.pushedByRoute = (leavingLocationInfo.pathname !== '') ? leavingLocationInfo.pathname : undefined; routeInfo.pushedByRoute =
} else if (routeInfo.routerAction === 'pop') { leavingLocationInfo.pathname !== ""
? leavingLocationInfo.pathname
: undefined;
} else if (routeInfo.routerAction === "pop") {
const route = locationHistory.findLastLocation(routeInfo); const route = locationHistory.findLastLocation(routeInfo);
routeInfo.pushedByRoute = route?.pushedByRoute; routeInfo.pushedByRoute = route?.pushedByRoute;
} else if (routeInfo.routerAction === 'push' && routeInfo.tab !== leavingLocationInfo.tab) { } else if (
const lastRoute = locationHistory.getCurrentRouteInfoForTab(routeInfo.tab); routeInfo.routerAction === "push" &&
routeInfo.tab !== leavingLocationInfo.tab
) {
const lastRoute = locationHistory.getCurrentRouteInfoForTab(
routeInfo.tab
);
routeInfo.pushedByRoute = lastRoute?.pushedByRoute; routeInfo.pushedByRoute = lastRoute?.pushedByRoute;
} else if (routeInfo.routerAction === 'replace') { } else if (routeInfo.routerAction === "replace") {
/** /**
* When replacing a route, we want to make sure we select the current route * When replacing a route, we want to make sure we select the current route
* that we are on, not the last route in the stack. The last route in the stack * that we are on, not the last route in the stack. The last route in the stack
@ -337,7 +395,10 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* be replaced with /page3 even though /page2 is the last * be replaced with /page3 even though /page2 is the last
* item in the stack/ * item in the stack/
*/ */
const currentRouteInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition); const currentRouteInfo = locationHistory.current(
initialHistoryPosition,
currentHistoryPosition
);
/** /**
* If going from /home to /child, then replacing from * If going from /home to /child, then replacing from
@ -345,15 +406,21 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* say that /home was pushed by /home which is not correct. * say that /home was pushed by /home which is not correct.
*/ */
const currentPushedBy = currentRouteInfo?.pushedByRoute; const currentPushedBy = currentRouteInfo?.pushedByRoute;
const pushedByRoute = (currentPushedBy !== undefined && currentPushedBy !== routeInfo.pathname) ? currentPushedBy : routeInfo.pushedByRoute; const pushedByRoute =
currentPushedBy !== undefined &&
currentPushedBy !== routeInfo.pathname
? currentPushedBy
: routeInfo.pushedByRoute;
routeInfo.lastPathname = currentRouteInfo?.pathname || routeInfo.lastPathname; routeInfo.lastPathname =
currentRouteInfo?.pathname || routeInfo.lastPathname;
routeInfo.pushedByRoute = pushedByRoute; routeInfo.pushedByRoute = pushedByRoute;
routeInfo.routerDirection = currentRouteInfo?.routerDirection || routeInfo.routerDirection; routeInfo.routerDirection =
routeInfo.routerAnimation = currentRouteInfo?.routerAnimation || routeInfo.routerAnimation; currentRouteInfo?.routerDirection || routeInfo.routerDirection;
routeInfo.routerAnimation =
currentRouteInfo?.routerAnimation || routeInfo.routerAnimation;
routeInfo.prevRouteLastPathname = currentRouteInfo?.lastPathname; routeInfo.prevRouteLastPathname = currentRouteInfo?.lastPathname;
} }
} }
routeInfo.position = currentHistoryPosition; routeInfo.position = currentHistoryPosition;
@ -379,7 +446,8 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* we want to make sure we exclude that * we want to make sure we exclude that
* action by ensuring historySize > 0. * action by ensuring historySize > 0.
*/ */
const isReplacing = historySize === historyDiff && historySize > 0 && action === 'replace'; const isReplacing =
historySize === historyDiff && historySize > 0 && action === "replace";
if (historySize > historyDiff || isReplacing) { if (historySize > historyDiff || isReplacing) {
/** /**
* When navigating back through the history, * When navigating back through the history,
@ -400,7 +468,8 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
*/ */
if ( if (
(routeInfo.routerAction === 'push' || routeInfo.routerAction === 'replace') && (routeInfo.routerAction === "push" ||
routeInfo.routerAction === "replace") &&
delta === undefined delta === undefined
) { ) {
locationHistory.clearHistory(routeInfo); locationHistory.clearHistory(routeInfo);
@ -422,20 +491,25 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
currentRouteInfo = routeInfo; currentRouteInfo = routeInfo;
} }
incomingRouteParams = undefined; incomingRouteParams = undefined;
historyChangeListeners.forEach(cb => cb(currentRouteInfo)); historyChangeListeners.forEach((cb) => cb(currentRouteInfo));
} };
const getCurrentRouteInfo = () => currentRouteInfo; const getCurrentRouteInfo = () => currentRouteInfo;
const canGoBack = (deep: number = 1) => locationHistory.canGoBack(deep, initialHistoryPosition, currentHistoryPosition); const canGoBack = (deep = 1) =>
locationHistory.canGoBack(
deep,
initialHistoryPosition,
currentHistoryPosition
);
const navigate = (navigationOptions: ExternalNavigationOptions) => { const navigate = (navigationOptions: ExternalNavigationOptions) => {
const { routerAnimation, routerDirection, routerLink } = navigationOptions; const { routerAnimation, routerDirection, routerLink } = navigationOptions;
setIncomingRouteParams('push', routerDirection, routerAnimation); setIncomingRouteParams("push", routerDirection, routerAnimation);
router.push(routerLink); router.push(routerLink);
} };
const resetTab = (tab: string) => { const resetTab = (tab: string) => {
/** /**
@ -454,21 +528,21 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
if (routeInfo) { if (routeInfo) {
router.go(routeInfo.position - currentHistoryPosition); router.go(routeInfo.position - currentHistoryPosition);
} }
} };
const changeTab = (tab: string, path?: string) => { const changeTab = (tab: string, path?: string) => {
if (!path) return; if (!path) return;
const routeInfo = locationHistory.getCurrentRouteInfoForTab(tab); const routeInfo = locationHistory.getCurrentRouteInfoForTab(tab);
const [pathname] = path.split('?'); const [pathname] = path.split("?");
if (routeInfo) { if (routeInfo) {
incomingRouteParams = { incomingRouteParams = {
...incomingRouteParams, ...incomingRouteParams,
routerAction: 'push', routerAction: "push",
routerDirection: 'none', routerDirection: "none",
tab tab,
} };
/** /**
* When going back to a tab * When going back to a tab
@ -478,15 +552,17 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* tab you are on. * tab you are on.
*/ */
if (routeInfo.pathname === pathname) { if (routeInfo.pathname === pathname) {
router.push({ path: routeInfo.pathname, query: parseQuery(routeInfo.search) }); router.push({
path: routeInfo.pathname,
query: parseQuery(routeInfo.search),
});
} else { } else {
router.push({ path: pathname, query: parseQuery(routeInfo.search) }); router.push({ path: pathname, query: parseQuery(routeInfo.search) });
} }
} else {
handleNavigate(pathname, "push", "none", undefined, tab);
} }
else { };
handleNavigate(pathname, 'push', 'none', undefined, tab);
}
}
/** /**
* This method is invoked by the IonTabs component * This method is invoked by the IonTabs component
@ -506,7 +582,12 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
* in the locationHistory stack. As a result, * in the locationHistory stack. As a result,
* we cannot use locationHistory.last() here. * we cannot use locationHistory.last() here.
*/ */
const ri = { ...locationHistory.current(initialHistoryPosition, currentHistoryPosition) }; const ri = {
...locationHistory.current(
initialHistoryPosition,
currentHistoryPosition
),
};
/** /**
* handleHistoryChange is tabs-agnostic by design. * handleHistoryChange is tabs-agnostic by design.
@ -559,34 +640,42 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
ri.pushedByRoute = undefined; ri.pushedByRoute = undefined;
locationHistory.update(ri); locationHistory.update(ri);
} }
} };
const registerHistoryChangeListener = (cb: any) => { const registerHistoryChangeListener = (cb: any) => {
historyChangeListeners.push(cb); historyChangeListeners.push(cb);
} };
const setIncomingRouteParams = (routerAction: RouteAction = 'push', routerDirection: RouteDirection = 'forward', routerAnimation?: AnimationBuilder, tab?: string) => { const setIncomingRouteParams = (
routerAction: RouteAction = "push",
routerDirection: RouteDirection = "forward",
routerAnimation?: AnimationBuilder,
tab?: string
) => {
incomingRouteParams = { incomingRouteParams = {
routerAction, routerAction,
routerDirection, routerDirection,
routerAnimation, routerAnimation,
tab tab,
}; };
} };
const goBack = (routerAnimation?: AnimationBuilder) => { const goBack = (routerAnimation?: AnimationBuilder) => {
setIncomingRouteParams('pop', 'back', routerAnimation); setIncomingRouteParams("pop", "back", routerAnimation);
router.back() router.back();
}; };
const goForward = (routerAnimation?: AnimationBuilder) => { const goForward = (routerAnimation?: AnimationBuilder) => {
setIncomingRouteParams('push', 'forward', routerAnimation); setIncomingRouteParams("push", "forward", routerAnimation);
router.forward(); router.forward();
} };
const getLeavingRouteInfo = () => { const getLeavingRouteInfo = () => {
return locationHistory.current(initialHistoryPosition, currentHistoryPosition); return locationHistory.current(
} initialHistoryPosition,
currentHistoryPosition
);
};
return { return {
handleNavigate, handleNavigate,
@ -600,6 +689,6 @@ export const createIonRouter = (opts: IonicVueRouterOptions, router: Router) =>
changeTab, changeTab,
registerHistoryChangeListener, registerHistoryChangeListener,
goBack, goBack,
goForward goForward,
} };
} };

View File

@ -1,6 +1,6 @@
import { AnimationBuilder } from '@ionic/vue'; import type { AnimationBuilder } from "@ionic/vue";
import { RouteLocationMatched, RouterOptions } from 'vue-router'; import type { Ref } from "vue";
import { Ref } from 'vue'; import type { RouteLocationMatched, RouterOptions } from "vue-router";
// TODO(FW-2969): types // TODO(FW-2969): types
@ -56,8 +56,8 @@ export interface RouteParams {
id?: string; id?: string;
} }
export type RouteAction = 'push' | 'pop' | 'replace'; export type RouteAction = "push" | "pop" | "replace";
export type RouteDirection = 'forward' | 'back' | 'root' | 'none'; export type RouteDirection = "forward" | "back" | "root" | "none";
export interface ViewItem { export interface ViewItem {
id: string; id: string;

View File

@ -1,7 +1,7 @@
const ids: { [k: string]: number } = { main: 0 }; const ids: { [k: string]: number } = { main: 0 };
export const generateId = (type = 'main') => { export const generateId = (type = "main") => {
const id = (ids[type] ?? 0) + 1; const id = (ids[type] ?? 0) + 1;
ids[type] = id; ids[type] = id;
return (id).toString(); return id.toString();
}; };

View File

@ -1,13 +1,11 @@
import { generateId } from './utils'; import { shallowRef } from "vue";
import { RouteInfo, import type { RouteLocationMatched, Router } from "vue-router";
ViewItem,
ViewStacks, import type { RouteInfo, ViewItem, ViewStacks } from "./types";
} from './types'; import { generateId } from "./utils";
import { RouteLocationMatched, Router } from 'vue-router';
import { shallowRef } from 'vue';
export const createViewStacks = (router: Router) => { export const createViewStacks = (router: Router) => {
let viewStacks: ViewStacks = {}; const viewStacks: ViewStacks = {};
/** /**
* Returns the number of active stacks. * Returns the number of active stacks.
@ -20,11 +18,11 @@ export const createViewStacks = (router: Router) => {
const clear = (outletId: number) => { const clear = (outletId: number) => {
delete viewStacks[outletId]; delete viewStacks[outletId];
} };
const getViewStack = (outletId: number) => { const getViewStack = (outletId: number) => {
return viewStacks[outletId]; return viewStacks[outletId];
} };
const registerIonPage = (viewItem: ViewItem, ionPage: HTMLElement) => { const registerIonPage = (viewItem: ViewItem, ionPage: HTMLElement) => {
viewItem.ionPageElement = ionPage; viewItem.ionPageElement = ionPage;
@ -36,45 +34,57 @@ export const createViewStacks = (router: Router) => {
* and will not run route guards that * and will not run route guards that
* are written in the component. * are written in the component.
*/ */
viewItem.matchedRoute.instances = { default: viewItem.vueComponentRef.value }; viewItem.matchedRoute.instances = {
} default: viewItem.vueComponentRef.value,
};
};
const findViewItemByRouteInfo = (routeInfo: RouteInfo, outletId?: number) => { const findViewItemByRouteInfo = (routeInfo: RouteInfo, outletId?: number) => {
return findViewItemByPath(routeInfo.pathname, outletId, false); return findViewItemByPath(routeInfo.pathname, outletId, false);
} };
const findLeavingViewItemByRouteInfo = (routeInfo: RouteInfo, outletId?: number, mustBeIonRoute: boolean = true) => { const findLeavingViewItemByRouteInfo = (
routeInfo: RouteInfo,
outletId?: number,
mustBeIonRoute = true
) => {
return findViewItemByPath(routeInfo.lastPathname, outletId, mustBeIonRoute); return findViewItemByPath(routeInfo.lastPathname, outletId, mustBeIonRoute);
} };
const findViewItemByPathname = (pathname: string, outletId?: number) => { const findViewItemByPathname = (pathname: string, outletId?: number) => {
return findViewItemByPath(pathname, outletId, false); return findViewItemByPath(pathname, outletId, false);
} };
const findViewItemInStack = (path: string, stack: ViewItem[]): ViewItem | undefined => { const findViewItemInStack = (
path: string,
stack: ViewItem[]
): ViewItem | undefined => {
return stack.find((viewItem: ViewItem) => { return stack.find((viewItem: ViewItem) => {
if (viewItem.pathname === path) { if (viewItem.pathname === path) {
return viewItem; return viewItem;
} }
return undefined; return undefined;
}) });
} };
const findViewItemByPath = (path: string, outletId?: number, mustBeIonRoute: boolean = false): ViewItem | undefined => { const findViewItemByPath = (
path: string,
outletId?: number,
mustBeIonRoute = false
): ViewItem | undefined => {
const matchView = (viewItem: ViewItem) => { const matchView = (viewItem: ViewItem) => {
if ( if ((mustBeIonRoute && !viewItem.ionRoute) || path === "") {
(mustBeIonRoute && !viewItem.ionRoute) ||
path === ''
) {
return false; return false;
} }
const resolvedPath = router.resolve(path); const resolvedPath = router.resolve(path);
const findMatchedRoute = resolvedPath.matched.find((matchedRoute: RouteLocationMatched) => matchedRoute === viewItem.matchedRoute); const findMatchedRoute = resolvedPath.matched.find(
(matchedRoute: RouteLocationMatched) =>
matchedRoute === viewItem.matchedRoute
);
if (findMatchedRoute) { if (findMatchedRoute) {
/** /**
* /page/1 and /page/2 should not match * /page/1 and /page/2 should not match
* to the same view item otherwise there will * to the same view item otherwise there will
@ -83,7 +93,7 @@ export const createViewStacks = (router: Router) => {
* so the page 2 params are properly passed * so the page 2 params are properly passed
* to the developer's app. * to the developer's app.
*/ */
const hasParameter = findMatchedRoute.path.includes(':'); const hasParameter = findMatchedRoute.path.includes(":");
if (hasParameter && path !== viewItem.pathname) { if (hasParameter && path !== viewItem.pathname) {
return false; return false;
} }
@ -92,31 +102,39 @@ export const createViewStacks = (router: Router) => {
} }
return undefined; return undefined;
} };
if (outletId) { if (outletId) {
const stack = viewStacks[outletId]; const stack = viewStacks[outletId];
if (!stack) return undefined; if (!stack) return undefined;
const match = (router) ? stack.find(matchView) : findViewItemInStack(path, stack) const match = router
? stack.find(matchView)
: findViewItemInStack(path, stack);
if (match) return match; if (match) return match;
} else { } else {
for (let outletId in viewStacks) { for (const outletId in viewStacks) {
const stack = viewStacks[outletId]; const stack = viewStacks[outletId];
const viewItem = findViewItemInStack(path, stack); const viewItem = findViewItemInStack(path, stack);
if (viewItem) { if (viewItem) {
return viewItem; return viewItem;
} }
} }
} }
return undefined; return undefined;
} };
// TODO(FW-2969): type // TODO(FW-2969): type
const createViewItem = (outletId: number, vueComponent: any, matchedRoute: RouteLocationMatched, routeInfo: RouteInfo, ionPage?: HTMLElement): ViewItem => { const createViewItem = (
outletId: number,
vueComponent: any,
matchedRoute: RouteLocationMatched,
routeInfo: RouteInfo,
ionPage?: HTMLElement
): ViewItem => {
return { return {
id: generateId('viewItem'), id: generateId("viewItem"),
pathname: routeInfo.pathname, pathname: routeInfo.pathname,
outletId, outletId,
matchedRoute, matchedRoute,
@ -127,9 +145,9 @@ export const createViewStacks = (router: Router) => {
mount: false, mount: false,
exact: routeInfo.pathname === matchedRoute.path, exact: routeInfo.pathname === matchedRoute.path,
params: routeInfo.params, params: routeInfo.params,
vueComponentData: {} vueComponentData: {},
}; };
} };
const add = (viewItem: ViewItem): void => { const add = (viewItem: ViewItem): void => {
const { outletId } = viewItem; const { outletId } = viewItem;
@ -138,25 +156,29 @@ export const createViewStacks = (router: Router) => {
} else { } else {
viewStacks[outletId].push(viewItem); viewStacks[outletId].push(viewItem);
} }
} };
const remove = (viewItem: ViewItem, outletId?: number): void => { const remove = (viewItem: ViewItem, outletId?: number): void => {
if (!outletId) { throw Error('outletId required') } if (!outletId) {
throw Error("outletId required");
}
const viewStack = viewStacks[outletId]; const viewStack = viewStacks[outletId];
if (viewStack) { if (viewStack) {
viewStacks[outletId] = viewStack.filter(item => item.id !== viewItem.id); viewStacks[outletId] = viewStack.filter(
(item) => item.id !== viewItem.id
);
} }
} };
const getChildrenToRender = (outletId: number): ViewItem[] => { const getChildrenToRender = (outletId: number): ViewItem[] => {
const viewStack = viewStacks[outletId]; const viewStack = viewStacks[outletId];
if (viewStack) { if (viewStack) {
const components = viewStacks[outletId].filter(v => v.mount); const components = viewStacks[outletId].filter((v) => v.mount);
return components; return components;
} }
return []; return [];
} };
/** /**
* When navigating backwards, we need to clean up and * When navigating backwards, we need to clean up and
@ -165,11 +187,15 @@ export const createViewStacks = (router: Router) => {
* important when using router.go and stepping back * important when using router.go and stepping back
* multiple pages at a time. * multiple pages at a time.
*/ */
const unmountLeavingViews = (outletId: number, viewItem: ViewItem, delta: number = 1) => { const unmountLeavingViews = (
outletId: number,
viewItem: ViewItem,
delta = 1
) => {
const viewStack = viewStacks[outletId]; const viewStack = viewStacks[outletId];
if (!viewStack) return; if (!viewStack) return;
const startIndex = viewStack.findIndex(v => v === viewItem); const startIndex = viewStack.findIndex((v) => v === viewItem);
for (let i = startIndex + 1; i < startIndex - delta; i++) { for (let i = startIndex + 1; i < startIndex - delta; i++) {
const viewItem = viewStack[i]; const viewItem = viewStack[i];
@ -178,7 +204,7 @@ export const createViewStacks = (router: Router) => {
viewItem.ionRoute = false; viewItem.ionRoute = false;
viewItem.matchedRoute.instances = {}; viewItem.matchedRoute.instances = {};
} }
} };
/** /**
* When navigating forward it is possible for * When navigating forward it is possible for
@ -198,16 +224,20 @@ export const createViewStacks = (router: Router) => {
* to go back to /page2 and /home, so we need both pages mounted * to go back to /page2 and /home, so we need both pages mounted
* in the DOM. * in the DOM.
*/ */
const mountIntermediaryViews = (outletId: number, viewItem: ViewItem, delta: number = 1) => { const mountIntermediaryViews = (
outletId: number,
viewItem: ViewItem,
delta = 1
) => {
const viewStack = viewStacks[outletId]; const viewStack = viewStacks[outletId];
if (!viewStack) return; if (!viewStack) return;
const startIndex = viewStack.findIndex(v => v === viewItem); const startIndex = viewStack.findIndex((v) => v === viewItem);
for (let i = startIndex + 1; i < startIndex + delta; i++) { for (let i = startIndex + 1; i < startIndex + delta; i++) {
viewStack[i].mount = true; viewStack[i].mount = true;
} }
} };
return { return {
unmountLeavingViews, unmountLeavingViews,
@ -222,6 +252,6 @@ export const createViewStacks = (router: Router) => {
remove, remove,
registerIonPage, registerIonPage,
getViewStack, getViewStack,
size size,
} };
} };