@amazon-devices/react-navigation__routers 7.0.1759135970 → 8.0.9000000000-rn-83

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 (75) hide show
  1. package/SystemBundles/b6cc62e27d70be8722b0e7aef9fd04a870d49127e6c724fe934d1f8ca4ff8177.bundle +1298 -0
  2. package/SystemBundles/b6cc62e27d70be8722b0e7aef9fd04a870d49127e6c724fe934d1f8ca4ff8177.map +373 -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 +261 -80
  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 +73 -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/oss-attribution/attribution.txt +0 -0
  46. package/oss-attribution/licenseInfos.json +1 -0
  47. package/package.json +80 -1
  48. package/src/BaseRouter.tsx +11 -6
  49. package/src/CommonActions.tsx +147 -56
  50. package/src/DrawerRouter.tsx +15 -13
  51. package/src/StackRouter.tsx +437 -199
  52. package/src/TabRouter.tsx +192 -53
  53. package/src/createParamsFromAction.tsx +22 -0
  54. package/src/createRouteFromAction.tsx +24 -0
  55. package/src/index.tsx +4 -4
  56. package/src/types.tsx +5 -5
  57. package/SystemBundles/2c2b2e72cf9d128296ac83ba53082313ac31f6e9fd98b8e87fe0af40e15fbec1.bundle +0 -3515
  58. package/SystemBundles/2c2b2e72cf9d128296ac83ba53082313ac31f6e9fd98b8e87fe0af40e15fbec1.map +0 -1
  59. package/SystemModules/amzn__react-navigation__routers-7.modules.txt +0 -21
  60. package/SystemModules/amzn__react-navigation__routers-7.private.modules.txt +0 -13
  61. package/THIRD_PARTY_NOTICES.txt +0 -24
  62. package/lib/commonjs/BaseRouter.js +0 -61
  63. package/lib/commonjs/BaseRouter.js.map +0 -1
  64. package/lib/commonjs/CommonActions.js +0 -53
  65. package/lib/commonjs/CommonActions.js.map +0 -1
  66. package/lib/commonjs/DrawerRouter.js +0 -154
  67. package/lib/commonjs/DrawerRouter.js.map +0 -1
  68. package/lib/commonjs/StackRouter.js +0 -310
  69. package/lib/commonjs/StackRouter.js.map +0 -1
  70. package/lib/commonjs/TabRouter.js +0 -253
  71. package/lib/commonjs/TabRouter.js.map +0 -1
  72. package/lib/commonjs/index.js +0 -80
  73. package/lib/commonjs/index.js.map +0 -1
  74. package/lib/commonjs/types.js +0 -6
  75. 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 screen Name of the new route that will replace the current one.
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: RouteName extends unknown
74
+ ? undefined extends ParamList[RouteName]
75
+ ? [screen: RouteName, params?: ParamList[RouteName]]
76
+ : [screen: RouteName, params: ParamList[RouteName]]
77
+ : never
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 screen 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: RouteName extends unknown
88
+ ? undefined extends ParamList[RouteName]
89
+ ? [screen: RouteName, params?: ParamList[RouteName]]
90
+ : [screen: RouteName, params: ParamList[RouteName]]
91
+ : never
71
92
  ): void;
72
93
 
73
94
  /**
@@ -79,24 +100,73 @@ 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 screen Name of the route to pop to.
109
+ * @param [params] Params object for the route.
110
+ * @param [options.merge] Whether to merge the params onto the route. Defaults to `false`.
111
+ */
112
+ popTo<RouteName extends keyof ParamList>(
113
+ ...args: RouteName extends unknown
114
+ ? undefined extends ParamList[RouteName]
115
+ ? [
116
+ screen: RouteName,
117
+ params?: ParamList[RouteName],
118
+ options?: { merge?: boolean },
119
+ ]
120
+ : [
121
+ screen: RouteName,
122
+ params: ParamList[RouteName],
123
+ options?: { merge?: boolean },
124
+ ]
125
+ : never
126
+ ): void;
82
127
  };
83
128
 
84
129
  export const StackActions = {
85
- replace(name: string, params?: object): StackActionType {
86
- return { type: 'REPLACE', payload: { name, params } };
130
+ replace(name: string, params?: object) {
131
+ return {
132
+ type: 'REPLACE',
133
+ payload: { name, params },
134
+ } as const satisfies StackActionType;
87
135
  },
88
- push(name: string, params?: object): StackActionType {
89
- return { type: 'PUSH', payload: { name, params } };
136
+ push(name: string, params?: object) {
137
+ return {
138
+ type: 'PUSH',
139
+ payload: { name, params },
140
+ } as const satisfies StackActionType;
90
141
  },
91
- pop(count: number = 1): StackActionType {
92
- return { type: 'POP', payload: { count } };
142
+ pop(count: number = 1) {
143
+ return {
144
+ type: 'POP',
145
+ payload: { count },
146
+ } as const satisfies StackActionType;
93
147
  },
94
- popToTop(): StackActionType {
95
- return { type: 'POP_TO_TOP' };
148
+ popToTop() {
149
+ return { type: 'POP_TO_TOP' } as const satisfies StackActionType;
150
+ },
151
+ popTo(name: string, params?: object, options?: { merge?: boolean }) {
152
+ if (typeof options === 'boolean') {
153
+ console.warn(
154
+ `Passing a boolean as the third argument to 'popTo' is deprecated. Pass '{ merge: true }' instead.`
155
+ );
156
+ }
157
+
158
+ return {
159
+ type: 'POP_TO',
160
+ payload: {
161
+ name,
162
+ params,
163
+ merge: typeof options === 'boolean' ? options : options?.merge,
164
+ },
165
+ } as const satisfies StackActionType;
96
166
  },
97
167
  };
98
168
 
99
- export default function StackRouter(options: StackRouterOptions) {
169
+ export function StackRouter(options: StackRouterOptions) {
100
170
  const router: Router<
101
171
  StackNavigationState<ParamListBase>,
102
172
  CommonNavigationAction | StackActionType
@@ -118,6 +188,7 @@ export default function StackRouter(options: StackRouterOptions) {
118
188
  key: `stack-${nanoid()}`,
119
189
  index: 0,
120
190
  routeNames,
191
+ preloadedRoutes: [],
121
192
  routes: [
122
193
  {
123
194
  key: `${initialRouteName}-${nanoid()}`,
@@ -129,7 +200,7 @@ export default function StackRouter(options: StackRouterOptions) {
129
200
  },
130
201
 
131
202
  getRehydratedState(partialState, { routeNames, routeParamList }) {
132
- let state = partialState;
203
+ const state = partialState;
133
204
 
134
205
  if (state.stale === false) {
135
206
  return state;
@@ -137,20 +208,35 @@ export default function StackRouter(options: StackRouterOptions) {
137
208
 
138
209
  const routes = state.routes
139
210
  .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
- );
211
+ .map((route) => ({
212
+ ...route,
213
+ key: route.key || `${route.name}-${nanoid()}`,
214
+ params:
215
+ routeParamList[route.name] !== undefined
216
+ ? {
217
+ ...routeParamList[route.name],
218
+ ...route.params,
219
+ }
220
+ : route.params,
221
+ }));
222
+
223
+ const preloadedRoutes =
224
+ state.preloadedRoutes
225
+ ?.filter((route) => routeNames.includes(route.name))
226
+ .map(
227
+ (route) =>
228
+ ({
229
+ ...route,
230
+ key: route.key || `${route.name}-${nanoid()}`,
231
+ params:
232
+ routeParamList[route.name] !== undefined
233
+ ? {
234
+ ...routeParamList[route.name],
235
+ ...route.params,
236
+ }
237
+ : route.params,
238
+ }) as Route<string>
239
+ ) ?? [];
154
240
 
155
241
  if (routes.length === 0) {
156
242
  const initialRouteName =
@@ -172,6 +258,7 @@ export default function StackRouter(options: StackRouterOptions) {
172
258
  index: routes.length - 1,
173
259
  routeNames,
174
260
  routes,
261
+ preloadedRoutes,
175
262
  };
176
263
  },
177
264
 
@@ -226,105 +313,244 @@ export default function StackRouter(options: StackRouterOptions) {
226
313
 
227
314
  switch (action.type) {
228
315
  case 'REPLACE': {
229
- const index =
316
+ const currentIndex =
230
317
  action.target === state.key && action.source
231
318
  ? state.routes.findIndex((r) => r.key === action.source)
232
319
  : state.index;
233
320
 
234
- if (index === -1) {
321
+ if (currentIndex === -1) {
235
322
  return null;
236
323
  }
237
324
 
238
- const { name, key, params } = action.payload;
239
-
240
- if (!state.routeNames.includes(name)) {
325
+ if (!state.routeNames.includes(action.payload.name)) {
241
326
  return null;
242
327
  }
243
328
 
329
+ const getId = options.routeGetIdList[action.payload.name];
330
+ const id = getId?.({ params: action.payload.params });
331
+
332
+ // Re-use preloaded route if available
333
+ let route = state.preloadedRoutes.find(
334
+ (route) =>
335
+ route.name === action.payload.name &&
336
+ id === getId?.({ params: route.params })
337
+ );
338
+
339
+ if (!route) {
340
+ route = createRouteFromAction({ action, routeParamList });
341
+ }
342
+
244
343
  return {
245
344
  ...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
345
+ routes: state.routes.map((r, i) =>
346
+ i === currentIndex ? route : r
347
+ ),
348
+ preloadedRoutes: state.preloadedRoutes.filter(
349
+ (r) => r.key !== route.key
260
350
  ),
261
351
  };
262
352
  }
263
353
 
264
354
  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;
355
+ case 'NAVIGATE': {
356
+ if (!state.routeNames.includes(action.payload.name)) {
357
+ return null;
358
+ }
276
359
 
277
- let routes: Route<string>[];
360
+ const getId = options.routeGetIdList[action.payload.name];
361
+ const id = getId?.({ params: action.payload.params });
278
362
 
279
- if (route) {
363
+ let route: Route<string> | undefined;
364
+
365
+ if (id !== undefined) {
366
+ route = state.routes.findLast(
367
+ (route) =>
368
+ route.name === action.payload.name &&
369
+ id === getId?.({ params: route.params })
370
+ );
371
+ } else if (action.type === 'NAVIGATE') {
372
+ const currentRoute = state.routes[state.index];
373
+
374
+ // If the route matches the current one, then navigate to it
375
+ if (action.payload.name === currentRoute.name) {
376
+ route = currentRoute;
377
+ } else if (action.payload.pop) {
378
+ route = state.routes.findLast(
379
+ (route) => route.name === action.payload.name
380
+ );
381
+ }
382
+ }
383
+
384
+ if (!route) {
385
+ route = state.preloadedRoutes.find(
386
+ (route) =>
387
+ route.name === action.payload.name &&
388
+ id === getId?.({ params: route.params })
389
+ );
390
+ }
391
+
392
+ let params;
393
+
394
+ if (action.type === 'NAVIGATE' && action.payload.merge && route) {
395
+ params =
396
+ action.payload.params !== undefined ||
397
+ routeParamList[action.payload.name] !== undefined
398
+ ? {
399
+ ...routeParamList[action.payload.name],
400
+ ...route.params,
401
+ ...action.payload.params,
402
+ }
403
+ : route.params;
404
+ } else {
405
+ params = createParamsFromAction({ action, routeParamList });
406
+ }
407
+
408
+ let routes: Route<string>[];
409
+
410
+ if (route) {
411
+ if (action.type === 'NAVIGATE' && action.payload.pop) {
412
+ routes = [];
413
+
414
+ // Get all routes until the matching one
415
+ for (const r of state.routes) {
416
+ if (r.key === route.key) {
417
+ routes.push({
418
+ ...route,
419
+ path:
420
+ action.payload.path !== undefined
421
+ ? action.payload.path
422
+ : route.path,
423
+ params,
424
+ });
425
+ break;
426
+ }
427
+
428
+ routes.push(r);
429
+ }
430
+ } else {
280
431
  routes = state.routes.filter((r) => r.key !== route.key);
281
432
  routes.push({
282
433
  ...route,
283
- params:
284
- routeParamList[action.payload.name] !== undefined
285
- ? {
286
- ...routeParamList[action.payload.name],
287
- ...action.payload.params,
288
- }
289
- : action.payload.params,
434
+ path:
435
+ action.type === 'NAVIGATE' &&
436
+ action.payload.path !== undefined
437
+ ? action.payload.path
438
+ : route.path,
439
+ params,
290
440
  });
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
- ];
306
441
  }
442
+ } else {
443
+ routes = [
444
+ ...state.routes,
445
+ {
446
+ key: `${action.payload.name}-${nanoid()}`,
447
+ name: action.payload.name,
448
+ path:
449
+ action.type === 'NAVIGATE' ? action.payload.path : undefined,
450
+ params,
451
+ },
452
+ ];
453
+ }
454
+
455
+ return {
456
+ ...state,
457
+ index: routes.length - 1,
458
+ preloadedRoutes: state.preloadedRoutes.filter(
459
+ (route) => routes[routes.length - 1].key !== route.key
460
+ ),
461
+ routes,
462
+ };
463
+ }
464
+
465
+ case 'NAVIGATE_DEPRECATED': {
466
+ if (!state.routeNames.includes(action.payload.name)) {
467
+ return null;
468
+ }
469
+
470
+ if (
471
+ state.preloadedRoutes.find(
472
+ (route) =>
473
+ route.name === action.payload.name &&
474
+ id === getId?.({ params: route.params })
475
+ )
476
+ ) {
477
+ return null;
478
+ }
479
+
480
+ // If the route already exists, navigate to that
481
+ let index = -1;
482
+
483
+ const getId = options.routeGetIdList[action.payload.name];
484
+ const id = getId?.({ params: action.payload.params });
485
+
486
+ if (id !== undefined) {
487
+ index = state.routes.findIndex(
488
+ (route) =>
489
+ route.name === action.payload.name &&
490
+ id === getId?.({ params: route.params })
491
+ );
492
+ } else if (state.routes[state.index].name === action.payload.name) {
493
+ index = state.index;
494
+ } else {
495
+ index = state.routes.findLastIndex(
496
+ (route) => route.name === action.payload.name
497
+ );
498
+ }
499
+
500
+ if (index === -1) {
501
+ const routes = [
502
+ ...state.routes,
503
+ createRouteFromAction({ action, routeParamList }),
504
+ ];
307
505
 
308
506
  return {
309
507
  ...state,
310
- index: routes.length - 1,
311
508
  routes,
509
+ index: routes.length - 1,
312
510
  };
313
511
  }
314
512
 
315
- return null;
513
+ const route = state.routes[index];
514
+
515
+ let params;
516
+
517
+ if (action.payload.merge) {
518
+ params =
519
+ action.payload.params !== undefined ||
520
+ routeParamList[route.name] !== undefined
521
+ ? {
522
+ ...routeParamList[route.name],
523
+ ...route.params,
524
+ ...action.payload.params,
525
+ }
526
+ : route.params;
527
+ } else {
528
+ params = createParamsFromAction({ action, routeParamList });
529
+ }
530
+
531
+ return {
532
+ ...state,
533
+ index,
534
+ routes: [
535
+ ...state.routes.slice(0, index),
536
+ params !== route.params
537
+ ? { ...route, params }
538
+ : state.routes[index],
539
+ ],
540
+ };
541
+ }
316
542
 
317
543
  case 'POP': {
318
- const index =
544
+ const currentIndex =
319
545
  action.target === state.key && action.source
320
546
  ? state.routes.findIndex((r) => r.key === action.source)
321
547
  : state.index;
322
548
 
323
- if (index > 0) {
324
- const count = Math.max(index - action.payload.count + 1, 1);
549
+ if (currentIndex > 0) {
550
+ const count = Math.max(currentIndex - action.payload.count + 1, 1);
325
551
  const routes = state.routes
326
552
  .slice(0, count)
327
- .concat(state.routes.slice(index + 1));
553
+ .concat(state.routes.slice(currentIndex + 1));
328
554
 
329
555
  return {
330
556
  ...state,
@@ -346,126 +572,97 @@ export default function StackRouter(options: StackRouterOptions) {
346
572
  options
347
573
  );
348
574
 
349
- case 'NAVIGATE':
350
- if (
351
- action.payload.name !== undefined &&
352
- !state.routeNames.includes(action.payload.name)
353
- ) {
575
+ case 'POP_TO': {
576
+ const currentIndex =
577
+ action.target === state.key && action.source
578
+ ? state.routes.findLastIndex((r) => r.key === action.source)
579
+ : state.index;
580
+
581
+ if (currentIndex === -1) {
354
582
  return null;
355
583
  }
356
584
 
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
- }
391
- }
392
- }
585
+ if (!state.routeNames.includes(action.payload.name)) {
586
+ return null;
587
+ }
393
588
 
394
- if (
395
- index === -1 &&
396
- action.payload.key &&
397
- action.payload.name === undefined
398
- ) {
399
- return null;
400
- }
589
+ // If the route already exists, navigate to it
590
+ let index = -1;
401
591
 
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
- };
425
- }
592
+ const getId = options.routeGetIdList[action.payload.name];
593
+ const id = getId?.({ params: action.payload.params });
426
594
 
427
- const route = state.routes[index];
595
+ if (id !== undefined) {
596
+ index = state.routes.findIndex(
597
+ (route) =>
598
+ route.name === action.payload.name &&
599
+ id === getId?.({ params: route.params })
600
+ );
601
+ } else if (state.routes[currentIndex].name === action.payload.name) {
602
+ index = currentIndex;
603
+ } else {
604
+ for (let i = currentIndex; i >= 0; i--) {
605
+ if (state.routes[i].name === action.payload.name) {
606
+ index = i;
607
+ break;
608
+ }
609
+ }
610
+ }
428
611
 
429
- let params;
612
+ // If the route doesn't exist, remove the current route and add the new one
613
+ if (index === -1) {
614
+ // Re-use preloaded route if available
615
+ let route = state.preloadedRoutes.find(
616
+ (route) =>
617
+ route.name === action.payload.name &&
618
+ id === getId?.({ params: route.params })
619
+ );
430
620
 
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;
621
+ if (!route) {
622
+ route = createRouteFromAction({ action, routeParamList });
449
623
  }
450
624
 
625
+ const routes = state.routes.slice(0, currentIndex).concat(route);
626
+
451
627
  return {
452
628
  ...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
- ],
629
+ index: routes.length - 1,
630
+ routes,
631
+ preloadedRoutes: state.preloadedRoutes.filter(
632
+ (r) => r.key !== route.key
633
+ ),
465
634
  };
466
635
  }
467
636
 
468
- return null;
637
+ const route = state.routes[index];
638
+
639
+ let params;
640
+
641
+ if (action.payload.merge) {
642
+ params =
643
+ action.payload.params !== undefined ||
644
+ routeParamList[route.name] !== undefined
645
+ ? {
646
+ ...routeParamList[route.name],
647
+ ...route.params,
648
+ ...action.payload.params,
649
+ }
650
+ : route.params;
651
+ } else {
652
+ params = createParamsFromAction({ action, routeParamList });
653
+ }
654
+
655
+ return {
656
+ ...state,
657
+ index,
658
+ routes: [
659
+ ...state.routes.slice(0, index),
660
+ params !== route.params
661
+ ? { ...route, params }
662
+ : state.routes[index],
663
+ ],
664
+ };
665
+ }
469
666
 
470
667
  case 'GO_BACK':
471
668
  if (state.index > 0) {
@@ -483,6 +680,47 @@ export default function StackRouter(options: StackRouterOptions) {
483
680
 
484
681
  return null;
485
682
 
683
+ case 'PRELOAD': {
684
+ const getId = options.routeGetIdList[action.payload.name];
685
+ const id = getId?.({ params: action.payload.params });
686
+
687
+ let route: Route<string> | undefined;
688
+
689
+ if (id !== undefined) {
690
+ route = state.routes.find(
691
+ (route) =>
692
+ route.name === action.payload.name &&
693
+ id === getId?.({ params: route.params })
694
+ );
695
+ }
696
+
697
+ if (route) {
698
+ return {
699
+ ...state,
700
+ routes: state.routes.map((r) => {
701
+ if (r.key !== route?.key) {
702
+ return r;
703
+ }
704
+ return {
705
+ ...r,
706
+ params: createParamsFromAction({ action, routeParamList }),
707
+ };
708
+ }),
709
+ };
710
+ } else {
711
+ return {
712
+ ...state,
713
+ preloadedRoutes: state.preloadedRoutes
714
+ .filter(
715
+ (r) =>
716
+ r.name !== action.payload.name ||
717
+ id !== getId?.({ params: r.params })
718
+ )
719
+ .concat(createRouteFromAction({ action, routeParamList })),
720
+ };
721
+ }
722
+ }
723
+
486
724
  default:
487
725
  return BaseRouter.getStateForAction(state, action);
488
726
  }