@react-navigation/routers 8.0.0-alpha.13 → 8.0.0-alpha.15

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.
@@ -6,105 +6,116 @@ import type {
6
6
  PartialState,
7
7
  } from './types';
8
8
 
9
- /**
10
- * Base router object that can be used when writing custom routers.
11
- * This provides few helper methods to handle common actions such as `RESET`.
12
- */
13
- export const BaseRouter = {
14
- getStateForAction<State extends NavigationState>(
15
- state: State,
16
- action: CommonNavigationAction
17
- ): State | PartialState<State> | null {
18
- switch (action.type) {
19
- case 'SET_PARAMS':
20
- case 'REPLACE_PARAMS': {
21
- const index = action.source
22
- ? state.routes.findIndex((r) => r.key === action.source)
23
- : state.index;
24
-
25
- if (index === -1) {
26
- return null;
27
- }
9
+ function getStateForAction<State extends NavigationState>(
10
+ state: State,
11
+ action: CommonNavigationAction
12
+ ): State | PartialState<State> | null;
13
+ function getStateForAction(
14
+ state: NavigationState,
15
+ action: CommonNavigationAction
16
+ ) {
17
+ switch (action.type) {
18
+ case 'SET_PARAMS':
19
+ case 'REPLACE_PARAMS': {
20
+ const index = action.source
21
+ ? state.routes.findIndex((r) => r.key === action.source)
22
+ : state.index;
28
23
 
29
- return {
30
- ...state,
31
- routes: state.routes.map((r, i) =>
32
- i === index
33
- ? {
34
- ...r,
35
- params:
36
- action.type === 'REPLACE_PARAMS'
37
- ? action.payload.params
38
- : { ...r.params, ...action.payload.params },
39
- }
40
- : r
41
- ),
42
- };
24
+ if (index === -1) {
25
+ return null;
43
26
  }
44
27
 
45
- case 'PUSH_PARAMS': {
46
- const index = action.source
47
- ? state.routes.findIndex((r) => r.key === action.source)
48
- : state.index;
28
+ return {
29
+ ...state,
30
+ routes: state.routes.map((r, i) =>
31
+ i === index
32
+ ? {
33
+ ...r,
34
+ params:
35
+ action.type === 'REPLACE_PARAMS'
36
+ ? action.payload.params
37
+ : { ...r.params, ...action.payload.params },
38
+ }
39
+ : r
40
+ ),
41
+ };
42
+ }
49
43
 
50
- if (index === -1) {
51
- return null;
52
- }
44
+ case 'PUSH_PARAMS': {
45
+ const index = action.source
46
+ ? state.routes.findIndex((r) => r.key === action.source)
47
+ : state.index;
53
48
 
54
- return {
55
- ...state,
56
- routes: state.routes.map((r, i) =>
57
- i === index
58
- ? {
59
- ...r,
60
- params: action.payload.params,
61
- history: [
62
- ...(r.history ?? []),
63
- { type: 'params', params: r.params },
64
- ],
65
- }
66
- : r
67
- ),
68
- };
49
+ if (index === -1) {
50
+ return null;
69
51
  }
70
52
 
71
- case 'RESET': {
72
- const nextState = action.payload as State | PartialState<State>;
53
+ return {
54
+ ...state,
55
+ routes: state.routes.map((r, i) =>
56
+ i === index
57
+ ? {
58
+ ...r,
59
+ params: action.payload.params,
60
+ history: [
61
+ ...(r.history ?? []),
62
+ { type: 'params', params: r.params },
63
+ ],
64
+ }
65
+ : r
66
+ ),
67
+ };
68
+ }
73
69
 
74
- const routeNamesSet = new Set(state.routeNames);
70
+ case 'RESET': {
71
+ const nextState = action.payload;
75
72
 
73
+ const routeNamesSet = new Set(state.routeNames);
74
+
75
+ if (
76
+ nextState.routes.length === 0 ||
77
+ nextState.routes.some(
78
+ (route: { name: string }) => !routeNamesSet.has(route.name)
79
+ )
80
+ ) {
81
+ return null;
82
+ }
83
+
84
+ if (nextState.stale === false) {
76
85
  if (
77
- nextState.routes.length === 0 ||
78
- nextState.routes.some(
79
- (route: { name: string }) => !routeNamesSet.has(route.name)
80
- )
86
+ state.routeNames.length !== nextState.routeNames.length ||
87
+ nextState.routeNames.some((name) => !routeNamesSet.has(name)) ||
88
+ !Number.isInteger(nextState.index) ||
89
+ nextState.index < 0 ||
90
+ nextState.index >= nextState.routes.length
81
91
  ) {
82
92
  return null;
83
93
  }
84
94
 
85
- if (nextState.stale === false) {
86
- if (
87
- state.routeNames.length !== nextState.routeNames.length ||
88
- nextState.routeNames.some((name) => !routeNamesSet.has(name))
89
- ) {
90
- return null;
91
- }
92
-
93
- return {
94
- ...nextState,
95
- routes: nextState.routes.map((route) =>
96
- route.key ? route : { ...route, key: `${route.name}-${nanoid()}` }
97
- ),
98
- };
99
- }
100
-
101
- return nextState;
95
+ return {
96
+ ...nextState,
97
+ routes: nextState.routes.map((route) =>
98
+ 'key' in route && route.key
99
+ ? route
100
+ : { ...route, key: `${route.name}-${nanoid()}` }
101
+ ),
102
+ };
102
103
  }
103
104
 
104
- default:
105
- return null;
105
+ return nextState;
106
106
  }
107
- },
107
+
108
+ default:
109
+ return null;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Base router object that can be used when writing custom routers.
115
+ * This provides few helper methods to handle common actions such as `RESET`.
116
+ */
117
+ export const BaseRouter = {
118
+ getStateForAction,
108
119
 
109
120
  shouldActionChangeFocus(action: CommonNavigationAction) {
110
121
  return action.type === 'NAVIGATE';
@@ -1,11 +1,11 @@
1
1
  import { nanoid } from 'nanoid/non-secure';
2
2
 
3
+ import { SwitchRouter } from './SwitchRouter';
3
4
  import {
4
5
  type TabActionHelpers,
5
6
  TabActions,
6
7
  type TabActionType,
7
8
  type TabNavigationState,
8
- TabRouter,
9
9
  type TabRouterOptions,
10
10
  } from './TabRouter';
11
11
  import type {
@@ -14,6 +14,7 @@ import type {
14
14
  PartialState,
15
15
  Router,
16
16
  } from './types';
17
+
17
18
  export type DrawerStatus = 'open' | 'closed';
18
19
 
19
20
  export type DrawerActionType =
@@ -44,7 +45,11 @@ export type DrawerNavigationState<ParamList extends ParamListBase> = Omit<
44
45
  * List of previously visited route keys and drawer open status.
45
46
  */
46
47
  history: (
47
- | { type: 'route'; key: string }
48
+ | {
49
+ type: 'route';
50
+ key: string;
51
+ params?: object | undefined;
52
+ }
48
53
  | { type: 'drawer'; status: DrawerStatus }
49
54
  )[];
50
55
  };
@@ -87,10 +92,7 @@ export function DrawerRouter({
87
92
  DrawerNavigationState<ParamListBase>,
88
93
  DrawerActionType | CommonNavigationAction
89
94
  > {
90
- const router = TabRouter(rest) as unknown as Router<
91
- DrawerNavigationState<ParamListBase>,
92
- TabActionType | CommonNavigationAction
93
- >;
95
+ const router = SwitchRouter<'drawer'>(rest);
94
96
 
95
97
  const isDrawerInHistory = (
96
98
  state:
@@ -179,11 +181,16 @@ export function DrawerRouter({
179
181
  return partialState;
180
182
  }
181
183
 
182
- let state = router.getRehydratedState(partialState, {
183
- routeNames,
184
- routeParamList,
185
- routeGetIdList,
186
- });
184
+ let state: DrawerNavigationState<ParamListBase> = {
185
+ ...router.getRehydratedState(partialState, {
186
+ routeNames,
187
+ routeParamList,
188
+ routeGetIdList,
189
+ }),
190
+ default: defaultStatus,
191
+ type: 'drawer',
192
+ key: `drawer-${nanoid()}`,
193
+ };
187
194
 
188
195
  if (isDrawerInHistory(partialState)) {
189
196
  // Re-sync the drawer entry in history to correct it if it was wrong
@@ -191,12 +198,7 @@ export function DrawerRouter({
191
198
  state = addDrawerToHistory(state);
192
199
  }
193
200
 
194
- return {
195
- ...state,
196
- default: defaultStatus,
197
- type: 'drawer',
198
- key: `drawer-${nanoid()}`,
199
- };
201
+ return state;
200
202
  },
201
203
 
202
204
  getStateForRouteFocus(state, key) {
@@ -224,8 +226,12 @@ export function DrawerRouter({
224
226
  case 'NAVIGATE': {
225
227
  const result = router.getStateForAction(state, action, options);
226
228
 
227
- if (result != null && result.index !== state.index) {
228
- return closeDrawer(result as DrawerNavigationState<ParamListBase>);
229
+ if (
230
+ result != null &&
231
+ result.stale === false &&
232
+ result.index !== state.index
233
+ ) {
234
+ return closeDrawer(result);
229
235
  }
230
236
 
231
237
  return result;
@@ -335,7 +335,8 @@ export function StackRouter(options: StackRouterOptions) {
335
335
 
336
336
  if (routes.length === 0) {
337
337
  const initialRouteName =
338
- options.initialRouteName !== undefined
338
+ options.initialRouteName !== undefined &&
339
+ routeNames.includes(options.initialRouteName)
339
340
  ? options.initialRouteName
340
341
  : routeNames[0];
341
342
 
@@ -703,20 +704,23 @@ export function StackRouter(options: StackRouterOptions) {
703
704
  /**
704
705
  * When popping entries,
705
706
  * - Pop entries from route.history
706
- * - Pop routes from state.routes
707
+ * - Pop the route itself and continue from the route below it
707
708
  *
708
709
  * Repeat until:
709
710
  * - We have popped the amount of items in count
710
711
  * - There are no more items to pop
712
+ *
713
+ * Routes above the current route (e.g. above the source) are kept intact
711
714
  */
712
715
  if (route.history?.length || currentIndex > 0) {
713
716
  let count = action.payload.count;
714
- let nextRoutes = routes;
717
+
718
+ let removedFromIndex: number | undefined;
719
+
720
+ const sourceIndex = currentIndex;
715
721
 
716
722
  while (count > 0 && (route.history?.length || currentIndex > 0)) {
717
723
  if (route.history?.length) {
718
- nextRoutes = [...nextRoutes];
719
-
720
724
  const end = Math.min(count, route.history.length);
721
725
  const last = route.history.at(-end);
722
726
  const history = route.history.slice(0, -end);
@@ -724,7 +728,7 @@ export function StackRouter(options: StackRouterOptions) {
724
728
 
725
729
  count = count - removed;
726
730
 
727
- nextRoutes[currentIndex] = {
731
+ route = {
728
732
  ...route,
729
733
  params: last?.type === 'params' ? last.params : route.params,
730
734
  history,
@@ -732,19 +736,11 @@ export function StackRouter(options: StackRouterOptions) {
732
736
  }
733
737
 
734
738
  if (currentIndex > 0 && count > 0) {
735
- const length = nextRoutes.length;
736
- const end = Math.max(currentIndex - count + 1, 1);
739
+ count = count - 1;
740
+ removedFromIndex = currentIndex;
741
+ currentIndex = currentIndex - 1;
737
742
 
738
- nextRoutes = nextRoutes
739
- .slice(0, end)
740
- .concat(nextRoutes.slice(currentIndex + 1));
741
-
742
- const removed = length - nextRoutes.length;
743
-
744
- count = count - removed;
745
- currentIndex = nextRoutes.length - 1;
746
-
747
- const currentRoute = nextRoutes[currentIndex];
743
+ const currentRoute = routes[currentIndex];
748
744
 
749
745
  if (currentRoute == null) {
750
746
  throw new Error(
@@ -756,6 +752,21 @@ export function StackRouter(options: StackRouterOptions) {
756
752
  }
757
753
  }
758
754
 
755
+ let nextRoutes =
756
+ removedFromIndex === undefined
757
+ ? routes
758
+ : routes
759
+ .slice(0, removedFromIndex)
760
+ .concat(routes.slice(sourceIndex + 1));
761
+
762
+ if (route !== routes[currentIndex]) {
763
+ if (nextRoutes === routes) {
764
+ nextRoutes = [...routes];
765
+ }
766
+
767
+ nextRoutes[currentIndex] = route;
768
+ }
769
+
759
770
  return retainRoutes(state, getStateWithRoutes(state, nextRoutes));
760
771
  }
761
772