@amazon-devices/react-navigation__routers 8.0.9000000000 → 8.0.9000000002

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 DELETED
@@ -1,546 +0,0 @@
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
- ParamListBase,
10
- PartialState,
11
- Route,
12
- Router,
13
- } from './types';
14
-
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
- };
42
-
43
- export type TabNavigationState<ParamList extends ParamListBase> = Omit<
44
- NavigationState<ParamList>,
45
- 'history'
46
- > & {
47
- /**
48
- * Type of the router, in this case, it's tab.
49
- */
50
- type: 'tab';
51
- /**
52
- * List of previously visited route keys.
53
- */
54
- history: { type: 'route'; key: string; params?: object | undefined }[];
55
- /**
56
- * List of routes' key, which are supposed to be preloaded before navigating to.
57
- */
58
- preloadedRouteKeys: string[];
59
- };
60
-
61
- export type TabActionHelpers<ParamList extends ParamListBase> = {
62
- /**
63
- * Jump to an existing tab.
64
- *
65
- * @param name Name of the route for the tab.
66
- * @param [params] Params object for the route.
67
- */
68
- jumpTo<RouteName extends keyof ParamList>(
69
- ...args: RouteName extends unknown
70
- ? undefined extends ParamList[RouteName]
71
- ? [screen: RouteName, params?: ParamList[RouteName]]
72
- : [screen: RouteName, params: ParamList[RouteName]]
73
- : never
74
- ): void;
75
- };
76
-
77
- const TYPE_ROUTE = 'route' as const;
78
-
79
- export const TabActions = {
80
- jumpTo(name: string, params?: object) {
81
- return {
82
- type: 'JUMP_TO',
83
- payload: { name, params },
84
- } as const satisfies TabActionType;
85
- },
86
- };
87
-
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
- }
187
-
188
- 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,
204
-
205
- type: 'tab',
206
-
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
- );
225
-
226
- return {
227
- stale: false,
228
- type: 'tab',
229
- key: `tab-${nanoid()}`,
230
- index,
231
- routeNames,
232
- history,
233
- routes,
234
- preloadedRouteKeys: [],
235
- };
236
- },
237
-
238
- getRehydratedState(partialState, { routeNames, routeParamList }) {
239
- const state = partialState;
240
-
241
- if (state.stale === false) {
242
- return state;
243
- }
244
-
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
- }
332
-
333
- return {
334
- ...state,
335
- history,
336
- routeNames,
337
- routes,
338
- index,
339
- };
340
- },
341
-
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
- actionCreators: TabActions,
543
- };
544
-
545
- return router;
546
- }
@@ -1,22 +0,0 @@
1
- import type { ParamListBase } from './types';
2
-
3
- type Options = {
4
- action: {
5
- payload: {
6
- name: string;
7
- params?: object;
8
- };
9
- };
10
- routeParamList: ParamListBase;
11
- };
12
-
13
- export function createParamsFromAction({ action, routeParamList }: Options) {
14
- const { name, params } = action.payload;
15
-
16
- return routeParamList[name] !== undefined
17
- ? {
18
- ...routeParamList[name],
19
- ...params,
20
- }
21
- : params;
22
- }
@@ -1,24 +0,0 @@
1
- import { nanoid } from 'nanoid/non-secure';
2
-
3
- import { createParamsFromAction } from './createParamsFromAction';
4
- import type { ParamListBase } from './types';
5
-
6
- type Options = {
7
- action: {
8
- payload: {
9
- name: string;
10
- params?: object;
11
- };
12
- };
13
- routeParamList: ParamListBase;
14
- };
15
-
16
- export function createRouteFromAction({ action, routeParamList }: Options) {
17
- const { name } = action.payload;
18
-
19
- return {
20
- key: `${name}-${nanoid()}`,
21
- name,
22
- params: createParamsFromAction({ action, routeParamList }),
23
- };
24
- }