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