@react-navigation/core 8.0.0-alpha.25 → 8.0.0-alpha.27

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 (85) hide show
  1. package/lib/module/BaseNavigationContainer.js +12 -6
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js.map +1 -1
  4. package/lib/module/DataLoading.js +29 -4
  5. package/lib/module/DataLoading.js.map +1 -1
  6. package/lib/module/NavigationIndependentTree.js +8 -4
  7. package/lib/module/NavigationIndependentTree.js.map +1 -1
  8. package/lib/module/SceneView.js +16 -39
  9. package/lib/module/SceneView.js.map +1 -1
  10. package/lib/module/StaticNavigation.js +4 -3
  11. package/lib/module/StaticNavigation.js.map +1 -1
  12. package/lib/module/findFocusedRoute.js +1 -1
  13. package/lib/module/findFocusedRoute.js.map +1 -1
  14. package/lib/module/getActionFromState.js +1 -2
  15. package/lib/module/getActionFromState.js.map +1 -1
  16. package/lib/module/getPathFromState.js +41 -26
  17. package/lib/module/getPathFromState.js.map +1 -1
  18. package/lib/module/getPatternParts.js +15 -2
  19. package/lib/module/getPatternParts.js.map +1 -1
  20. package/lib/module/getStateFromPath.js +45 -19
  21. package/lib/module/getStateFromPath.js.map +1 -1
  22. package/lib/module/index.js +1 -0
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/types.js.map +1 -1
  25. package/lib/module/useFocusEvents.js +6 -2
  26. package/lib/module/useFocusEvents.js.map +1 -1
  27. package/lib/module/useLazyValue.js.map +1 -1
  28. package/lib/module/useNavigationBuilder.js +8 -6
  29. package/lib/module/useNavigationBuilder.js.map +1 -1
  30. package/lib/module/useOnAction.js +6 -16
  31. package/lib/module/useOnAction.js.map +1 -1
  32. package/lib/module/useOnPreventRemove.js +1 -3
  33. package/lib/module/useOnPreventRemove.js.map +1 -1
  34. package/lib/module/useRouteCache.js +1 -3
  35. package/lib/module/useRouteCache.js.map +1 -1
  36. package/lib/module/useSyncState.js +3 -2
  37. package/lib/module/useSyncState.js.map +1 -1
  38. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  39. package/lib/typescript/src/ConsumedParamsContext.d.ts +1 -6
  40. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -1
  41. package/lib/typescript/src/DataLoading.d.ts +1 -0
  42. package/lib/typescript/src/DataLoading.d.ts.map +1 -1
  43. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  44. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  45. package/lib/typescript/src/StaticNavigation.d.ts +39 -30
  46. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  47. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  48. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  49. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  50. package/lib/typescript/src/index.d.ts +2 -1
  51. package/lib/typescript/src/index.d.ts.map +1 -1
  52. package/lib/typescript/src/types.d.ts +10 -7
  53. package/lib/typescript/src/types.d.ts.map +1 -1
  54. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  55. package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
  56. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  57. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  58. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  59. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  60. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  61. package/lib/typescript/src/utilities.d.ts +10 -0
  62. package/lib/typescript/src/utilities.d.ts.map +1 -1
  63. package/package.json +5 -5
  64. package/src/BaseNavigationContainer.tsx +13 -7
  65. package/src/ConsumedParamsContext.tsx +1 -6
  66. package/src/DataLoading.tsx +68 -4
  67. package/src/NavigationIndependentTree.tsx +8 -5
  68. package/src/SceneView.tsx +18 -52
  69. package/src/StaticNavigation.tsx +88 -64
  70. package/src/findFocusedRoute.tsx +1 -1
  71. package/src/getActionFromState.tsx +7 -3
  72. package/src/getPathFromState.tsx +59 -32
  73. package/src/getPatternParts.tsx +15 -2
  74. package/src/getStateFromPath.tsx +77 -17
  75. package/src/index.tsx +5 -0
  76. package/src/types.tsx +14 -7
  77. package/src/useDescriptors.tsx +1 -1
  78. package/src/useFocusEvents.tsx +7 -5
  79. package/src/useLazyValue.tsx +2 -2
  80. package/src/useNavigationBuilder.tsx +12 -7
  81. package/src/useOnAction.tsx +11 -40
  82. package/src/useOnPreventRemove.tsx +4 -3
  83. package/src/useRouteCache.tsx +1 -4
  84. package/src/useSyncState.tsx +3 -2
  85. package/src/utilities.tsx +19 -0
@@ -71,12 +71,33 @@ export function getLoaderForState(
71
71
  tree: TreeForPathConfig,
72
72
  state: PartialState<NavigationState> | NavigationState | undefined
73
73
  ): (() => Promise<void>) | undefined {
74
- const focusedRoute = state?.routes[state.index ?? 0];
74
+ return getLoaderForStateChange(tree, state, undefined, undefined);
75
+ }
76
+
77
+ export function getLoaderForStateChange(
78
+ tree: TreeForPathConfig,
79
+ state: PartialState<NavigationState> | NavigationState | undefined,
80
+ previousState: PartialState<NavigationState> | NavigationState | undefined,
81
+ consumedParams: WeakMap<object, true> | undefined
82
+ ): (() => Promise<void>) | undefined {
83
+ const focusedRoute = state?.routes[state.index ?? state.routes.length - 1];
84
+ const previousFocusedRoute =
85
+ previousState?.routes[
86
+ previousState.index ?? previousState.routes.length - 1
87
+ ];
75
88
 
76
89
  if (!focusedRoute) {
77
90
  return undefined;
78
91
  }
79
92
 
93
+ const isNewlyFocused =
94
+ previousState === undefined ||
95
+ previousFocusedRoute == null ||
96
+ focusedRoute.name !== previousFocusedRoute.name ||
97
+ (focusedRoute.key != null &&
98
+ previousFocusedRoute.key != null &&
99
+ focusedRoute.key !== previousFocusedRoute.key);
100
+
80
101
  const item = findScreenInConfig(tree.config, focusedRoute.name);
81
102
 
82
103
  if (item == null) {
@@ -95,7 +116,11 @@ export function getLoaderForState(
95
116
 
96
117
  const loaders: (() => Promise<void>)[] = [];
97
118
 
98
- if ('UNSTABLE_loader' in item && typeof item.UNSTABLE_loader === 'function') {
119
+ if (
120
+ isNewlyFocused &&
121
+ 'UNSTABLE_loader' in item &&
122
+ typeof item.UNSTABLE_loader === 'function'
123
+ ) {
99
124
  const loader = item.UNSTABLE_loader;
100
125
 
101
126
  loaders.push(() =>
@@ -116,13 +141,52 @@ export function getLoaderForState(
116
141
  if (nested) {
117
142
  const initialRouteName = findInitialRouteName(nested.config);
118
143
 
119
- let childState = focusedRoute.state ?? getStateFromRouteParams(params);
144
+ const stateFromParams =
145
+ focusedRoute.params != null && consumedParams?.has(focusedRoute.params)
146
+ ? undefined
147
+ : getStateFromRouteParams(params);
148
+
149
+ let childState =
150
+ previousState !== undefined && stateFromParams != null
151
+ ? stateFromParams
152
+ : (focusedRoute.state ?? stateFromParams);
120
153
 
121
154
  if (childState == null && initialRouteName != null) {
122
155
  childState = { routes: [{ name: initialRouteName }] };
123
156
  }
124
157
 
125
- const childLoader = getLoaderForState(nested, childState);
158
+ let previousChildState:
159
+ | PartialState<NavigationState>
160
+ | NavigationState
161
+ | undefined;
162
+
163
+ if (!isNewlyFocused) {
164
+ const previousRouteParams = previousFocusedRoute?.params;
165
+ const previousParams =
166
+ initialParams != null || previousRouteParams != null
167
+ ? { ...initialParams, ...previousRouteParams }
168
+ : undefined;
169
+ const previousStateFromParams =
170
+ previousRouteParams != null && consumedParams?.has(previousRouteParams)
171
+ ? undefined
172
+ : getStateFromRouteParams(previousParams);
173
+
174
+ previousChildState =
175
+ previousFocusedRoute?.state ?? previousStateFromParams;
176
+
177
+ if (previousChildState == null && initialRouteName != null) {
178
+ previousChildState = { routes: [{ name: initialRouteName }] };
179
+ }
180
+ }
181
+
182
+ const childLoader = getLoaderForStateChange(
183
+ nested,
184
+ childState,
185
+ previousState === undefined
186
+ ? undefined
187
+ : (previousChildState ?? { routes: [] }),
188
+ consumedParams
189
+ );
126
190
 
127
191
  if (childLoader) {
128
192
  loaders.push(childLoader);
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
 
3
+ import { NavigationFocusedRouteStateContext } from './NavigationFocusedRouteStateContext';
3
4
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
4
5
  import {
5
6
  NamedRouteContextListContext,
@@ -23,11 +24,13 @@ export function NavigationIndependentTree({
23
24
  <NamedRouteContextListContext.Provider value={undefined}>
24
25
  <NavigationRouteContext.Provider value={undefined}>
25
26
  <NavigationContext.Provider value={undefined}>
26
- <IsFocusedContext.Provider value={undefined}>
27
- <NavigationIndependentTreeContext.Provider value={true}>
28
- {children}
29
- </NavigationIndependentTreeContext.Provider>
30
- </IsFocusedContext.Provider>
27
+ <NavigationFocusedRouteStateContext.Provider value={undefined}>
28
+ <IsFocusedContext.Provider value={undefined}>
29
+ <NavigationIndependentTreeContext.Provider value={true}>
30
+ {children}
31
+ </NavigationIndependentTreeContext.Provider>
32
+ </IsFocusedContext.Provider>
33
+ </NavigationFocusedRouteStateContext.Provider>
31
34
  </NavigationContext.Provider>
32
35
  </NavigationRouteContext.Provider>
33
36
  </NamedRouteContextListContext.Provider>
package/src/SceneView.tsx CHANGED
@@ -6,7 +6,6 @@ import type {
6
6
  } from '@react-navigation/routers';
7
7
  import * as React from 'react';
8
8
 
9
- import { ConsumedParamsContext } from './ConsumedParamsContext';
10
9
  import { EnsureSingleNavigator } from './EnsureSingleNavigator';
11
10
  import { isArrayEqual } from './isArrayEqual';
12
11
  import {
@@ -105,37 +104,6 @@ export function SceneView<
105
104
 
106
105
  const getIsInitial = React.useCallback(() => isInitialRef.current, []);
107
106
 
108
- const [consumedParams, setConsumedParams] = React.useState<WeakMap<
109
- object,
110
- true
111
- > | null>(null);
112
-
113
- const consumedParamsContext = React.useMemo(
114
- () => ({
115
- isConsumed: (params: object) => {
116
- if (consumedParams) {
117
- return consumedParams.has(params);
118
- }
119
-
120
- return false;
121
- },
122
- setConsumed: (params: object) => {
123
- setConsumedParams((prev) => {
124
- if (prev && prev.has(params)) {
125
- return prev;
126
- }
127
-
128
- const map = new WeakMap<object, true>();
129
-
130
- map.set(params, true);
131
-
132
- return map;
133
- });
134
- },
135
- }),
136
- [consumedParams]
137
- );
138
-
139
107
  const parentFocusedRouteState = React.use(NavigationFocusedRouteStateContext);
140
108
 
141
109
  const focusedRouteState = React.useMemo(() => {
@@ -205,25 +173,23 @@ export function SceneView<
205
173
  : screen.component;
206
174
 
207
175
  return (
208
- <ConsumedParamsContext.Provider value={consumedParamsContext}>
209
- <NavigationStateContext.Provider value={context}>
210
- <NavigationFocusedRouteStateContext.Provider value={focusedRouteState}>
211
- <EnsureSingleNavigator>
212
- <StaticContainer
213
- name={screen.name}
214
- render={ScreenComponent || screen.children}
215
- navigation={navigation}
216
- route={route}
217
- >
218
- {ScreenComponent !== undefined ? (
219
- <ScreenComponent navigation={navigation} route={route} />
220
- ) : screen.children !== undefined ? (
221
- screen.children({ navigation, route })
222
- ) : null}
223
- </StaticContainer>
224
- </EnsureSingleNavigator>
225
- </NavigationFocusedRouteStateContext.Provider>
226
- </NavigationStateContext.Provider>
227
- </ConsumedParamsContext.Provider>
176
+ <NavigationStateContext.Provider value={context}>
177
+ <NavigationFocusedRouteStateContext.Provider value={focusedRouteState}>
178
+ <EnsureSingleNavigator>
179
+ <StaticContainer
180
+ name={screen.name}
181
+ render={ScreenComponent || screen.children}
182
+ navigation={navigation}
183
+ route={route}
184
+ >
185
+ {ScreenComponent !== undefined ? (
186
+ <ScreenComponent navigation={navigation} route={route} />
187
+ ) : screen.children !== undefined ? (
188
+ screen.children({ navigation, route })
189
+ ) : null}
190
+ </StaticContainer>
191
+ </EnsureSingleNavigator>
192
+ </NavigationFocusedRouteStateContext.Provider>
193
+ </NavigationStateContext.Provider>
228
194
  );
229
195
  }
@@ -32,7 +32,9 @@ import type {
32
32
  HasArguments,
33
33
  InferParamsFromLinking,
34
34
  KeysOf,
35
+ NoExcessCallbackReturn,
35
36
  NoExcessObject,
37
+ NoExcessValue,
36
38
  NotUndefinedObject,
37
39
  StandardSchemaV1,
38
40
  UnionToIntersection,
@@ -53,19 +55,23 @@ type ParamsForScreenComponent<T> = [T] extends [(...args: any[]) => any]
53
55
  : undefined
54
56
  : ScreenComponentParams<T>;
55
57
 
56
- // If every nested route's params include `undefined`, the nested navigator
57
- // itself can be omitted. Otherwise, require `NavigatorScreenParams`.
58
+ // If the initial route's params include `undefined`, the nested navigator itself can be optional.
59
+ // If no initial route is specified, check if every nested route's params include `undefined`.
60
+ // Otherwise the nested navigator needs to be required.
58
61
  type ParamsForNestedNavigator<
59
62
  T extends { config: any },
60
63
  ParamList extends {} = StaticParamList<T>,
61
64
  Params = NavigatorScreenParams<ParamList>,
65
+ RouteNames extends keyof ParamList = T['config'] extends {
66
+ initialRouteName: infer RouteName extends keyof ParamList;
67
+ }
68
+ ? RouteName
69
+ : keyof ParamList,
62
70
  > = {
63
- // Map each route to `never` when params are optional, or name when params are required
64
- // If every route has optional params, the resulting union is `never`
65
- [RouteName in keyof ParamList]-?: undefined extends ParamList[RouteName]
71
+ [RouteName in RouteNames]-?: undefined extends ParamList[RouteName]
66
72
  ? never
67
73
  : RouteName;
68
- }[keyof ParamList] extends never
74
+ }[RouteNames] extends never
69
75
  ? Params | undefined
70
76
  : Params;
71
77
 
@@ -132,6 +138,8 @@ type ParamListForScreens<Screens> = {
132
138
  any,
133
139
  any,
134
140
  any,
141
+ any,
142
+ any,
135
143
  any
136
144
  >
137
145
  ? ParamsForConfig<Linking, Screen>
@@ -146,22 +154,22 @@ type ParamListForGroups<Groups> = Groups extends {
146
154
  ? ParamListForScreens<UnionToIntersection<Screens>>
147
155
  : {};
148
156
 
149
- type StaticRoute<Params, P = AnyToUnknown<Params>> = Readonly<
150
- Omit<Route<string>, 'params'> &
151
- (undefined extends Params
152
- ? {
153
- /**
154
- * Params for this route
155
- */
156
- params?: P extends object ? Readonly<P> : P;
157
- }
158
- : {
159
- /**
160
- * Params for this route
161
- */
162
- params: Readonly<P>;
163
- })
164
- >;
157
+ type StaticRouteBase = Readonly<Omit<Route<string>, 'params'>>;
158
+
159
+ type StaticRoute<Params, P = AnyToUnknown<Params>> = StaticRouteBase &
160
+ (undefined extends Params
161
+ ? {
162
+ /**
163
+ * Params for this route
164
+ */
165
+ readonly params?: P extends object ? Readonly<P> : P;
166
+ }
167
+ : {
168
+ /**
169
+ * Params for this route
170
+ */
171
+ readonly params: Readonly<P>;
172
+ });
165
173
 
166
174
  type StaticScreenConfigLinkingAlias = {
167
175
  /**
@@ -193,7 +201,7 @@ type StaticScreenConfigLinkingAlias = {
193
201
  >;
194
202
  /**
195
203
  * An object mapping the param name to a function which converts the param value to a string.
196
- * By default, all params are converted to strings using `String(value)`.
204
+ * By default, arrays and null are preserved for query params, and other values are converted using `String(value)`.
197
205
  *
198
206
  * @example
199
207
  * ```js
@@ -246,15 +254,17 @@ export type StaticScreenConfigScreen =
246
254
  | React.ComponentType<any>
247
255
  | StaticNavigation<any>;
248
256
 
249
- export type StaticScreenConfig<
250
- Linking extends StaticScreenConfigLinking,
251
- Screen,
252
- State extends NavigationState,
253
- ScreenOptions extends {},
254
- EventMap extends EventMapBase,
255
- Navigation,
256
- Params = ParamsForConfig<Linking, Screen>,
257
- > = {
257
+ export interface StaticScreenConfig<
258
+ out Linking extends StaticScreenConfigLinking,
259
+ out Screen,
260
+ in out State extends NavigationState,
261
+ in out ScreenOptions extends {},
262
+ in out EventMap extends EventMapBase,
263
+ in out Navigation,
264
+ in out Params = ParamsForConfig<Linking, Screen>,
265
+ in out ProvidedOptions extends {} = {},
266
+ in out ProvidedListeners extends {} = {},
267
+ > {
258
268
  /**
259
269
  * Static navigation config or Component to render for the screen.
260
270
  */
@@ -291,7 +301,7 @@ export type StaticScreenConfig<
291
301
  route: StaticRoute<Params>;
292
302
  navigation: Navigation;
293
303
  theme: Theme;
294
- }) => ScreenOptions);
304
+ }) => NoExcessCallbackReturn<ProvidedOptions, ScreenOptions>);
295
305
 
296
306
  /**
297
307
  * Event listeners for this screen.
@@ -310,7 +320,10 @@ export type StaticScreenConfig<
310
320
  | ((props: {
311
321
  route: StaticRoute<Params>;
312
322
  navigation: Navigation;
313
- }) => ScreenListeners<State, EventMap>);
323
+ }) => NoExcessCallbackReturn<
324
+ ProvidedListeners,
325
+ ScreenListeners<State, EventMap>
326
+ >);
314
327
 
315
328
  /**
316
329
  * Layout for this screen.
@@ -392,14 +405,13 @@ export type StaticScreenConfig<
392
405
  name: string;
393
406
  params: AnyToUnknown<Params>;
394
407
  }) => Promise<void>;
395
- };
408
+ }
396
409
 
397
410
  export type StaticScreenFactory<in out Bag extends NavigatorTypeBagBase> = <
398
411
  const Linking extends StaticScreenConfigLinking,
399
412
  const Screen extends StaticScreenConfigScreen,
400
- const ProvidedOptions extends
401
- | Bag['ScreenOptions']
402
- | ((...args: never[]) => unknown) = Bag['ScreenOptions'],
413
+ ProvidedOptions extends {} = {},
414
+ ProvidedListeners extends {} = {},
403
415
  >(
404
416
  config: StaticScreenConfig<
405
417
  Linking,
@@ -407,11 +419,11 @@ export type StaticScreenFactory<in out Bag extends NavigatorTypeBagBase> = <
407
419
  Bag['State'],
408
420
  Bag['ScreenOptions'],
409
421
  Bag['EventMap'],
410
- Bag['NavigationList'][keyof Bag['ParamList']]
411
- > & {
412
- options?: ProvidedOptions &
413
- NoExcessOptions<ProvidedOptions, Bag['ScreenOptions']>;
414
- }
422
+ Bag['NavigationList'][keyof Bag['ParamList']],
423
+ ParamsForConfig<Linking, Screen>,
424
+ ProvidedOptions,
425
+ ProvidedListeners
426
+ >
415
427
  ) => StaticScreenConfig<
416
428
  Linking,
417
429
  Screen,
@@ -446,7 +458,9 @@ type StaticConfigScreens<
446
458
  ScreenOptions,
447
459
  EventMap,
448
460
  NavigationList[RouteName],
449
- any
461
+ any,
462
+ {},
463
+ {}
450
464
  >;
451
465
  };
452
466
 
@@ -529,23 +543,26 @@ export type StaticConfig<
529
543
  }
530
544
  );
531
545
 
532
- type NoExcessOptions<Provided, Allowed extends {}> = Provided extends (
533
- ...args: never[]
534
- ) => infer Return
535
- ? (...args: never[]) => Allowed & NoExcessObject<Return, Allowed>
536
- : NoExcessObject<Provided, Allowed>;
537
-
538
- type NoExcessScreen<Screen, ScreenOptions extends {}> = Screen extends {
539
- screen: unknown;
540
- options: unknown;
541
- }
542
- ? { options: NoExcessOptions<Screen['options'], ScreenOptions> }
543
- : unknown;
546
+ type NoExcessScreen<
547
+ Screen,
548
+ ScreenOptions extends {},
549
+ Listeners extends {},
550
+ > = (Screen extends { screen: unknown; options: unknown }
551
+ ? { options: NoExcessValue<Screen['options'], ScreenOptions> }
552
+ : unknown) &
553
+ (Screen extends { screen: unknown; listeners: unknown }
554
+ ? { listeners: NoExcessValue<Screen['listeners'], Listeners> }
555
+ : unknown);
544
556
 
545
- type NoExcessScreens<Screens, ScreenOptions extends {}> = {
557
+ type NoExcessScreens<
558
+ Screens,
559
+ ScreenOptions extends {},
560
+ Listeners extends {},
561
+ > = {
546
562
  [RouteName in keyof Screens]: NoExcessScreen<
547
563
  Screens[RouteName],
548
- ScreenOptions
564
+ ScreenOptions,
565
+ Listeners
549
566
  >;
550
567
  };
551
568
 
@@ -553,6 +570,7 @@ type NoExcessGroups<
553
570
  Groups,
554
571
  Bag extends NavigatorTypeBagBase,
555
572
  ScreenOptions extends {},
573
+ Listeners extends {},
556
574
  > = {
557
575
  [GroupName in keyof Groups]: NoExcessObject<
558
576
  Groups[GroupName],
@@ -564,7 +582,7 @@ type NoExcessGroups<
564
582
  NavigationListBase<ParamListBase>
565
583
  >
566
584
  > &
567
- NoExcessStaticConfig<Groups[GroupName], Bag, ScreenOptions>;
585
+ NoExcessStaticConfig<Groups[GroupName], Bag, ScreenOptions, Listeners>;
568
586
  };
569
587
 
570
588
  // Each property is probed with `Config extends { key: infer X }` and the results are intersected.
@@ -574,19 +592,25 @@ export type NoExcessStaticConfig<
574
592
  Config,
575
593
  Bag extends NavigatorTypeBagBase,
576
594
  ScreenOptions extends {} = Bag['ScreenOptions'],
595
+ Listeners extends {} = ScreenListeners<Bag['State'], Bag['EventMap']>,
577
596
  > = (Config extends { screenOptions: infer ProvidedOptions }
578
597
  ? {
579
- screenOptions: NoExcessOptions<ProvidedOptions, ScreenOptions>;
598
+ screenOptions: NoExcessValue<ProvidedOptions, ScreenOptions>;
580
599
  }
581
600
  : unknown) &
601
+ (Config extends { screenListeners: infer ProvidedListeners }
602
+ ? {
603
+ screenListeners: NoExcessValue<ProvidedListeners, Listeners>;
604
+ }
605
+ : unknown) &
582
606
  (Config extends { screens: infer Screens }
583
607
  ? {
584
- screens: NoExcessScreens<Screens, ScreenOptions>;
608
+ screens: NoExcessScreens<Screens, ScreenOptions, Listeners>;
585
609
  }
586
610
  : unknown) &
587
611
  (Config extends { groups: infer Groups }
588
612
  ? {
589
- groups: NoExcessGroups<Groups, Bag, ScreenOptions>;
613
+ groups: NoExcessGroups<Groups, Bag, ScreenOptions, Listeners>;
590
614
  }
591
615
  : unknown);
592
616
 
@@ -1138,8 +1162,8 @@ export function createPathConfigForStaticNavigation<ParamList extends {}>(
1138
1162
 
1139
1163
  screenConfig.path = normalizePath(
1140
1164
  key
1141
- .replace(/([A-Z]+)/g, '-$1')
1142
- .replace(/^-/, '')
1165
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1166
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
1143
1167
  .toLowerCase()
1144
1168
  );
1145
1169
  }
@@ -14,7 +14,7 @@ export function findFocusedRoute(state: InitialState): Result {
14
14
 
15
15
  while (current != null) {
16
16
  const route: InitialState['routes'][number] | undefined =
17
- current.routes[current.index ?? 0];
17
+ current.routes[current.index ?? current.routes.length - 1];
18
18
 
19
19
  if (route?.state == null) {
20
20
  return route;
@@ -195,11 +195,16 @@ export function getActionFromState(
195
195
  };
196
196
  }
197
197
 
198
- const createNormalizedConfigItem = (config: PathConfig<object> | string) =>
198
+ const createNormalizedConfigItem = (
199
+ config: PathConfig<object> | string
200
+ ): ConfigItem =>
199
201
  typeof config === 'object' && config != null
200
202
  ? {
201
203
  initialRouteName:
202
- 'initialRouteName' in config ? config.initialRouteName : undefined,
204
+ 'initialRouteName' in config &&
205
+ typeof config.initialRouteName === 'string'
206
+ ? config.initialRouteName
207
+ : undefined,
203
208
  screens:
204
209
  'screens' in config && config.screens != null
205
210
  ? createNormalizedConfigs(config.screens)
@@ -209,7 +214,6 @@ const createNormalizedConfigItem = (config: PathConfig<object> | string) =>
209
214
 
210
215
  const createNormalizedConfigs = (options: PathConfigMap<object>) =>
211
216
  Object.entries(options).reduce<Record<string, ConfigItem>>((acc, [k, v]) => {
212
- // @ts-expect-error -we can't properly type this
213
217
  acc[k] = createNormalizedConfigItem(v);
214
218
  return acc;
215
219
  }, {});