@react-navigation/routers 8.0.0-alpha.1 → 8.0.0-alpha.11
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/lib/module/BaseRouter.js +3 -2
- package/lib/module/BaseRouter.js.map +1 -1
- package/lib/module/CommonActions.js.map +1 -1
- package/lib/module/StackRouter.js +334 -149
- package/lib/module/StackRouter.js.map +1 -1
- package/lib/module/TabRouter.js +52 -11
- package/lib/module/TabRouter.js.map +1 -1
- package/lib/typescript/src/BaseRouter.d.ts +7 -7
- package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
- package/lib/typescript/src/CommonActions.d.ts +35 -35
- package/lib/typescript/src/CommonActions.d.ts.map +1 -1
- package/lib/typescript/src/DrawerRouter.d.ts +5 -5
- package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
- package/lib/typescript/src/StackRouter.d.ts +41 -18
- package/lib/typescript/src/StackRouter.d.ts.map +1 -1
- package/lib/typescript/src/TabRouter.d.ts +6 -6
- package/lib/typescript/src/TabRouter.d.ts.map +1 -1
- package/lib/typescript/src/createParamsFromAction.d.ts +2 -2
- package/lib/typescript/src/createParamsFromAction.d.ts.map +1 -1
- package/lib/typescript/src/createRouteFromAction.d.ts +2 -2
- package/lib/typescript/src/createRouteFromAction.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -9
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +17 -27
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/BaseRouter.tsx +4 -4
- package/src/CommonActions.tsx +36 -34
- package/src/DrawerRouter.tsx +3 -3
- package/src/StackRouter.tsx +546 -187
- package/src/TabRouter.tsx +79 -18
- package/src/createParamsFromAction.tsx +1 -1
- package/src/createRouteFromAction.tsx +1 -1
- package/src/types.tsx +30 -39
package/src/StackRouter.tsx
CHANGED
|
@@ -6,7 +6,6 @@ import { createRouteFromAction } from './createRouteFromAction';
|
|
|
6
6
|
import type {
|
|
7
7
|
CommonNavigationAction,
|
|
8
8
|
DefaultRouterOptions,
|
|
9
|
-
NavigationRoute,
|
|
10
9
|
NavigationState,
|
|
11
10
|
ParamListBase,
|
|
12
11
|
Route,
|
|
@@ -16,36 +15,42 @@ import type {
|
|
|
16
15
|
export type StackActionType =
|
|
17
16
|
| {
|
|
18
17
|
type: 'REPLACE';
|
|
19
|
-
payload: { name: string; params?: object };
|
|
20
|
-
source?: string;
|
|
21
|
-
target?: string;
|
|
18
|
+
payload: { name: string; params?: object | undefined };
|
|
19
|
+
source?: string | undefined;
|
|
20
|
+
target?: string | undefined;
|
|
22
21
|
}
|
|
23
22
|
| {
|
|
24
23
|
type: 'PUSH';
|
|
25
|
-
payload: { name: string; params?: object };
|
|
26
|
-
source?: string;
|
|
27
|
-
target?: string;
|
|
24
|
+
payload: { name: string; params?: object | undefined };
|
|
25
|
+
source?: string | undefined;
|
|
26
|
+
target?: string | undefined;
|
|
28
27
|
}
|
|
29
28
|
| {
|
|
30
29
|
type: 'POP';
|
|
31
30
|
payload: { count: number };
|
|
32
|
-
source?: string;
|
|
33
|
-
target?: string;
|
|
31
|
+
source?: string | undefined;
|
|
32
|
+
target?: string | undefined;
|
|
34
33
|
}
|
|
35
34
|
| {
|
|
36
35
|
type: 'POP_TO_TOP';
|
|
37
|
-
source?: string;
|
|
38
|
-
target?: string;
|
|
36
|
+
source?: string | undefined;
|
|
37
|
+
target?: string | undefined;
|
|
39
38
|
}
|
|
40
39
|
| {
|
|
41
40
|
type: 'POP_TO';
|
|
42
41
|
payload: {
|
|
43
42
|
name: string;
|
|
44
|
-
params?: object;
|
|
45
|
-
merge?: boolean;
|
|
43
|
+
params?: object | undefined;
|
|
44
|
+
merge?: boolean | undefined;
|
|
46
45
|
};
|
|
47
|
-
source?: string;
|
|
48
|
-
target?: string;
|
|
46
|
+
source?: string | undefined;
|
|
47
|
+
target?: string | undefined;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
type: 'RETAIN';
|
|
51
|
+
payload: { enable: boolean };
|
|
52
|
+
source?: string | undefined;
|
|
53
|
+
target?: string | undefined;
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
export type StackRouterOptions = DefaultRouterOptions;
|
|
@@ -57,9 +62,11 @@ export type StackNavigationState<ParamList extends ParamListBase> =
|
|
|
57
62
|
*/
|
|
58
63
|
type: 'stack';
|
|
59
64
|
/**
|
|
60
|
-
* List of
|
|
65
|
+
* List of route keys to retain in the stack.
|
|
66
|
+
* When a retained route gets removed from the stack,
|
|
67
|
+
* it'll be kept in routes and can be navigated to again.
|
|
61
68
|
*/
|
|
62
|
-
|
|
69
|
+
retainedRouteKeys: string[];
|
|
63
70
|
};
|
|
64
71
|
|
|
65
72
|
export type StackActionHelpers<ParamList extends ParamListBase> = {
|
|
@@ -124,6 +131,15 @@ export type StackActionHelpers<ParamList extends ParamListBase> = {
|
|
|
124
131
|
]
|
|
125
132
|
: never
|
|
126
133
|
): void;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Enable or disable retaining the current route in the stack.
|
|
137
|
+
* When a retained route gets removed from the stack,
|
|
138
|
+
* it'll be kept in routes and can be navigated to again.
|
|
139
|
+
*
|
|
140
|
+
* @param enable Whether to retain the current route in the stack or not.
|
|
141
|
+
*/
|
|
142
|
+
retain(enable: boolean): void;
|
|
127
143
|
};
|
|
128
144
|
|
|
129
145
|
export const StackActions = {
|
|
@@ -158,6 +174,103 @@ export const StackActions = {
|
|
|
158
174
|
},
|
|
159
175
|
} as const satisfies StackActionType;
|
|
160
176
|
},
|
|
177
|
+
retain(enable: boolean) {
|
|
178
|
+
return {
|
|
179
|
+
type: 'RETAIN',
|
|
180
|
+
payload: { enable },
|
|
181
|
+
} as const satisfies StackActionType;
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function retainRoutes<ParamList extends ParamListBase>(
|
|
186
|
+
previousState: StackNavigationState<ParamList>,
|
|
187
|
+
nextState: StackNavigationState<ParamList>
|
|
188
|
+
) {
|
|
189
|
+
if (!nextState.retainedRouteKeys.length) {
|
|
190
|
+
return nextState;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const retainedRouteKeySet = new Set(nextState.retainedRouteKeys);
|
|
194
|
+
const nextRouteKeySet = new Set(nextState.routes.map((route) => route.key));
|
|
195
|
+
const retainedRoutes = getActiveRoutes(previousState).filter(
|
|
196
|
+
(route) =>
|
|
197
|
+
retainedRouteKeySet.has(route.key) && !nextRouteKeySet.has(route.key)
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!retainedRoutes.length) {
|
|
201
|
+
return nextState;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const removedRetainedRouteKeys = new Set(
|
|
205
|
+
retainedRoutes.map((route) => route.key)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return getStateWithRoutes(
|
|
209
|
+
nextState,
|
|
210
|
+
getActiveRoutes(nextState),
|
|
211
|
+
getPreloadedRoutes(nextState),
|
|
212
|
+
retainedRoutes.concat(
|
|
213
|
+
getRetainedRoutes(nextState).filter(
|
|
214
|
+
(route) => !removedRetainedRouteKeys.has(route.key)
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const getActiveRoutes = <ParamList extends ParamListBase>(
|
|
221
|
+
state: StackNavigationState<ParamList>
|
|
222
|
+
) => state.routes.slice(0, state.index + 1);
|
|
223
|
+
|
|
224
|
+
const getInactiveRoutes = <ParamList extends ParamListBase>(
|
|
225
|
+
state: StackNavigationState<ParamList>
|
|
226
|
+
) => state.routes.slice(state.index + 1);
|
|
227
|
+
|
|
228
|
+
const getPreloadedRoutes = <ParamList extends ParamListBase>(
|
|
229
|
+
state: StackNavigationState<ParamList>
|
|
230
|
+
) => {
|
|
231
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
232
|
+
|
|
233
|
+
return getInactiveRoutes(state).filter(
|
|
234
|
+
(route) => !retainedRouteKeySet.has(route.key)
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const getRetainedRoutes = <ParamList extends ParamListBase>(
|
|
239
|
+
state: StackNavigationState<ParamList>
|
|
240
|
+
) => {
|
|
241
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
242
|
+
|
|
243
|
+
return getInactiveRoutes(state).filter((route) =>
|
|
244
|
+
retainedRouteKeySet.has(route.key)
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const getStateWithRoutes = <ParamList extends ParamListBase>(
|
|
249
|
+
state: StackNavigationState<ParamList>,
|
|
250
|
+
activeRoutes: Route<string>[],
|
|
251
|
+
preloadedRoutes = getPreloadedRoutes(state),
|
|
252
|
+
retainedRoutes = getRetainedRoutes(state)
|
|
253
|
+
) => {
|
|
254
|
+
const routeKeySet = new Set(activeRoutes.map((route) => route.key));
|
|
255
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
256
|
+
|
|
257
|
+
const filteredRetainedRoutes = retainedRoutes.filter(
|
|
258
|
+
(route) => !routeKeySet.has(route.key)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
const filteredPreloadedRoutes = preloadedRoutes.filter(
|
|
262
|
+
(route) =>
|
|
263
|
+
!retainedRouteKeySet.has(route.key) && !routeKeySet.has(route.key)
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
...state,
|
|
268
|
+
index: activeRoutes.length - 1,
|
|
269
|
+
routes: activeRoutes.concat(
|
|
270
|
+
filteredRetainedRoutes,
|
|
271
|
+
filteredPreloadedRoutes
|
|
272
|
+
),
|
|
273
|
+
};
|
|
161
274
|
};
|
|
162
275
|
|
|
163
276
|
export function StackRouter(options: StackRouterOptions) {
|
|
@@ -176,13 +289,19 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
176
289
|
? options.initialRouteName
|
|
177
290
|
: routeNames[0];
|
|
178
291
|
|
|
292
|
+
if (initialRouteName == null) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
"Couldn't initialize the stack navigator. Have you defined any screens?"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
179
298
|
return {
|
|
180
299
|
stale: false,
|
|
181
300
|
type: 'stack',
|
|
182
301
|
key: `stack-${nanoid()}`,
|
|
183
302
|
index: 0,
|
|
184
303
|
routeNames,
|
|
185
|
-
|
|
304
|
+
retainedRouteKeys: [],
|
|
186
305
|
routes: [
|
|
187
306
|
{
|
|
188
307
|
key: `${initialRouteName}-${nanoid()}`,
|
|
@@ -214,30 +333,18 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
214
333
|
: route.params,
|
|
215
334
|
}));
|
|
216
335
|
|
|
217
|
-
const preloadedRoutes =
|
|
218
|
-
state.preloadedRoutes
|
|
219
|
-
?.filter((route) => routeNames.includes(route.name))
|
|
220
|
-
.map(
|
|
221
|
-
(route) =>
|
|
222
|
-
({
|
|
223
|
-
...route,
|
|
224
|
-
key: route.key || `${route.name}-${nanoid()}`,
|
|
225
|
-
params:
|
|
226
|
-
routeParamList[route.name] !== undefined
|
|
227
|
-
? {
|
|
228
|
-
...routeParamList[route.name],
|
|
229
|
-
...route.params,
|
|
230
|
-
}
|
|
231
|
-
: route.params,
|
|
232
|
-
}) as Route<string>
|
|
233
|
-
) ?? [];
|
|
234
|
-
|
|
235
336
|
if (routes.length === 0) {
|
|
236
337
|
const initialRouteName =
|
|
237
338
|
options.initialRouteName !== undefined
|
|
238
339
|
? options.initialRouteName
|
|
239
340
|
: routeNames[0];
|
|
240
341
|
|
|
342
|
+
if (initialRouteName == null) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
"Couldn't initialize the stack navigator. Have you defined any screens?"
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
241
348
|
routes.push({
|
|
242
349
|
key: `${initialRouteName}-${nanoid()}`,
|
|
243
350
|
name: initialRouteName,
|
|
@@ -245,14 +352,23 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
245
352
|
});
|
|
246
353
|
}
|
|
247
354
|
|
|
355
|
+
const index = Math.min(
|
|
356
|
+
Math.max(state.index ?? routes.length - 1, 0),
|
|
357
|
+
routes.length - 1
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
361
|
+
const retainedRouteKeys =
|
|
362
|
+
state.retainedRouteKeys?.filter((key) => routeKeys.has(key)) ?? [];
|
|
363
|
+
|
|
248
364
|
return {
|
|
249
365
|
stale: false,
|
|
250
366
|
type: 'stack',
|
|
251
367
|
key: `stack-${nanoid()}`,
|
|
252
|
-
index
|
|
368
|
+
index,
|
|
253
369
|
routeNames,
|
|
254
370
|
routes,
|
|
255
|
-
|
|
371
|
+
retainedRouteKeys,
|
|
256
372
|
};
|
|
257
373
|
},
|
|
258
374
|
|
|
@@ -260,7 +376,19 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
260
376
|
state,
|
|
261
377
|
{ routeNames, routeParamList, routeKeyChanges }
|
|
262
378
|
) {
|
|
263
|
-
const routes = state.
|
|
379
|
+
const routes = getActiveRoutes(state).filter(
|
|
380
|
+
(route) =>
|
|
381
|
+
routeNames.includes(route.name) &&
|
|
382
|
+
!routeKeyChanges.includes(route.name)
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const preloadedRoutes = getPreloadedRoutes(state).filter(
|
|
386
|
+
(route) =>
|
|
387
|
+
routeNames.includes(route.name) &&
|
|
388
|
+
!routeKeyChanges.includes(route.name)
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const retainedRoutes = getRetainedRoutes(state).filter(
|
|
264
392
|
(route) =>
|
|
265
393
|
routeNames.includes(route.name) &&
|
|
266
394
|
!routeKeyChanges.includes(route.name)
|
|
@@ -273,6 +401,12 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
273
401
|
? options.initialRouteName
|
|
274
402
|
: routeNames[0];
|
|
275
403
|
|
|
404
|
+
if (initialRouteName == null) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
"Couldn't initialize the stack navigator. Have you defined any screens?"
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
276
410
|
routes.push({
|
|
277
411
|
key: `${initialRouteName}-${nanoid()}`,
|
|
278
412
|
name: initialRouteName,
|
|
@@ -280,36 +414,54 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
280
414
|
});
|
|
281
415
|
}
|
|
282
416
|
|
|
283
|
-
|
|
284
|
-
...
|
|
285
|
-
|
|
417
|
+
const routeKeys = new Set(
|
|
418
|
+
[...routes, ...preloadedRoutes, ...retainedRoutes].map(
|
|
419
|
+
(route) => route.key
|
|
420
|
+
)
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const retainedRouteKeys = state.retainedRouteKeys.filter((key) =>
|
|
424
|
+
routeKeys.has(key)
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
return getStateWithRoutes(
|
|
428
|
+
{
|
|
429
|
+
...state,
|
|
430
|
+
routeNames,
|
|
431
|
+
retainedRouteKeys,
|
|
432
|
+
},
|
|
286
433
|
routes,
|
|
287
|
-
|
|
288
|
-
|
|
434
|
+
preloadedRoutes,
|
|
435
|
+
retainedRoutes
|
|
436
|
+
);
|
|
289
437
|
},
|
|
290
438
|
|
|
291
439
|
getStateForRouteFocus(state, key) {
|
|
292
|
-
const
|
|
440
|
+
const routes = getActiveRoutes(state);
|
|
441
|
+
const index = routes.findIndex((r) => r.key === key);
|
|
293
442
|
|
|
294
443
|
if (index === -1 || index === state.index) {
|
|
295
444
|
return state;
|
|
296
445
|
}
|
|
297
446
|
|
|
298
|
-
return
|
|
299
|
-
|
|
300
|
-
index
|
|
301
|
-
|
|
302
|
-
};
|
|
447
|
+
return retainRoutes(
|
|
448
|
+
state,
|
|
449
|
+
getStateWithRoutes(state, routes.slice(0, index + 1))
|
|
450
|
+
);
|
|
303
451
|
},
|
|
304
452
|
|
|
305
453
|
getStateForAction(state, action, options) {
|
|
306
454
|
const { routeParamList } = options;
|
|
307
455
|
|
|
456
|
+
const routes = getActiveRoutes(state);
|
|
457
|
+
const preloadedRoutes = getPreloadedRoutes(state);
|
|
458
|
+
const inactiveRoutes = getInactiveRoutes(state);
|
|
459
|
+
|
|
308
460
|
switch (action.type) {
|
|
309
461
|
case 'REPLACE': {
|
|
310
462
|
const currentIndex =
|
|
311
463
|
action.target === state.key && action.source
|
|
312
|
-
?
|
|
464
|
+
? routes.findIndex((r) => r.key === action.source)
|
|
313
465
|
: state.index;
|
|
314
466
|
|
|
315
467
|
if (currentIndex === -1) {
|
|
@@ -323,8 +475,8 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
323
475
|
const getId = options.routeGetIdList[action.payload.name];
|
|
324
476
|
const id = getId?.({ params: action.payload.params });
|
|
325
477
|
|
|
326
|
-
// Re-use
|
|
327
|
-
let route =
|
|
478
|
+
// Re-use inactive route if available
|
|
479
|
+
let route = inactiveRoutes.find(
|
|
328
480
|
(route) =>
|
|
329
481
|
route.name === action.payload.name &&
|
|
330
482
|
id === getId?.({ params: route.params })
|
|
@@ -332,17 +484,22 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
332
484
|
|
|
333
485
|
if (!route) {
|
|
334
486
|
route = createRouteFromAction({ action, routeParamList });
|
|
487
|
+
} else {
|
|
488
|
+
const params = createParamsFromAction({ action, routeParamList });
|
|
489
|
+
|
|
490
|
+
if (route.params !== params) {
|
|
491
|
+
route = { ...route, params };
|
|
492
|
+
}
|
|
335
493
|
}
|
|
336
494
|
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
};
|
|
495
|
+
return retainRoutes(
|
|
496
|
+
state,
|
|
497
|
+
getStateWithRoutes(
|
|
498
|
+
state,
|
|
499
|
+
routes.map((r, i) => (i === currentIndex ? route : r)),
|
|
500
|
+
preloadedRoutes.filter((r) => r.key !== route.key)
|
|
501
|
+
)
|
|
502
|
+
);
|
|
346
503
|
}
|
|
347
504
|
|
|
348
505
|
case 'PUSH':
|
|
@@ -356,27 +513,79 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
356
513
|
|
|
357
514
|
let route: Route<string> | undefined;
|
|
358
515
|
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
(
|
|
374
|
-
|
|
516
|
+
if (action.type === 'NAVIGATE') {
|
|
517
|
+
const currentRoute = routes[state.index];
|
|
518
|
+
|
|
519
|
+
if (currentRoute == null) {
|
|
520
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (id !== undefined) {
|
|
524
|
+
if (
|
|
525
|
+
currentRoute.name === action.payload.name &&
|
|
526
|
+
id === getId?.({ params: currentRoute.params })
|
|
527
|
+
) {
|
|
528
|
+
route = currentRoute;
|
|
529
|
+
} else if (action.payload.pop) {
|
|
530
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
531
|
+
const r = routes[i];
|
|
532
|
+
|
|
533
|
+
if (r == null) {
|
|
534
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (r.name !== action.payload.name) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (id === getId?.({ params: r.params })) {
|
|
542
|
+
route = r;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (r.history?.length) {
|
|
547
|
+
for (let j = r.history.length - 1; j >= 0; j--) {
|
|
548
|
+
const historyEntry = r.history[j];
|
|
549
|
+
|
|
550
|
+
if (historyEntry == null) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Couldn't find a route history entry at index ${j}.`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (
|
|
557
|
+
historyEntry.type === 'params' &&
|
|
558
|
+
id === getId?.({ params: historyEntry.params })
|
|
559
|
+
) {
|
|
560
|
+
route = {
|
|
561
|
+
...r,
|
|
562
|
+
params: historyEntry.params,
|
|
563
|
+
history: r.history.slice(0, j),
|
|
564
|
+
};
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (route) {
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
// If the route matches the current one, then navigate to it
|
|
577
|
+
if (action.payload.name === currentRoute.name) {
|
|
578
|
+
route = currentRoute;
|
|
579
|
+
} else if (action.payload.pop) {
|
|
580
|
+
route = routes.findLast(
|
|
581
|
+
(route) => route.name === action.payload.name
|
|
582
|
+
);
|
|
583
|
+
}
|
|
375
584
|
}
|
|
376
585
|
}
|
|
377
586
|
|
|
378
587
|
if (!route) {
|
|
379
|
-
route =
|
|
588
|
+
route = inactiveRoutes.find(
|
|
380
589
|
(route) =>
|
|
381
590
|
route.name === action.payload.name &&
|
|
382
591
|
id === getId?.({ params: route.params })
|
|
@@ -399,16 +608,16 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
399
608
|
params = createParamsFromAction({ action, routeParamList });
|
|
400
609
|
}
|
|
401
610
|
|
|
402
|
-
let
|
|
611
|
+
let nextRoutes: Route<string>[];
|
|
403
612
|
|
|
404
613
|
if (route) {
|
|
405
614
|
if (action.type === 'NAVIGATE' && action.payload.pop) {
|
|
406
|
-
|
|
615
|
+
nextRoutes = [];
|
|
407
616
|
|
|
408
617
|
// Get all routes until the matching one
|
|
409
|
-
for (const r of
|
|
618
|
+
for (const r of routes) {
|
|
410
619
|
if (r.key === route.key) {
|
|
411
|
-
|
|
620
|
+
nextRoutes.push({
|
|
412
621
|
...route,
|
|
413
622
|
path:
|
|
414
623
|
action.payload.path !== undefined
|
|
@@ -419,11 +628,22 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
419
628
|
break;
|
|
420
629
|
}
|
|
421
630
|
|
|
422
|
-
|
|
631
|
+
nextRoutes.push(r);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (!nextRoutes.some((r) => r.key === route.key)) {
|
|
635
|
+
nextRoutes.push({
|
|
636
|
+
...route,
|
|
637
|
+
path:
|
|
638
|
+
action.payload.path !== undefined
|
|
639
|
+
? action.payload.path
|
|
640
|
+
: route.path,
|
|
641
|
+
params,
|
|
642
|
+
});
|
|
423
643
|
}
|
|
424
644
|
} else {
|
|
425
|
-
|
|
426
|
-
|
|
645
|
+
nextRoutes = routes.filter((r) => r.key !== route.key);
|
|
646
|
+
nextRoutes.push({
|
|
427
647
|
...route,
|
|
428
648
|
path:
|
|
429
649
|
action.type === 'NAVIGATE' &&
|
|
@@ -434,8 +654,8 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
434
654
|
});
|
|
435
655
|
}
|
|
436
656
|
} else {
|
|
437
|
-
|
|
438
|
-
...
|
|
657
|
+
nextRoutes = [
|
|
658
|
+
...routes,
|
|
439
659
|
{
|
|
440
660
|
key: `${action.payload.name}-${nanoid()}`,
|
|
441
661
|
name: action.payload.name,
|
|
@@ -446,23 +666,39 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
446
666
|
];
|
|
447
667
|
}
|
|
448
668
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
)
|
|
455
|
-
|
|
456
|
-
|
|
669
|
+
const lastRoute = nextRoutes[nextRoutes.length - 1];
|
|
670
|
+
|
|
671
|
+
if (lastRoute == null) {
|
|
672
|
+
throw new Error(
|
|
673
|
+
`Couldn't find a route at index ${nextRoutes.length - 1}.`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return retainRoutes(
|
|
678
|
+
state,
|
|
679
|
+
getStateWithRoutes(
|
|
680
|
+
state,
|
|
681
|
+
nextRoutes,
|
|
682
|
+
preloadedRoutes.filter((route) => lastRoute.key !== route.key)
|
|
683
|
+
)
|
|
684
|
+
);
|
|
457
685
|
}
|
|
458
686
|
|
|
459
687
|
case 'POP': {
|
|
460
688
|
let currentIndex =
|
|
461
689
|
action.target === state.key && action.source
|
|
462
|
-
?
|
|
690
|
+
? routes.findIndex((r) => r.key === action.source)
|
|
463
691
|
: state.index;
|
|
464
692
|
|
|
465
|
-
|
|
693
|
+
if (currentIndex === -1) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
let route = routes[currentIndex];
|
|
698
|
+
|
|
699
|
+
if (route == null) {
|
|
700
|
+
throw new Error(`Couldn't find a route at index ${currentIndex}.`);
|
|
701
|
+
}
|
|
466
702
|
|
|
467
703
|
/**
|
|
468
704
|
* When popping entries,
|
|
@@ -475,11 +711,11 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
475
711
|
*/
|
|
476
712
|
if (route.history?.length || currentIndex > 0) {
|
|
477
713
|
let count = action.payload.count;
|
|
478
|
-
let
|
|
714
|
+
let nextRoutes = routes;
|
|
479
715
|
|
|
480
716
|
while (count > 0 && (route.history?.length || currentIndex > 0)) {
|
|
481
717
|
if (route.history?.length) {
|
|
482
|
-
|
|
718
|
+
nextRoutes = [...nextRoutes];
|
|
483
719
|
|
|
484
720
|
const end = Math.min(count, route.history.length);
|
|
485
721
|
const last = route.history.at(-end);
|
|
@@ -488,7 +724,7 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
488
724
|
|
|
489
725
|
count = count - removed;
|
|
490
726
|
|
|
491
|
-
|
|
727
|
+
nextRoutes[currentIndex] = {
|
|
492
728
|
...route,
|
|
493
729
|
params: last?.type === 'params' ? last.params : route.params,
|
|
494
730
|
history,
|
|
@@ -496,33 +732,42 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
496
732
|
}
|
|
497
733
|
|
|
498
734
|
if (currentIndex > 0 && count > 0) {
|
|
499
|
-
const length =
|
|
735
|
+
const length = nextRoutes.length;
|
|
500
736
|
const end = Math.max(currentIndex - count + 1, 1);
|
|
501
737
|
|
|
502
|
-
|
|
738
|
+
nextRoutes = nextRoutes
|
|
503
739
|
.slice(0, end)
|
|
504
|
-
.concat(
|
|
740
|
+
.concat(nextRoutes.slice(currentIndex + 1));
|
|
505
741
|
|
|
506
|
-
const removed = length -
|
|
742
|
+
const removed = length - nextRoutes.length;
|
|
507
743
|
|
|
508
744
|
count = count - removed;
|
|
509
|
-
currentIndex =
|
|
510
|
-
|
|
745
|
+
currentIndex = nextRoutes.length - 1;
|
|
746
|
+
|
|
747
|
+
const currentRoute = nextRoutes[currentIndex];
|
|
748
|
+
|
|
749
|
+
if (currentRoute == null) {
|
|
750
|
+
throw new Error(
|
|
751
|
+
`Couldn't find a route at index ${currentIndex}.`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
route = currentRoute;
|
|
511
756
|
}
|
|
512
757
|
}
|
|
513
758
|
|
|
514
|
-
return
|
|
515
|
-
...state,
|
|
516
|
-
index: routes.length - 1,
|
|
517
|
-
routes,
|
|
518
|
-
};
|
|
759
|
+
return retainRoutes(state, getStateWithRoutes(state, nextRoutes));
|
|
519
760
|
}
|
|
520
761
|
|
|
521
762
|
return null;
|
|
522
763
|
}
|
|
523
764
|
|
|
524
765
|
case 'POP_TO_TOP': {
|
|
525
|
-
let route =
|
|
766
|
+
let route = routes[0];
|
|
767
|
+
|
|
768
|
+
if (route == null) {
|
|
769
|
+
throw new Error("Couldn't find a route at index 0.");
|
|
770
|
+
}
|
|
526
771
|
|
|
527
772
|
if (state.index > 0 || route.history?.length) {
|
|
528
773
|
if (route.history?.length) {
|
|
@@ -532,11 +777,7 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
532
777
|
};
|
|
533
778
|
}
|
|
534
779
|
|
|
535
|
-
return
|
|
536
|
-
...state,
|
|
537
|
-
index: 0,
|
|
538
|
-
routes: [route],
|
|
539
|
-
};
|
|
780
|
+
return retainRoutes(state, getStateWithRoutes(state, [route]));
|
|
540
781
|
}
|
|
541
782
|
|
|
542
783
|
return null;
|
|
@@ -545,7 +786,7 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
545
786
|
case 'POP_TO': {
|
|
546
787
|
const currentIndex =
|
|
547
788
|
action.target === state.key && action.source
|
|
548
|
-
?
|
|
789
|
+
? routes.findLastIndex((r) => r.key === action.source)
|
|
549
790
|
: state.index;
|
|
550
791
|
|
|
551
792
|
if (currentIndex === -1) {
|
|
@@ -558,21 +799,76 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
558
799
|
|
|
559
800
|
// If the route already exists, navigate to it
|
|
560
801
|
let index = -1;
|
|
802
|
+
let historyIndex: number | undefined;
|
|
561
803
|
|
|
562
804
|
const getId = options.routeGetIdList[action.payload.name];
|
|
563
805
|
const id = getId?.({ params: action.payload.params });
|
|
564
806
|
|
|
565
807
|
if (id !== undefined) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
808
|
+
for (let i = currentIndex; i >= 0; i--) {
|
|
809
|
+
const r = routes[i];
|
|
810
|
+
|
|
811
|
+
if (r == null) {
|
|
812
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (r.name !== action.payload.name) {
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (id === getId?.({ params: r.params })) {
|
|
820
|
+
index = i;
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (r.history?.length) {
|
|
825
|
+
for (let j = r.history.length - 1; j >= 0; j--) {
|
|
826
|
+
const historyEntry = r.history[j];
|
|
827
|
+
|
|
828
|
+
if (historyEntry == null) {
|
|
829
|
+
throw new Error(
|
|
830
|
+
`Couldn't find a route history entry at index ${j}.`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (
|
|
835
|
+
historyEntry.type === 'params' &&
|
|
836
|
+
id === getId?.({ params: historyEntry.params })
|
|
837
|
+
) {
|
|
838
|
+
index = i;
|
|
839
|
+
historyIndex = j;
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (index !== -1) {
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
573
849
|
} else {
|
|
850
|
+
const currentRoute = routes[currentIndex];
|
|
851
|
+
|
|
852
|
+
if (currentRoute == null) {
|
|
853
|
+
throw new Error(
|
|
854
|
+
`Couldn't find a route at index ${currentIndex}.`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (currentRoute.name === action.payload.name) {
|
|
859
|
+
index = currentIndex;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (id == null && index === -1) {
|
|
574
864
|
for (let i = currentIndex; i >= 0; i--) {
|
|
575
|
-
|
|
865
|
+
const route = routes[i];
|
|
866
|
+
|
|
867
|
+
if (route == null) {
|
|
868
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (route.name === action.payload.name) {
|
|
576
872
|
index = i;
|
|
577
873
|
break;
|
|
578
874
|
}
|
|
@@ -581,8 +877,8 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
581
877
|
|
|
582
878
|
// If the route doesn't exist, remove the current route and add the new one
|
|
583
879
|
if (index === -1) {
|
|
584
|
-
// Re-use
|
|
585
|
-
let route =
|
|
880
|
+
// Re-use inactive route if available
|
|
881
|
+
let route = inactiveRoutes.find(
|
|
586
882
|
(route) =>
|
|
587
883
|
route.name === action.payload.name &&
|
|
588
884
|
id === getId?.({ params: route.params })
|
|
@@ -590,21 +886,47 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
590
886
|
|
|
591
887
|
if (!route) {
|
|
592
888
|
route = createRouteFromAction({ action, routeParamList });
|
|
889
|
+
} else {
|
|
890
|
+
const params = createParamsFromAction({ action, routeParamList });
|
|
891
|
+
|
|
892
|
+
if (route.params !== params) {
|
|
893
|
+
route = { ...route, params };
|
|
894
|
+
}
|
|
593
895
|
}
|
|
594
896
|
|
|
595
|
-
const
|
|
897
|
+
const nextRoutes = routes.slice(0, currentIndex).concat(route);
|
|
596
898
|
|
|
597
|
-
return
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
(r) => r.key !== route.key
|
|
603
|
-
)
|
|
604
|
-
|
|
899
|
+
return retainRoutes(
|
|
900
|
+
state,
|
|
901
|
+
getStateWithRoutes(
|
|
902
|
+
state,
|
|
903
|
+
nextRoutes,
|
|
904
|
+
preloadedRoutes.filter((r) => r.key !== route.key)
|
|
905
|
+
)
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const route = routes[index];
|
|
910
|
+
|
|
911
|
+
if (route == null) {
|
|
912
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
605
913
|
}
|
|
606
914
|
|
|
607
|
-
|
|
915
|
+
let baseParams = route.params;
|
|
916
|
+
let history = route.history;
|
|
917
|
+
|
|
918
|
+
if (historyIndex !== undefined && route.history) {
|
|
919
|
+
const historyEntry = route.history[historyIndex];
|
|
920
|
+
|
|
921
|
+
if (historyEntry == null) {
|
|
922
|
+
throw new Error(
|
|
923
|
+
`Couldn't find a route history entry at index ${historyIndex}.`
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
baseParams = historyEntry.params;
|
|
928
|
+
history = route.history.slice(0, historyIndex);
|
|
929
|
+
}
|
|
608
930
|
|
|
609
931
|
let params;
|
|
610
932
|
|
|
@@ -614,28 +936,33 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
614
936
|
routeParamList[route.name] !== undefined
|
|
615
937
|
? {
|
|
616
938
|
...routeParamList[route.name],
|
|
617
|
-
...
|
|
939
|
+
...baseParams,
|
|
618
940
|
...action.payload.params,
|
|
619
941
|
}
|
|
620
|
-
:
|
|
942
|
+
: baseParams;
|
|
621
943
|
} else {
|
|
622
944
|
params = createParamsFromAction({ action, routeParamList });
|
|
623
945
|
}
|
|
624
946
|
|
|
625
|
-
return
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
};
|
|
947
|
+
return retainRoutes(
|
|
948
|
+
state,
|
|
949
|
+
getStateWithRoutes(state, [
|
|
950
|
+
...routes.slice(0, index),
|
|
951
|
+
params !== baseParams || historyIndex !== undefined
|
|
952
|
+
? { ...route, params, history }
|
|
953
|
+
: route,
|
|
954
|
+
])
|
|
955
|
+
);
|
|
635
956
|
}
|
|
636
957
|
|
|
637
|
-
case 'GO_BACK':
|
|
638
|
-
|
|
958
|
+
case 'GO_BACK': {
|
|
959
|
+
const route = routes[state.index];
|
|
960
|
+
|
|
961
|
+
if (route == null) {
|
|
962
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (state.index > 0 || route.history?.length) {
|
|
639
966
|
return router.getStateForAction(
|
|
640
967
|
state,
|
|
641
968
|
{
|
|
@@ -649,46 +976,82 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
649
976
|
}
|
|
650
977
|
|
|
651
978
|
return null;
|
|
979
|
+
}
|
|
652
980
|
|
|
653
|
-
case '
|
|
654
|
-
const
|
|
655
|
-
const id = getId?.({ params: action.payload.params });
|
|
981
|
+
case 'RETAIN': {
|
|
982
|
+
const route = routes[state.index];
|
|
656
983
|
|
|
657
|
-
|
|
984
|
+
if (route == null) {
|
|
985
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
986
|
+
}
|
|
658
987
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
id === getId?.({ params: route.params })
|
|
664
|
-
);
|
|
988
|
+
const routeKey = action.source ?? route.key;
|
|
989
|
+
|
|
990
|
+
if (!state.routes.some((route) => route.key === routeKey)) {
|
|
991
|
+
return null;
|
|
665
992
|
}
|
|
666
993
|
|
|
667
|
-
if (
|
|
994
|
+
if (action.payload.enable) {
|
|
995
|
+
if (state.retainedRouteKeys.includes(routeKey)) {
|
|
996
|
+
return state;
|
|
997
|
+
}
|
|
998
|
+
|
|
668
999
|
return {
|
|
669
1000
|
...state,
|
|
670
|
-
|
|
671
|
-
if (r.key !== route?.key) {
|
|
672
|
-
return r;
|
|
673
|
-
}
|
|
674
|
-
return {
|
|
675
|
-
...r,
|
|
676
|
-
params: createParamsFromAction({ action, routeParamList }),
|
|
677
|
-
};
|
|
678
|
-
}),
|
|
1001
|
+
retainedRouteKeys: [...state.retainedRouteKeys, routeKey],
|
|
679
1002
|
};
|
|
680
|
-
}
|
|
681
|
-
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (!state.retainedRouteKeys.includes(routeKey)) {
|
|
1006
|
+
return state;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return getStateWithRoutes(
|
|
1010
|
+
{
|
|
682
1011
|
...state,
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
1012
|
+
retainedRouteKeys: state.retainedRouteKeys.filter(
|
|
1013
|
+
(key) => key !== routeKey
|
|
1014
|
+
),
|
|
1015
|
+
},
|
|
1016
|
+
routes,
|
|
1017
|
+
preloadedRoutes.filter((route) => route.key !== routeKey)
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
case 'PRELOAD': {
|
|
1022
|
+
if (!state.routeNames.includes(action.payload.name)) {
|
|
1023
|
+
return null;
|
|
691
1024
|
}
|
|
1025
|
+
|
|
1026
|
+
const getId = options.routeGetIdList[action.payload.name];
|
|
1027
|
+
const id = getId?.({ params: action.payload.params });
|
|
1028
|
+
const params = createParamsFromAction({ action, routeParamList });
|
|
1029
|
+
|
|
1030
|
+
const route = preloadedRoutes.findLast(
|
|
1031
|
+
(route) =>
|
|
1032
|
+
route.name === action.payload.name &&
|
|
1033
|
+
(id === undefined || id === getId?.({ params: route.params }))
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
if (route) {
|
|
1037
|
+
return getStateWithRoutes(
|
|
1038
|
+
state,
|
|
1039
|
+
routes,
|
|
1040
|
+
preloadedRoutes.map((r) =>
|
|
1041
|
+
r.key === route.key && r.params !== params
|
|
1042
|
+
? { ...r, params }
|
|
1043
|
+
: r
|
|
1044
|
+
)
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return getStateWithRoutes(
|
|
1049
|
+
state,
|
|
1050
|
+
routes,
|
|
1051
|
+
preloadedRoutes.concat(
|
|
1052
|
+
createRouteFromAction({ action, routeParamList })
|
|
1053
|
+
)
|
|
1054
|
+
);
|
|
692
1055
|
}
|
|
693
1056
|
|
|
694
1057
|
default:
|
|
@@ -696,10 +1059,6 @@ export function StackRouter(options: StackRouterOptions) {
|
|
|
696
1059
|
}
|
|
697
1060
|
},
|
|
698
1061
|
|
|
699
|
-
getRoutesFromState(state) {
|
|
700
|
-
return [...state.routes, ...state.preloadedRoutes];
|
|
701
|
-
},
|
|
702
|
-
|
|
703
1062
|
actionCreators: StackActions,
|
|
704
1063
|
};
|
|
705
1064
|
|