@react-navigation/routers 8.0.0-alpha.2 → 8.0.0-alpha.20
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 -61
- package/lib/module/BaseRouter.js.map +1 -1
- package/lib/module/CommonActions.js +9 -21
- package/lib/module/CommonActions.js.map +1 -1
- package/lib/module/DrawerRouter.js +15 -14
- package/lib/module/DrawerRouter.js.map +1 -1
- package/lib/module/StackRouter.js +287 -166
- package/lib/module/StackRouter.js.map +1 -1
- package/lib/module/SwitchRouter.js +426 -0
- package/lib/module/SwitchRouter.js.map +1 -0
- package/lib/module/TabRouter.js +14 -290
- package/lib/module/TabRouter.js.map +1 -1
- package/lib/typescript/src/BaseRouter.d.ts +10 -8
- package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
- package/lib/typescript/src/CommonActions.d.ts +42 -39
- package/lib/typescript/src/CommonActions.d.ts.map +1 -1
- package/lib/typescript/src/DrawerRouter.d.ts +6 -5
- package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
- package/lib/typescript/src/StackRouter.d.ts +70 -24
- 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/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 +20 -30
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/BaseRouter.tsx +93 -80
- package/src/CommonActions.tsx +44 -66
- package/src/DrawerRouter.tsx +28 -22
- package/src/StackRouter.tsx +551 -238
- package/src/SwitchRouter.tsx +718 -0
- package/src/TabRouter.tsx +24 -463
- package/src/createParamsFromAction.tsx +1 -1
- package/src/createRouteFromAction.tsx +1 -1
- package/src/types.tsx +39 -41
|
@@ -45,7 +45,57 @@ export const StackActions = {
|
|
|
45
45
|
merge: options?.merge
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
+
},
|
|
49
|
+
retain(enable) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'RETAIN',
|
|
52
|
+
payload: {
|
|
53
|
+
enable
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
dismiss(count = 1) {
|
|
58
|
+
return {
|
|
59
|
+
type: 'DISMISS',
|
|
60
|
+
payload: {
|
|
61
|
+
count
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function retainRoutes(previousState, nextState) {
|
|
67
|
+
if (!nextState.retainedRouteKeys.length) {
|
|
68
|
+
return nextState;
|
|
48
69
|
}
|
|
70
|
+
const retainedRouteKeySet = new Set(nextState.retainedRouteKeys);
|
|
71
|
+
const nextRouteKeySet = new Set(nextState.routes.map(route => route.key));
|
|
72
|
+
const retainedRoutes = getActiveRoutes(previousState).filter(route => retainedRouteKeySet.has(route.key) && !nextRouteKeySet.has(route.key));
|
|
73
|
+
if (!retainedRoutes.length) {
|
|
74
|
+
return nextState;
|
|
75
|
+
}
|
|
76
|
+
const removedRetainedRouteKeys = new Set(retainedRoutes.map(route => route.key));
|
|
77
|
+
return getStateWithRoutes(nextState, getActiveRoutes(nextState), getPreloadedRoutes(nextState), retainedRoutes.concat(getRetainedRoutes(nextState).filter(route => !removedRetainedRouteKeys.has(route.key))));
|
|
78
|
+
}
|
|
79
|
+
const getActiveRoutes = state => state.routes.slice(0, state.index + 1);
|
|
80
|
+
const getInactiveRoutes = state => state.routes.slice(state.index + 1);
|
|
81
|
+
const getPreloadedRoutes = state => {
|
|
82
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
83
|
+
return getInactiveRoutes(state).filter(route => !retainedRouteKeySet.has(route.key));
|
|
84
|
+
};
|
|
85
|
+
const getRetainedRoutes = state => {
|
|
86
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
87
|
+
return getInactiveRoutes(state).filter(route => retainedRouteKeySet.has(route.key));
|
|
88
|
+
};
|
|
89
|
+
const getStateWithRoutes = (state, activeRoutes, preloadedRoutes = getPreloadedRoutes(state), retainedRoutes = getRetainedRoutes(state)) => {
|
|
90
|
+
const routeKeySet = new Set(activeRoutes.map(route => route.key));
|
|
91
|
+
const retainedRouteKeySet = new Set(state.retainedRouteKeys);
|
|
92
|
+
const filteredRetainedRoutes = retainedRoutes.filter(route => !routeKeySet.has(route.key));
|
|
93
|
+
const filteredPreloadedRoutes = preloadedRoutes.filter(route => !retainedRouteKeySet.has(route.key) && !routeKeySet.has(route.key));
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
index: activeRoutes.length - 1,
|
|
97
|
+
routes: activeRoutes.concat(filteredRetainedRoutes, filteredPreloadedRoutes)
|
|
98
|
+
};
|
|
49
99
|
};
|
|
50
100
|
export function StackRouter(options) {
|
|
51
101
|
const router = {
|
|
@@ -56,13 +106,16 @@ export function StackRouter(options) {
|
|
|
56
106
|
routeParamList
|
|
57
107
|
}) {
|
|
58
108
|
const initialRouteName = options.initialRouteName !== undefined && routeNames.includes(options.initialRouteName) ? options.initialRouteName : routeNames[0];
|
|
109
|
+
if (initialRouteName == null) {
|
|
110
|
+
throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
|
|
111
|
+
}
|
|
59
112
|
return {
|
|
60
113
|
stale: false,
|
|
61
114
|
type: 'stack',
|
|
62
115
|
key: `stack-${nanoid()}`,
|
|
63
116
|
index: 0,
|
|
64
117
|
routeNames,
|
|
65
|
-
|
|
118
|
+
retainedRouteKeys: [],
|
|
66
119
|
routes: [{
|
|
67
120
|
key: `${initialRouteName}-${nanoid()}`,
|
|
68
121
|
name: initialRouteName,
|
|
@@ -86,30 +139,28 @@ export function StackRouter(options) {
|
|
|
86
139
|
...route.params
|
|
87
140
|
} : route.params
|
|
88
141
|
}));
|
|
89
|
-
const preloadedRoutes = state.preloadedRoutes?.filter(route => routeNames.includes(route.name)).map(route => ({
|
|
90
|
-
...route,
|
|
91
|
-
key: route.key || `${route.name}-${nanoid()}`,
|
|
92
|
-
params: routeParamList[route.name] !== undefined ? {
|
|
93
|
-
...routeParamList[route.name],
|
|
94
|
-
...route.params
|
|
95
|
-
} : route.params
|
|
96
|
-
})) ?? [];
|
|
97
142
|
if (routes.length === 0) {
|
|
98
|
-
const initialRouteName = options.initialRouteName !== undefined ? options.initialRouteName : routeNames[0];
|
|
143
|
+
const initialRouteName = options.initialRouteName !== undefined && routeNames.includes(options.initialRouteName) ? options.initialRouteName : routeNames[0];
|
|
144
|
+
if (initialRouteName == null) {
|
|
145
|
+
throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
|
|
146
|
+
}
|
|
99
147
|
routes.push({
|
|
100
148
|
key: `${initialRouteName}-${nanoid()}`,
|
|
101
149
|
name: initialRouteName,
|
|
102
150
|
params: routeParamList[initialRouteName]
|
|
103
151
|
});
|
|
104
152
|
}
|
|
153
|
+
const index = Math.min(Math.max(state.index ?? routes.length - 1, 0), routes.length - 1);
|
|
154
|
+
const routeKeys = new Set(routes.map(route => route.key));
|
|
155
|
+
const retainedRouteKeys = state.retainedRouteKeys?.filter(key => routeKeys.has(key)) ?? [];
|
|
105
156
|
return {
|
|
106
157
|
stale: false,
|
|
107
158
|
type: 'stack',
|
|
108
159
|
key: `stack-${nanoid()}`,
|
|
109
|
-
index
|
|
160
|
+
index,
|
|
110
161
|
routeNames,
|
|
111
162
|
routes,
|
|
112
|
-
|
|
163
|
+
retainedRouteKeys
|
|
113
164
|
};
|
|
114
165
|
},
|
|
115
166
|
getStateForRouteNamesChange(state, {
|
|
@@ -117,45 +168,53 @@ export function StackRouter(options) {
|
|
|
117
168
|
routeParamList,
|
|
118
169
|
routeKeyChanges
|
|
119
170
|
}) {
|
|
120
|
-
const routes = state.
|
|
171
|
+
const routes = getActiveRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
|
|
172
|
+
const preloadedRoutes = getPreloadedRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
|
|
173
|
+
const retainedRoutes = getRetainedRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
|
|
121
174
|
if (routes.length === 0) {
|
|
122
175
|
const initialRouteName = options.initialRouteName !== undefined && routeNames.includes(options.initialRouteName) ? options.initialRouteName : routeNames[0];
|
|
176
|
+
if (initialRouteName == null) {
|
|
177
|
+
throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
|
|
178
|
+
}
|
|
123
179
|
routes.push({
|
|
124
180
|
key: `${initialRouteName}-${nanoid()}`,
|
|
125
181
|
name: initialRouteName,
|
|
126
182
|
params: routeParamList[initialRouteName]
|
|
127
183
|
});
|
|
128
184
|
}
|
|
129
|
-
|
|
185
|
+
const routeKeys = new Set([...routes, ...preloadedRoutes, ...retainedRoutes].map(route => route.key));
|
|
186
|
+
const retainedRouteKeys = state.retainedRouteKeys.filter(key => routeKeys.has(key));
|
|
187
|
+
return getStateWithRoutes({
|
|
130
188
|
...state,
|
|
131
189
|
routeNames,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
};
|
|
190
|
+
retainedRouteKeys
|
|
191
|
+
}, routes, preloadedRoutes, retainedRoutes);
|
|
135
192
|
},
|
|
136
193
|
getStateForRouteFocus(state, key) {
|
|
137
|
-
const
|
|
138
|
-
|
|
194
|
+
const activeRoutes = getActiveRoutes(state);
|
|
195
|
+
const route = state.routes.find(route => route.key === key);
|
|
196
|
+
if (route == null || route === activeRoutes.at(-1)) {
|
|
139
197
|
return state;
|
|
140
198
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
routes: state.routes.slice(0, index + 1)
|
|
145
|
-
};
|
|
199
|
+
const index = activeRoutes.indexOf(route);
|
|
200
|
+
const routes = index === -1 ? activeRoutes.concat(route) : activeRoutes.slice(0, index + 1);
|
|
201
|
+
return retainRoutes(state, getStateWithRoutes(state, routes));
|
|
146
202
|
},
|
|
147
203
|
getStateForAction(state, action, options) {
|
|
148
204
|
const {
|
|
149
205
|
routeParamList
|
|
150
206
|
} = options;
|
|
207
|
+
const routes = getActiveRoutes(state);
|
|
208
|
+
const preloadedRoutes = getPreloadedRoutes(state);
|
|
209
|
+
const inactiveRoutes = getInactiveRoutes(state);
|
|
151
210
|
switch (action.type) {
|
|
152
211
|
case 'REPLACE':
|
|
153
212
|
{
|
|
154
|
-
|
|
155
|
-
if (currentIndex === -1) {
|
|
213
|
+
if (!state.routeNames.includes(action.payload.name)) {
|
|
156
214
|
return null;
|
|
157
215
|
}
|
|
158
|
-
|
|
216
|
+
const currentIndex = action.source !== undefined ? routes.findIndex(r => r.key === action.source) : state.index;
|
|
217
|
+
if (currentIndex === -1) {
|
|
159
218
|
return null;
|
|
160
219
|
}
|
|
161
220
|
const getId = options.routeGetIdList[action.payload.name];
|
|
@@ -163,8 +222,8 @@ export function StackRouter(options) {
|
|
|
163
222
|
params: action.payload.params
|
|
164
223
|
});
|
|
165
224
|
|
|
166
|
-
// Re-use
|
|
167
|
-
let route =
|
|
225
|
+
// Re-use inactive route if available
|
|
226
|
+
let route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
|
|
168
227
|
params: route.params
|
|
169
228
|
}));
|
|
170
229
|
if (!route) {
|
|
@@ -172,12 +231,19 @@ export function StackRouter(options) {
|
|
|
172
231
|
action,
|
|
173
232
|
routeParamList
|
|
174
233
|
});
|
|
234
|
+
} else {
|
|
235
|
+
const params = createParamsFromAction({
|
|
236
|
+
action,
|
|
237
|
+
routeParamList
|
|
238
|
+
});
|
|
239
|
+
if (route.params !== params) {
|
|
240
|
+
route = {
|
|
241
|
+
...route,
|
|
242
|
+
params
|
|
243
|
+
};
|
|
244
|
+
}
|
|
175
245
|
}
|
|
176
|
-
return
|
|
177
|
-
...state,
|
|
178
|
-
routes: state.routes.map((r, i) => i === currentIndex ? route : r),
|
|
179
|
-
preloadedRoutes: state.preloadedRoutes.filter(r => r.key !== route.key)
|
|
180
|
-
};
|
|
246
|
+
return retainRoutes(state, getStateWithRoutes(state, routes.map((r, i) => i === currentIndex ? route : r), preloadedRoutes.filter(r => r.key !== route.key)));
|
|
181
247
|
}
|
|
182
248
|
case 'PUSH':
|
|
183
249
|
case 'NAVIGATE':
|
|
@@ -189,17 +255,31 @@ export function StackRouter(options) {
|
|
|
189
255
|
const id = getId?.({
|
|
190
256
|
params: action.payload.params
|
|
191
257
|
});
|
|
258
|
+
let remainingRoutes = routes;
|
|
192
259
|
let route;
|
|
193
260
|
if (action.type === 'NAVIGATE') {
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
261
|
+
const currentIndex = action.source !== undefined ? routes.findIndex(r => r.key === action.source) : state.index;
|
|
262
|
+
if (currentIndex === -1) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* When navigating,
|
|
268
|
+
* - If the current route matches the destination, reuse it.
|
|
269
|
+
* - Or, if a route matching the destination exists after the current, reuse it.
|
|
270
|
+
* - Remove all non-matching routes after the current.
|
|
271
|
+
*/
|
|
272
|
+
remainingRoutes = routes.slice(0, currentIndex + 1);
|
|
273
|
+
route = routes.find((route, i) => i >= currentIndex && route.name === action.payload.name && (id === undefined || id === getId?.({
|
|
274
|
+
params: route.params
|
|
275
|
+
})));
|
|
276
|
+
if (!route && action.payload.pop) {
|
|
277
|
+
if (id !== undefined) {
|
|
278
|
+
for (let i = remainingRoutes.length - 1; i >= 0; i--) {
|
|
279
|
+
const r = remainingRoutes[i];
|
|
280
|
+
if (r == null) {
|
|
281
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
282
|
+
}
|
|
203
283
|
if (r.name !== action.payload.name) {
|
|
204
284
|
continue;
|
|
205
285
|
}
|
|
@@ -211,12 +291,16 @@ export function StackRouter(options) {
|
|
|
211
291
|
}
|
|
212
292
|
if (r.history?.length) {
|
|
213
293
|
for (let j = r.history.length - 1; j >= 0; j--) {
|
|
214
|
-
|
|
215
|
-
|
|
294
|
+
const historyEntry = r.history[j];
|
|
295
|
+
if (historyEntry == null) {
|
|
296
|
+
throw new Error(`Couldn't find a route history entry at index ${j}.`);
|
|
297
|
+
}
|
|
298
|
+
if (historyEntry.type === 'params' && id === getId?.({
|
|
299
|
+
params: historyEntry.params
|
|
216
300
|
})) {
|
|
217
301
|
route = {
|
|
218
302
|
...r,
|
|
219
|
-
params:
|
|
303
|
+
params: historyEntry.params,
|
|
220
304
|
history: r.history.slice(0, j)
|
|
221
305
|
};
|
|
222
306
|
break;
|
|
@@ -227,18 +311,13 @@ export function StackRouter(options) {
|
|
|
227
311
|
}
|
|
228
312
|
}
|
|
229
313
|
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// If the route matches the current one, then navigate to it
|
|
233
|
-
if (action.payload.name === currentRoute.name) {
|
|
234
|
-
route = currentRoute;
|
|
235
|
-
} else if (action.payload.pop) {
|
|
236
|
-
route = state.routes.findLast(route => route.name === action.payload.name);
|
|
314
|
+
} else {
|
|
315
|
+
route = remainingRoutes.findLast(route => route.name === action.payload.name);
|
|
237
316
|
}
|
|
238
317
|
}
|
|
239
318
|
}
|
|
240
319
|
if (!route) {
|
|
241
|
-
route =
|
|
320
|
+
route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
|
|
242
321
|
params: route.params
|
|
243
322
|
}));
|
|
244
323
|
}
|
|
@@ -255,98 +334,107 @@ export function StackRouter(options) {
|
|
|
255
334
|
routeParamList
|
|
256
335
|
});
|
|
257
336
|
}
|
|
258
|
-
let
|
|
337
|
+
let nextRoutes;
|
|
259
338
|
if (route) {
|
|
260
339
|
if (action.type === 'NAVIGATE' && action.payload.pop) {
|
|
261
|
-
|
|
340
|
+
nextRoutes = [];
|
|
262
341
|
|
|
263
|
-
// Get all routes
|
|
264
|
-
for (const r of
|
|
342
|
+
// Get all routes before the matching one
|
|
343
|
+
for (const r of remainingRoutes) {
|
|
265
344
|
if (r.key === route.key) {
|
|
266
|
-
routes.push({
|
|
267
|
-
...route,
|
|
268
|
-
path: action.payload.path !== undefined ? action.payload.path : route.path,
|
|
269
|
-
params
|
|
270
|
-
});
|
|
271
345
|
break;
|
|
272
346
|
}
|
|
273
|
-
|
|
347
|
+
nextRoutes.push(r);
|
|
274
348
|
}
|
|
275
349
|
} else {
|
|
276
|
-
|
|
277
|
-
routes.push({
|
|
278
|
-
...route,
|
|
279
|
-
path: action.type === 'NAVIGATE' && action.payload.path !== undefined ? action.payload.path : route.path,
|
|
280
|
-
params
|
|
281
|
-
});
|
|
350
|
+
nextRoutes = remainingRoutes.filter(r => r.key !== route.key);
|
|
282
351
|
}
|
|
352
|
+
nextRoutes.push({
|
|
353
|
+
...route,
|
|
354
|
+
path: action.type === 'NAVIGATE' && action.payload.path !== undefined ? action.payload.path : route.path,
|
|
355
|
+
params
|
|
356
|
+
});
|
|
283
357
|
} else {
|
|
284
|
-
|
|
358
|
+
nextRoutes = [...remainingRoutes, {
|
|
285
359
|
key: `${action.payload.name}-${nanoid()}`,
|
|
286
360
|
name: action.payload.name,
|
|
287
361
|
path: action.type === 'NAVIGATE' ? action.payload.path : undefined,
|
|
288
362
|
params
|
|
289
363
|
}];
|
|
290
364
|
}
|
|
291
|
-
return
|
|
292
|
-
...state,
|
|
293
|
-
index: routes.length - 1,
|
|
294
|
-
preloadedRoutes: state.preloadedRoutes.filter(route => routes[routes.length - 1].key !== route.key),
|
|
295
|
-
routes
|
|
296
|
-
};
|
|
365
|
+
return retainRoutes(state, getStateWithRoutes(state, nextRoutes));
|
|
297
366
|
}
|
|
298
367
|
case 'POP':
|
|
299
368
|
{
|
|
300
|
-
let currentIndex = action.
|
|
301
|
-
|
|
369
|
+
let currentIndex = action.source !== undefined ? routes.findIndex(r => r.key === action.source) : state.index;
|
|
370
|
+
if (currentIndex === -1) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
let route = routes[currentIndex];
|
|
374
|
+
if (route == null) {
|
|
375
|
+
throw new Error(`Couldn't find a route at index ${currentIndex}.`);
|
|
376
|
+
}
|
|
302
377
|
|
|
303
378
|
/**
|
|
304
379
|
* When popping entries,
|
|
305
380
|
* - Pop entries from route.history
|
|
306
|
-
* - Pop
|
|
381
|
+
* - Pop the route itself and continue from the route below it
|
|
307
382
|
*
|
|
308
383
|
* Repeat until:
|
|
309
384
|
* - We have popped the amount of items in count
|
|
310
385
|
* - There are no more items to pop
|
|
386
|
+
*
|
|
387
|
+
* Routes above the current route are removed.
|
|
311
388
|
*/
|
|
312
389
|
if (route.history?.length || currentIndex > 0) {
|
|
313
390
|
let count = action.payload.count;
|
|
314
|
-
let routes = state.routes;
|
|
315
391
|
while (count > 0 && (route.history?.length || currentIndex > 0)) {
|
|
316
392
|
if (route.history?.length) {
|
|
317
|
-
routes = [...routes];
|
|
318
393
|
const end = Math.min(count, route.history.length);
|
|
319
394
|
const last = route.history.at(-end);
|
|
320
395
|
const history = route.history.slice(0, -end);
|
|
321
396
|
const removed = route.history.length - history.length;
|
|
322
397
|
count = count - removed;
|
|
323
|
-
|
|
398
|
+
route = {
|
|
324
399
|
...route,
|
|
325
400
|
params: last?.type === 'params' ? last.params : route.params,
|
|
326
401
|
history
|
|
327
402
|
};
|
|
328
403
|
}
|
|
329
404
|
if (currentIndex > 0 && count > 0) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
route =
|
|
405
|
+
count = count - 1;
|
|
406
|
+
currentIndex = currentIndex - 1;
|
|
407
|
+
const currentRoute = routes[currentIndex];
|
|
408
|
+
if (currentRoute == null) {
|
|
409
|
+
throw new Error(`Couldn't find a route at index ${currentIndex}.`);
|
|
410
|
+
}
|
|
411
|
+
route = currentRoute;
|
|
337
412
|
}
|
|
338
413
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
414
|
+
const nextRoutes = routes.slice(0, currentIndex + 1);
|
|
415
|
+
if (route !== routes[currentIndex]) {
|
|
416
|
+
nextRoutes[currentIndex] = route;
|
|
417
|
+
}
|
|
418
|
+
return retainRoutes(state, getStateWithRoutes(state, nextRoutes));
|
|
344
419
|
}
|
|
345
420
|
return null;
|
|
346
421
|
}
|
|
422
|
+
case 'DISMISS':
|
|
423
|
+
{
|
|
424
|
+
const currentIndex = action.source !== undefined ? routes.findIndex(route => route.key === action.source) : state.index;
|
|
425
|
+
if (currentIndex === -1 || routes.length === 1 || action.payload.count < 1) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
const count = Math.min(action.payload.count, currentIndex + 1, routes.length - 1);
|
|
429
|
+
const firstIndex = currentIndex - count + 1;
|
|
430
|
+
return retainRoutes(state, getStateWithRoutes(state, routes.slice(0, firstIndex).concat(routes.slice(currentIndex + 1))));
|
|
431
|
+
}
|
|
347
432
|
case 'POP_TO_TOP':
|
|
348
433
|
{
|
|
349
|
-
let route =
|
|
434
|
+
let route = routes[0];
|
|
435
|
+
if (route == null) {
|
|
436
|
+
throw new Error("Couldn't find a route at index 0.");
|
|
437
|
+
}
|
|
350
438
|
if (state.index > 0 || route.history?.length) {
|
|
351
439
|
if (route.history?.length) {
|
|
352
440
|
route = {
|
|
@@ -354,21 +442,17 @@ export function StackRouter(options) {
|
|
|
354
442
|
history: undefined
|
|
355
443
|
};
|
|
356
444
|
}
|
|
357
|
-
return
|
|
358
|
-
...state,
|
|
359
|
-
index: 0,
|
|
360
|
-
routes: [route]
|
|
361
|
-
};
|
|
445
|
+
return retainRoutes(state, getStateWithRoutes(state, [route]));
|
|
362
446
|
}
|
|
363
447
|
return null;
|
|
364
448
|
}
|
|
365
449
|
case 'POP_TO':
|
|
366
450
|
{
|
|
367
|
-
|
|
368
|
-
if (currentIndex === -1) {
|
|
451
|
+
if (!state.routeNames.includes(action.payload.name)) {
|
|
369
452
|
return null;
|
|
370
453
|
}
|
|
371
|
-
|
|
454
|
+
const currentIndex = action.source !== undefined ? routes.findIndex(r => r.key === action.source) : state.index;
|
|
455
|
+
if (currentIndex === -1) {
|
|
372
456
|
return null;
|
|
373
457
|
}
|
|
374
458
|
|
|
@@ -381,7 +465,10 @@ export function StackRouter(options) {
|
|
|
381
465
|
});
|
|
382
466
|
if (id !== undefined) {
|
|
383
467
|
for (let i = currentIndex; i >= 0; i--) {
|
|
384
|
-
const r =
|
|
468
|
+
const r = routes[i];
|
|
469
|
+
if (r == null) {
|
|
470
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
471
|
+
}
|
|
385
472
|
if (r.name !== action.payload.name) {
|
|
386
473
|
continue;
|
|
387
474
|
}
|
|
@@ -393,8 +480,12 @@ export function StackRouter(options) {
|
|
|
393
480
|
}
|
|
394
481
|
if (r.history?.length) {
|
|
395
482
|
for (let j = r.history.length - 1; j >= 0; j--) {
|
|
396
|
-
|
|
397
|
-
|
|
483
|
+
const historyEntry = r.history[j];
|
|
484
|
+
if (historyEntry == null) {
|
|
485
|
+
throw new Error(`Couldn't find a route history entry at index ${j}.`);
|
|
486
|
+
}
|
|
487
|
+
if (historyEntry.type === 'params' && id === getId?.({
|
|
488
|
+
params: historyEntry.params
|
|
398
489
|
})) {
|
|
399
490
|
index = i;
|
|
400
491
|
historyIndex = j;
|
|
@@ -406,11 +497,22 @@ export function StackRouter(options) {
|
|
|
406
497
|
}
|
|
407
498
|
}
|
|
408
499
|
}
|
|
409
|
-
} else if (state.routes[currentIndex].name === action.payload.name) {
|
|
410
|
-
index = currentIndex;
|
|
411
500
|
} else {
|
|
501
|
+
const currentRoute = routes[currentIndex];
|
|
502
|
+
if (currentRoute == null) {
|
|
503
|
+
throw new Error(`Couldn't find a route at index ${currentIndex}.`);
|
|
504
|
+
}
|
|
505
|
+
if (currentRoute.name === action.payload.name) {
|
|
506
|
+
index = currentIndex;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (id == null && index === -1) {
|
|
412
510
|
for (let i = currentIndex; i >= 0; i--) {
|
|
413
|
-
|
|
511
|
+
const route = routes[i];
|
|
512
|
+
if (route == null) {
|
|
513
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
514
|
+
}
|
|
515
|
+
if (route.name === action.payload.name) {
|
|
414
516
|
index = i;
|
|
415
517
|
break;
|
|
416
518
|
}
|
|
@@ -419,8 +521,8 @@ export function StackRouter(options) {
|
|
|
419
521
|
|
|
420
522
|
// If the route doesn't exist, remove the current route and add the new one
|
|
421
523
|
if (index === -1) {
|
|
422
|
-
// Re-use
|
|
423
|
-
let route =
|
|
524
|
+
// Re-use inactive route if available
|
|
525
|
+
let route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
|
|
424
526
|
params: route.params
|
|
425
527
|
}));
|
|
426
528
|
if (!route) {
|
|
@@ -428,20 +530,33 @@ export function StackRouter(options) {
|
|
|
428
530
|
action,
|
|
429
531
|
routeParamList
|
|
430
532
|
});
|
|
533
|
+
} else {
|
|
534
|
+
const params = createParamsFromAction({
|
|
535
|
+
action,
|
|
536
|
+
routeParamList
|
|
537
|
+
});
|
|
538
|
+
if (route.params !== params) {
|
|
539
|
+
route = {
|
|
540
|
+
...route,
|
|
541
|
+
params
|
|
542
|
+
};
|
|
543
|
+
}
|
|
431
544
|
}
|
|
432
|
-
const
|
|
433
|
-
return
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
};
|
|
545
|
+
const nextRoutes = routes.slice(0, currentIndex).concat(route);
|
|
546
|
+
return retainRoutes(state, getStateWithRoutes(state, nextRoutes, preloadedRoutes.filter(r => r.key !== route.key)));
|
|
547
|
+
}
|
|
548
|
+
const route = routes[index];
|
|
549
|
+
if (route == null) {
|
|
550
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
439
551
|
}
|
|
440
|
-
const route = state.routes[index];
|
|
441
552
|
let baseParams = route.params;
|
|
442
553
|
let history = route.history;
|
|
443
554
|
if (historyIndex !== undefined && route.history) {
|
|
444
|
-
|
|
555
|
+
const historyEntry = route.history[historyIndex];
|
|
556
|
+
if (historyEntry == null) {
|
|
557
|
+
throw new Error(`Couldn't find a route history entry at index ${historyIndex}.`);
|
|
558
|
+
}
|
|
559
|
+
baseParams = historyEntry.params;
|
|
445
560
|
history = route.history.slice(0, historyIndex);
|
|
446
561
|
}
|
|
447
562
|
let params;
|
|
@@ -457,18 +572,14 @@ export function StackRouter(options) {
|
|
|
457
572
|
routeParamList
|
|
458
573
|
});
|
|
459
574
|
}
|
|
460
|
-
return {
|
|
461
|
-
...
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
params,
|
|
466
|
-
history
|
|
467
|
-
} : state.routes[index]]
|
|
468
|
-
};
|
|
575
|
+
return retainRoutes(state, getStateWithRoutes(state, [...routes.slice(0, index), params !== baseParams || historyIndex !== undefined ? {
|
|
576
|
+
...route,
|
|
577
|
+
params,
|
|
578
|
+
history
|
|
579
|
+
} : route]));
|
|
469
580
|
}
|
|
470
581
|
case 'GO_BACK':
|
|
471
|
-
|
|
582
|
+
{
|
|
472
583
|
return router.getStateForAction(state, {
|
|
473
584
|
type: 'POP',
|
|
474
585
|
payload: {
|
|
@@ -478,54 +589,64 @@ export function StackRouter(options) {
|
|
|
478
589
|
source: action.source
|
|
479
590
|
}, options);
|
|
480
591
|
}
|
|
481
|
-
|
|
592
|
+
case 'RETAIN':
|
|
593
|
+
{
|
|
594
|
+
const route = routes[state.index];
|
|
595
|
+
if (route == null) {
|
|
596
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
597
|
+
}
|
|
598
|
+
const routeKey = action.source ?? route.key;
|
|
599
|
+
if (!state.routes.some(route => route.key === routeKey)) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
if (action.payload.enable) {
|
|
603
|
+
if (state.retainedRouteKeys.includes(routeKey)) {
|
|
604
|
+
return state;
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
...state,
|
|
608
|
+
retainedRouteKeys: [...state.retainedRouteKeys, routeKey]
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
if (!state.retainedRouteKeys.includes(routeKey)) {
|
|
612
|
+
return state;
|
|
613
|
+
}
|
|
614
|
+
return getStateWithRoutes({
|
|
615
|
+
...state,
|
|
616
|
+
retainedRouteKeys: state.retainedRouteKeys.filter(key => key !== routeKey)
|
|
617
|
+
}, routes, preloadedRoutes.filter(route => route.key !== routeKey));
|
|
618
|
+
}
|
|
482
619
|
case 'PRELOAD':
|
|
483
620
|
{
|
|
621
|
+
if (!state.routeNames.includes(action.payload.name)) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
484
624
|
const getId = options.routeGetIdList[action.payload.name];
|
|
485
625
|
const id = getId?.({
|
|
486
626
|
params: action.payload.params
|
|
487
627
|
});
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
628
|
+
const params = createParamsFromAction({
|
|
629
|
+
action,
|
|
630
|
+
routeParamList
|
|
631
|
+
});
|
|
632
|
+
const route = preloadedRoutes.findLast(route => route.name === action.payload.name && id === getId?.({
|
|
633
|
+
params: route.params
|
|
634
|
+
}));
|
|
494
635
|
if (route) {
|
|
495
|
-
return {
|
|
496
|
-
...
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
return r;
|
|
500
|
-
}
|
|
501
|
-
return {
|
|
502
|
-
...r,
|
|
503
|
-
params: createParamsFromAction({
|
|
504
|
-
action,
|
|
505
|
-
routeParamList
|
|
506
|
-
})
|
|
507
|
-
};
|
|
508
|
-
})
|
|
509
|
-
};
|
|
510
|
-
} else {
|
|
511
|
-
return {
|
|
512
|
-
...state,
|
|
513
|
-
preloadedRoutes: state.preloadedRoutes.filter(r => r.name !== action.payload.name || id !== getId?.({
|
|
514
|
-
params: r.params
|
|
515
|
-
})).concat(createRouteFromAction({
|
|
516
|
-
action,
|
|
517
|
-
routeParamList
|
|
518
|
-
}))
|
|
519
|
-
};
|
|
636
|
+
return getStateWithRoutes(state, routes, preloadedRoutes.map(r => r.key === route.key && r.params !== params ? {
|
|
637
|
+
...r,
|
|
638
|
+
params
|
|
639
|
+
} : r));
|
|
520
640
|
}
|
|
641
|
+
return getStateWithRoutes(state, routes, preloadedRoutes.concat(createRouteFromAction({
|
|
642
|
+
action,
|
|
643
|
+
routeParamList
|
|
644
|
+
})));
|
|
521
645
|
}
|
|
522
646
|
default:
|
|
523
647
|
return BaseRouter.getStateForAction(state, action);
|
|
524
648
|
}
|
|
525
649
|
},
|
|
526
|
-
getRoutesFromState(state) {
|
|
527
|
-
return [...state.routes, ...state.preloadedRoutes];
|
|
528
|
-
},
|
|
529
650
|
actionCreators: StackActions
|
|
530
651
|
};
|
|
531
652
|
return router;
|