@react-navigation/core 8.0.0-alpha.3 → 8.0.0-alpha.31
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/BaseNavigationContainer.js +38 -19
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/ConsumedParamsContext.js +5 -0
- package/lib/module/ConsumedParamsContext.js.map +1 -0
- package/lib/module/DataLoading.js +127 -0
- package/lib/module/DataLoading.js.map +1 -0
- package/lib/module/NavigationBuilderContext.js +1 -0
- package/lib/module/NavigationBuilderContext.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +20 -8
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/NavigationProvider.js +22 -11
- package/lib/module/NavigationProvider.js.map +1 -1
- package/lib/module/NavigationStateContext.js +3 -0
- package/lib/module/NavigationStateContext.js.map +1 -1
- package/lib/module/PreventRemoveContext.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +37 -27
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/SceneView.js +10 -41
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/StaticNavigation.js +179 -50
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/StaticTreeContext.js +5 -0
- package/lib/module/StaticTreeContext.js.map +1 -0
- package/lib/module/checkSerializable.js +18 -13
- package/lib/module/checkSerializable.js.map +1 -1
- package/lib/module/createNavigationContainerRef.js +6 -8
- package/lib/module/createNavigationContainerRef.js.map +1 -1
- package/lib/module/createNavigatorFactory.js +27 -5
- package/lib/module/createNavigatorFactory.js.map +1 -1
- package/lib/module/findFocusedRoute.js +7 -4
- package/lib/module/findFocusedRoute.js.map +1 -1
- package/lib/module/getActionFromState.js +45 -20
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +171 -90
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +20 -4
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +487 -217
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/getStateFromRouteParams.js +23 -0
- package/lib/module/getStateFromRouteParams.js.map +1 -0
- package/lib/module/index.js +5 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/theming/useTheme.js +1 -1
- package/lib/module/theming/useTheme.js.map +1 -1
- package/lib/module/types.js +35 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDescriptors.js +21 -11
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useEventEmitter.js +48 -33
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useFocusEvents.js +12 -4
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
- package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/module/useIsFocused.js +7 -12
- package/lib/module/useIsFocused.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js +4 -2
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useLazyValue.js.map +1 -1
- package/lib/module/useNavigation.js +2 -15
- package/lib/module/useNavigation.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +204 -131
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +22 -17
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +28 -10
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useNavigationIndependentTree.js +1 -1
- package/lib/module/useNavigationIndependentTree.js.map +1 -1
- package/lib/module/useNavigationState.js +125 -45
- package/lib/module/useNavigationState.js.map +1 -1
- package/lib/module/useOnAction.js +37 -7
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOnGetState.js +6 -3
- package/lib/module/useOnGetState.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +39 -30
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useOnRouteFocus.js +1 -1
- package/lib/module/useOnRouteFocus.js.map +1 -1
- package/lib/module/useOptionsGetters.js +10 -5
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemove.js +10 -3
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/module/usePreventRemoveContext.js +1 -1
- package/lib/module/usePreventRemoveContext.js.map +1 -1
- package/lib/module/useRegisterNavigator.js +2 -2
- package/lib/module/useRegisterNavigator.js.map +1 -1
- package/lib/module/useRoute.js +2 -0
- package/lib/module/useRoute.js.map +1 -1
- package/lib/module/useRouteCache.js +17 -11
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +9 -9
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/useStateForPath.js +1 -1
- package/lib/module/useStateForPath.js.map +1 -1
- package/lib/module/useSyncState.js +32 -9
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/module/validatePathConfig.js +50 -36
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
- package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
- package/lib/typescript/src/DataLoading.d.ts +21 -0
- package/lib/typescript/src/DataLoading.d.ts.map +1 -0
- package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
- package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
- package/lib/typescript/src/Group.d.ts +1 -1
- package/lib/typescript/src/Group.d.ts.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +9 -8
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts +12 -13
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/NavigationStateContext.d.ts +4 -3
- package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
- package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +4 -2
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/Screen.d.ts +1 -1
- package/lib/typescript/src/Screen.d.ts.map +1 -1
- package/lib/typescript/src/StaticContainer.d.ts +1 -1
- package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts +189 -109
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
- package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
- package/lib/typescript/src/checkSerializable.d.ts +8 -4
- package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
- package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
- package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
- package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
- package/lib/typescript/src/getActionFromState.d.ts +4 -4
- package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts +3 -3
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPatternParts.d.ts +1 -0
- package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts +4 -7
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
- package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +36 -33
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
- package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +260 -159
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useChildListeners.d.ts +3 -3
- package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useComponent.d.ts +1 -1
- package/lib/typescript/src/useComponent.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +10 -9
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useEventEmitter.d.ts +2 -2
- package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEvents.d.ts +2 -2
- package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
- package/lib/typescript/src/useIsFocused.d.ts +3 -0
- package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
- package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
- package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
- package/lib/typescript/src/useNavigation.d.ts +4 -2
- package/lib/typescript/src/useNavigation.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +23 -30
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts +3 -3
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +19 -27
- package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationState.d.ts +17 -3
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts +9 -9
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/useOnGetState.d.ts +1 -1
- package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
- package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/useRoute.d.ts +7 -3
- package/lib/typescript/src/useRoute.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts +5 -5
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
- package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/src/useStateForPath.d.ts +1 -1
- package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts +1 -0
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/lib/typescript/src/utilities.d.ts +74 -37
- package/lib/typescript/src/utilities.d.ts.map +1 -1
- package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
- package/package.json +23 -22
- package/src/BaseNavigationContainer.tsx +362 -334
- package/src/ConsumedParamsContext.tsx +5 -0
- package/src/DataLoading.tsx +203 -0
- package/src/NavigationBuilderContext.tsx +14 -9
- package/src/NavigationFocusedRouteStateContext.tsx +4 -4
- package/src/NavigationIndependentTree.tsx +23 -12
- package/src/NavigationProvider.tsx +26 -15
- package/src/NavigationStateContext.tsx +9 -6
- package/src/PreventRemoveContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +51 -46
- package/src/SceneView.tsx +11 -39
- package/src/StaticNavigation.tsx +659 -307
- package/src/StaticTreeContext.tsx +7 -0
- package/src/checkSerializable.tsx +35 -19
- package/src/createNavigationContainerRef.tsx +12 -9
- package/src/createNavigatorFactory.tsx +92 -8
- package/src/findFocusedRoute.tsx +16 -16
- package/src/getActionFromState.tsx +87 -51
- package/src/getPathFromState.tsx +254 -128
- package/src/getPatternParts.tsx +25 -4
- package/src/getStateFromPath.tsx +821 -331
- package/src/getStateFromRouteParams.tsx +59 -0
- package/src/index.tsx +55 -5
- package/src/theming/useTheme.tsx +1 -1
- package/src/types.tsx +798 -373
- package/src/useDescriptors.tsx +31 -14
- package/src/useEventEmitter.tsx +52 -38
- package/src/useFocusEvents.tsx +15 -7
- package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
- package/src/useIsFocused.tsx +14 -21
- package/src/useKeyedChildListeners.tsx +4 -2
- package/src/useLazyValue.tsx +2 -2
- package/src/useNavigation.tsx +7 -24
- package/src/useNavigationBuilder.tsx +314 -186
- package/src/useNavigationCache.tsx +32 -25
- package/src/useNavigationHelpers.tsx +27 -10
- package/src/useNavigationIndependentTree.tsx +1 -1
- package/src/useNavigationState.tsx +168 -69
- package/src/useOnAction.tsx +61 -13
- package/src/useOnGetState.tsx +5 -3
- package/src/useOnPreventRemove.tsx +75 -42
- package/src/useOnRouteFocus.tsx +10 -12
- package/src/useOptionsGetters.tsx +11 -5
- package/src/usePreventRemove.tsx +12 -3
- package/src/usePreventRemoveContext.tsx +1 -1
- package/src/useRegisterNavigator.tsx +2 -2
- package/src/useRoute.tsx +20 -6
- package/src/useRouteCache.tsx +22 -16
- package/src/useScheduleUpdate.tsx +9 -10
- package/src/useStateForPath.tsx +1 -1
- package/src/useSyncState.tsx +35 -9
- package/src/utilities.tsx +144 -31
- package/src/validatePathConfig.tsx +86 -57
- package/lib/module/CurrentRenderContext.js +0 -10
- package/lib/module/CurrentRenderContext.js.map +0 -1
- package/lib/module/useCurrentRender.js +0 -19
- package/lib/module/useCurrentRender.js.map +0 -1
- package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
- package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
- package/lib/typescript/src/useCurrentRender.d.ts +0 -14
- package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
- package/src/CurrentRenderContext.tsx +0 -9
- package/src/useCurrentRender.tsx +0 -31
package/src/getPathFromState.tsx
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
PartialState,
|
|
4
|
-
Route,
|
|
5
|
-
} from '@react-navigation/routers';
|
|
6
|
-
import * as queryString from 'query-string';
|
|
1
|
+
import type { NavigationState, PartialState } from '@react-navigation/routers';
|
|
2
|
+
import queryString from 'query-string';
|
|
7
3
|
|
|
8
4
|
import { getPatternParts, type PatternPart } from './getPatternParts';
|
|
5
|
+
import { getStateFromRouteParams } from './getStateFromRouteParams';
|
|
9
6
|
import type { PathConfig, PathConfigMap } from './types';
|
|
10
7
|
import { validatePathConfig } from './validatePathConfig';
|
|
11
8
|
|
|
12
9
|
type Options<ParamList extends {}> = {
|
|
13
|
-
path?: string;
|
|
14
|
-
initialRouteName?: string;
|
|
10
|
+
path?: string | undefined;
|
|
11
|
+
initialRouteName?: string | undefined;
|
|
15
12
|
screens: PathConfigMap<ParamList>;
|
|
16
13
|
};
|
|
17
14
|
|
|
@@ -19,40 +16,87 @@ type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
|
|
|
19
16
|
|
|
20
17
|
type StringifyConfig = Record<string, ((value: unknown) => string) | undefined>;
|
|
21
18
|
|
|
19
|
+
type SerializedParamValue = string | string[] | null;
|
|
20
|
+
|
|
22
21
|
type ConfigItem = {
|
|
23
|
-
parts?: PatternPart[];
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
parts?: PatternPart[] | undefined;
|
|
23
|
+
ownParts: PatternPart[];
|
|
24
|
+
stringify?: StringifyConfig | undefined;
|
|
25
|
+
screens?: Record<string, ConfigItem> | undefined;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const serializeParamValue = (value: unknown): SerializedParamValue =>
|
|
29
|
+
value === null
|
|
30
|
+
? null
|
|
31
|
+
: Array.isArray(value)
|
|
32
|
+
? value.map(String)
|
|
33
|
+
: String(value);
|
|
34
|
+
|
|
35
|
+
const encodePathParam = (value: string) => {
|
|
36
|
+
let result = '';
|
|
37
|
+
|
|
38
|
+
for (const char of value) {
|
|
39
|
+
const charCode = char.charCodeAt(0);
|
|
40
|
+
const safe =
|
|
41
|
+
(charCode >= 65 && charCode <= 90) ||
|
|
42
|
+
(charCode >= 97 && charCode <= 122) ||
|
|
43
|
+
(charCode >= 48 && charCode <= 57) ||
|
|
44
|
+
"-._~!$&'()*+,;=:@".includes(char);
|
|
45
|
+
|
|
46
|
+
result += safe ? char : encodeURIComponent(char);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
26
50
|
};
|
|
27
51
|
|
|
28
|
-
const getActiveRoute = (
|
|
52
|
+
const getActiveRoute = (
|
|
53
|
+
state: State,
|
|
54
|
+
configs: Record<string, ConfigItem> | undefined,
|
|
55
|
+
getRouteState: (
|
|
56
|
+
route: State['routes'][number],
|
|
57
|
+
config: ConfigItem | undefined
|
|
58
|
+
) => State | undefined
|
|
59
|
+
): { name: string; params?: object | undefined } => {
|
|
29
60
|
const route =
|
|
30
61
|
typeof state.index === 'number'
|
|
31
62
|
? state.routes[state.index]
|
|
32
63
|
: state.routes[state.routes.length - 1];
|
|
33
64
|
|
|
34
|
-
if (route
|
|
35
|
-
|
|
65
|
+
if (route == null) {
|
|
66
|
+
throw new Error(`Couldn't find the active route.`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const config = configs?.[route.name];
|
|
70
|
+
const routeState = getRouteState(route, config);
|
|
71
|
+
|
|
72
|
+
if (routeState) {
|
|
73
|
+
return getActiveRoute(routeState, config?.screens, getRouteState);
|
|
36
74
|
}
|
|
37
75
|
|
|
38
76
|
return route;
|
|
39
77
|
};
|
|
40
78
|
|
|
41
79
|
const cachedNormalizedConfigs = new WeakMap<
|
|
42
|
-
|
|
80
|
+
Options<{}>,
|
|
43
81
|
Record<string, ConfigItem>
|
|
44
82
|
>();
|
|
45
83
|
|
|
46
84
|
const getNormalizedConfigs = (options?: Options<{}>) => {
|
|
47
|
-
if (!options
|
|
85
|
+
if (!options) {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
48
88
|
|
|
49
|
-
const cached = cachedNormalizedConfigs.get(options
|
|
89
|
+
const cached = cachedNormalizedConfigs.get(options);
|
|
50
90
|
|
|
51
|
-
if (cached)
|
|
91
|
+
if (cached) {
|
|
92
|
+
return cached;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
validatePathConfig(options);
|
|
52
96
|
|
|
53
97
|
const normalizedConfigs = createNormalizedConfigs(options.screens);
|
|
54
98
|
|
|
55
|
-
cachedNormalizedConfigs.set(options
|
|
99
|
+
cachedNormalizedConfigs.set(options, normalizedConfigs);
|
|
56
100
|
|
|
57
101
|
return normalizedConfigs;
|
|
58
102
|
};
|
|
@@ -96,105 +140,177 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
96
140
|
);
|
|
97
141
|
}
|
|
98
142
|
|
|
99
|
-
if (options) {
|
|
100
|
-
validatePathConfig(options);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
143
|
const configs = getNormalizedConfigs(options);
|
|
144
|
+
const cachedRouteStates = new Map<object, State | undefined>();
|
|
145
|
+
|
|
146
|
+
const getRouteState = (
|
|
147
|
+
route: State['routes'][number],
|
|
148
|
+
config: ConfigItem | undefined
|
|
149
|
+
) => {
|
|
150
|
+
if (route.state) {
|
|
151
|
+
return route.state;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const hasScreenParams =
|
|
155
|
+
route.params &&
|
|
156
|
+
'screen' in route.params &&
|
|
157
|
+
typeof route.params.screen === 'string' &&
|
|
158
|
+
config?.screens?.[route.params.screen];
|
|
159
|
+
|
|
160
|
+
const hasStateParams =
|
|
161
|
+
route.params && 'state' in route.params && config?.screens;
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
!route.params ||
|
|
165
|
+
!config?.screens ||
|
|
166
|
+
(!hasScreenParams && !hasStateParams)
|
|
167
|
+
) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!cachedRouteStates.has(route)) {
|
|
172
|
+
cachedRouteStates.set(route, getStateFromRouteParams(route.params));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return cachedRouteStates.get(route);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const focusedRoute = getActiveRoute(state, configs, getRouteState);
|
|
104
179
|
|
|
105
180
|
let path = '/';
|
|
106
181
|
let current: State | undefined = state;
|
|
107
182
|
|
|
108
|
-
const allParams: Record<string, string> = {};
|
|
109
|
-
|
|
110
183
|
while (current) {
|
|
111
|
-
let index = typeof current.index === 'number' ? current.index : 0;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
184
|
+
let index: number = typeof current.index === 'number' ? current.index : 0;
|
|
185
|
+
|
|
186
|
+
const initialRoute: State['routes'][number] | undefined =
|
|
187
|
+
current.routes[index];
|
|
188
|
+
|
|
189
|
+
if (initialRoute == null) {
|
|
190
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let route: State['routes'][number] = initialRoute;
|
|
115
194
|
|
|
116
195
|
let parts: PatternPart[] | undefined;
|
|
117
196
|
|
|
118
|
-
|
|
119
|
-
|
|
197
|
+
// Values for each pattern part object, so the same param name
|
|
198
|
+
// at different nesting levels gets the value from its own level
|
|
199
|
+
const partValues = new Map<PatternPart, SerializedParamValue | undefined>();
|
|
120
200
|
|
|
121
|
-
|
|
201
|
+
let focusedParams: Record<string, SerializedParamValue> | undefined;
|
|
202
|
+
let currentOptions = configs;
|
|
122
203
|
|
|
123
204
|
// Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
|
|
124
205
|
const nestedRouteNames = [];
|
|
125
206
|
|
|
126
207
|
let hasNext = true;
|
|
127
208
|
|
|
128
|
-
while (
|
|
129
|
-
|
|
209
|
+
while (hasNext) {
|
|
210
|
+
const config = currentOptions[route.name];
|
|
211
|
+
|
|
212
|
+
if (config == null) {
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
parts = config.parts;
|
|
217
|
+
|
|
218
|
+
const ownParts = config.ownParts;
|
|
130
219
|
|
|
131
220
|
nestedRouteNames.push(route.name);
|
|
132
221
|
|
|
133
222
|
if (route.params) {
|
|
134
|
-
const options =
|
|
135
|
-
|
|
136
|
-
const currentParams =
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} else {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
223
|
+
const options = config;
|
|
224
|
+
const params = route.params as Record<string, unknown>;
|
|
225
|
+
const currentParams: Record<string, SerializedParamValue> = {};
|
|
226
|
+
|
|
227
|
+
for (const key in params) {
|
|
228
|
+
const value = params[key];
|
|
229
|
+
|
|
230
|
+
if (value === undefined) {
|
|
231
|
+
let optional = false;
|
|
232
|
+
|
|
233
|
+
for (const part of ownParts) {
|
|
234
|
+
if (part.param === key) {
|
|
235
|
+
optional = part.optional === true;
|
|
236
|
+
break;
|
|
151
237
|
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (optional) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const stringify = options.stringify?.[key];
|
|
246
|
+
|
|
247
|
+
currentParams[key] = stringify
|
|
248
|
+
? stringify(value)
|
|
249
|
+
: serializeParamValue(value);
|
|
250
|
+
}
|
|
152
251
|
|
|
153
|
-
|
|
252
|
+
const claimedParams = new Set<string>();
|
|
154
253
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
);
|
|
254
|
+
for (const part of ownParts) {
|
|
255
|
+
if (part.param && part.param in currentParams) {
|
|
256
|
+
const value = currentParams[part.param];
|
|
159
257
|
|
|
160
|
-
|
|
161
|
-
|
|
258
|
+
if (value !== undefined) {
|
|
259
|
+
partValues.set(part, value);
|
|
260
|
+
claimedParams.add(part.param);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
162
263
|
}
|
|
163
264
|
|
|
164
265
|
if (focusedRoute === route) {
|
|
165
266
|
// If this is the focused route, keep the params for later use
|
|
166
267
|
// We save it here since it's been stringified already
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
268
|
+
// Params claimed by the pattern shouldn't be repeated in the query string
|
|
269
|
+
focusedParams = {};
|
|
270
|
+
|
|
271
|
+
for (const key in currentParams) {
|
|
272
|
+
const value = currentParams[key];
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
!claimedParams.has(key) &&
|
|
276
|
+
value !== undefined &&
|
|
277
|
+
value !== 'undefined'
|
|
278
|
+
) {
|
|
279
|
+
focusedParams[key] = value;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
for (const part of ownParts) {
|
|
286
|
+
if (part.param && !partValues.has(part)) {
|
|
287
|
+
partValues.set(part, undefined);
|
|
180
288
|
}
|
|
181
289
|
}
|
|
182
290
|
|
|
183
291
|
// If there is no `screens` property or no nested state, we return pattern
|
|
184
|
-
|
|
292
|
+
const routeState = getRouteState(route, config);
|
|
293
|
+
|
|
294
|
+
if (!config.screens || routeState == null) {
|
|
185
295
|
hasNext = false;
|
|
186
296
|
} else {
|
|
187
297
|
index =
|
|
188
|
-
typeof
|
|
189
|
-
?
|
|
190
|
-
:
|
|
298
|
+
typeof routeState.index === 'number'
|
|
299
|
+
? routeState.index
|
|
300
|
+
: routeState.routes.length - 1;
|
|
301
|
+
|
|
302
|
+
const nextRoute: State['routes'][number] | undefined =
|
|
303
|
+
routeState.routes[index];
|
|
191
304
|
|
|
192
|
-
|
|
193
|
-
|
|
305
|
+
if (nextRoute == null) {
|
|
306
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const nestedConfig = config.screens;
|
|
194
310
|
|
|
195
311
|
// if there is config for next route name, we go deeper
|
|
196
|
-
if (
|
|
197
|
-
route = nextRoute
|
|
312
|
+
if (nextRoute.name in nestedConfig) {
|
|
313
|
+
route = nextRoute;
|
|
198
314
|
currentOptions = nestedConfig;
|
|
199
315
|
} else {
|
|
200
316
|
// If not, there is no sense in going deeper in config
|
|
@@ -203,62 +319,68 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
203
319
|
}
|
|
204
320
|
}
|
|
205
321
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
322
|
+
const routeState = getRouteState(route, currentOptions[route.name]);
|
|
323
|
+
|
|
324
|
+
if (currentOptions[route.name] != null) {
|
|
325
|
+
if (parts) {
|
|
326
|
+
let index = 0;
|
|
327
|
+
|
|
328
|
+
for (const part of parts) {
|
|
329
|
+
const { segment, param, optional } = part;
|
|
330
|
+
|
|
331
|
+
if (index > 0) {
|
|
332
|
+
path += '/';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
index++;
|
|
336
|
+
|
|
209
337
|
// We don't know what to show for wildcard patterns
|
|
210
338
|
// Showing the route name seems ok, though whatever we show here will be incorrect
|
|
211
339
|
// Since the page doesn't actually exist
|
|
212
340
|
if (segment === '*') {
|
|
213
|
-
|
|
341
|
+
path += route.name;
|
|
342
|
+
continue;
|
|
214
343
|
}
|
|
215
344
|
|
|
216
345
|
// If the path has a pattern for a param, put the param in the path
|
|
217
346
|
if (param) {
|
|
218
|
-
const value =
|
|
347
|
+
const value = partValues.get(part);
|
|
219
348
|
|
|
220
349
|
if (value === undefined && optional) {
|
|
221
350
|
// Optional params without value assigned in route.params should be ignored
|
|
222
|
-
|
|
351
|
+
continue;
|
|
223
352
|
}
|
|
224
353
|
|
|
225
354
|
// Valid characters according to
|
|
226
355
|
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 (see pchar definition)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
/[^A-Za-z0-9\-._~!$&'()*+,;=:@]/g.test(char)
|
|
230
|
-
? encodeURIComponent(char)
|
|
231
|
-
: char
|
|
232
|
-
)
|
|
233
|
-
.join('');
|
|
356
|
+
path += encodePathParam(String(value));
|
|
357
|
+
continue;
|
|
234
358
|
}
|
|
235
359
|
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
360
|
+
path += encodeURIComponent(segment);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
239
363
|
} else {
|
|
240
364
|
path += encodeURIComponent(route.name);
|
|
241
365
|
}
|
|
242
366
|
|
|
243
367
|
if (!focusedParams && focusedRoute.params) {
|
|
244
|
-
focusedParams =
|
|
245
|
-
Object.entries(focusedRoute.params).map(([key, value]) => [
|
|
246
|
-
key,
|
|
247
|
-
String(value),
|
|
248
|
-
])
|
|
249
|
-
);
|
|
250
|
-
}
|
|
368
|
+
focusedParams = {};
|
|
251
369
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
370
|
+
const params = focusedRoute.params as Record<string, unknown>;
|
|
371
|
+
|
|
372
|
+
for (const key in params) {
|
|
373
|
+
const value = serializeParamValue(params[key]);
|
|
374
|
+
|
|
375
|
+
if (value !== 'undefined') {
|
|
376
|
+
focusedParams[key] = value;
|
|
259
377
|
}
|
|
260
378
|
}
|
|
379
|
+
}
|
|
261
380
|
|
|
381
|
+
if (routeState) {
|
|
382
|
+
path += '/';
|
|
383
|
+
} else if (focusedParams) {
|
|
262
384
|
const query = queryString.stringify(focusedParams, { sort: false });
|
|
263
385
|
|
|
264
386
|
if (query) {
|
|
@@ -266,7 +388,7 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
266
388
|
}
|
|
267
389
|
}
|
|
268
390
|
|
|
269
|
-
current =
|
|
391
|
+
current = routeState;
|
|
270
392
|
}
|
|
271
393
|
|
|
272
394
|
// Include the root path if specified
|
|
@@ -292,14 +414,13 @@ const createConfigItem = (
|
|
|
292
414
|
parentParts?: PatternPart[]
|
|
293
415
|
): ConfigItem => {
|
|
294
416
|
if (typeof config === 'string') {
|
|
295
|
-
|
|
296
|
-
const parts = getPatternParts(config);
|
|
417
|
+
const ownParts = getPatternParts(config);
|
|
297
418
|
|
|
298
419
|
if (parentParts) {
|
|
299
|
-
return { parts: [...parentParts, ...
|
|
420
|
+
return { parts: [...parentParts, ...ownParts], ownParts };
|
|
300
421
|
}
|
|
301
422
|
|
|
302
|
-
return { parts };
|
|
423
|
+
return { parts: ownParts, ownParts };
|
|
303
424
|
}
|
|
304
425
|
|
|
305
426
|
if (config.exact && config.path === undefined) {
|
|
@@ -310,14 +431,12 @@ const createConfigItem = (
|
|
|
310
431
|
|
|
311
432
|
// If an object is specified as the value (e.g. Foo: { ... }),
|
|
312
433
|
// It can have `path` property and `screens` prop which has nested configs
|
|
434
|
+
const ownParts = config.path ? getPatternParts(config.path) : [];
|
|
313
435
|
const parts =
|
|
314
436
|
config.exact !== true
|
|
315
|
-
? [
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
]
|
|
319
|
-
: config.path
|
|
320
|
-
? getPatternParts(config.path)
|
|
437
|
+
? [...(parentParts || []), ...ownParts]
|
|
438
|
+
: ownParts.length
|
|
439
|
+
? ownParts
|
|
321
440
|
: undefined;
|
|
322
441
|
|
|
323
442
|
const screens =
|
|
@@ -327,6 +446,7 @@ const createConfigItem = (
|
|
|
327
446
|
|
|
328
447
|
return {
|
|
329
448
|
parts,
|
|
449
|
+
ownParts,
|
|
330
450
|
stringify: config.stringify,
|
|
331
451
|
screens,
|
|
332
452
|
};
|
|
@@ -335,11 +455,17 @@ const createConfigItem = (
|
|
|
335
455
|
const createNormalizedConfigs = (
|
|
336
456
|
options: PathConfigMap<object>,
|
|
337
457
|
parts?: PatternPart[]
|
|
338
|
-
): Record<string, ConfigItem> =>
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
458
|
+
): Record<string, ConfigItem> => {
|
|
459
|
+
const configs: Record<string, ConfigItem> = {};
|
|
460
|
+
const screens = options as Record<string, PathConfig<{}> | string>;
|
|
461
|
+
|
|
462
|
+
for (const name in screens) {
|
|
463
|
+
const config = screens[name];
|
|
464
|
+
|
|
465
|
+
if (config !== undefined) {
|
|
466
|
+
configs[name] = createConfigItem(config, parts);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return configs;
|
|
471
|
+
};
|
package/src/getPatternParts.tsx
CHANGED
|
@@ -15,6 +15,8 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
15
15
|
|
|
16
16
|
let isRegex = false;
|
|
17
17
|
let isParam = false;
|
|
18
|
+
let isEscaped = false;
|
|
19
|
+
let isInCharClass = false;
|
|
18
20
|
let regexInnerParens = 0;
|
|
19
21
|
|
|
20
22
|
// One extra iteration to add the last character
|
|
@@ -34,7 +36,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
34
36
|
`Encountered ':' in the middle of a segment in path: ${path}`
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
|
-
} else if (char === '(') {
|
|
39
|
+
} else if (char === '(' && !isEscaped && !isInCharClass) {
|
|
38
40
|
if (isParam) {
|
|
39
41
|
if (isRegex) {
|
|
40
42
|
// The '(' is part of the regex if we're already inside one
|
|
@@ -47,13 +49,13 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
47
49
|
`Encountered '(' without preceding ':' in path: ${path}`
|
|
48
50
|
);
|
|
49
51
|
}
|
|
50
|
-
} else if (char === ')') {
|
|
52
|
+
} else if (char === ')' && !isEscaped && !isInCharClass) {
|
|
51
53
|
if (isParam && isRegex) {
|
|
52
54
|
if (regexInnerParens) {
|
|
53
55
|
// The ')' is part of the regex if we're already inside one
|
|
54
56
|
regexInnerParens--;
|
|
55
|
-
current.regex += char;
|
|
56
57
|
} else {
|
|
58
|
+
current.regex += char;
|
|
57
59
|
isRegex = false;
|
|
58
60
|
isParam = false;
|
|
59
61
|
}
|
|
@@ -62,7 +64,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
62
64
|
`Encountered ')' without preceding '(' in path: ${path}`
|
|
63
65
|
);
|
|
64
66
|
}
|
|
65
|
-
} else if (char === '?') {
|
|
67
|
+
} else if (char === '?' && !isRegex) {
|
|
66
68
|
if (current.param) {
|
|
67
69
|
isParam = false;
|
|
68
70
|
|
|
@@ -102,6 +104,17 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
102
104
|
if (isRegex) {
|
|
103
105
|
current.regex = current.regex || '';
|
|
104
106
|
current.regex += char;
|
|
107
|
+
|
|
108
|
+
// Track escapes and character classes so parens inside them are not special
|
|
109
|
+
if (isEscaped) {
|
|
110
|
+
isEscaped = false;
|
|
111
|
+
} else if (char === '\\') {
|
|
112
|
+
isEscaped = true;
|
|
113
|
+
} else if (char === '[') {
|
|
114
|
+
isInCharClass = true;
|
|
115
|
+
} else if (char === ']') {
|
|
116
|
+
isInCharClass = false;
|
|
117
|
+
}
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
if (isParam && !isRegex) {
|
|
@@ -124,3 +137,11 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
124
137
|
|
|
125
138
|
return parts;
|
|
126
139
|
}
|
|
140
|
+
|
|
141
|
+
export function combinePatternParts<T extends PatternPart>(
|
|
142
|
+
parentParts: T[],
|
|
143
|
+
parts: T[],
|
|
144
|
+
exact = false
|
|
145
|
+
): T[] {
|
|
146
|
+
return exact ? parts : [...parentParts, ...parts];
|
|
147
|
+
}
|