@react-navigation/routers 7.6.0 → 7.6.2

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