@react-navigation/routers 8.0.0-alpha.13 → 8.0.0-alpha.14
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 +64 -62
- package/lib/module/BaseRouter.js.map +1 -1
- package/lib/module/DrawerRouter.js +15 -14
- package/lib/module/DrawerRouter.js.map +1 -1
- package/lib/module/StackRouter.js +17 -11
- package/lib/module/StackRouter.js.map +1 -1
- package/lib/module/SwitchRouter.js +369 -0
- package/lib/module/SwitchRouter.js.map +1 -0
- package/lib/module/TabRouter.js +14 -331
- package/lib/module/TabRouter.js.map +1 -1
- package/lib/typescript/src/BaseRouter.d.ts +3 -1
- package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
- package/lib/typescript/src/DrawerRouter.d.ts +1 -0
- package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
- package/lib/typescript/src/StackRouter.d.ts.map +1 -1
- package/lib/typescript/src/SwitchRouter.d.ts +69 -0
- package/lib/typescript/src/SwitchRouter.d.ts.map +1 -0
- package/lib/typescript/src/TabRouter.d.ts +4 -24
- package/lib/typescript/src/TabRouter.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/BaseRouter.tsx +93 -82
- package/src/DrawerRouter.tsx +25 -19
- package/src/StackRouter.tsx +27 -17
- package/src/SwitchRouter.tsx +621 -0
- package/src/TabRouter.tsx +24 -524
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid/non-secure';
|
|
2
|
+
|
|
3
|
+
import { BaseRouter } from './BaseRouter';
|
|
4
|
+
import { createParamsFromAction } from './createParamsFromAction';
|
|
5
|
+
import type {
|
|
6
|
+
CommonNavigationAction,
|
|
7
|
+
DefaultRouterOptions,
|
|
8
|
+
NavigationState,
|
|
9
|
+
PartialState,
|
|
10
|
+
Route,
|
|
11
|
+
RouterConfigOptions,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
export type SwitchActionType = {
|
|
15
|
+
type: 'JUMP_TO';
|
|
16
|
+
payload: { name: string; params?: object | undefined };
|
|
17
|
+
source?: string | undefined;
|
|
18
|
+
target?: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type SwitchRouterOptions = DefaultRouterOptions & {
|
|
22
|
+
/**
|
|
23
|
+
* Control how going back should behave
|
|
24
|
+
* - `firstRoute` - return to the first defined route
|
|
25
|
+
* - `initialRoute` - return to the route from `initialRouteName`
|
|
26
|
+
* - `order` - return to the route defined before the focused route
|
|
27
|
+
* - `history` - return to last visited route; if the same route is visited multiple times, the older entries are dropped from the history
|
|
28
|
+
* - `fullHistory` - return to last visited route; doesn't drop duplicate entries unlike `history` - matches behavior of web pages
|
|
29
|
+
* - `none` - do not handle going back
|
|
30
|
+
*/
|
|
31
|
+
backBehavior?: BackBehavior | undefined;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type BackBehavior =
|
|
35
|
+
| 'firstRoute'
|
|
36
|
+
| 'initialRoute'
|
|
37
|
+
| 'order'
|
|
38
|
+
| 'history'
|
|
39
|
+
| 'fullHistory'
|
|
40
|
+
| 'none';
|
|
41
|
+
|
|
42
|
+
type RouteHistory = {
|
|
43
|
+
type: 'route';
|
|
44
|
+
key: string;
|
|
45
|
+
params?: object | undefined;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type DrawerHistory = {
|
|
49
|
+
type: 'drawer';
|
|
50
|
+
status: 'open' | 'closed';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type SwitchRouterState<History> = Omit<NavigationState, 'history' | 'type'> & {
|
|
54
|
+
history: History[];
|
|
55
|
+
preloadedRouteKeys: string[];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type SwitchRouterStateMap = {
|
|
59
|
+
tab: SwitchRouterState<RouteHistory> & { type: 'tab' };
|
|
60
|
+
drawer: SwitchRouterState<RouteHistory | DrawerHistory> & {
|
|
61
|
+
type: 'drawer';
|
|
62
|
+
default: DrawerHistory['status'];
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type SwitchRouterType = 'tab' | 'drawer';
|
|
67
|
+
|
|
68
|
+
const TYPE_ROUTE = 'route' as const;
|
|
69
|
+
|
|
70
|
+
const getRouteHistory = (
|
|
71
|
+
routes: Route<string>[],
|
|
72
|
+
index: number,
|
|
73
|
+
backBehavior: BackBehavior,
|
|
74
|
+
initialRouteName: string | undefined
|
|
75
|
+
) => {
|
|
76
|
+
const route = routes[index];
|
|
77
|
+
|
|
78
|
+
if (route == null) {
|
|
79
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const history: RouteHistory[] = [
|
|
83
|
+
{
|
|
84
|
+
type: TYPE_ROUTE,
|
|
85
|
+
key: route.key,
|
|
86
|
+
params: backBehavior === 'fullHistory' ? route.params : undefined,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
let initialRouteIndex;
|
|
91
|
+
|
|
92
|
+
switch (backBehavior) {
|
|
93
|
+
case 'order':
|
|
94
|
+
for (let i = index; i > 0; i--) {
|
|
95
|
+
const route = routes[i - 1];
|
|
96
|
+
|
|
97
|
+
if (route == null) {
|
|
98
|
+
throw new Error(`Couldn't find a route at index ${i - 1}.`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
history.unshift({
|
|
102
|
+
type: TYPE_ROUTE,
|
|
103
|
+
key: route.key,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'firstRoute':
|
|
110
|
+
if (index !== 0) {
|
|
111
|
+
const route = routes[0];
|
|
112
|
+
|
|
113
|
+
if (route == null) {
|
|
114
|
+
throw new Error("Couldn't find a route at index 0.");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
history.unshift({
|
|
118
|
+
type: TYPE_ROUTE,
|
|
119
|
+
key: route.key,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case 'initialRoute':
|
|
126
|
+
initialRouteIndex = routes.findIndex(
|
|
127
|
+
(route) => route.name === initialRouteName
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
initialRouteIndex = initialRouteIndex === -1 ? 0 : initialRouteIndex;
|
|
131
|
+
|
|
132
|
+
if (index !== initialRouteIndex) {
|
|
133
|
+
const route = routes[initialRouteIndex];
|
|
134
|
+
|
|
135
|
+
if (route == null) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Couldn't find a route at index ${initialRouteIndex}.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
history.unshift({
|
|
142
|
+
type: TYPE_ROUTE,
|
|
143
|
+
key: route.key,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'history':
|
|
150
|
+
case 'fullHistory':
|
|
151
|
+
// The history will fill up on navigation
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return history;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const changeIndex = <Type extends SwitchRouterType>(
|
|
159
|
+
state: Pick<SwitchRouterStateMap[Type], 'routes' | 'history'>,
|
|
160
|
+
index: number,
|
|
161
|
+
backBehavior: BackBehavior,
|
|
162
|
+
initialRouteName: string | undefined
|
|
163
|
+
): Pick<SwitchRouterStateMap[Type], 'index' | 'history'> => {
|
|
164
|
+
let history = state.history;
|
|
165
|
+
|
|
166
|
+
if (backBehavior === 'history' || backBehavior === 'fullHistory') {
|
|
167
|
+
const currentRoute = state.routes[index];
|
|
168
|
+
|
|
169
|
+
if (currentRoute == null) {
|
|
170
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (backBehavior === 'history') {
|
|
174
|
+
// Remove the existing key from the history to de-duplicate it
|
|
175
|
+
history = history.filter((it) =>
|
|
176
|
+
it.type === 'route' ? it.key !== currentRoute.key : true
|
|
177
|
+
);
|
|
178
|
+
} else if (backBehavior === 'fullHistory') {
|
|
179
|
+
const lastHistoryRouteItemIndex = history.findLastIndex(
|
|
180
|
+
(item) => item.type === 'route'
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const lastHistoryRouteItem = history[lastHistoryRouteItemIndex];
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
lastHistoryRouteItem?.type === 'route' &&
|
|
187
|
+
currentRoute.key === lastHistoryRouteItem.key
|
|
188
|
+
) {
|
|
189
|
+
// For full-history, only remove if it matches the last route
|
|
190
|
+
// Useful for drawer, if current route was in history, then drawer state changed
|
|
191
|
+
// Then we only need to move the route to the front
|
|
192
|
+
history = [
|
|
193
|
+
...history.slice(0, lastHistoryRouteItemIndex),
|
|
194
|
+
...history.slice(lastHistoryRouteItemIndex + 1),
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
history = history.concat({
|
|
200
|
+
type: TYPE_ROUTE,
|
|
201
|
+
key: currentRoute.key,
|
|
202
|
+
params: backBehavior === 'fullHistory' ? currentRoute.params : undefined,
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
// Preserve non-route entries (e.g. drawer status) when rebuilding the history
|
|
206
|
+
history = [
|
|
207
|
+
...state.history.filter((it) => it.type !== 'route'),
|
|
208
|
+
...getRouteHistory(state.routes, index, backBehavior, initialRouteName),
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
index,
|
|
214
|
+
history,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export function SwitchRouter<Type extends SwitchRouterType>({
|
|
219
|
+
initialRouteName,
|
|
220
|
+
backBehavior = 'firstRoute',
|
|
221
|
+
}: SwitchRouterOptions) {
|
|
222
|
+
type State = SwitchRouterStateMap[Type];
|
|
223
|
+
|
|
224
|
+
const router = {
|
|
225
|
+
...BaseRouter,
|
|
226
|
+
|
|
227
|
+
getInitialState({
|
|
228
|
+
routeNames,
|
|
229
|
+
routeParamList,
|
|
230
|
+
}: RouterConfigOptions): SwitchRouterState<RouteHistory> {
|
|
231
|
+
const index =
|
|
232
|
+
initialRouteName !== undefined && routeNames.includes(initialRouteName)
|
|
233
|
+
? routeNames.indexOf(initialRouteName)
|
|
234
|
+
: 0;
|
|
235
|
+
|
|
236
|
+
const routes = routeNames.map((name) => ({
|
|
237
|
+
name,
|
|
238
|
+
key: `${name}-${nanoid()}`,
|
|
239
|
+
params: routeParamList[name],
|
|
240
|
+
}));
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
stale: false,
|
|
244
|
+
key: nanoid(),
|
|
245
|
+
index,
|
|
246
|
+
routeNames,
|
|
247
|
+
history: getRouteHistory(routes, index, backBehavior, initialRouteName),
|
|
248
|
+
routes,
|
|
249
|
+
preloadedRouteKeys: [],
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
getRehydratedState(
|
|
254
|
+
state: PartialState<State>,
|
|
255
|
+
{ routeNames, routeParamList }: RouterConfigOptions
|
|
256
|
+
): SwitchRouterState<RouteHistory> {
|
|
257
|
+
const routes = routeNames.map((name) => {
|
|
258
|
+
const route = state.routes.find((r) => r.name === name);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
...route,
|
|
262
|
+
name,
|
|
263
|
+
key:
|
|
264
|
+
route && route.name === name && route.key
|
|
265
|
+
? route.key
|
|
266
|
+
: `${name}-${nanoid()}`,
|
|
267
|
+
params:
|
|
268
|
+
routeParamList[name] !== undefined
|
|
269
|
+
? {
|
|
270
|
+
...routeParamList[name],
|
|
271
|
+
...(route ? route.params : undefined),
|
|
272
|
+
}
|
|
273
|
+
: route
|
|
274
|
+
? route.params
|
|
275
|
+
: undefined,
|
|
276
|
+
} satisfies Route<string>;
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const currentRoute = state.routes[state.index ?? 0];
|
|
280
|
+
const index = Math.min(
|
|
281
|
+
Math.max(currentRoute ? routeNames.indexOf(currentRoute.name) : 0, 0),
|
|
282
|
+
routes.length - 1
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const routeKeys = routes.map((route) => route.key);
|
|
286
|
+
const history: RouteHistory[] = [];
|
|
287
|
+
|
|
288
|
+
for (const item of state.history ?? []) {
|
|
289
|
+
if (item.type === 'route' && routeKeys.includes(item.key)) {
|
|
290
|
+
history.push(item);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const stateData: SwitchRouterState<RouteHistory> = {
|
|
295
|
+
stale: false,
|
|
296
|
+
key: nanoid(),
|
|
297
|
+
index,
|
|
298
|
+
routeNames,
|
|
299
|
+
history,
|
|
300
|
+
routes,
|
|
301
|
+
preloadedRouteKeys:
|
|
302
|
+
state.preloadedRouteKeys?.filter((key) => routeKeys.includes(key)) ??
|
|
303
|
+
[],
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
...stateData,
|
|
308
|
+
...changeIndex<'tab'>(stateData, index, backBehavior, initialRouteName),
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
getStateForRouteNamesChange(
|
|
313
|
+
state: State,
|
|
314
|
+
{
|
|
315
|
+
routeNames,
|
|
316
|
+
routeParamList,
|
|
317
|
+
routeKeyChanges,
|
|
318
|
+
}: RouterConfigOptions & { routeKeyChanges: string[] }
|
|
319
|
+
): State {
|
|
320
|
+
const routes = routeNames.map(
|
|
321
|
+
(name) =>
|
|
322
|
+
state.routes.find(
|
|
323
|
+
(r) => r.name === name && !routeKeyChanges.includes(r.name)
|
|
324
|
+
) ?? {
|
|
325
|
+
name,
|
|
326
|
+
key: `${name}-${nanoid()}`,
|
|
327
|
+
params: routeParamList[name],
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
332
|
+
|
|
333
|
+
const currentRoute = state.routes[state.index];
|
|
334
|
+
|
|
335
|
+
if (currentRoute == null) {
|
|
336
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const history = state.history.filter(
|
|
340
|
+
// Type will always be 'route' for tabs, but could be different in a router extending this (e.g. drawer)
|
|
341
|
+
(item) => item.type !== 'route' || routeKeys.has(item.key)
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
let index = routeNames.indexOf(currentRoute.name);
|
|
345
|
+
|
|
346
|
+
if (index === -1) {
|
|
347
|
+
// The focused route was removed, so focus the most recently visited surviving route
|
|
348
|
+
const previousRouteItem = history.findLast(
|
|
349
|
+
(item): item is RouteHistory => item.type === 'route'
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
index = Math.max(
|
|
353
|
+
0,
|
|
354
|
+
routes.findIndex((route) => route.key === previousRouteItem?.key)
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
...state,
|
|
360
|
+
routeNames,
|
|
361
|
+
routes,
|
|
362
|
+
...changeIndex<Type>(
|
|
363
|
+
{ routes, history },
|
|
364
|
+
index,
|
|
365
|
+
backBehavior,
|
|
366
|
+
initialRouteName
|
|
367
|
+
),
|
|
368
|
+
preloadedRouteKeys: state.preloadedRouteKeys.filter((key) =>
|
|
369
|
+
routeKeys.has(key)
|
|
370
|
+
),
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
getStateForRouteFocus(state: State, key: string): State {
|
|
375
|
+
const index = state.routes.findIndex((r) => r.key === key);
|
|
376
|
+
|
|
377
|
+
if (index === -1 || index === state.index) {
|
|
378
|
+
return state;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
...state,
|
|
383
|
+
...changeIndex<Type>(state, index, backBehavior, initialRouteName),
|
|
384
|
+
};
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
getStateForAction(
|
|
388
|
+
state: State,
|
|
389
|
+
action: SwitchActionType | CommonNavigationAction,
|
|
390
|
+
{ routeParamList, routeGetIdList }: RouterConfigOptions
|
|
391
|
+
): State | PartialState<State> | null {
|
|
392
|
+
switch (action.type) {
|
|
393
|
+
case 'JUMP_TO':
|
|
394
|
+
case 'NAVIGATE': {
|
|
395
|
+
const index = state.routes.findIndex(
|
|
396
|
+
(route) => route.name === action.payload.name
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
if (index === -1) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const route = state.routes[index];
|
|
404
|
+
|
|
405
|
+
if (route == null) {
|
|
406
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const getId = routeGetIdList[route.name];
|
|
410
|
+
|
|
411
|
+
const currentId = getId?.({ params: route.params });
|
|
412
|
+
const nextId = getId?.({ params: action.payload.params });
|
|
413
|
+
|
|
414
|
+
const key =
|
|
415
|
+
currentId === nextId ? route.key : `${route.name}-${nanoid()}`;
|
|
416
|
+
|
|
417
|
+
let params;
|
|
418
|
+
|
|
419
|
+
if (
|
|
420
|
+
action.type === 'NAVIGATE' &&
|
|
421
|
+
action.payload.merge &&
|
|
422
|
+
currentId === nextId
|
|
423
|
+
) {
|
|
424
|
+
params =
|
|
425
|
+
action.payload.params !== undefined ||
|
|
426
|
+
routeParamList[route.name] !== undefined
|
|
427
|
+
? {
|
|
428
|
+
...routeParamList[route.name],
|
|
429
|
+
...route.params,
|
|
430
|
+
...action.payload.params,
|
|
431
|
+
}
|
|
432
|
+
: route.params;
|
|
433
|
+
} else {
|
|
434
|
+
params = createParamsFromAction({ action, routeParamList });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const path =
|
|
438
|
+
action.type === 'NAVIGATE' && action.payload.path != null
|
|
439
|
+
? action.payload.path
|
|
440
|
+
: route.path;
|
|
441
|
+
|
|
442
|
+
const nextRoute =
|
|
443
|
+
params !== route.params || path !== route.path || key !== route.key
|
|
444
|
+
? { ...route, key, path, params }
|
|
445
|
+
: route;
|
|
446
|
+
|
|
447
|
+
const routes = state.routes.map((item, routeIndex) =>
|
|
448
|
+
routeIndex === index ? nextRoute : item
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const updatedState: State = {
|
|
452
|
+
...state,
|
|
453
|
+
routes,
|
|
454
|
+
history:
|
|
455
|
+
key === route.key
|
|
456
|
+
? state.history
|
|
457
|
+
: state.history.filter(
|
|
458
|
+
(it) => it.type !== 'route' || it.key !== route.key
|
|
459
|
+
),
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
...updatedState,
|
|
464
|
+
...changeIndex<Type>(
|
|
465
|
+
updatedState,
|
|
466
|
+
index,
|
|
467
|
+
backBehavior,
|
|
468
|
+
initialRouteName
|
|
469
|
+
),
|
|
470
|
+
preloadedRouteKeys: updatedState.preloadedRouteKeys.filter(
|
|
471
|
+
(key) => key !== route.key && key !== nextRoute.key
|
|
472
|
+
),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
case 'GO_BACK': {
|
|
477
|
+
const focusedRoute = state.routes[state.index];
|
|
478
|
+
|
|
479
|
+
if (focusedRoute == null) {
|
|
480
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (state.history.length === 1 && !focusedRoute.history?.length) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const lastHistoryItem = state.history[state.history.length - 1];
|
|
488
|
+
|
|
489
|
+
if (
|
|
490
|
+
lastHistoryItem?.type === 'route' &&
|
|
491
|
+
focusedRoute.history?.length
|
|
492
|
+
) {
|
|
493
|
+
const routes = [...state.routes];
|
|
494
|
+
const history = [...focusedRoute.history];
|
|
495
|
+
const last = history.pop();
|
|
496
|
+
|
|
497
|
+
routes[state.index] = {
|
|
498
|
+
...focusedRoute,
|
|
499
|
+
params:
|
|
500
|
+
last?.type === 'params' ? last.params : focusedRoute.params,
|
|
501
|
+
history,
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
...state,
|
|
506
|
+
routes,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const previousHistoryItem = state.history[state.history.length - 2];
|
|
511
|
+
|
|
512
|
+
if (
|
|
513
|
+
previousHistoryItem == null ||
|
|
514
|
+
previousHistoryItem.type !== 'route'
|
|
515
|
+
) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const previousKey = previousHistoryItem.key;
|
|
520
|
+
const index = state.routes.findLastIndex(
|
|
521
|
+
(route) => route.key === previousKey
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
if (index === -1) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
let routes = state.routes;
|
|
529
|
+
|
|
530
|
+
const previousRoute = routes[index];
|
|
531
|
+
|
|
532
|
+
if (previousRoute == null) {
|
|
533
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (
|
|
537
|
+
backBehavior === 'fullHistory' &&
|
|
538
|
+
previousRoute.params !== previousHistoryItem.params
|
|
539
|
+
) {
|
|
540
|
+
routes = [...state.routes];
|
|
541
|
+
routes[index] = {
|
|
542
|
+
...previousRoute,
|
|
543
|
+
params: previousHistoryItem.params,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
...state,
|
|
549
|
+
routes,
|
|
550
|
+
preloadedRouteKeys: state.preloadedRouteKeys.filter(
|
|
551
|
+
(key) => key !== previousRoute.key
|
|
552
|
+
),
|
|
553
|
+
history: state.history.slice(0, -1),
|
|
554
|
+
index,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
case 'PRELOAD': {
|
|
559
|
+
const routeIndex = state.routes.findIndex(
|
|
560
|
+
(route) => route.name === action.payload.name
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
if (routeIndex === -1) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const route = state.routes[routeIndex];
|
|
568
|
+
|
|
569
|
+
if (route == null) {
|
|
570
|
+
throw new Error(`Couldn't find a route at index ${routeIndex}.`);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const getId = routeGetIdList[route.name];
|
|
574
|
+
|
|
575
|
+
const currentId = getId?.({ params: route.params });
|
|
576
|
+
const nextId = getId?.({ params: action.payload.params });
|
|
577
|
+
|
|
578
|
+
const key =
|
|
579
|
+
currentId === nextId ? route.key : `${route.name}-${nanoid()}`;
|
|
580
|
+
|
|
581
|
+
const params = createParamsFromAction({ action, routeParamList });
|
|
582
|
+
const newRoute =
|
|
583
|
+
params !== route.params ? { ...route, key, params } : route;
|
|
584
|
+
|
|
585
|
+
let history = state.history;
|
|
586
|
+
|
|
587
|
+
if (key !== route.key) {
|
|
588
|
+
history = history.filter(
|
|
589
|
+
(record) => record.type !== 'route' || record.key !== route.key
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
if (routeIndex === state.index) {
|
|
593
|
+
history = history.concat({
|
|
594
|
+
type: TYPE_ROUTE,
|
|
595
|
+
key: newRoute.key,
|
|
596
|
+
params:
|
|
597
|
+
backBehavior === 'fullHistory' ? newRoute.params : undefined,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
...state,
|
|
604
|
+
preloadedRouteKeys: state.preloadedRouteKeys
|
|
605
|
+
.filter((key) => key !== route.key)
|
|
606
|
+
.concat(newRoute.key),
|
|
607
|
+
routes: state.routes.map((route, index) =>
|
|
608
|
+
index === routeIndex ? newRoute : route
|
|
609
|
+
),
|
|
610
|
+
history,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
default:
|
|
615
|
+
return BaseRouter.getStateForAction(state, action);
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
return router;
|
|
621
|
+
}
|