@react-navigation/core 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.
Files changed (163) hide show
  1. package/lib/module/BaseNavigationContainer.js +14 -7
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js +5 -0
  4. package/lib/module/ConsumedParamsContext.js.map +1 -0
  5. package/lib/module/NavigationBuilderContext.js +1 -0
  6. package/lib/module/NavigationBuilderContext.js.map +1 -1
  7. package/lib/module/NavigationIndependentTree.js +8 -4
  8. package/lib/module/NavigationIndependentTree.js.map +1 -1
  9. package/lib/module/NavigationProvider.js +14 -3
  10. package/lib/module/NavigationProvider.js.map +1 -1
  11. package/lib/module/NavigationStateContext.js.map +1 -1
  12. package/lib/module/PreventRemoveProvider.js +3 -3
  13. package/lib/module/PreventRemoveProvider.js.map +1 -1
  14. package/lib/module/SceneView.js +46 -56
  15. package/lib/module/SceneView.js.map +1 -1
  16. package/lib/module/StaticNavigation.js +34 -4
  17. package/lib/module/StaticNavigation.js.map +1 -1
  18. package/lib/module/createNavigatorFactory.js +22 -1
  19. package/lib/module/createNavigatorFactory.js.map +1 -1
  20. package/lib/module/getPathFromState.js +25 -3
  21. package/lib/module/getPathFromState.js.map +1 -1
  22. package/lib/module/getStateFromPath.js +158 -73
  23. package/lib/module/getStateFromPath.js.map +1 -1
  24. package/lib/module/getStateFromRouteParams.js +24 -0
  25. package/lib/module/getStateFromRouteParams.js.map +1 -0
  26. package/lib/module/index.js +1 -1
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/module/theming/useTheme.js +1 -1
  29. package/lib/module/theming/useTheme.js.map +1 -1
  30. package/lib/module/types.js.map +1 -1
  31. package/lib/module/useCurrentRender.js +1 -1
  32. package/lib/module/useCurrentRender.js.map +1 -1
  33. package/lib/module/useDescriptors.js +12 -43
  34. package/lib/module/useDescriptors.js.map +1 -1
  35. package/lib/module/useFocusEvents.js +1 -1
  36. package/lib/module/useFocusEvents.js.map +1 -1
  37. package/lib/module/useFocusedListenersChildrenAdapter.js +1 -1
  38. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  39. package/lib/module/useIsFocused.js +7 -12
  40. package/lib/module/useIsFocused.js.map +1 -1
  41. package/lib/module/useNavigationBuilder.js +84 -38
  42. package/lib/module/useNavigationBuilder.js.map +1 -1
  43. package/lib/module/useNavigationCache.js +14 -56
  44. package/lib/module/useNavigationCache.js.map +1 -1
  45. package/lib/module/useNavigationHelpers.js +1 -1
  46. package/lib/module/useNavigationHelpers.js.map +1 -1
  47. package/lib/module/useNavigationIndependentTree.js +1 -1
  48. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  49. package/lib/module/useNavigationState.js +4 -2
  50. package/lib/module/useNavigationState.js.map +1 -1
  51. package/lib/module/useOnAction.js +1 -1
  52. package/lib/module/useOnAction.js.map +1 -1
  53. package/lib/module/useOnGetState.js +2 -2
  54. package/lib/module/useOnGetState.js.map +1 -1
  55. package/lib/module/useOnPreventRemove.js +2 -2
  56. package/lib/module/useOnPreventRemove.js.map +1 -1
  57. package/lib/module/useOnRouteFocus.js +1 -1
  58. package/lib/module/useOnRouteFocus.js.map +1 -1
  59. package/lib/module/useOptionsGetters.js +2 -2
  60. package/lib/module/useOptionsGetters.js.map +1 -1
  61. package/lib/module/usePreventRemoveContext.js +1 -1
  62. package/lib/module/usePreventRemoveContext.js.map +1 -1
  63. package/lib/module/useRegisterNavigator.js +1 -1
  64. package/lib/module/useRegisterNavigator.js.map +1 -1
  65. package/lib/module/useScheduleUpdate.js +1 -1
  66. package/lib/module/useScheduleUpdate.js.map +1 -1
  67. package/lib/module/useStateForPath.js +1 -1
  68. package/lib/module/useStateForPath.js.map +1 -1
  69. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  70. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  71. package/lib/typescript/src/ConsumedParamsContext.d.ts +8 -0
  72. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  73. package/lib/typescript/src/NavigationBuilderContext.d.ts +11 -5
  74. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  75. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  76. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  77. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  78. package/lib/typescript/src/NavigationProvider.d.ts +4 -4
  79. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  80. package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
  81. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  82. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  83. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  84. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  85. package/lib/typescript/src/StaticNavigation.d.ts +39 -24
  86. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  87. package/lib/typescript/src/createNavigatorFactory.d.ts +1 -1
  88. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  89. package/lib/typescript/src/findFocusedRoute.d.ts +3 -3
  90. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  91. package/lib/typescript/src/getActionFromState.d.ts +3 -3
  92. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  93. package/lib/typescript/src/getPathFromState.d.ts +2 -2
  94. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  95. package/lib/typescript/src/getStateFromPath.d.ts +3 -3
  96. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  97. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  98. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  99. package/lib/typescript/src/index.d.ts +2 -1
  100. package/lib/typescript/src/index.d.ts.map +1 -1
  101. package/lib/typescript/src/types.d.ts +144 -82
  102. package/lib/typescript/src/types.d.ts.map +1 -1
  103. package/lib/typescript/src/useDescriptors.d.ts +12 -25
  104. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  105. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  106. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  107. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  108. package/lib/typescript/src/useNavigationBuilder.d.ts +20 -29
  109. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  110. package/lib/typescript/src/useNavigationCache.d.ts +2 -42
  111. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  112. package/lib/typescript/src/useNavigationHelpers.d.ts +18 -16
  113. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  114. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  115. package/lib/typescript/src/useOnAction.d.ts +6 -6
  116. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  117. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  118. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  119. package/lib/typescript/src/useRouteCache.d.ts +2 -2
  120. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  121. package/lib/typescript/src/utilities.d.ts +42 -3
  122. package/lib/typescript/src/utilities.d.ts.map +1 -1
  123. package/package.json +20 -18
  124. package/src/BaseNavigationContainer.tsx +332 -326
  125. package/src/ConsumedParamsContext.tsx +10 -0
  126. package/src/NavigationBuilderContext.tsx +14 -8
  127. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  128. package/src/NavigationIndependentTree.tsx +8 -5
  129. package/src/NavigationProvider.tsx +17 -3
  130. package/src/NavigationStateContext.tsx +5 -6
  131. package/src/PreventRemoveProvider.tsx +3 -3
  132. package/src/SceneView.tsx +58 -56
  133. package/src/StaticNavigation.tsx +121 -51
  134. package/src/createNavigatorFactory.tsx +35 -4
  135. package/src/findFocusedRoute.tsx +3 -3
  136. package/src/getActionFromState.tsx +7 -7
  137. package/src/getPathFromState.tsx +53 -9
  138. package/src/getStateFromPath.tsx +255 -97
  139. package/src/getStateFromRouteParams.tsx +60 -0
  140. package/src/index.tsx +1 -1
  141. package/src/theming/useTheme.tsx +1 -1
  142. package/src/types.tsx +330 -187
  143. package/src/useCurrentRender.tsx +1 -1
  144. package/src/useDescriptors.tsx +13 -48
  145. package/src/useFocusEvents.tsx +1 -1
  146. package/src/useFocusedListenersChildrenAdapter.tsx +1 -1
  147. package/src/useIsFocused.tsx +14 -21
  148. package/src/useNavigation.tsx +2 -2
  149. package/src/useNavigationBuilder.tsx +131 -49
  150. package/src/useNavigationCache.tsx +12 -73
  151. package/src/useNavigationHelpers.tsx +1 -1
  152. package/src/useNavigationIndependentTree.tsx +1 -1
  153. package/src/useNavigationState.tsx +6 -4
  154. package/src/useOnAction.tsx +8 -8
  155. package/src/useOnGetState.tsx +2 -2
  156. package/src/useOnPreventRemove.tsx +2 -2
  157. package/src/useOnRouteFocus.tsx +10 -12
  158. package/src/useOptionsGetters.tsx +2 -2
  159. package/src/usePreventRemoveContext.tsx +1 -1
  160. package/src/useRegisterNavigator.tsx +1 -1
  161. package/src/useScheduleUpdate.tsx +1 -3
  162. package/src/useStateForPath.tsx +1 -1
  163. package/src/utilities.tsx +94 -4
@@ -17,7 +17,7 @@ type Options<
17
17
  ScreenOptions extends {},
18
18
  EventMap extends Record<string, any>,
19
19
  > = {
20
- state: State;
20
+ routes: State['routes'];
21
21
  getState: () => State;
22
22
  navigation: NavigationHelpers<ParamListBase> &
23
23
  Partial<NavigationProp<ParamListBase, string, any, any, any>>;
@@ -65,76 +65,15 @@ export function useNavigationCache<
65
65
  EventMap extends Record<string, any>,
66
66
  ActionHelpers extends Record<string, (...args: any) => void>,
67
67
  >({
68
- state,
68
+ routes,
69
69
  getState,
70
70
  navigation,
71
71
  setOptions,
72
72
  router,
73
73
  emitter,
74
74
  }: Options<State, ScreenOptions, EventMap>) {
75
- const parentNavigationHelpers = React.useContext(NavigationContext);
76
- const { stackRef } = React.useContext(NavigationBuilderContext);
77
-
78
- const base = React.useMemo((): NavigationItem<
79
- State,
80
- ScreenOptions,
81
- EventMap,
82
- ActionHelpers
83
- > &
84
- ActionHelpers => {
85
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
86
- const { emit, ...rest } = navigation;
87
-
88
- const actions = {
89
- ...router.actionCreators,
90
- ...CommonActions,
91
- };
92
-
93
- const dispatch = () => {
94
- throw new Error(
95
- 'Actions cannot be dispatched from a placeholder screen.'
96
- );
97
- };
98
-
99
- const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
100
- (acc, name) => {
101
- acc[name] = dispatch;
102
-
103
- return acc;
104
- },
105
- {}
106
- ) as ActionHelpers;
107
-
108
- // @ts-expect-error: type of getParent does not match
109
- return {
110
- ...rest,
111
- ...helpers,
112
- addListener: () => {
113
- // Event listeners are not supported for placeholder screens
114
-
115
- return () => {
116
- // Empty function
117
- };
118
- },
119
- removeListener: () => {
120
- // Event listeners are not supported for placeholder screens
121
- },
122
- dispatch,
123
- getParent: (routeName) => {
124
- if (routeName !== undefined) {
125
- throw new Error(
126
- 'Getting parent by route name is not supported from a placeholder screen.'
127
- );
128
- }
129
-
130
- return parentNavigationHelpers;
131
- },
132
- setOptions: () => {
133
- throw new Error('Options cannot be set from a placeholder screen.');
134
- },
135
- isFocused: () => false,
136
- };
137
- }, [parentNavigationHelpers, navigation, router.actionCreators]);
75
+ const parentNavigationHelpers = React.use(NavigationContext);
76
+ const { stackRef } = React.use(NavigationBuilderContext);
138
77
 
139
78
  // Cache object which holds navigation objects for each screen
140
79
  // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
@@ -149,10 +88,10 @@ export function useNavigationCache<
149
88
  >,
150
89
  }),
151
90
  // eslint-disable-next-line react-hooks/exhaustive-deps
152
- [base, getState, parentNavigationHelpers, navigation, setOptions, emitter]
91
+ [getState, parentNavigationHelpers, navigation, setOptions, emitter]
153
92
  );
154
93
 
155
- cache.current = state.routes.reduce<
94
+ cache.current = routes.reduce<
156
95
  NavigationCache<State, ScreenOptions, EventMap, ActionHelpers>
157
96
  >((acc, route) => {
158
97
  const previous = cache.current[route.key];
@@ -213,8 +152,11 @@ export function useNavigationCache<
213
152
  {}
214
153
  );
215
154
 
155
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
156
+ const { emit, ...rest } = navigation;
157
+
216
158
  acc[route.key] = {
217
- ...base,
159
+ ...rest,
218
160
  ...helpers,
219
161
  // FIXME: too much work to fix the types for now
220
162
  ...(emitter.create(route.key) as any),
@@ -239,7 +181,7 @@ export function useNavigationCache<
239
181
  }));
240
182
  },
241
183
  isFocused: () => {
242
- const state = base.getState();
184
+ const state = rest.getState();
243
185
 
244
186
  if (state.routes[state.index].key !== route.key) {
245
187
  return false;
@@ -255,8 +197,5 @@ export function useNavigationCache<
255
197
  return acc;
256
198
  }, {});
257
199
 
258
- return {
259
- base,
260
- navigations: cache.current,
261
- };
200
+ return cache.current;
262
201
  }
@@ -41,7 +41,7 @@ export function useNavigationHelpers<
41
41
  router,
42
42
  stateRef,
43
43
  }: Options<State, Action>) {
44
- const parentNavigationHelpers = React.useContext(NavigationContext);
44
+ const parentNavigationHelpers = React.use(NavigationContext);
45
45
 
46
46
  return React.useMemo(() => {
47
47
  const dispatch = (op: Action | ((state: State) => Action)) => {
@@ -3,5 +3,5 @@ import * as React from 'react';
3
3
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
4
4
 
5
5
  export function useNavigationIndependentTree() {
6
- return React.useContext(NavigationIndependentTreeContext);
6
+ return React.use(NavigationIndependentTreeContext);
7
7
  }
@@ -35,8 +35,8 @@ type NavigationStateForNested<
35
35
  export function useNavigationState<
36
36
  const T,
37
37
  const Navigator = RootNavigator,
38
- const RouteName extends
39
- keyof NavigationListForNested<Navigator> = keyof NavigationListForNested<Navigator>,
38
+ const RouteName extends keyof NavigationListForNested<Navigator> =
39
+ keyof NavigationListForNested<Navigator>,
40
40
  >(
41
41
  routeName: RouteName,
42
42
  selector: (state: NavigationStateForNested<Navigator, RouteName>) => T
@@ -117,8 +117,9 @@ export function NavigationStateListenerProvider({
117
117
  children: React.ReactNode;
118
118
  }) {
119
119
  const listeners = React.useRef<(() => void)[]>([]);
120
+ const stateRef = React.useRef(state);
120
121
 
121
- const getState = useLatestCallback(() => state);
122
+ const getState = useLatestCallback(() => stateRef.current);
122
123
 
123
124
  const subscribe = useLatestCallback((callback: () => void) => {
124
125
  listeners.current.push(callback);
@@ -128,7 +129,8 @@ export function NavigationStateListenerProvider({
128
129
  };
129
130
  });
130
131
 
131
- React.useEffect(() => {
132
+ React.useLayoutEffect(() => {
133
+ stateRef.current = state;
132
134
  listeners.current.forEach((callback) => callback());
133
135
  }, [state]);
134
136
 
@@ -16,11 +16,11 @@ import type { EventMapCore } from './types';
16
16
  import type { NavigationEventEmitter } from './useEventEmitter';
17
17
  import { shouldPreventRemove, useOnPreventRemove } from './useOnPreventRemove';
18
18
 
19
- type Options = {
20
- router: Router<NavigationState, NavigationAction>;
21
- key?: string;
22
- getState: () => NavigationState;
23
- setState: (state: NavigationState | PartialState<NavigationState>) => void;
19
+ type Options<State extends NavigationState> = {
20
+ router: Router<State, NavigationAction>;
21
+ key?: string | undefined;
22
+ getState: () => State;
23
+ setState: (state: State | PartialState<State>) => void;
24
24
  actionListeners: ChildActionListener[];
25
25
  beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>;
26
26
  routerConfigOptions: RouterConfigOptions;
@@ -36,7 +36,7 @@ type Options = {
36
36
  *
37
37
  * When the action handler handles as action, it returns `true`, otherwise `false`.
38
38
  */
39
- export function useOnAction({
39
+ export function useOnAction<State extends NavigationState>({
40
40
  router,
41
41
  getState,
42
42
  setState,
@@ -45,13 +45,13 @@ export function useOnAction({
45
45
  beforeRemoveListeners,
46
46
  routerConfigOptions,
47
47
  emitter,
48
- }: Options) {
48
+ }: Options<State>) {
49
49
  const {
50
50
  onAction: onActionParent,
51
51
  onRouteFocus: onRouteFocusParent,
52
52
  addListener: addListenerParent,
53
53
  onDispatchAction,
54
- } = React.useContext(NavigationBuilderContext);
54
+ } = React.use(NavigationBuilderContext);
55
55
 
56
56
  const routerConfigOptionsRef =
57
57
  React.useRef<RouterConfigOptions>(routerConfigOptions);
@@ -14,8 +14,8 @@ type Options = {
14
14
  };
15
15
 
16
16
  export function useOnGetState({ getState, getStateListeners }: Options) {
17
- const { addKeyedListener } = React.useContext(NavigationBuilderContext);
18
- const route = React.useContext(NavigationRouteContext);
17
+ const { addKeyedListener } = React.use(NavigationBuilderContext);
18
+ const route = React.use(NavigationRouteContext);
19
19
  const key = route ? route.key : 'root';
20
20
 
21
21
  const getRehydratedState = React.useCallback(() => {
@@ -78,8 +78,8 @@ export function useOnPreventRemove({
78
78
  emitter,
79
79
  beforeRemoveListeners,
80
80
  }: Options) {
81
- const { addKeyedListener } = React.useContext(NavigationBuilderContext);
82
- const route = React.useContext(NavigationRouteContext);
81
+ const { addKeyedListener } = React.use(NavigationBuilderContext);
82
+ const route = React.use(NavigationRouteContext);
83
83
  const routeKey = route?.key;
84
84
 
85
85
  React.useEffect(() => {
@@ -7,11 +7,11 @@ import * as React from 'react';
7
7
 
8
8
  import { NavigationBuilderContext } from './NavigationBuilderContext';
9
9
 
10
- type Options<Action extends NavigationAction> = {
11
- router: Router<NavigationState, Action>;
12
- getState: () => NavigationState;
13
- setState: (state: NavigationState) => void;
14
- key?: string;
10
+ type Options<State extends NavigationState, Action extends NavigationAction> = {
11
+ router: Router<State, Action>;
12
+ getState: () => State;
13
+ setState: (state: State) => void;
14
+ key?: string | undefined;
15
15
  };
16
16
 
17
17
  /**
@@ -19,13 +19,11 @@ type Options<Action extends NavigationAction> = {
19
19
  * Focus action needs to be treated specially, coz when a nested route is focused,
20
20
  * the parent navigators also needs to be focused.
21
21
  */
22
- export function useOnRouteFocus<Action extends NavigationAction>({
23
- router,
24
- getState,
25
- key: sourceRouteKey,
26
- setState,
27
- }: Options<Action>) {
28
- const { onRouteFocus: onRouteFocusParent } = React.useContext(
22
+ export function useOnRouteFocus<
23
+ State extends NavigationState,
24
+ Action extends NavigationAction,
25
+ >({ router, getState, key: sourceRouteKey, setState }: Options<State, Action>) {
26
+ const { onRouteFocus: onRouteFocusParent } = React.use(
29
27
  NavigationBuilderContext
30
28
  );
31
29
 
@@ -17,8 +17,8 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
17
17
  Record<string, () => object | undefined | null>
18
18
  >({});
19
19
 
20
- const { onOptionsChange } = React.useContext(NavigationBuilderContext);
21
- const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
20
+ const { onOptionsChange } = React.use(NavigationBuilderContext);
21
+ const { addOptionsGetter: parentAddOptionsGetter } = React.use(
22
22
  NavigationStateContext
23
23
  );
24
24
 
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { PreventRemoveContext } from './PreventRemoveContext';
4
4
 
5
5
  export function usePreventRemoveContext() {
6
- const value = React.useContext(PreventRemoveContext);
6
+ const value = React.use(PreventRemoveContext);
7
7
 
8
8
  if (value == null) {
9
9
  throw new Error(
@@ -9,7 +9,7 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
9
9
  */
10
10
  export function useRegisterNavigator() {
11
11
  const [key] = React.useState(() => nanoid());
12
- const container = React.useContext(SingleNavigatorContext);
12
+ const container = React.use(SingleNavigatorContext);
13
13
 
14
14
  if (container === undefined) {
15
15
  throw new Error(
@@ -10,9 +10,7 @@ import { NavigationBuilderContext } from './NavigationBuilderContext';
10
10
  * Then we call setState during after the component updates.
11
11
  */
12
12
  export function useScheduleUpdate(callback: () => void) {
13
- const { scheduleUpdate, flushUpdates } = React.useContext(
14
- NavigationBuilderContext
15
- );
13
+ const { scheduleUpdate, flushUpdates } = React.use(NavigationBuilderContext);
16
14
 
17
15
  // FIXME: This is potentially unsafe
18
16
  // However, since we are using sync store, it might be fine
@@ -9,7 +9,7 @@ import { NavigationFocusedRouteStateContext } from './NavigationFocusedRouteStat
9
9
  * @returns Minimal state to build a path for the current route.
10
10
  */
11
11
  export function useStateForPath() {
12
- const state = React.useContext(NavigationFocusedRouteStateContext);
12
+ const state = React.use(NavigationFocusedRouteStateContext);
13
13
 
14
14
  return state;
15
15
  }
package/src/utilities.tsx CHANGED
@@ -35,6 +35,16 @@ export type NotUndefined<T> = T extends undefined ? never : T;
35
35
 
36
36
  export type AnyToUnknown<T> = 0 extends 1 & T ? unknown : T;
37
37
 
38
+ export type RequiredKeys<T extends object> = {
39
+ [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
40
+ }[keyof T];
41
+
42
+ export type UndefinedIfAllOptional<T> = T extends object
43
+ ? RequiredKeys<T> extends never
44
+ ? T | undefined
45
+ : T
46
+ : T;
47
+
38
48
  /**
39
49
  * Check if a function type has arguments.
40
50
  */
@@ -66,6 +76,24 @@ type ExtractSegmentParam<Segment extends string> =
66
76
  ? { [K in StripRegex<Param>]: string }
67
77
  : {};
68
78
 
79
+ export type StandardSchemaValidationResult<Output> =
80
+ | { value: Output; issues?: undefined }
81
+ | { value?: undefined; issues: readonly unknown[] };
82
+
83
+ export type StandardSchemaV1<Input = unknown, Output = Input> = {
84
+ readonly '~standard': {
85
+ readonly version: 1;
86
+ readonly vendor: string;
87
+ readonly validate: (
88
+ value: Input
89
+ ) =>
90
+ | StandardSchemaValidationResult<Output>
91
+ | Promise<StandardSchemaValidationResult<Output>>;
92
+ };
93
+ };
94
+
95
+ export type QueryParamInput = string | string[] | null | undefined;
96
+
69
97
  /**
70
98
  * Extract path params from a path string.
71
99
  * e.g. `/foo/:userId/:postId` -> `{ userId: string; postId: string }`
@@ -78,17 +106,67 @@ export type ExtractParamStrings<Path extends string> =
78
106
  : ExtractSegmentParam<Path>;
79
107
 
80
108
  /**
81
- * Extract the parsed params type from base params and parse functions.
82
- * Applies the return type of parse functions to the corresponding params.
109
+ * Get the type of params based on the `parse` config and the path pattern.
83
110
  */
84
111
  export type ExtractParamsType<Params, Parse> = {
112
+ /**
113
+ * Base param types from path pattern
114
+ * Refine the type based on standard schema, then parse function
115
+ * Otherwise, use the type from path pattern
116
+ */
85
117
  [K in keyof Params]: K extends keyof Parse
86
- ? Parse[K] extends (value: string) => infer R
118
+ ? Parse[K] extends StandardSchemaV1<unknown, infer R>
87
119
  ? R
88
- : Params[K]
120
+ : Parse[K] extends (value: string) => infer R
121
+ ? R
122
+ : Params[K]
89
123
  : Params[K];
124
+ } & {
125
+ /**
126
+ * Optional param types not in path pattern (for query params)
127
+ */
128
+ [K in QueryParamOptionalKeys<Params, Parse>]?: QueryParamValue<Parse[K]>;
129
+ } & {
130
+ /**
131
+ * Required param types not in path pattern (for query params)
132
+ */
133
+ [K in QueryParamRequiredKeys<Params, Parse>]: QueryParamValue<Parse[K]>;
90
134
  };
91
135
 
136
+ type QueryParamValue<ParseValue> =
137
+ ParseValue extends StandardSchemaV1<unknown, infer R>
138
+ ? R
139
+ : ParseValue extends (value: string) => infer R
140
+ ? R
141
+ : never;
142
+
143
+ /**
144
+ * For schema, it's optional if the output has `undefined`, otherwise required
145
+ * For parse function, it's always optional as it can't say if it's required or not
146
+ */
147
+ type QueryParamOptionalKeys<Params, Parse> = {
148
+ [K in Exclude<keyof Parse, keyof Params>]: Parse[K] extends StandardSchemaV1<
149
+ unknown,
150
+ infer R
151
+ >
152
+ ? undefined extends R
153
+ ? K
154
+ : never
155
+ : Parse[K] extends (value: string) => unknown
156
+ ? K
157
+ : never;
158
+ }[Exclude<keyof Parse, keyof Params>];
159
+
160
+ /**
161
+ * Exclude optional keys to get required keys
162
+ * For schema, it's required if the output doesn't have `undefined`, otherwise optional
163
+ * It doesn't include parse functions (as they are always optional)
164
+ */
165
+ type QueryParamRequiredKeys<Params, Parse> = Exclude<
166
+ Exclude<keyof Parse, keyof Params>,
167
+ QueryParamOptionalKeys<Params, Parse>
168
+ >;
169
+
92
170
  /**
93
171
  * Infer the path string from a linking config.
94
172
  */
@@ -101,6 +179,18 @@ export type InferPath<T> = T extends { path: infer P extends string }
101
179
  */
102
180
  export type InferParse<T> = T extends { parse: infer P } ? P : {};
103
181
 
182
+ export type InferParamsFromLinking<T> = T extends {
183
+ path: infer P extends string;
184
+ }
185
+ ? UndefinedIfAllOptional<
186
+ ExtractParamsType<ExtractParamStrings<P>, InferParse<T>>
187
+ >
188
+ : T extends string
189
+ ? UndefinedIfAllOptional<
190
+ ExtractParamsType<ExtractParamStrings<T>, undefined>
191
+ >
192
+ : undefined;
193
+
104
194
  /**
105
195
  * Infer the params type from a screen component or nested navigator.
106
196
  */