@ionic/vue-router 7.5.3-dev.11698858201.14165f23 → 7.5.3-dev.11699994454.1cbf1283

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,28 +1,1011 @@
1
- import {
2
- createRouter as createVueRouter,
3
- createWebHistory as createVueWebHistory,
4
- createWebHashHistory as createVueWebHashHistory,
5
- createMemoryHistory as createVueMemoryHistory
6
- } from "vue-router";
7
- import { createIonRouter } from "./router";
8
- import { createViewStacks } from "./viewStacks";
9
- export const createRouter = (opts) => {
10
- const routerOptions = { ...opts };
11
- delete routerOptions.tabsPrefix;
12
- const router = createVueRouter(routerOptions);
13
- const ionRouter = createIonRouter(opts, router);
14
- const viewStacks = createViewStacks(router);
15
- const oldInstall = router.install.bind(router);
16
- router.install = (app) => {
17
- app.provide("navManager", ionRouter);
18
- app.provide("viewStacks", viewStacks);
19
- oldInstall(app);
20
- };
21
- const oldIsReady = router.isReady.bind(router);
22
- router.isReady = () => oldIsReady();
23
- return router;
1
+ import { parseQuery, createRouter as createRouter$1, createWebHistory as createWebHistory$1, createWebHashHistory as createWebHashHistory$1, createMemoryHistory as createMemoryHistory$1 } from 'vue-router';
2
+ import { shallowRef } from 'vue';
3
+
4
+ const createLocationHistory = () => {
5
+ const locationHistory = [];
6
+ const tabsHistory = {};
7
+ const add = (routeInfo) => {
8
+ switch (routeInfo.routerAction) {
9
+ case "pop":
10
+ pop(routeInfo);
11
+ break;
12
+ default:
13
+ addRoute(routeInfo);
14
+ break;
15
+ }
16
+ if (routeInfo.routerDirection === "root") {
17
+ clearHistory();
18
+ addRoute(routeInfo);
19
+ }
20
+ };
21
+ const update = (routeInfo) => {
22
+ const locationIndex = locationHistory.findIndex((x) => x.id === routeInfo.id);
23
+ if (locationIndex > -1) {
24
+ locationHistory.splice(locationIndex, 1, routeInfo);
25
+ }
26
+ const tabArray = tabsHistory[routeInfo.tab || ""];
27
+ if (tabArray) {
28
+ const tabIndex = tabArray.findIndex((x) => x.id === routeInfo.id);
29
+ if (tabIndex > -1) {
30
+ tabArray.splice(tabIndex, 1, routeInfo);
31
+ }
32
+ else {
33
+ tabArray.push(routeInfo);
34
+ }
35
+ }
36
+ else if (routeInfo.tab) {
37
+ tabsHistory[routeInfo.tab] = [routeInfo];
38
+ }
39
+ };
40
+ const pop = (routeInfo) => {
41
+ const tabHistory = getTabsHistory(routeInfo.tab);
42
+ let ri;
43
+ if (tabHistory) {
44
+ // Pop all routes until we are back
45
+ ri = tabHistory[tabHistory.length - 1];
46
+ while (ri && ri.id !== routeInfo.id) {
47
+ tabHistory.pop();
48
+ ri = tabHistory[tabHistory.length - 1];
49
+ }
50
+ // Replace with updated route
51
+ tabHistory.pop();
52
+ tabHistory.push(routeInfo);
53
+ }
54
+ ri = locationHistory[locationHistory.length - 1];
55
+ while (ri && ri.id !== routeInfo.id) {
56
+ locationHistory.pop();
57
+ ri = locationHistory[locationHistory.length - 1];
58
+ }
59
+ // Replace with updated route
60
+ locationHistory.pop();
61
+ locationHistory.push(routeInfo);
62
+ };
63
+ const addRoute = (routeInfo) => {
64
+ const tabHistory = getTabsHistory(routeInfo.tab);
65
+ if (tabHistory) {
66
+ // If the latest routeInfo is the same (going back and forth between tabs), replace it
67
+ if (tabHistory[tabHistory.length - 1] &&
68
+ tabHistory[tabHistory.length - 1].id === routeInfo.id) {
69
+ tabHistory.pop();
70
+ }
71
+ tabHistory.push(routeInfo);
72
+ }
73
+ locationHistory.push(routeInfo);
74
+ };
75
+ /**
76
+ * Wipes the location history arrays.
77
+ * You can optionally provide a routeInfo
78
+ * object which will wipe that entry
79
+ * and every entry that appears after it.
80
+ */
81
+ const clearHistory = (routeInfo) => {
82
+ if (routeInfo) {
83
+ const { position, tab } = routeInfo;
84
+ /**
85
+ * If there is no route index in locationHistory
86
+ * then there will not be any route index in
87
+ * tabs either.
88
+ */
89
+ const existingRouteIndex = locationHistory.findIndex((r) => r.position === position);
90
+ if (existingRouteIndex === -1)
91
+ return;
92
+ locationHistory.splice(existingRouteIndex);
93
+ const clearTabHistory = (tab) => {
94
+ const existingTabRouteIndex = tabsHistory[tab].findIndex((r) => r.position === position);
95
+ if (existingTabRouteIndex === -1)
96
+ return;
97
+ tabsHistory[tab].splice(existingTabRouteIndex);
98
+ };
99
+ /**
100
+ * We also need to search the current tab
101
+ * to correctly reset the individual tab
102
+ * stack. We should not clear the entire
103
+ * tab stack as that means we will lose
104
+ * a reference to the root tab route.
105
+ */
106
+ const tabHistory = tabsHistory[tab];
107
+ if (tab && tabHistory) {
108
+ clearTabHistory(tab);
109
+ /**
110
+ * If we are not clearing items after
111
+ * a tabs page, it is still possible
112
+ * that there are future tabs pages to clear.
113
+ * As a result, we need to search through
114
+ * all the tab stacks and remove views that appear
115
+ * after the given routeInfo.
116
+ *
117
+ * Example: /non-tabs-page --> /tabs/tab1 --> /non-tabs-page
118
+ * (via router.go(-1)) --> /tabs/tab2. The /tabs/tab1 history
119
+ * has been overwritten with /tabs/tab2. As a result,
120
+ * the /tabs/tab1 route info in the Tab 1 stack should be removed.
121
+ */
122
+ }
123
+ else {
124
+ for (const tab in tabsHistory) {
125
+ clearTabHistory(tab);
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ for (const tab in tabsHistory) {
131
+ tabsHistory[tab] = [];
132
+ }
133
+ locationHistory.length = 0;
134
+ }
135
+ };
136
+ const getTabsHistory = (tab) => {
137
+ let history;
138
+ if (tab) {
139
+ history = tabsHistory[tab];
140
+ if (!history) {
141
+ history = tabsHistory[tab] = [];
142
+ }
143
+ }
144
+ return history;
145
+ };
146
+ const size = () => locationHistory.length;
147
+ /**
148
+ * Finds and returns the location history item
149
+ * given the state of browser's history API.
150
+ * This is useful when jumping around in browser
151
+ * history using router.go.
152
+ */
153
+ const current = (initialHistory, currentHistory) => {
154
+ /**
155
+ * initialHistory does not always start at 0 if users navigated
156
+ * to app from another website, so doing this math lets us
157
+ * find the correct index in our locationHistory array.
158
+ */
159
+ const index = currentHistory - initialHistory;
160
+ return locationHistory[index] || last();
161
+ };
162
+ const last = () => locationHistory[locationHistory.length - 1];
163
+ /**
164
+ * With the introduction of router.go support, we no longer remove
165
+ * items from locationHistory as they may be needed again in the future.
166
+ * As a result, we need to look at the current position in location history
167
+ * to see if users can navigate back n pages. Previously we were checking
168
+ * the length of locationHistory, but that only worked since we were pruning
169
+ * the array.
170
+ */
171
+ const canGoBack = (deep = 1, initialHistory, currentHistory) => {
172
+ return currentHistory - deep >= initialHistory;
173
+ };
174
+ const getFirstRouteInfoForTab = (tab) => {
175
+ const tabHistory = getTabsHistory(tab);
176
+ if (tabHistory) {
177
+ return tabHistory[0];
178
+ }
179
+ return undefined;
180
+ };
181
+ const getCurrentRouteInfoForTab = (tab) => {
182
+ const tabHistory = getTabsHistory(tab);
183
+ if (tabHistory) {
184
+ return tabHistory[tabHistory.length - 1];
185
+ }
186
+ return undefined;
187
+ };
188
+ /**
189
+ * Finds and returns the previous view based upon
190
+ * what originally pushed it (pushedByRoute).
191
+ * When `delta` < -1 then we should just index into
192
+ * to array because the previous view that we want is not
193
+ * necessarily the view that pushed our current view.
194
+ * Additionally, when jumping around in history, we
195
+ * do not modify the locationHistory stack so we would
196
+ * not update pushedByRoute anyways.
197
+ */
198
+ const findLastLocation = (routeInfo, delta = -1) => {
199
+ const routeInfos = getTabsHistory(routeInfo.tab);
200
+ if (routeInfos) {
201
+ if (delta < -1) {
202
+ return routeInfos[routeInfos.length - 1 + delta];
203
+ }
204
+ else {
205
+ for (let i = routeInfos.length - 2; i >= 0; i--) {
206
+ const ri = routeInfos[i];
207
+ if (ri) {
208
+ if (ri.pathname === routeInfo.pushedByRoute) {
209
+ return ri;
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ if (delta < -1) {
216
+ return locationHistory[locationHistory.length - 1 + delta];
217
+ }
218
+ else {
219
+ for (let i = locationHistory.length - 2; i >= 0; i--) {
220
+ const ri = locationHistory[i];
221
+ if (ri) {
222
+ if (ri.pathname === routeInfo.pushedByRoute) {
223
+ return ri;
224
+ }
225
+ }
226
+ }
227
+ }
228
+ return undefined;
229
+ };
230
+ return {
231
+ current,
232
+ size,
233
+ last,
234
+ add,
235
+ canGoBack,
236
+ update,
237
+ getFirstRouteInfoForTab,
238
+ getCurrentRouteInfoForTab,
239
+ findLastLocation,
240
+ clearHistory,
241
+ };
24
242
  };
25
- export const createWebHistory = (base) => createVueWebHistory(base);
26
- export const createWebHashHistory = (base) => createVueWebHashHistory(base);
27
- export const createMemoryHistory = (base) => createVueMemoryHistory(base);
243
+
244
+ const ids = { main: 0 };
245
+ const generateId = (type = "main") => {
246
+ var _a;
247
+ const id = ((_a = ids[type]) !== null && _a !== void 0 ? _a : 0) + 1;
248
+ ids[type] = id;
249
+ return id.toString();
250
+ };
251
+
252
+ // TODO(FW-2969): types
253
+ const createIonRouter = (opts, router) => {
254
+ let currentNavigationInfo = {
255
+ direction: undefined,
256
+ action: undefined,
257
+ delta: undefined,
258
+ };
259
+ /**
260
+ * Ionic Vue should only react to navigation
261
+ * changes once they have been confirmed and should
262
+ * never affect the outcome of navigation (with the
263
+ * exception of going back or selecting a tab).
264
+ * As a result, we should do our work in afterEach
265
+ * which is fired once navigation is confirmed
266
+ * and any user guards have run.
267
+ */
268
+ router.afterEach((to, _, failure) => {
269
+ if (failure)
270
+ return;
271
+ const { direction, action, delta } = currentNavigationInfo;
272
+ /**
273
+ * When calling router.replace, we are not informed
274
+ * about the replace action in opts.history.listen
275
+ * but we can check to see if the latest routing action
276
+ * was a replace action by looking at the history state.
277
+ * We need to use opts.history rather than window.history
278
+ * because window.history will be undefined when using SSR.
279
+ */
280
+ currentHistoryPosition = opts.history.state.position;
281
+ const replaceAction = opts.history.state.replaced ? "replace" : undefined;
282
+ handleHistoryChange(to, action || replaceAction, direction, delta);
283
+ currentNavigationInfo = {
284
+ direction: undefined,
285
+ action: undefined,
286
+ delta: undefined,
287
+ };
288
+ });
289
+ const locationHistory = createLocationHistory();
290
+ /**
291
+ * Keeping track of the history position
292
+ * allows us to determine if a user is pushing
293
+ * new pages or updating history via the forward
294
+ * and back browser buttons.
295
+ */
296
+ let initialHistoryPosition = opts.history.state.position;
297
+ let currentHistoryPosition = opts.history.state.position;
298
+ let currentRouteInfo;
299
+ let incomingRouteParams;
300
+ const historyChangeListeners = [];
301
+ if (typeof document !== "undefined") {
302
+ document.addEventListener("ionBackButton", (ev) => {
303
+ ev.detail.register(0, (processNextHandler) => {
304
+ opts.history.go(-1);
305
+ processNextHandler();
306
+ });
307
+ });
308
+ }
309
+ opts.history.listen((_, _x, info) => {
310
+ /**
311
+ * history.listen only fires on certain
312
+ * event such as when the user clicks the
313
+ * browser back button. It also gives us
314
+ * additional information as to the type
315
+ * of navigation (forward, backward, etc).
316
+ *
317
+ * We can use this to better handle the
318
+ * `handleHistoryChange` call in
319
+ * router.beforeEach
320
+ */
321
+ currentNavigationInfo = {
322
+ delta: info.delta,
323
+ /**
324
+ * Both the browser forward and backward actions
325
+ * are considered "pop" actions, but when going forward
326
+ * we want to make sure the forward animation is used.
327
+ */
328
+ action: info.type === "pop" && info.delta >= 1 ? "push" : info.type,
329
+ direction: info.direction === "" ? "forward" : info.direction,
330
+ };
331
+ });
332
+ const handleNavigateBack = (defaultHref, routerAnimation) => {
333
+ const routeInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition);
334
+ if (routeInfo && routeInfo.pushedByRoute) {
335
+ const prevInfo = locationHistory.findLastLocation(routeInfo);
336
+ if (prevInfo) {
337
+ incomingRouteParams = Object.assign(Object.assign({}, prevInfo), { routerAction: "pop", routerDirection: "back", routerAnimation: routerAnimation || routeInfo.routerAnimation });
338
+ if (routeInfo.lastPathname === routeInfo.pushedByRoute ||
339
+ /**
340
+ * We need to exclude tab switches/tab
341
+ * context changes here because tabbed
342
+ * navigation is not linear, but router.back()
343
+ * will go back in a linear fashion.
344
+ */
345
+ (prevInfo.pathname === routeInfo.pushedByRoute &&
346
+ /**
347
+ * Tab info can be undefined or '' (empty string)
348
+ * both are false-y values, so we can just use !.
349
+ */
350
+ !routeInfo.tab &&
351
+ !prevInfo.tab)) {
352
+ router.back();
353
+ }
354
+ else {
355
+ /**
356
+ * When going back to a child page of a tab
357
+ * after being on another tab, we need to use
358
+ * router.go() here instead of pushing or replacing.
359
+ * Consider the following example:
360
+ * /tabs/tab1 --> /tabs/tab1/child1 --> /tabs/tab1/child2
361
+ * --> /tabs/tab2 (via Tab 2 button) --> /tabs/tab1/child2 (via Tab 1 button)
362
+ *
363
+ * Pressing the ion-back-button on /tabs/tab1/child2 should take
364
+ * us back to /tabs/tab1/child1 not /tabs/tab2 because each tab
365
+ * is its own stack.
366
+ *
367
+ * If we called pressed the ion-back-button and this code called
368
+ * router.replace, then the state of /tabs/tab1/child2 would
369
+ * be replaced with /tabs/tab1/child1. However, this means that
370
+ * there would be two /tabs/tab1/child1 entries in the location
371
+ * history as the original /tabs/tab1/child1 entry is still there.
372
+ * As a result, clicking the ion-back-button on /tabs/tab1/child1 does
373
+ * nothing because this code would try to route to the same page
374
+ * we are currently on.
375
+ *
376
+ * If we called router.push instead then we would push a
377
+ * new /tabs/tab1/child1 entry to the location history. This
378
+ * is not good because we would have two /tabs/tab1/child1 entries
379
+ * separated by a /tabs/tab1/child2 entry.
380
+ */
381
+ router.go(prevInfo.position - routeInfo.position);
382
+ }
383
+ }
384
+ else {
385
+ handleNavigate(defaultHref, "pop", "back", routerAnimation);
386
+ }
387
+ }
388
+ else {
389
+ handleNavigate(defaultHref, "pop", "back", routerAnimation);
390
+ }
391
+ };
392
+ const handleNavigate = (path, routerAction, routerDirection, routerAnimation, tab) => {
393
+ setIncomingRouteParams(routerAction, routerDirection, routerAnimation, tab);
394
+ if (routerAction === "push") {
395
+ router.push(path);
396
+ }
397
+ else {
398
+ router.replace(path);
399
+ }
400
+ };
401
+ // TODO RouteLocationNormalized
402
+ const handleHistoryChange = (location, action, direction, delta) => {
403
+ let leavingLocationInfo;
404
+ if (incomingRouteParams) {
405
+ /**
406
+ * If we are replacing the state of a route
407
+ * with another route, the "leaving" route
408
+ * is at the same position in location history
409
+ * as where the replaced route will exist.
410
+ */
411
+ if (incomingRouteParams.routerAction === "replace") {
412
+ leavingLocationInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition);
413
+ }
414
+ else if (incomingRouteParams.routerAction === "pop") {
415
+ leavingLocationInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition + 1);
416
+ /**
417
+ * If the Ionic Router action was "pop"
418
+ * and the browser history action was "replace", then
419
+ * it is the case that the user clicked an IonBackButton
420
+ * that is trying to go back to the route specified
421
+ * by the defaultHref property.
422
+ *
423
+ * The problem is that this route currently does
424
+ * not exist in the browser history, and we cannot
425
+ * prepend an item in the browser's history stack.
426
+ * To work around this, we replace the state of
427
+ * the current item instead.
428
+ * Given this scenario:
429
+ * /page2 --> /page3 --> (back) /page2 --> (defaultHref) /page1
430
+ * We would replace the state of /page2 with the state of /page1.
431
+ *
432
+ * When doing this, we are essentially re-writing past
433
+ * history which makes the future history no longer relevant.
434
+ * As a result, we clear out the location history so that users
435
+ * can begin pushing new routes to the stack.
436
+ *
437
+ * This pattern is aligned with how the browser handles
438
+ * pushing new routes after going back as well as how
439
+ * other stack based operations such as undo/redo work.
440
+ * For example, if you do tasks A, B, C, undo B and C, and
441
+ * then do task D, you cannot "redo" B and C because you
442
+ * rewrote the stack's past history.
443
+ *
444
+ * With browser history, it is a similar concept.
445
+ * Going /page1 --> /page2 --> /page3 and then doing
446
+ * router.go(-2) will bring you back to /page1.
447
+ * If you then push /page4, you have rewritten
448
+ * the past history and you can no longer go
449
+ * forward to /page2 or /page3.
450
+ */
451
+ if (action === "replace") {
452
+ locationHistory.clearHistory();
453
+ }
454
+ }
455
+ else {
456
+ /**
457
+ * If the routerDirection was specified as "root", then
458
+ * we are replacing the initial state of location history
459
+ * with this incoming route. As a result, the leaving
460
+ * history info is stored at the same location as
461
+ * where the incoming history location will be stored.
462
+ *
463
+ * Otherwise, we can assume this is just another route
464
+ * that will be pushed onto the end of location history,
465
+ * so we can grab the previous item in history relative
466
+ * to where the history state currently is.
467
+ */
468
+ const position = incomingRouteParams.routerDirection === "root"
469
+ ? currentHistoryPosition
470
+ : currentHistoryPosition - 1;
471
+ leavingLocationInfo = locationHistory.current(initialHistoryPosition, position);
472
+ }
473
+ }
474
+ else {
475
+ leavingLocationInfo = currentRouteInfo;
476
+ }
477
+ if (!leavingLocationInfo) {
478
+ leavingLocationInfo = {
479
+ pathname: "",
480
+ search: "",
481
+ };
482
+ }
483
+ const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search;
484
+ if (leavingUrl !== location.fullPath) {
485
+ if (!incomingRouteParams) {
486
+ if (action === "replace") {
487
+ incomingRouteParams = {
488
+ routerAction: "replace",
489
+ routerDirection: "none",
490
+ };
491
+ }
492
+ else if (action === "pop") {
493
+ const routeInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition - delta);
494
+ if (routeInfo && routeInfo.pushedByRoute) {
495
+ const prevRouteInfo = locationHistory.findLastLocation(routeInfo, delta);
496
+ incomingRouteParams = Object.assign(Object.assign({}, prevRouteInfo), { routerAction: "pop", routerDirection: "back" });
497
+ }
498
+ else {
499
+ incomingRouteParams = {
500
+ routerAction: "pop",
501
+ routerDirection: "none",
502
+ };
503
+ }
504
+ }
505
+ if (!incomingRouteParams) {
506
+ incomingRouteParams = {
507
+ routerAction: "push",
508
+ routerDirection: direction || "forward",
509
+ };
510
+ }
511
+ }
512
+ let routeInfo;
513
+ if (incomingRouteParams === null || incomingRouteParams === void 0 ? void 0 : incomingRouteParams.id) {
514
+ routeInfo = Object.assign(Object.assign({}, incomingRouteParams), { lastPathname: leavingLocationInfo.pathname });
515
+ }
516
+ else {
517
+ const isPushed = incomingRouteParams.routerAction === "push" &&
518
+ incomingRouteParams.routerDirection === "forward";
519
+ routeInfo = Object.assign(Object.assign({ id: generateId("routeInfo") }, incomingRouteParams), { lastPathname: leavingLocationInfo.pathname, pathname: location.path, search: (location.fullPath && location.fullPath.split("?")[1]) || "", params: location.params && location.params, prevRouteLastPathname: leavingLocationInfo.lastPathname });
520
+ if (isPushed) {
521
+ routeInfo.pushedByRoute =
522
+ leavingLocationInfo.pathname !== ""
523
+ ? leavingLocationInfo.pathname
524
+ : undefined;
525
+ }
526
+ else if (routeInfo.routerAction === "pop") {
527
+ const route = locationHistory.findLastLocation(routeInfo);
528
+ routeInfo.pushedByRoute = route === null || route === void 0 ? void 0 : route.pushedByRoute;
529
+ }
530
+ else if (routeInfo.routerAction === "push" &&
531
+ routeInfo.tab !== leavingLocationInfo.tab) {
532
+ const lastRoute = locationHistory.getCurrentRouteInfoForTab(routeInfo.tab);
533
+ routeInfo.pushedByRoute = lastRoute === null || lastRoute === void 0 ? void 0 : lastRoute.pushedByRoute;
534
+ }
535
+ else if (routeInfo.routerAction === "replace") {
536
+ /**
537
+ * When replacing a route, we want to make sure we select the current route
538
+ * that we are on, not the last route in the stack. The last route in the stack
539
+ * is not always the current route.
540
+ * Example:
541
+ * Given the following history: /page1 --> /page2
542
+ * Doing router.go(-1) would bring you to /page1.
543
+ * If you then did router.replace('/page3'), /page1 should
544
+ * be replaced with /page3 even though /page2 is the last
545
+ * item in the stack/
546
+ */
547
+ const currentRouteInfo = locationHistory.current(initialHistoryPosition, currentHistoryPosition);
548
+ /**
549
+ * If going from /home to /child, then replacing from
550
+ * /child to /home, we don't want the route info to
551
+ * say that /home was pushed by /home which is not correct.
552
+ */
553
+ const currentPushedBy = currentRouteInfo === null || currentRouteInfo === void 0 ? void 0 : currentRouteInfo.pushedByRoute;
554
+ const pushedByRoute = currentPushedBy !== undefined &&
555
+ currentPushedBy !== routeInfo.pathname
556
+ ? currentPushedBy
557
+ : routeInfo.pushedByRoute;
558
+ routeInfo.lastPathname =
559
+ (currentRouteInfo === null || currentRouteInfo === void 0 ? void 0 : currentRouteInfo.pathname) || routeInfo.lastPathname;
560
+ routeInfo.pushedByRoute = pushedByRoute;
561
+ routeInfo.routerDirection =
562
+ (currentRouteInfo === null || currentRouteInfo === void 0 ? void 0 : currentRouteInfo.routerDirection) || routeInfo.routerDirection;
563
+ routeInfo.routerAnimation =
564
+ (currentRouteInfo === null || currentRouteInfo === void 0 ? void 0 : currentRouteInfo.routerAnimation) || routeInfo.routerAnimation;
565
+ routeInfo.prevRouteLastPathname = currentRouteInfo === null || currentRouteInfo === void 0 ? void 0 : currentRouteInfo.lastPathname;
566
+ }
567
+ }
568
+ routeInfo.position = currentHistoryPosition;
569
+ routeInfo.delta = delta;
570
+ const historySize = locationHistory.size();
571
+ const historyDiff = currentHistoryPosition - initialHistoryPosition;
572
+ /**
573
+ * If the size of location history is greater
574
+ * than the difference between the current history
575
+ * position and the initial history position
576
+ * then we are guaranteed to already have a history
577
+ * item for this route. In other words, a user
578
+ * is navigating within the history without pushing
579
+ * new items within the stack.
580
+ *
581
+ * If the historySize === historyDiff,
582
+ * then we are still re-writing history
583
+ * by replacing the current route state
584
+ * with a new route state. The initial
585
+ * action when loading an app is
586
+ * going to be replace operation, so
587
+ * we want to make sure we exclude that
588
+ * action by ensuring historySize > 0.
589
+ */
590
+ const isReplacing = historySize === historyDiff && historySize > 0 && action === "replace";
591
+ if (historySize > historyDiff || isReplacing) {
592
+ /**
593
+ * When navigating back through the history,
594
+ * if users then push a new route the future
595
+ * history stack is no longer relevant. As
596
+ * a result, we need to clear out all entries
597
+ * that appear after the current routeInfo
598
+ * so that we can then append the new history.
599
+ *
600
+ * This does not apply when using router.go
601
+ * as that is traversing through the history,
602
+ * not altering it.
603
+ *
604
+ * Previously we had only updated the existing route
605
+ * and then left the future history alone. That
606
+ * worked for some use cases but was not sufficient
607
+ * in other scenarios.
608
+ */
609
+ if ((routeInfo.routerAction === "push" ||
610
+ routeInfo.routerAction === "replace") &&
611
+ delta === undefined) {
612
+ locationHistory.clearHistory(routeInfo);
613
+ locationHistory.add(routeInfo);
614
+ }
615
+ }
616
+ else {
617
+ locationHistory.add(routeInfo);
618
+ }
619
+ /**
620
+ * If we recently reset the location history
621
+ * then we also need to update the initial
622
+ * history position.
623
+ */
624
+ if (locationHistory.size() === 1) {
625
+ initialHistoryPosition = routeInfo.position;
626
+ }
627
+ currentRouteInfo = routeInfo;
628
+ }
629
+ incomingRouteParams = undefined;
630
+ historyChangeListeners.forEach((cb) => cb(currentRouteInfo));
631
+ };
632
+ const getCurrentRouteInfo = () => currentRouteInfo;
633
+ const canGoBack = (deep = 1) => locationHistory.canGoBack(deep, initialHistoryPosition, currentHistoryPosition);
634
+ const navigate = (navigationOptions) => {
635
+ const { routerAnimation, routerDirection, routerLink } = navigationOptions;
636
+ setIncomingRouteParams("push", routerDirection, routerAnimation);
637
+ router.push(routerLink);
638
+ };
639
+ const resetTab = (tab) => {
640
+ /**
641
+ * Resetting the tab should go back
642
+ * to the initial view in the tab stack.
643
+ * It should not push a new instance of the
644
+ * root tab page onto the stack.
645
+ *
646
+ * To do this, we get the initial view in the
647
+ * tab stack and subtract the position of that
648
+ * entry from our current position. From there
649
+ * we call router.go() to move us back the
650
+ * appropriate number of positions.
651
+ */
652
+ const routeInfo = locationHistory.getFirstRouteInfoForTab(tab);
653
+ if (routeInfo) {
654
+ router.go(routeInfo.position - currentHistoryPosition);
655
+ }
656
+ };
657
+ const changeTab = (tab, path) => {
658
+ if (!path)
659
+ return;
660
+ const routeInfo = locationHistory.getCurrentRouteInfoForTab(tab);
661
+ const [pathname] = path.split("?");
662
+ if (routeInfo) {
663
+ incomingRouteParams = Object.assign(Object.assign({}, incomingRouteParams), { routerAction: "push", routerDirection: "none", tab });
664
+ /**
665
+ * When going back to a tab
666
+ * you just left, it's possible
667
+ * for the route info to be incorrect
668
+ * as the tab you want is not the
669
+ * tab you are on.
670
+ */
671
+ if (routeInfo.pathname === pathname) {
672
+ router.push({
673
+ path: routeInfo.pathname,
674
+ query: parseQuery(routeInfo.search),
675
+ });
676
+ }
677
+ else {
678
+ router.push({ path: pathname, query: parseQuery(routeInfo.search) });
679
+ }
680
+ }
681
+ else {
682
+ handleNavigate(pathname, "push", "none", undefined, tab);
683
+ }
684
+ };
685
+ /**
686
+ * This method is invoked by the IonTabs component
687
+ * during a history change callback. It is responsible
688
+ * for ensuring that tabbed routes have the correct
689
+ * "tab" field in its routeInfo object.
690
+ *
691
+ * IonTabs will determine if the current route
692
+ * is in tabs and assign it the correct tab.
693
+ * If the current route is not in tabs,
694
+ * then IonTabs will not invoke this.
695
+ */
696
+ const handleSetCurrentTab = (tab) => {
697
+ /**
698
+ * Note that the current page that we
699
+ * are on is not necessarily the last item
700
+ * in the locationHistory stack. As a result,
701
+ * we cannot use locationHistory.last() here.
702
+ */
703
+ const ri = Object.assign({}, locationHistory.current(initialHistoryPosition, currentHistoryPosition));
704
+ /**
705
+ * handleHistoryChange is tabs-agnostic by design.
706
+ * One side effect of this is that certain tabs
707
+ * routes have extraneous/incorrect information
708
+ * that we need to remove. To not tightly couple
709
+ * handleHistoryChange with tabs, we let the
710
+ * handleSetCurrentTab function. This function is
711
+ * only called by IonTabs.
712
+ */
713
+ if (ri.tab !== tab) {
714
+ ri.tab = tab;
715
+ locationHistory.update(ri);
716
+ }
717
+ /**
718
+ * lastPathname typically equals pushedByRoute
719
+ * when navigating in a linear manner. When switching between
720
+ * tabs, this is almost never the case.
721
+ *
722
+ * Example: /tabs/tabs1 --> /tabs/tab2 --> /tabs/tab1
723
+ * The latest Tab 1 route would have the following information
724
+ * lastPathname: '/tabs/tab2'
725
+ * pushedByRoute: '/tabs/tab2'
726
+ *
727
+ * A tab cannot push another tab, so we need to set
728
+ * pushedByRoute to `undefined`. Alternative way of thinking
729
+ * about this: You cannot swipe to go back from Tab 1 to Tab 2.
730
+ *
731
+ * However, there are some instances where we do want to keep
732
+ * the pushedByRoute. As a result, we need to ensure that
733
+ * we only wipe the pushedByRoute state when the both of the
734
+ * following conditions are met:
735
+ * 1. pushedByRoute is different from lastPathname
736
+ * 2. The tab for the pushedByRoute info is different
737
+ * from the current route tab.
738
+ *
739
+ * Example of when we would not want to clear pushedByRoute:
740
+ * /tabs/tab1 --> /tabs/tab1/child --> /tabs/tab2 --> /tabs/tab1/child
741
+ * The latest Tab 1 Child route would have the following information:
742
+ * lastPathname: '/tabs/tab2'
743
+ * pushedByRoute: '/tabs/tab1
744
+ *
745
+ * In this case, /tabs/tab1/child should be able to swipe to go back
746
+ * to /tabs/tab1 so we want to keep the pushedByRoute.
747
+ */
748
+ const pushedByRoute = locationHistory.findLastLocation(ri);
749
+ if (ri.pushedByRoute !== ri.lastPathname && (pushedByRoute === null || pushedByRoute === void 0 ? void 0 : pushedByRoute.tab) !== tab) {
750
+ ri.pushedByRoute = undefined;
751
+ locationHistory.update(ri);
752
+ }
753
+ };
754
+ const registerHistoryChangeListener = (cb) => {
755
+ historyChangeListeners.push(cb);
756
+ };
757
+ const setIncomingRouteParams = (routerAction = "push", routerDirection = "forward", routerAnimation, tab) => {
758
+ incomingRouteParams = {
759
+ routerAction,
760
+ routerDirection,
761
+ routerAnimation,
762
+ tab,
763
+ };
764
+ };
765
+ const goBack = (routerAnimation) => {
766
+ setIncomingRouteParams("pop", "back", routerAnimation);
767
+ router.back();
768
+ };
769
+ const goForward = (routerAnimation) => {
770
+ setIncomingRouteParams("push", "forward", routerAnimation);
771
+ router.forward();
772
+ };
773
+ const getLeavingRouteInfo = () => {
774
+ return locationHistory.current(initialHistoryPosition, currentHistoryPosition);
775
+ };
776
+ return {
777
+ handleNavigate,
778
+ getLeavingRouteInfo,
779
+ handleNavigateBack,
780
+ handleSetCurrentTab,
781
+ getCurrentRouteInfo,
782
+ canGoBack,
783
+ navigate,
784
+ resetTab,
785
+ changeTab,
786
+ registerHistoryChangeListener,
787
+ goBack,
788
+ goForward,
789
+ };
790
+ };
791
+
792
+ const createViewStacks = (router) => {
793
+ const viewStacks = {};
794
+ /**
795
+ * Returns the number of active stacks.
796
+ * This is useful for determining if an app
797
+ * is using linear navigation only or non-linear
798
+ * navigation. Multiple stacks indiciate an app
799
+ * is using non-linear navigation.
800
+ */
801
+ const size = () => Object.keys(viewStacks).length;
802
+ const clear = (outletId) => {
803
+ delete viewStacks[outletId];
804
+ };
805
+ const getViewStack = (outletId) => {
806
+ return viewStacks[outletId];
807
+ };
808
+ const registerIonPage = (viewItem, ionPage) => {
809
+ viewItem.ionPageElement = ionPage;
810
+ viewItem.ionRoute = true;
811
+ /**
812
+ * This is needed otherwise Vue Router
813
+ * will not consider this component mounted
814
+ * and will not run route guards that
815
+ * are written in the component.
816
+ */
817
+ viewItem.matchedRoute.instances = {
818
+ default: viewItem.vueComponentRef.value,
819
+ };
820
+ };
821
+ const findViewItemByRouteInfo = (routeInfo, outletId) => {
822
+ return findViewItemByPath(routeInfo.pathname, outletId, false);
823
+ };
824
+ const findLeavingViewItemByRouteInfo = (routeInfo, outletId, mustBeIonRoute = true) => {
825
+ return findViewItemByPath(routeInfo.lastPathname, outletId, mustBeIonRoute);
826
+ };
827
+ const findViewItemByPathname = (pathname, outletId) => {
828
+ return findViewItemByPath(pathname, outletId, false);
829
+ };
830
+ const findViewItemInStack = (path, stack) => {
831
+ return stack.find((viewItem) => {
832
+ if (viewItem.pathname === path) {
833
+ return viewItem;
834
+ }
835
+ return undefined;
836
+ });
837
+ };
838
+ const findViewItemByPath = (path, outletId, mustBeIonRoute = false) => {
839
+ const matchView = (viewItem) => {
840
+ if ((mustBeIonRoute && !viewItem.ionRoute) || path === "") {
841
+ return false;
842
+ }
843
+ const resolvedPath = router.resolve(path);
844
+ const findMatchedRoute = resolvedPath.matched.find((matchedRoute) => matchedRoute === viewItem.matchedRoute);
845
+ if (findMatchedRoute) {
846
+ /**
847
+ * /page/1 and /page/2 should not match
848
+ * to the same view item otherwise there will
849
+ * be not page transition and we will need to
850
+ * explicitly clear out parameters from page 1
851
+ * so the page 2 params are properly passed
852
+ * to the developer's app.
853
+ */
854
+ const hasParameter = findMatchedRoute.path.includes(":");
855
+ if (hasParameter && path !== viewItem.pathname) {
856
+ return false;
857
+ }
858
+ return viewItem;
859
+ }
860
+ return undefined;
861
+ };
862
+ if (outletId) {
863
+ const stack = viewStacks[outletId];
864
+ if (!stack)
865
+ return undefined;
866
+ const match = router
867
+ ? stack.find(matchView)
868
+ : findViewItemInStack(path, stack);
869
+ if (match)
870
+ return match;
871
+ }
872
+ else {
873
+ for (const outletId in viewStacks) {
874
+ const stack = viewStacks[outletId];
875
+ const viewItem = findViewItemInStack(path, stack);
876
+ if (viewItem) {
877
+ return viewItem;
878
+ }
879
+ }
880
+ }
881
+ return undefined;
882
+ };
883
+ // TODO(FW-2969): type
884
+ const createViewItem = (outletId, vueComponent, matchedRoute, routeInfo, ionPage) => {
885
+ return {
886
+ id: generateId("viewItem"),
887
+ pathname: routeInfo.pathname,
888
+ outletId,
889
+ matchedRoute,
890
+ ionPageElement: ionPage,
891
+ vueComponent,
892
+ vueComponentRef: shallowRef(),
893
+ ionRoute: false,
894
+ mount: false,
895
+ exact: routeInfo.pathname === matchedRoute.path,
896
+ params: routeInfo.params,
897
+ vueComponentData: {},
898
+ };
899
+ };
900
+ const add = (viewItem) => {
901
+ const { outletId } = viewItem;
902
+ if (!viewStacks[outletId]) {
903
+ viewStacks[outletId] = [viewItem];
904
+ }
905
+ else {
906
+ viewStacks[outletId].push(viewItem);
907
+ }
908
+ };
909
+ const remove = (viewItem, outletId) => {
910
+ if (!outletId) {
911
+ throw Error("outletId required");
912
+ }
913
+ const viewStack = viewStacks[outletId];
914
+ if (viewStack) {
915
+ viewStacks[outletId] = viewStack.filter((item) => item.id !== viewItem.id);
916
+ }
917
+ };
918
+ const getChildrenToRender = (outletId) => {
919
+ const viewStack = viewStacks[outletId];
920
+ if (viewStack) {
921
+ const components = viewStacks[outletId].filter((v) => v.mount);
922
+ return components;
923
+ }
924
+ return [];
925
+ };
926
+ /**
927
+ * When navigating backwards, we need to clean up and
928
+ * leaving pages so that they are re-created if
929
+ * we ever navigate back to them. This is especially
930
+ * important when using router.go and stepping back
931
+ * multiple pages at a time.
932
+ */
933
+ const unmountLeavingViews = (outletId, viewItem, delta = 1) => {
934
+ const viewStack = viewStacks[outletId];
935
+ if (!viewStack)
936
+ return;
937
+ const startIndex = viewStack.findIndex((v) => v === viewItem);
938
+ for (let i = startIndex + 1; i < startIndex - delta; i++) {
939
+ const viewItem = viewStack[i];
940
+ viewItem.mount = false;
941
+ viewItem.ionPageElement = undefined;
942
+ viewItem.ionRoute = false;
943
+ viewItem.matchedRoute.instances = {};
944
+ }
945
+ };
946
+ /**
947
+ * When navigating forward it is possible for
948
+ * developers to step forward over multiple views.
949
+ * The intermediary views need to be remounted so that
950
+ * swipe to go back works properly.
951
+ * We need to account for the delta value here too because
952
+ * we do not want to remount an unrelated view.
953
+ * Example:
954
+ * /home --> /page2 --> router.back() --> /page3
955
+ * Going to /page3 would remount /page2 since we do
956
+ * not prune /page2 from the stack. However, /page2
957
+ * needs to remain in the stack.
958
+ * Example:
959
+ * /home --> /page2 --> /page3 --> router.go(-2) --> router.go(2)
960
+ * We would end up on /page3, but users need to be able to swipe
961
+ * to go back to /page2 and /home, so we need both pages mounted
962
+ * in the DOM.
963
+ */
964
+ const mountIntermediaryViews = (outletId, viewItem, delta = 1) => {
965
+ const viewStack = viewStacks[outletId];
966
+ if (!viewStack)
967
+ return;
968
+ const startIndex = viewStack.findIndex((v) => v === viewItem);
969
+ for (let i = startIndex + 1; i < startIndex + delta; i++) {
970
+ viewStack[i].mount = true;
971
+ }
972
+ };
973
+ return {
974
+ unmountLeavingViews,
975
+ mountIntermediaryViews,
976
+ clear,
977
+ findViewItemByRouteInfo,
978
+ findLeavingViewItemByRouteInfo,
979
+ findViewItemByPathname,
980
+ createViewItem,
981
+ getChildrenToRender,
982
+ add,
983
+ remove,
984
+ registerIonPage,
985
+ getViewStack,
986
+ size,
987
+ };
988
+ };
989
+
990
+ const createRouter = (opts) => {
991
+ const routerOptions = Object.assign({}, opts);
992
+ delete routerOptions.tabsPrefix;
993
+ const router = createRouter$1(routerOptions);
994
+ const ionRouter = createIonRouter(opts, router);
995
+ const viewStacks = createViewStacks(router);
996
+ const oldInstall = router.install.bind(router);
997
+ router.install = (app) => {
998
+ app.provide("navManager", ionRouter);
999
+ app.provide("viewStacks", viewStacks);
1000
+ oldInstall(app);
1001
+ };
1002
+ const oldIsReady = router.isReady.bind(router);
1003
+ router.isReady = () => oldIsReady();
1004
+ return router;
1005
+ };
1006
+ const createWebHistory = (base) => createWebHistory$1(base);
1007
+ const createWebHashHistory = (base) => createWebHashHistory$1(base);
1008
+ const createMemoryHistory = (base) => createMemoryHistory$1(base);
1009
+
1010
+ export { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory };
28
1011
  //# sourceMappingURL=index.js.map