@react-navigation/core 7.2.2 → 7.3.1
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/commonjs/StaticNavigation.js +2 -2
- package/lib/commonjs/StaticNavigation.js.map +1 -1
- package/lib/commonjs/getPatternParts.js +1 -1
- package/lib/commonjs/getPatternParts.js.map +1 -1
- package/lib/commonjs/getStateFromPath.js +33 -10
- package/lib/commonjs/getStateFromPath.js.map +1 -1
- package/lib/commonjs/useScheduleUpdate.js +2 -1
- package/lib/commonjs/useScheduleUpdate.js.map +1 -1
- package/lib/commonjs/validatePathConfig.js +9 -2
- package/lib/commonjs/validatePathConfig.js.map +1 -1
- package/lib/module/StaticNavigation.js +2 -2
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/getPatternParts.js +1 -1
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +33 -10
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +3 -1
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/validatePathConfig.js +9 -2
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/commonjs/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/types.d.ts +8 -6
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/validatePathConfig.d.ts.map +1 -1
- package/lib/typescript/module/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/module/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/module/src/types.d.ts +8 -6
- package/lib/typescript/module/src/types.d.ts.map +1 -1
- package/lib/typescript/module/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/module/src/validatePathConfig.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/StaticNavigation.tsx +2 -4
- package/src/getPatternParts.tsx +1 -1
- package/src/getStateFromPath.tsx +55 -26
- package/src/types.tsx +9 -8
- package/src/useScheduleUpdate.tsx +3 -1
- package/src/validatePathConfig.tsx +9 -2
package/src/getStateFromPath.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InitialState,
|
|
3
3
|
NavigationState,
|
|
4
|
+
ParamListBase,
|
|
4
5
|
PartialState,
|
|
5
6
|
} from '@react-navigation/routers';
|
|
6
7
|
import escape from 'escape-string-regexp';
|
|
@@ -10,7 +11,7 @@ import { arrayStartsWith } from './arrayStartsWith';
|
|
|
10
11
|
import { findFocusedRoute } from './findFocusedRoute';
|
|
11
12
|
import { getPatternParts, type PatternPart } from './getPatternParts';
|
|
12
13
|
import { isArrayEqual } from './isArrayEqual';
|
|
13
|
-
import type { PathConfigMap } from './types';
|
|
14
|
+
import type { PathConfig, PathConfigMap } from './types';
|
|
14
15
|
import { validatePathConfig } from './validatePathConfig';
|
|
15
16
|
|
|
16
17
|
type Options<ParamList extends {}> = {
|
|
@@ -48,7 +49,6 @@ type ParsedRoute = {
|
|
|
48
49
|
type ConfigResources = {
|
|
49
50
|
initialRoutes: InitialRouteConfig[];
|
|
50
51
|
configs: RouteConfig[];
|
|
51
|
-
configWithRegexes: RouteConfig[];
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -76,8 +76,7 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
76
76
|
path: string,
|
|
77
77
|
options?: Options<ParamList>
|
|
78
78
|
): ResultState | undefined {
|
|
79
|
-
const { initialRoutes, configs
|
|
80
|
-
getConfigResources(options);
|
|
79
|
+
const { initialRoutes, configs } = getConfigResources(options);
|
|
81
80
|
|
|
82
81
|
const screens = options?.screens;
|
|
83
82
|
|
|
@@ -143,10 +142,7 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
143
142
|
|
|
144
143
|
// We match the whole path against the regex instead of segments
|
|
145
144
|
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested
|
|
146
|
-
const { routes, remainingPath } = matchAgainstConfigs(
|
|
147
|
-
remaining,
|
|
148
|
-
configWithRegexes
|
|
149
|
-
);
|
|
145
|
+
const { routes, remainingPath } = matchAgainstConfigs(remaining, configs);
|
|
150
146
|
|
|
151
147
|
if (routes !== undefined) {
|
|
152
148
|
// This will always be empty if full path matched
|
|
@@ -189,8 +185,7 @@ function prepareConfigResources(options?: Options<{}>) {
|
|
|
189
185
|
}
|
|
190
186
|
|
|
191
187
|
const initialRoutes = getInitialRoutes(options);
|
|
192
|
-
|
|
193
|
-
const configs = getNormalizedConfigs(initialRoutes, options?.screens);
|
|
188
|
+
const configs = getSortedNormalizedConfigs(initialRoutes, options?.screens);
|
|
194
189
|
|
|
195
190
|
checkForDuplicatedConfigs(configs);
|
|
196
191
|
|
|
@@ -216,22 +211,15 @@ function getInitialRoutes(options?: Options<{}>) {
|
|
|
216
211
|
return initialRoutes;
|
|
217
212
|
}
|
|
218
213
|
|
|
219
|
-
function
|
|
214
|
+
function getSortedNormalizedConfigs(
|
|
220
215
|
initialRoutes: InitialRouteConfig[],
|
|
221
|
-
screens:
|
|
216
|
+
screens: Record<string, string | PathConfig<ParamListBase>> = {}
|
|
222
217
|
) {
|
|
223
218
|
// Create a normalized configs array which will be easier to use
|
|
224
219
|
return ([] as RouteConfig[])
|
|
225
220
|
.concat(
|
|
226
221
|
...Object.keys(screens).map((key) =>
|
|
227
|
-
createNormalizedConfigs(
|
|
228
|
-
key,
|
|
229
|
-
screens as PathConfigMap<object>,
|
|
230
|
-
initialRoutes,
|
|
231
|
-
[],
|
|
232
|
-
[],
|
|
233
|
-
[]
|
|
234
|
-
)
|
|
222
|
+
createNormalizedConfigs(key, screens, initialRoutes, [], [], [])
|
|
235
223
|
)
|
|
236
224
|
)
|
|
237
225
|
.sort((a, b) => {
|
|
@@ -430,7 +418,7 @@ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
|
|
430
418
|
|
|
431
419
|
const createNormalizedConfigs = (
|
|
432
420
|
screen: string,
|
|
433
|
-
routeConfig:
|
|
421
|
+
routeConfig: Record<string, string | PathConfig<ParamListBase>>,
|
|
434
422
|
initials: InitialRouteConfig[],
|
|
435
423
|
paths: { screen: string; path: string }[],
|
|
436
424
|
parentScreens: string[],
|
|
@@ -442,7 +430,6 @@ const createNormalizedConfigs = (
|
|
|
442
430
|
|
|
443
431
|
parentScreens.push(screen);
|
|
444
432
|
|
|
445
|
-
// @ts-expect-error: we can't strongly typecheck this for now
|
|
446
433
|
const config = routeConfig[screen];
|
|
447
434
|
|
|
448
435
|
if (typeof config === 'string') {
|
|
@@ -453,12 +440,42 @@ const createNormalizedConfigs = (
|
|
|
453
440
|
// it can have `path` property and
|
|
454
441
|
// it could have `screens` prop which has nested configs
|
|
455
442
|
if (typeof config.path === 'string') {
|
|
456
|
-
if (config.exact && config.path
|
|
443
|
+
if (config.exact && config.path == null) {
|
|
457
444
|
throw new Error(
|
|
458
|
-
|
|
445
|
+
`Screen '${screen}' doesn't specify a 'path'. A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. \`path: ''\`.`
|
|
459
446
|
);
|
|
460
447
|
}
|
|
461
448
|
|
|
449
|
+
// We should add alias configs after the main config
|
|
450
|
+
// So unless they are more specific, main config will be matched first
|
|
451
|
+
const aliasConfigs = [];
|
|
452
|
+
|
|
453
|
+
if (config.alias) {
|
|
454
|
+
for (const alias of config.alias) {
|
|
455
|
+
if (typeof alias === 'string') {
|
|
456
|
+
aliasConfigs.push(
|
|
457
|
+
createConfigItem(
|
|
458
|
+
screen,
|
|
459
|
+
[...routeNames],
|
|
460
|
+
[...paths, { screen, path: alias }],
|
|
461
|
+
config.parse
|
|
462
|
+
)
|
|
463
|
+
);
|
|
464
|
+
} else if (typeof alias === 'object') {
|
|
465
|
+
aliasConfigs.push(
|
|
466
|
+
createConfigItem(
|
|
467
|
+
screen,
|
|
468
|
+
[...routeNames],
|
|
469
|
+
alias.exact
|
|
470
|
+
? [{ screen, path: alias.path }]
|
|
471
|
+
: [...paths, { screen, path: alias.path }],
|
|
472
|
+
alias.parse
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
462
479
|
if (config.exact) {
|
|
463
480
|
// If it's an exact path, we don't need to keep track of the parent screens
|
|
464
481
|
// So we can clear it
|
|
@@ -469,6 +486,18 @@ const createNormalizedConfigs = (
|
|
|
469
486
|
configs.push(
|
|
470
487
|
createConfigItem(screen, [...routeNames], [...paths], config.parse)
|
|
471
488
|
);
|
|
489
|
+
|
|
490
|
+
configs.push(...aliasConfigs);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (
|
|
494
|
+
typeof config !== 'string' &&
|
|
495
|
+
typeof config.path !== 'string' &&
|
|
496
|
+
config.alias?.length
|
|
497
|
+
) {
|
|
498
|
+
throw new Error(
|
|
499
|
+
`Screen '${screen}' doesn't specify a 'path'. A 'path' needs to be specified in order to use 'alias'.`
|
|
500
|
+
);
|
|
472
501
|
}
|
|
473
502
|
|
|
474
503
|
if (config.screens) {
|
|
@@ -483,7 +512,7 @@ const createNormalizedConfigs = (
|
|
|
483
512
|
Object.keys(config.screens).forEach((nestedConfig) => {
|
|
484
513
|
const result = createNormalizedConfigs(
|
|
485
514
|
nestedConfig,
|
|
486
|
-
config.screens as
|
|
515
|
+
config.screens as Record<string, string | PathConfig<ParamListBase>>,
|
|
487
516
|
initials,
|
|
488
517
|
[...paths],
|
|
489
518
|
[...parentScreens],
|
|
@@ -525,7 +554,7 @@ const createConfigItem = (
|
|
|
525
554
|
|
|
526
555
|
return `${it.segment === '*' ? '.*' : escape(it.segment)}\\/`;
|
|
527
556
|
})
|
|
528
|
-
.join('')})
|
|
557
|
+
.join('')})$`
|
|
529
558
|
)
|
|
530
559
|
: undefined;
|
|
531
560
|
|
package/src/types.tsx
CHANGED
|
@@ -971,12 +971,12 @@ export type NavigatorScreenParams<ParamList extends {}> =
|
|
|
971
971
|
};
|
|
972
972
|
}[keyof ParamList];
|
|
973
973
|
|
|
974
|
-
|
|
974
|
+
type PathConfigAlias = {
|
|
975
975
|
/**
|
|
976
976
|
* Path string to match against.
|
|
977
977
|
* e.g. `/users/:id` will match `/users/1` and extract `id` param as `1`.
|
|
978
978
|
*/
|
|
979
|
-
path
|
|
979
|
+
path: string;
|
|
980
980
|
/**
|
|
981
981
|
* Whether the path should be consider parent paths or use the exact path.
|
|
982
982
|
* By default, paths are relating to the path config on the parent screen.
|
|
@@ -995,6 +995,9 @@ export type PathConfig<ParamList extends {}> = {
|
|
|
995
995
|
* ```
|
|
996
996
|
*/
|
|
997
997
|
parse?: Record<string, (value: string) => any>;
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
export type PathConfig<ParamList extends {}> = Partial<PathConfigAlias> & {
|
|
998
1001
|
/**
|
|
999
1002
|
* An object mapping the param name to a function which converts the param value to a string.
|
|
1000
1003
|
* By default, all params are converted to strings using `String(value)`.
|
|
@@ -1007,6 +1010,10 @@ export type PathConfig<ParamList extends {}> = {
|
|
|
1007
1010
|
* ```
|
|
1008
1011
|
*/
|
|
1009
1012
|
stringify?: Record<string, (value: any) => string>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Additional path alias that will be matched to the same screen.
|
|
1015
|
+
*/
|
|
1016
|
+
alias?: (string | PathConfigAlias)[];
|
|
1010
1017
|
/**
|
|
1011
1018
|
* Path configuration for child screens.
|
|
1012
1019
|
*/
|
|
@@ -1015,12 +1022,6 @@ export type PathConfig<ParamList extends {}> = {
|
|
|
1015
1022
|
* Name of the initial route to use for the navigator when the path matches.
|
|
1016
1023
|
*/
|
|
1017
1024
|
initialRouteName?: keyof ParamList;
|
|
1018
|
-
/**
|
|
1019
|
-
* A function returning a state, which may be set after modifying the routes name.
|
|
1020
|
-
*/
|
|
1021
|
-
getStateForRouteNamesChange?: (
|
|
1022
|
-
state: NavigationState
|
|
1023
|
-
) => PartialState<NavigationState> | undefined;
|
|
1024
1025
|
};
|
|
1025
1026
|
|
|
1026
1027
|
export type PathConfigMap<ParamList extends {}> = {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { NavigationBuilderContext } from './NavigationBuilderContext';
|
|
4
|
+
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* When screen config changes, we want to update the navigator in the same update phase.
|
|
6
8
|
* However, navigation state is in the root component and React won't let us update it from a child.
|
|
@@ -17,5 +19,5 @@ export function useScheduleUpdate(callback: () => void) {
|
|
|
17
19
|
// However, since we are using sync store, it might be fine
|
|
18
20
|
scheduleUpdate(callback);
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
useIsomorphicLayoutEffect(flushUpdates);
|
|
21
23
|
}
|
|
@@ -11,6 +11,7 @@ export function validatePathConfig(config: unknown, root = true) {
|
|
|
11
11
|
...(root
|
|
12
12
|
? null
|
|
13
13
|
: {
|
|
14
|
+
alias: 'array',
|
|
14
15
|
exact: 'boolean',
|
|
15
16
|
stringify: 'object',
|
|
16
17
|
parse: 'object',
|
|
@@ -33,8 +34,14 @@ export function validatePathConfig(config: unknown, root = true) {
|
|
|
33
34
|
// @ts-expect-error: we know the key exists
|
|
34
35
|
const value = config[key];
|
|
35
36
|
|
|
36
|
-
if (value !== undefined
|
|
37
|
-
|
|
37
|
+
if (value !== undefined) {
|
|
38
|
+
if (type === 'array') {
|
|
39
|
+
if (!Array.isArray(value)) {
|
|
40
|
+
return [key, `expected 'Array', got '${typeof value}'`];
|
|
41
|
+
}
|
|
42
|
+
} else if (typeof value !== type) {
|
|
43
|
+
return [key, `expected '${type}', got '${typeof value}'`];
|
|
44
|
+
}
|
|
38
45
|
}
|
|
39
46
|
} else {
|
|
40
47
|
return [key, 'extraneous'];
|