@amazon-devices/react-navigation__routers 7.0.1759135970 → 8.0.9000000000

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.
Files changed (73) hide show
  1. package/SystemBundles/21657b0b773752468bbe6711df32d141e12a1ad993b6abbe39ad0de460714386.bundle +1279 -0
  2. package/SystemBundles/21657b0b773752468bbe6711df32d141e12a1ad993b6abbe39ad0de460714386.map +372 -0
  3. package/SystemModules/amzn__react-navigation__routers-8.modules.txt +9 -0
  4. package/SystemModules/amzn__react-navigation__routers-8.private.modules.txt +1 -0
  5. package/SystemModules/keplerscript-system-bundle-config.json +4 -4
  6. package/kepler-compatibility.json +2 -2
  7. package/lib/module/BaseRouter.js +6 -4
  8. package/lib/module/BaseRouter.js.map +1 -1
  9. package/lib/module/CommonActions.js +48 -7
  10. package/lib/module/CommonActions.js.map +1 -1
  11. package/lib/module/DrawerRouter.js +6 -6
  12. package/lib/module/DrawerRouter.js.map +1 -1
  13. package/lib/module/StackRouter.js +240 -77
  14. package/lib/module/StackRouter.js.map +1 -1
  15. package/lib/module/TabRouter.js +117 -34
  16. package/lib/module/TabRouter.js.map +1 -1
  17. package/lib/module/createParamsFromAction.js +16 -0
  18. package/lib/module/createParamsFromAction.js.map +1 -0
  19. package/lib/module/createRouteFromAction.js +21 -0
  20. package/lib/module/createRouteFromAction.js.map +1 -0
  21. package/lib/module/index.js +8 -6
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/module/package.json +1 -0
  24. package/lib/module/types.js +2 -0
  25. package/lib/module/types.js.map +1 -1
  26. package/lib/typescript/package.json +1 -0
  27. package/lib/typescript/src/BaseRouter.d.ts +23 -20
  28. package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
  29. package/lib/typescript/src/CommonActions.d.ts +66 -18
  30. package/lib/typescript/src/CommonActions.d.ts.map +1 -1
  31. package/lib/typescript/src/DrawerRouter.d.ts +18 -6
  32. package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
  33. package/lib/typescript/src/StackRouter.d.ts +67 -11
  34. package/lib/typescript/src/StackRouter.d.ts.map +1 -1
  35. package/lib/typescript/src/TabRouter.d.ts +24 -4
  36. package/lib/typescript/src/TabRouter.d.ts.map +1 -1
  37. package/lib/typescript/src/createParamsFromAction.d.ts +13 -0
  38. package/lib/typescript/src/createParamsFromAction.d.ts.map +1 -0
  39. package/lib/typescript/src/createRouteFromAction.d.ts +17 -0
  40. package/lib/typescript/src/createRouteFromAction.d.ts.map +1 -0
  41. package/lib/typescript/src/index.d.ts +4 -4
  42. package/lib/typescript/src/index.d.ts.map +1 -1
  43. package/lib/typescript/src/types.d.ts +1 -2
  44. package/lib/typescript/src/types.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/BaseRouter.tsx +11 -6
  47. package/src/CommonActions.tsx +147 -56
  48. package/src/DrawerRouter.tsx +15 -13
  49. package/src/StackRouter.tsx +417 -203
  50. package/src/TabRouter.tsx +192 -53
  51. package/src/createParamsFromAction.tsx +22 -0
  52. package/src/createRouteFromAction.tsx +24 -0
  53. package/src/index.tsx +4 -4
  54. package/src/types.tsx +5 -5
  55. package/SystemBundles/2c2b2e72cf9d128296ac83ba53082313ac31f6e9fd98b8e87fe0af40e15fbec1.bundle +0 -3515
  56. package/SystemBundles/2c2b2e72cf9d128296ac83ba53082313ac31f6e9fd98b8e87fe0af40e15fbec1.map +0 -1
  57. package/SystemModules/amzn__react-navigation__routers-7.modules.txt +0 -21
  58. package/SystemModules/amzn__react-navigation__routers-7.private.modules.txt +0 -13
  59. package/THIRD_PARTY_NOTICES.txt +0 -24
  60. package/lib/commonjs/BaseRouter.js +0 -61
  61. package/lib/commonjs/BaseRouter.js.map +0 -1
  62. package/lib/commonjs/CommonActions.js +0 -53
  63. package/lib/commonjs/CommonActions.js.map +0 -1
  64. package/lib/commonjs/DrawerRouter.js +0 -154
  65. package/lib/commonjs/DrawerRouter.js.map +0 -1
  66. package/lib/commonjs/StackRouter.js +0 -310
  67. package/lib/commonjs/StackRouter.js.map +0 -1
  68. package/lib/commonjs/TabRouter.js +0 -253
  69. package/lib/commonjs/TabRouter.js.map +0 -1
  70. package/lib/commonjs/index.js +0 -80
  71. package/lib/commonjs/index.js.map +0 -1
  72. package/lib/commonjs/types.js +0 -6
  73. package/lib/commonjs/types.js.map +0 -1
@@ -1,9 +1,12 @@
1
1
  import { nanoid } from 'nanoid/non-secure';
2
2
 
3
- import BaseRouter from './BaseRouter';
3
+ import { BaseRouter } from './BaseRouter';
4
+ import { createParamsFromAction } from './createParamsFromAction';
5
+ import { createRouteFromAction } from './createRouteFromAction';
4
6
  import type {
5
7
  CommonNavigationAction,
6
8
  DefaultRouterOptions,
9
+ NavigationRoute,
7
10
  NavigationState,
8
11
  ParamListBase,
9
12
  Route,
@@ -13,7 +16,7 @@ import type {
13
16
  export type StackActionType =
14
17
  | {
15
18
  type: 'REPLACE';
16
- payload: { name: string; key?: string | undefined; params?: object };
19
+ payload: { name: string; params?: object };
17
20
  source?: string;
18
21
  target?: string;
19
22
  }
@@ -33,6 +36,16 @@ export type StackActionType =
33
36
  type: 'POP_TO_TOP';
34
37
  source?: string;
35
38
  target?: string;
39
+ }
40
+ | {
41
+ type: 'POP_TO';
42
+ payload: {
43
+ name: string;
44
+ params?: object;
45
+ merge?: boolean;
46
+ };
47
+ source?: string;
48
+ target?: string;
36
49
  };
37
50
 
38
51
  export type StackRouterOptions = DefaultRouterOptions;
@@ -43,31 +56,39 @@ export type StackNavigationState<ParamList extends ParamListBase> =
43
56
  * Type of the router, in this case, it's stack.
44
57
  */
45
58
  type: 'stack';
59
+ /**
60
+ * List of routes, which are supposed to be preloaded before navigating to.
61
+ */
62
+ preloadedRoutes: NavigationRoute<ParamList, keyof ParamList>[];
46
63
  };
47
64
 
48
65
  export type StackActionHelpers<ParamList extends ParamListBase> = {
49
66
  /**
50
67
  * Replace the current route with a new one.
51
68
  *
52
- * @param name Route name of the new route.
69
+ * @param name Name of the new route.
53
70
  * @param [params] Params object for the new route.
54
71
  */
55
72
  replace<RouteName extends keyof ParamList>(
56
- ...args: undefined extends ParamList[RouteName]
57
- ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
58
- : [screen: RouteName, params: ParamList[RouteName]]
73
+ ...args: {
74
+ [Screen in keyof ParamList]: undefined extends ParamList[Screen]
75
+ ? [screen: Screen] | [screen: Screen, params: ParamList[Screen]]
76
+ : [screen: Screen, params: ParamList[Screen]];
77
+ }[RouteName]
59
78
  ): void;
60
79
 
61
80
  /**
62
81
  * Push a new screen onto the stack.
63
82
  *
64
- * @param name Name of the route for the tab.
83
+ * @param name Name of the route to push onto the stack.
65
84
  * @param [params] Params object for the route.
66
85
  */
67
86
  push<RouteName extends keyof ParamList>(
68
- ...args: undefined extends ParamList[RouteName]
69
- ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
70
- : [screen: RouteName, params: ParamList[RouteName]]
87
+ ...args: {
88
+ [Screen in keyof ParamList]: undefined extends ParamList[Screen]
89
+ ? [screen: Screen] | [screen: Screen, params: ParamList[Screen]]
90
+ : [screen: Screen, params: ParamList[Screen]];
91
+ }[RouteName]
71
92
  ): void;
72
93
 
73
94
  /**
@@ -79,24 +100,70 @@ export type StackActionHelpers<ParamList extends ParamListBase> = {
79
100
  * Pop to the first route in the stack, dismissing all other screens.
80
101
  */
81
102
  popToTop(): void;
103
+
104
+ /**
105
+ * Pop any screens to go back to the specified screen.
106
+ * If the specified screen doesn't exist, it'll be added to the stack.
107
+ *
108
+ * @param name Name of the route to navigate to.
109
+ * @param [params] Params object for the route.
110
+ * @param [merge] Whether to merge the params onto the route.
111
+ */
112
+ popTo<RouteName extends keyof ParamList>(
113
+ ...args: {
114
+ [Screen in keyof ParamList]: undefined extends ParamList[Screen]
115
+ ?
116
+ | [screen: Screen]
117
+ | [screen: Screen, params: ParamList[Screen]]
118
+ | [screen: Screen, params: ParamList[Screen], merge: boolean]
119
+ :
120
+ | [screen: Screen, params: ParamList[Screen]]
121
+ | [screen: Screen, params: ParamList[Screen], merge: boolean];
122
+ }[RouteName]
123
+ ): void;
82
124
  };
83
125
 
84
126
  export const StackActions = {
85
- replace(name: string, params?: object): StackActionType {
86
- return { type: 'REPLACE', payload: { name, params } };
127
+ replace(name: string, params?: object) {
128
+ return {
129
+ type: 'REPLACE',
130
+ payload: { name, params },
131
+ } as const satisfies StackActionType;
87
132
  },
88
- push(name: string, params?: object): StackActionType {
89
- return { type: 'PUSH', payload: { name, params } };
133
+ push(name: string, params?: object) {
134
+ return {
135
+ type: 'PUSH',
136
+ payload: { name, params },
137
+ } as const satisfies StackActionType;
90
138
  },
91
- pop(count: number = 1): StackActionType {
92
- return { type: 'POP', payload: { count } };
139
+ pop(count: number = 1) {
140
+ return {
141
+ type: 'POP',
142
+ payload: { count },
143
+ } as const satisfies StackActionType;
93
144
  },
94
- popToTop(): StackActionType {
95
- return { type: 'POP_TO_TOP' };
145
+ popToTop() {
146
+ return { type: 'POP_TO_TOP' } as const satisfies StackActionType;
147
+ },
148
+ popTo(name: string, params?: object, options?: { merge?: boolean }) {
149
+ if (typeof options === 'boolean') {
150
+ console.warn(
151
+ `Passing a boolean as the third argument to 'popTo' is deprecated. Pass '{ merge: true }' instead.`
152
+ );
153
+ }
154
+
155
+ return {
156
+ type: 'POP_TO',
157
+ payload: {
158
+ name,
159
+ params,
160
+ merge: typeof options === 'boolean' ? options : options?.merge,
161
+ },
162
+ } as const satisfies StackActionType;
96
163
  },
97
164
  };
98
165
 
99
- export default function StackRouter(options: StackRouterOptions) {
166
+ export function StackRouter(options: StackRouterOptions) {
100
167
  const router: Router<
101
168
  StackNavigationState<ParamListBase>,
102
169
  CommonNavigationAction | StackActionType
@@ -118,6 +185,7 @@ export default function StackRouter(options: StackRouterOptions) {
118
185
  key: `stack-${nanoid()}`,
119
186
  index: 0,
120
187
  routeNames,
188
+ preloadedRoutes: [],
121
189
  routes: [
122
190
  {
123
191
  key: `${initialRouteName}-${nanoid()}`,
@@ -129,7 +197,7 @@ export default function StackRouter(options: StackRouterOptions) {
129
197
  },
130
198
 
131
199
  getRehydratedState(partialState, { routeNames, routeParamList }) {
132
- let state = partialState;
200
+ const state = partialState;
133
201
 
134
202
  if (state.stale === false) {
135
203
  return state;
@@ -137,20 +205,35 @@ export default function StackRouter(options: StackRouterOptions) {
137
205
 
138
206
  const routes = state.routes
139
207
  .filter((route) => routeNames.includes(route.name))
140
- .map(
141
- (route) =>
142
- ({
143
- ...route,
144
- key: route.key || `${route.name}-${nanoid()}`,
145
- params:
146
- routeParamList[route.name] !== undefined
147
- ? {
148
- ...routeParamList[route.name],
149
- ...route.params,
150
- }
151
- : route.params,
152
- } as Route<string>)
153
- );
208
+ .map((route) => ({
209
+ ...route,
210
+ key: route.key || `${route.name}-${nanoid()}`,
211
+ params:
212
+ routeParamList[route.name] !== undefined
213
+ ? {
214
+ ...routeParamList[route.name],
215
+ ...route.params,
216
+ }
217
+ : route.params,
218
+ }));
219
+
220
+ const preloadedRoutes =
221
+ state.preloadedRoutes
222
+ ?.filter((route) => routeNames.includes(route.name))
223
+ .map(
224
+ (route) =>
225
+ ({
226
+ ...route,
227
+ key: route.key || `${route.name}-${nanoid()}`,
228
+ params:
229
+ routeParamList[route.name] !== undefined
230
+ ? {
231
+ ...routeParamList[route.name],
232
+ ...route.params,
233
+ }
234
+ : route.params,
235
+ }) as Route<string>
236
+ ) ?? [];
154
237
 
155
238
  if (routes.length === 0) {
156
239
  const initialRouteName =
@@ -172,6 +255,7 @@ export default function StackRouter(options: StackRouterOptions) {
172
255
  index: routes.length - 1,
173
256
  routeNames,
174
257
  routes,
258
+ preloadedRoutes,
175
259
  };
176
260
  },
177
261
 
@@ -226,105 +310,223 @@ export default function StackRouter(options: StackRouterOptions) {
226
310
 
227
311
  switch (action.type) {
228
312
  case 'REPLACE': {
229
- const index =
313
+ const currentIndex =
230
314
  action.target === state.key && action.source
231
315
  ? state.routes.findIndex((r) => r.key === action.source)
232
316
  : state.index;
233
317
 
234
- if (index === -1) {
318
+ if (currentIndex === -1) {
235
319
  return null;
236
320
  }
237
321
 
238
- const { name, key, params } = action.payload;
239
-
240
- if (!state.routeNames.includes(name)) {
322
+ if (!state.routeNames.includes(action.payload.name)) {
241
323
  return null;
242
324
  }
243
325
 
326
+ const getId = options.routeGetIdList[action.payload.name];
327
+ const id = getId?.({ params: action.payload.params });
328
+
329
+ // Re-use preloaded route if available
330
+ let route = state.preloadedRoutes.find(
331
+ (route) =>
332
+ route.name === action.payload.name &&
333
+ id === getId?.({ params: route.params })
334
+ );
335
+
336
+ if (!route) {
337
+ route = createRouteFromAction({ action, routeParamList });
338
+ }
339
+
244
340
  return {
245
341
  ...state,
246
- routes: state.routes.map((route, i) =>
247
- i === index
248
- ? {
249
- key: key !== undefined ? key : `${name}-${nanoid()}`,
250
- name,
251
- params:
252
- routeParamList[name] !== undefined
253
- ? {
254
- ...routeParamList[name],
255
- ...params,
256
- }
257
- : params,
258
- }
259
- : route
342
+ routes: state.routes.map((r, i) =>
343
+ i === currentIndex ? route : r
344
+ ),
345
+ preloadedRoutes: state.preloadedRoutes.filter(
346
+ (r) => r.key !== route.key
260
347
  ),
261
348
  };
262
349
  }
263
350
 
264
351
  case 'PUSH':
265
- if (state.routeNames.includes(action.payload.name)) {
266
- const getId = options.routeGetIdList[action.payload.name];
267
- const id = getId?.({ params: action.payload.params });
268
-
269
- const route = id
270
- ? state.routes.find(
271
- (route) =>
272
- route.name === action.payload.name &&
273
- id === getId?.({ params: route.params })
274
- )
275
- : undefined;
352
+ case 'NAVIGATE': {
353
+ if (!state.routeNames.includes(action.payload.name)) {
354
+ return null;
355
+ }
276
356
 
277
- let routes: Route<string>[];
357
+ const getId = options.routeGetIdList[action.payload.name];
358
+ const id = getId?.({ params: action.payload.params });
278
359
 
279
- if (route) {
280
- routes = state.routes.filter((r) => r.key !== route.key);
281
- routes.push({
282
- ...route,
283
- params:
284
- routeParamList[action.payload.name] !== undefined
285
- ? {
286
- ...routeParamList[action.payload.name],
287
- ...action.payload.params,
288
- }
289
- : action.payload.params,
290
- });
291
- } else {
292
- routes = [
293
- ...state.routes,
294
- {
295
- key: `${action.payload.name}-${nanoid()}`,
296
- name: action.payload.name,
297
- params:
298
- routeParamList[action.payload.name] !== undefined
299
- ? {
300
- ...routeParamList[action.payload.name],
301
- ...action.payload.params,
302
- }
303
- : action.payload.params,
304
- },
305
- ];
360
+ let route: Route<string> | undefined;
361
+
362
+ if (id !== undefined) {
363
+ route = state.routes.find(
364
+ (route) =>
365
+ route.name === action.payload.name &&
366
+ id === getId?.({ params: route.params })
367
+ );
368
+ } else {
369
+ const currentRoute = state.routes[state.index];
370
+
371
+ // If the route matches the current one, then navigate to it
372
+ if (
373
+ action.type === 'NAVIGATE' &&
374
+ action.payload.name === currentRoute.name
375
+ ) {
376
+ route = currentRoute;
306
377
  }
378
+ }
379
+
380
+ if (!route) {
381
+ route = state.preloadedRoutes.find(
382
+ (route) =>
383
+ route.name === action.payload.name &&
384
+ id === getId?.({ params: route.params })
385
+ );
386
+ }
387
+
388
+ let params;
389
+
390
+ if (action.type === 'NAVIGATE' && action.payload.merge && route) {
391
+ params =
392
+ action.payload.params !== undefined ||
393
+ routeParamList[action.payload.name] !== undefined
394
+ ? {
395
+ ...routeParamList[action.payload.name],
396
+ ...route.params,
397
+ ...action.payload.params,
398
+ }
399
+ : route.params;
400
+ } else {
401
+ params = createParamsFromAction({ action, routeParamList });
402
+ }
403
+
404
+ let routes: Route<string>[];
405
+
406
+ if (route) {
407
+ const routeKey = route.key;
408
+
409
+ routes = state.routes.filter((r) => r.key !== routeKey);
410
+ routes.push({
411
+ ...route,
412
+ path:
413
+ action.type === 'NAVIGATE' && action.payload.path !== undefined
414
+ ? action.payload.path
415
+ : route.path,
416
+ params,
417
+ });
418
+ } else {
419
+ routes = [
420
+ ...state.routes,
421
+ {
422
+ key: `${action.payload.name}-${nanoid()}`,
423
+ name: action.payload.name,
424
+ path:
425
+ action.type === 'NAVIGATE' ? action.payload.path : undefined,
426
+ params,
427
+ },
428
+ ];
429
+ }
430
+
431
+ return {
432
+ ...state,
433
+ index: routes.length - 1,
434
+ preloadedRoutes: state.preloadedRoutes.filter(
435
+ (route) => routes[routes.length - 1].key !== route.key
436
+ ),
437
+ routes,
438
+ };
439
+ }
440
+
441
+ case 'NAVIGATE_DEPRECATED': {
442
+ if (!state.routeNames.includes(action.payload.name)) {
443
+ return null;
444
+ }
445
+
446
+ if (
447
+ state.preloadedRoutes.find(
448
+ (route) =>
449
+ route.name === action.payload.name &&
450
+ id === getId?.({ params: route.params })
451
+ )
452
+ ) {
453
+ return null;
454
+ }
455
+
456
+ // If the route already exists, navigate to that
457
+ let index = -1;
458
+
459
+ const getId = options.routeGetIdList[action.payload.name];
460
+ const id = getId?.({ params: action.payload.params });
461
+
462
+ if (id !== undefined) {
463
+ index = state.routes.findIndex(
464
+ (route) =>
465
+ route.name === action.payload.name &&
466
+ id === getId?.({ params: route.params })
467
+ );
468
+ } else if (state.routes[state.index].name === action.payload.name) {
469
+ index = state.index;
470
+ } else {
471
+ index = state.routes.findLastIndex(
472
+ (route) => route.name === action.payload.name
473
+ );
474
+ }
475
+
476
+ if (index === -1) {
477
+ const routes = [
478
+ ...state.routes,
479
+ createRouteFromAction({ action, routeParamList }),
480
+ ];
307
481
 
308
482
  return {
309
483
  ...state,
310
- index: routes.length - 1,
311
484
  routes,
485
+ index: routes.length - 1,
312
486
  };
313
487
  }
314
488
 
315
- return null;
489
+ const route = state.routes[index];
490
+
491
+ let params;
492
+
493
+ if (action.payload.merge) {
494
+ params =
495
+ action.payload.params !== undefined ||
496
+ routeParamList[route.name] !== undefined
497
+ ? {
498
+ ...routeParamList[route.name],
499
+ ...route.params,
500
+ ...action.payload.params,
501
+ }
502
+ : route.params;
503
+ } else {
504
+ params = createParamsFromAction({ action, routeParamList });
505
+ }
506
+
507
+ return {
508
+ ...state,
509
+ index,
510
+ routes: [
511
+ ...state.routes.slice(0, index),
512
+ params !== route.params
513
+ ? { ...route, params }
514
+ : state.routes[index],
515
+ ],
516
+ };
517
+ }
316
518
 
317
519
  case 'POP': {
318
- const index =
520
+ const currentIndex =
319
521
  action.target === state.key && action.source
320
522
  ? state.routes.findIndex((r) => r.key === action.source)
321
523
  : state.index;
322
524
 
323
- if (index > 0) {
324
- const count = Math.max(index - action.payload.count + 1, 1);
525
+ if (currentIndex > 0) {
526
+ const count = Math.max(currentIndex - action.payload.count + 1, 1);
325
527
  const routes = state.routes
326
528
  .slice(0, count)
327
- .concat(state.routes.slice(index + 1));
529
+ .concat(state.routes.slice(currentIndex + 1));
328
530
 
329
531
  return {
330
532
  ...state,
@@ -346,126 +548,97 @@ export default function StackRouter(options: StackRouterOptions) {
346
548
  options
347
549
  );
348
550
 
349
- case 'NAVIGATE':
350
- if (
351
- action.payload.name !== undefined &&
352
- !state.routeNames.includes(action.payload.name)
353
- ) {
551
+ case 'POP_TO': {
552
+ const currentIndex =
553
+ action.target === state.key && action.source
554
+ ? state.routes.findLastIndex((r) => r.key === action.source)
555
+ : state.index;
556
+
557
+ if (currentIndex === -1) {
354
558
  return null;
355
559
  }
356
560
 
357
- if (action.payload.key || action.payload.name) {
358
- // If the route already exists, navigate to that
359
- let index = -1;
360
-
361
- const getId =
362
- // `getId` and `key` can't be used together
363
- action.payload.key === undefined &&
364
- action.payload.name !== undefined
365
- ? options.routeGetIdList[action.payload.name]
366
- : undefined;
367
- const id = getId?.({ params: action.payload.params });
368
-
369
- if (id) {
370
- index = state.routes.findIndex(
371
- (route) =>
372
- route.name === action.payload.name &&
373
- id === getId?.({ params: route.params })
374
- );
375
- } else if (
376
- (state.routes[state.index].name === action.payload.name &&
377
- action.payload.key === undefined) ||
378
- state.routes[state.index].key === action.payload.key
379
- ) {
380
- index = state.index;
381
- } else {
382
- for (let i = state.routes.length - 1; i >= 0; i--) {
383
- if (
384
- (state.routes[i].name === action.payload.name &&
385
- action.payload.key === undefined) ||
386
- state.routes[i].key === action.payload.key
387
- ) {
388
- index = i;
389
- break;
390
- }
561
+ if (!state.routeNames.includes(action.payload.name)) {
562
+ return null;
563
+ }
564
+
565
+ // If the route already exists, navigate to it
566
+ let index = -1;
567
+
568
+ const getId = options.routeGetIdList[action.payload.name];
569
+ const id = getId?.({ params: action.payload.params });
570
+
571
+ if (id !== undefined) {
572
+ index = state.routes.findIndex(
573
+ (route) =>
574
+ route.name === action.payload.name &&
575
+ id === getId?.({ params: route.params })
576
+ );
577
+ } else if (state.routes[currentIndex].name === action.payload.name) {
578
+ index = currentIndex;
579
+ } else {
580
+ for (let i = currentIndex; i >= 0; i--) {
581
+ if (state.routes[i].name === action.payload.name) {
582
+ index = i;
583
+ break;
391
584
  }
392
585
  }
586
+ }
393
587
 
394
- if (
395
- index === -1 &&
396
- action.payload.key &&
397
- action.payload.name === undefined
398
- ) {
399
- return null;
400
- }
588
+ // If the route doesn't exist, remove the current route and add the new one
589
+ if (index === -1) {
590
+ // Re-use preloaded route if available
591
+ let route = state.preloadedRoutes.find(
592
+ (route) =>
593
+ route.name === action.payload.name &&
594
+ id === getId?.({ params: route.params })
595
+ );
401
596
 
402
- if (index === -1 && action.payload.name !== undefined) {
403
- const routes = [
404
- ...state.routes,
405
- {
406
- key:
407
- action.payload.key ?? `${action.payload.name}-${nanoid()}`,
408
- name: action.payload.name,
409
- path: action.payload.path,
410
- params:
411
- routeParamList[action.payload.name] !== undefined
412
- ? {
413
- ...routeParamList[action.payload.name],
414
- ...action.payload.params,
415
- }
416
- : action.payload.params,
417
- },
418
- ];
419
-
420
- return {
421
- ...state,
422
- routes,
423
- index: routes.length - 1,
424
- };
597
+ if (!route) {
598
+ route = createRouteFromAction({ action, routeParamList });
425
599
  }
426
600
 
427
- const route = state.routes[index];
428
-
429
- let params;
430
-
431
- if (action.payload.merge) {
432
- params =
433
- action.payload.params !== undefined ||
434
- routeParamList[route.name] !== undefined
435
- ? {
436
- ...routeParamList[route.name],
437
- ...route.params,
438
- ...action.payload.params,
439
- }
440
- : route.params;
441
- } else {
442
- params =
443
- routeParamList[route.name] !== undefined
444
- ? {
445
- ...routeParamList[route.name],
446
- ...action.payload.params,
447
- }
448
- : action.payload.params;
449
- }
601
+ const routes = state.routes.slice(0, currentIndex).concat(route);
450
602
 
451
603
  return {
452
604
  ...state,
453
- index,
454
- routes: [
455
- ...state.routes.slice(0, index),
456
- params !== route.params ||
457
- (action.payload.path && action.payload.path !== route.path)
458
- ? {
459
- ...route,
460
- path: action.payload.path ?? route.path,
461
- params,
462
- }
463
- : state.routes[index],
464
- ],
605
+ index: routes.length - 1,
606
+ routes,
607
+ preloadedRoutes: state.preloadedRoutes.filter(
608
+ (r) => r.key !== route.key
609
+ ),
465
610
  };
466
611
  }
467
612
 
468
- return null;
613
+ const route = state.routes[index];
614
+
615
+ let params;
616
+
617
+ if (action.payload.merge) {
618
+ params =
619
+ action.payload.params !== undefined ||
620
+ routeParamList[route.name] !== undefined
621
+ ? {
622
+ ...routeParamList[route.name],
623
+ ...route.params,
624
+ ...action.payload.params,
625
+ }
626
+ : route.params;
627
+ } else {
628
+ params = createParamsFromAction({ action, routeParamList });
629
+ }
630
+
631
+ return {
632
+ ...state,
633
+ index,
634
+ routes: [
635
+ ...state.routes.slice(0, index),
636
+ params !== route.params
637
+ ? { ...route, params }
638
+ : state.routes[index],
639
+ ],
640
+ };
641
+ }
469
642
 
470
643
  case 'GO_BACK':
471
644
  if (state.index > 0) {
@@ -483,6 +656,47 @@ export default function StackRouter(options: StackRouterOptions) {
483
656
 
484
657
  return null;
485
658
 
659
+ case 'PRELOAD': {
660
+ const getId = options.routeGetIdList[action.payload.name];
661
+ const id = getId?.({ params: action.payload.params });
662
+
663
+ let route: Route<string> | undefined;
664
+
665
+ if (id !== undefined) {
666
+ route = state.routes.find(
667
+ (route) =>
668
+ route.name === action.payload.name &&
669
+ id === getId?.({ params: route.params })
670
+ );
671
+ }
672
+
673
+ if (route) {
674
+ return {
675
+ ...state,
676
+ routes: state.routes.map((r) => {
677
+ if (r.key !== route?.key) {
678
+ return r;
679
+ }
680
+ return {
681
+ ...r,
682
+ params: createParamsFromAction({ action, routeParamList }),
683
+ };
684
+ }),
685
+ };
686
+ } else {
687
+ return {
688
+ ...state,
689
+ preloadedRoutes: state.preloadedRoutes
690
+ .filter(
691
+ (r) =>
692
+ r.name !== action.payload.name ||
693
+ id !== getId?.({ params: r.params })
694
+ )
695
+ .concat(createRouteFromAction({ action, routeParamList })),
696
+ };
697
+ }
698
+ }
699
+
486
700
  default:
487
701
  return BaseRouter.getStateForAction(state, action);
488
702
  }