@react-navigation/core 7.0.0-alpha.6 → 7.0.0-alpha.8
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/BaseNavigationContainer.js +6 -1
- package/lib/commonjs/BaseNavigationContainer.js.map +1 -1
- package/lib/commonjs/StaticNavigation.js +6 -2
- package/lib/commonjs/StaticNavigation.js.map +1 -1
- package/lib/commonjs/getFocusedRouteNameFromRoute.js.map +1 -1
- package/lib/commonjs/index.js +27 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/theming/ThemeContext.js +12 -0
- package/lib/commonjs/theming/ThemeContext.js.map +1 -0
- package/lib/commonjs/theming/ThemeProvider.js +20 -0
- package/lib/commonjs/theming/ThemeProvider.js.map +1 -0
- package/lib/commonjs/theming/useTheme.js +18 -0
- package/lib/commonjs/theming/useTheme.js.map +1 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useDescriptors.js +79 -16
- package/lib/commonjs/useDescriptors.js.map +1 -1
- package/lib/commonjs/useNavigationBuilder.js +10 -3
- package/lib/commonjs/useNavigationBuilder.js.map +1 -1
- package/lib/commonjs/useNavigationCache.js +55 -14
- package/lib/commonjs/useNavigationCache.js.map +1 -1
- package/lib/commonjs/useOnAction.js +3 -1
- package/lib/commonjs/useOnAction.js.map +1 -1
- package/lib/commonjs/usePreventRemove.js +2 -2
- package/lib/commonjs/usePreventRemove.js.map +1 -1
- package/lib/module/BaseNavigationContainer.js +6 -1
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/StaticNavigation.js +6 -2
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/getFocusedRouteNameFromRoute.js.map +1 -1
- package/lib/module/index.js +4 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/theming/ThemeContext.js +4 -0
- package/lib/module/theming/ThemeContext.js.map +1 -0
- package/lib/module/theming/ThemeProvider.js +12 -0
- package/lib/module/theming/ThemeProvider.js.map +1 -0
- package/lib/module/theming/useTheme.js +10 -0
- package/lib/module/theming/useTheme.js.map +1 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDescriptors.js +79 -16
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +10 -3
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +55 -14
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useOnAction.js +3 -1
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/usePreventRemove.js +1 -1
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts +1 -0
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/NavigationStateContext.d.ts +2 -18
- package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/findFocusedRoute.d.ts +1 -9
- package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts +3 -0
- package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -0
- package/lib/typescript/src/theming/ThemeProvider.d.ts +8 -0
- package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/src/theming/useTheme.d.ts +2 -0
- package/lib/typescript/src/theming/useTheme.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +47 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +106 -54
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +78 -64
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts +52 -2
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +8 -55
- package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/BaseNavigationContainer.tsx +6 -1
- package/src/StaticNavigation.tsx +13 -6
- package/src/getFocusedRouteNameFromRoute.tsx +3 -3
- package/src/index.tsx +4 -1
- package/src/theming/ThemeContext.tsx +7 -0
- package/src/theming/ThemeProvider.tsx +14 -0
- package/src/theming/useTheme.tsx +15 -0
- package/src/types.tsx +66 -1
- package/src/useDescriptors.tsx +148 -34
- package/src/useNavigationBuilder.tsx +21 -7
- package/src/useNavigationCache.tsx +84 -23
- package/src/useOnAction.tsx +6 -1
- package/src/usePreventRemove.tsx +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { ThemeContext } from './ThemeContext';
|
|
4
|
+
|
|
5
|
+
export function useTheme() {
|
|
6
|
+
const theme = React.useContext(ThemeContext);
|
|
7
|
+
|
|
8
|
+
if (theme == null) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"Couldn't find a theme. Is your component inside NavigationContainer or does it have a theme?"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return theme;
|
|
15
|
+
}
|
package/src/types.tsx
CHANGED
|
@@ -14,6 +14,9 @@ declare global {
|
|
|
14
14
|
namespace ReactNavigation {
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
16
16
|
interface RootParamList {}
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
19
|
+
interface Theme {}
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -29,13 +32,15 @@ export type DefaultNavigatorOptions<
|
|
|
29
32
|
* Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent.
|
|
30
33
|
*/
|
|
31
34
|
id?: string;
|
|
35
|
+
|
|
32
36
|
/**
|
|
33
37
|
* Children React Elements to extract the route configuration from.
|
|
34
38
|
* Only `Screen`, `Group` and `React.Fragment` are supported as children.
|
|
35
39
|
*/
|
|
36
40
|
children: React.ReactNode;
|
|
41
|
+
|
|
37
42
|
/**
|
|
38
|
-
* Layout
|
|
43
|
+
* Layout for the navigator.
|
|
39
44
|
* Useful for wrapping with a component with access to navigator's state and options.
|
|
40
45
|
*/
|
|
41
46
|
layout?: (props: {
|
|
@@ -58,6 +63,7 @@ export type DefaultNavigatorOptions<
|
|
|
58
63
|
>;
|
|
59
64
|
children: React.ReactNode;
|
|
60
65
|
}) => React.ReactElement;
|
|
66
|
+
|
|
61
67
|
/**
|
|
62
68
|
* Event listeners for all the screens in the navigator.
|
|
63
69
|
*/
|
|
@@ -67,6 +73,7 @@ export type DefaultNavigatorOptions<
|
|
|
67
73
|
route: RouteProp<ParamList>;
|
|
68
74
|
navigation: any;
|
|
69
75
|
}) => ScreenListeners<State, EventMap>);
|
|
76
|
+
|
|
70
77
|
/**
|
|
71
78
|
* Default options for all screens under this navigator.
|
|
72
79
|
*/
|
|
@@ -75,7 +82,19 @@ export type DefaultNavigatorOptions<
|
|
|
75
82
|
| ((props: {
|
|
76
83
|
route: RouteProp<ParamList>;
|
|
77
84
|
navigation: any;
|
|
85
|
+
theme: ReactNavigation.Theme;
|
|
78
86
|
}) => ScreenOptions);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Layout for all screens under this navigator.
|
|
90
|
+
*/
|
|
91
|
+
screenLayout?: (props: {
|
|
92
|
+
route: RouteProp<ParamList, keyof ParamList>;
|
|
93
|
+
navigation: any;
|
|
94
|
+
theme: ReactNavigation.Theme;
|
|
95
|
+
children: React.ReactElement;
|
|
96
|
+
}) => React.ReactElement;
|
|
97
|
+
|
|
79
98
|
/**
|
|
80
99
|
A function returning a state, which may be set after modifying the routes name.
|
|
81
100
|
*/
|
|
@@ -281,6 +300,22 @@ type NavigationHelpersCommon<
|
|
|
281
300
|
: never
|
|
282
301
|
): void;
|
|
283
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Preloads the route in current navigation tree.
|
|
305
|
+
*
|
|
306
|
+
* @param name Name of the route to navigate to.
|
|
307
|
+
* @param [params] Params object for the route.
|
|
308
|
+
*/
|
|
309
|
+
preload<RouteName extends keyof ParamList>(
|
|
310
|
+
...args: RouteName extends unknown
|
|
311
|
+
? undefined extends ParamList[RouteName]
|
|
312
|
+
?
|
|
313
|
+
| [screen: RouteName]
|
|
314
|
+
| [screen: RouteName, params: ParamList[RouteName]]
|
|
315
|
+
: [screen: RouteName, params: ParamList[RouteName]]
|
|
316
|
+
: never
|
|
317
|
+
): void;
|
|
318
|
+
|
|
284
319
|
/**
|
|
285
320
|
* Reset the navigation state to the provided state.
|
|
286
321
|
*
|
|
@@ -379,6 +414,10 @@ export type NavigationContainerProps = {
|
|
|
379
414
|
* @deprecated Use nested navigation API instead
|
|
380
415
|
*/
|
|
381
416
|
navigationInChildEnabled?: boolean;
|
|
417
|
+
/**
|
|
418
|
+
* Theme object for the UI elements.
|
|
419
|
+
*/
|
|
420
|
+
theme?: ReactNavigation.Theme;
|
|
382
421
|
/**
|
|
383
422
|
* Children elements to render.
|
|
384
423
|
*/
|
|
@@ -590,6 +629,7 @@ export type RouteConfig<
|
|
|
590
629
|
| ((props: {
|
|
591
630
|
route: RouteProp<ParamList, RouteName>;
|
|
592
631
|
navigation: any;
|
|
632
|
+
theme: ReactNavigation.Theme;
|
|
593
633
|
}) => ScreenOptions);
|
|
594
634
|
|
|
595
635
|
/**
|
|
@@ -602,6 +642,18 @@ export type RouteConfig<
|
|
|
602
642
|
navigation: any;
|
|
603
643
|
}) => ScreenListeners<State, EventMap>);
|
|
604
644
|
|
|
645
|
+
/**
|
|
646
|
+
* Layout for this screen.
|
|
647
|
+
* Useful for wrapping the screen with custom containers.
|
|
648
|
+
* e.g. for styling, error boundaries, suspense, etc.
|
|
649
|
+
*/
|
|
650
|
+
layout?: (props: {
|
|
651
|
+
route: RouteProp<ParamList, keyof ParamList>;
|
|
652
|
+
navigation: any;
|
|
653
|
+
theme: ReactNavigation.Theme;
|
|
654
|
+
children: React.ReactElement;
|
|
655
|
+
}) => React.ReactElement;
|
|
656
|
+
|
|
605
657
|
/**
|
|
606
658
|
* Function to return an unique ID for this screen.
|
|
607
659
|
* Receives an object with the route params.
|
|
@@ -638,7 +690,20 @@ export type RouteGroupConfig<
|
|
|
638
690
|
| ((props: {
|
|
639
691
|
route: RouteProp<ParamList, keyof ParamList>;
|
|
640
692
|
navigation: any;
|
|
693
|
+
theme: ReactNavigation.Theme;
|
|
641
694
|
}) => ScreenOptions);
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Layout for the screens inside the group.
|
|
698
|
+
* This will override the `screenLayout` of parent group or navigator.
|
|
699
|
+
*/
|
|
700
|
+
screenLayout?: (props: {
|
|
701
|
+
route: RouteProp<ParamList, keyof ParamList>;
|
|
702
|
+
navigation: any;
|
|
703
|
+
theme: ReactNavigation.Theme;
|
|
704
|
+
children: React.ReactElement;
|
|
705
|
+
}) => React.ReactElement;
|
|
706
|
+
|
|
642
707
|
/**
|
|
643
708
|
* Children React Elements to extract the route configuration from.
|
|
644
709
|
* Only `Screen`, `Group` and `React.Fragment` are supported as children.
|
package/src/useDescriptors.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
NavigationAction,
|
|
3
3
|
NavigationState,
|
|
4
4
|
ParamListBase,
|
|
5
|
+
PartialState,
|
|
5
6
|
Router,
|
|
6
7
|
} from '@react-navigation/routers';
|
|
7
8
|
import * as React from 'react';
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
import { NavigationContext } from './NavigationContext';
|
|
15
16
|
import { NavigationRouteContext } from './NavigationRouteContext';
|
|
16
17
|
import { SceneView } from './SceneView';
|
|
18
|
+
import { ThemeContext } from './theming/ThemeContext';
|
|
17
19
|
import type {
|
|
18
20
|
Descriptor,
|
|
19
21
|
EventMapBase,
|
|
@@ -33,14 +35,23 @@ export type ScreenConfigWithParent<
|
|
|
33
35
|
> = {
|
|
34
36
|
keys: (string | undefined)[];
|
|
35
37
|
options: (ScreenOptionsOrCallback<ScreenOptions> | undefined)[] | undefined;
|
|
38
|
+
layout: ScreenLayout | undefined;
|
|
36
39
|
props: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
|
37
40
|
};
|
|
38
41
|
|
|
42
|
+
type ScreenLayout = (props: {
|
|
43
|
+
route: RouteProp<ParamListBase, string>;
|
|
44
|
+
navigation: any;
|
|
45
|
+
theme: ReactNavigation.Theme;
|
|
46
|
+
children: React.ReactElement;
|
|
47
|
+
}) => React.ReactElement;
|
|
48
|
+
|
|
39
49
|
type ScreenOptionsOrCallback<ScreenOptions extends {}> =
|
|
40
50
|
| ScreenOptions
|
|
41
51
|
| ((props: {
|
|
42
52
|
route: RouteProp<ParamListBase, string>;
|
|
43
53
|
navigation: any;
|
|
54
|
+
theme: ReactNavigation.Theme;
|
|
44
55
|
}) => ScreenOptions);
|
|
45
56
|
|
|
46
57
|
type Options<
|
|
@@ -54,7 +65,8 @@ type Options<
|
|
|
54
65
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
|
55
66
|
>;
|
|
56
67
|
navigation: NavigationHelpers<ParamListBase>;
|
|
57
|
-
screenOptions
|
|
68
|
+
screenOptions: ScreenOptionsOrCallback<ScreenOptions> | undefined;
|
|
69
|
+
screenLayout: ScreenLayout | undefined;
|
|
58
70
|
onAction: (action: NavigationAction) => boolean;
|
|
59
71
|
getState: () => State;
|
|
60
72
|
setState: (state: State) => void;
|
|
@@ -83,6 +95,7 @@ export function useDescriptors<
|
|
|
83
95
|
screens,
|
|
84
96
|
navigation,
|
|
85
97
|
screenOptions,
|
|
98
|
+
screenLayout,
|
|
86
99
|
onAction,
|
|
87
100
|
getState,
|
|
88
101
|
setState,
|
|
@@ -92,6 +105,7 @@ export function useDescriptors<
|
|
|
92
105
|
router,
|
|
93
106
|
emitter,
|
|
94
107
|
}: Options<State, ScreenOptions, EventMap>) {
|
|
108
|
+
const theme = React.useContext(ThemeContext);
|
|
95
109
|
const [options, setOptions] = React.useState<Record<string, ScreenOptions>>(
|
|
96
110
|
{}
|
|
97
111
|
);
|
|
@@ -122,7 +136,12 @@ export function useDescriptors<
|
|
|
122
136
|
]
|
|
123
137
|
);
|
|
124
138
|
|
|
125
|
-
const navigations = useNavigationCache<
|
|
139
|
+
const { base, navigations } = useNavigationCache<
|
|
140
|
+
State,
|
|
141
|
+
ScreenOptions,
|
|
142
|
+
EventMap,
|
|
143
|
+
ActionHelpers
|
|
144
|
+
>({
|
|
126
145
|
state,
|
|
127
146
|
getState,
|
|
128
147
|
navigation,
|
|
@@ -133,27 +152,20 @@ export function useDescriptors<
|
|
|
133
152
|
|
|
134
153
|
const routes = useRouteCache(state.routes);
|
|
135
154
|
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
const getOptions = (
|
|
156
|
+
route: RouteProp<ParamListBase, string>,
|
|
157
|
+
navigation: NavigationProp<
|
|
158
|
+
ParamListBase,
|
|
138
159
|
string,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ScreenOptions,
|
|
147
|
-
EventMap
|
|
148
|
-
> &
|
|
149
|
-
ActionHelpers,
|
|
150
|
-
RouteProp<ParamListBase>
|
|
151
|
-
>
|
|
152
|
-
>
|
|
153
|
-
>((acc, route, i) => {
|
|
160
|
+
string | undefined,
|
|
161
|
+
State,
|
|
162
|
+
ScreenOptions,
|
|
163
|
+
EventMap
|
|
164
|
+
>,
|
|
165
|
+
overrides: Record<string, ScreenOptions>
|
|
166
|
+
) => {
|
|
154
167
|
const config = screens[route.name];
|
|
155
168
|
const screen = config.props;
|
|
156
|
-
const navigation = navigations[route.key];
|
|
157
169
|
|
|
158
170
|
const optionsList = [
|
|
159
171
|
// The default `screenOptions` passed to the navigator
|
|
@@ -165,18 +177,35 @@ export function useDescriptors<
|
|
|
165
177
|
// The `options` prop passed to `Screen` elements,
|
|
166
178
|
screen.options,
|
|
167
179
|
// The options set via `navigation.setOptions`
|
|
168
|
-
|
|
180
|
+
overrides,
|
|
169
181
|
];
|
|
170
182
|
|
|
171
|
-
|
|
183
|
+
return optionsList.reduce<ScreenOptions>(
|
|
172
184
|
(acc, curr) =>
|
|
173
185
|
Object.assign(
|
|
174
186
|
acc,
|
|
175
187
|
// @ts-expect-error: we check for function but TS still complains
|
|
176
|
-
typeof curr !== 'function' ? curr : curr({ route, navigation })
|
|
188
|
+
typeof curr !== 'function' ? curr : curr({ route, navigation, theme })
|
|
177
189
|
),
|
|
178
190
|
{} as ScreenOptions
|
|
179
191
|
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const render = (
|
|
195
|
+
route: RouteProp<ParamListBase, string>,
|
|
196
|
+
navigation: NavigationProp<
|
|
197
|
+
ParamListBase,
|
|
198
|
+
string,
|
|
199
|
+
string | undefined,
|
|
200
|
+
State,
|
|
201
|
+
ScreenOptions,
|
|
202
|
+
EventMap
|
|
203
|
+
>,
|
|
204
|
+
customOptions: ScreenOptions,
|
|
205
|
+
routeState: NavigationState | PartialState<NavigationState> | undefined
|
|
206
|
+
) => {
|
|
207
|
+
const config = screens[route.name];
|
|
208
|
+
const screen = config.props;
|
|
180
209
|
|
|
181
210
|
const clearOptions = () =>
|
|
182
211
|
setOptions((o) => {
|
|
@@ -189,24 +218,74 @@ export function useDescriptors<
|
|
|
189
218
|
return o;
|
|
190
219
|
});
|
|
191
220
|
|
|
192
|
-
const
|
|
221
|
+
const layout =
|
|
222
|
+
// The `layout` prop passed to `Screen` elements,
|
|
223
|
+
screen.layout ??
|
|
224
|
+
// The `screenLayout` props passed to `Group` elements
|
|
225
|
+
config.layout ??
|
|
226
|
+
// The default `screenLayout` passed to the navigator
|
|
227
|
+
screenLayout;
|
|
228
|
+
|
|
229
|
+
let element = (
|
|
230
|
+
<SceneView
|
|
231
|
+
navigation={navigation}
|
|
232
|
+
route={route}
|
|
233
|
+
screen={screen}
|
|
234
|
+
routeState={routeState}
|
|
235
|
+
getState={getState}
|
|
236
|
+
setState={setState}
|
|
237
|
+
options={customOptions}
|
|
238
|
+
clearOptions={clearOptions}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
if (layout != null) {
|
|
243
|
+
element = layout({
|
|
244
|
+
route,
|
|
245
|
+
navigation,
|
|
246
|
+
// @ts-expect-error: in practice `theme` will be defined
|
|
247
|
+
theme,
|
|
248
|
+
children: element,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return (
|
|
193
253
|
<NavigationBuilderContext.Provider key={route.key} value={context}>
|
|
194
254
|
<NavigationContext.Provider value={navigation}>
|
|
195
255
|
<NavigationRouteContext.Provider value={route}>
|
|
196
|
-
|
|
197
|
-
navigation={navigation}
|
|
198
|
-
route={route}
|
|
199
|
-
screen={screen}
|
|
200
|
-
routeState={state.routes[i].state}
|
|
201
|
-
getState={getState}
|
|
202
|
-
setState={setState}
|
|
203
|
-
options={customOptions}
|
|
204
|
-
clearOptions={clearOptions}
|
|
205
|
-
/>
|
|
256
|
+
{element}
|
|
206
257
|
</NavigationRouteContext.Provider>
|
|
207
258
|
</NavigationContext.Provider>
|
|
208
259
|
</NavigationBuilderContext.Provider>
|
|
209
260
|
);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const descriptors = routes.reduce<
|
|
264
|
+
Record<
|
|
265
|
+
string,
|
|
266
|
+
Descriptor<
|
|
267
|
+
ScreenOptions,
|
|
268
|
+
NavigationProp<
|
|
269
|
+
ParamListBase,
|
|
270
|
+
string,
|
|
271
|
+
string | undefined,
|
|
272
|
+
State,
|
|
273
|
+
ScreenOptions,
|
|
274
|
+
EventMap
|
|
275
|
+
> &
|
|
276
|
+
ActionHelpers,
|
|
277
|
+
RouteProp<ParamListBase>
|
|
278
|
+
>
|
|
279
|
+
>
|
|
280
|
+
>((acc, route, i) => {
|
|
281
|
+
const navigation = navigations[route.key];
|
|
282
|
+
const customOptions = getOptions(route, navigation, options[route.key]);
|
|
283
|
+
const element = render(
|
|
284
|
+
route,
|
|
285
|
+
navigation,
|
|
286
|
+
customOptions,
|
|
287
|
+
state.routes[i].state
|
|
288
|
+
);
|
|
210
289
|
|
|
211
290
|
acc[route.key] = {
|
|
212
291
|
route,
|
|
@@ -220,4 +299,39 @@ export function useDescriptors<
|
|
|
220
299
|
|
|
221
300
|
return acc;
|
|
222
301
|
}, {});
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Create a descriptor object for a route.
|
|
305
|
+
*
|
|
306
|
+
* @param route Route object for which the descriptor should be created
|
|
307
|
+
* @param placeholder Whether the descriptor should be a placeholder, e.g. for a route not yet in the state
|
|
308
|
+
* @returns Descriptor object
|
|
309
|
+
*/
|
|
310
|
+
const describe = (route: RouteProp<ParamListBase>, placeholder: boolean) => {
|
|
311
|
+
if (!placeholder) {
|
|
312
|
+
if (!(route.key in descriptors)) {
|
|
313
|
+
throw new Error(`Couldn't find a route with the key ${route.key}.`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return descriptors[route.key];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const navigation = base;
|
|
320
|
+
const customOptions = getOptions(route, navigation, {});
|
|
321
|
+
const element = render(route, navigation, customOptions, undefined);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
route,
|
|
325
|
+
navigation,
|
|
326
|
+
render() {
|
|
327
|
+
return element;
|
|
328
|
+
},
|
|
329
|
+
options: customOptions as ScreenOptions,
|
|
330
|
+
};
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
describe,
|
|
335
|
+
descriptors,
|
|
336
|
+
};
|
|
223
337
|
}
|
|
@@ -72,7 +72,8 @@ const getRouteConfigsFromChildren = <
|
|
|
72
72
|
State,
|
|
73
73
|
ScreenOptions,
|
|
74
74
|
EventMap
|
|
75
|
-
>['options']
|
|
75
|
+
>['options'],
|
|
76
|
+
groupLayout?: ScreenConfigWithParent<State, ScreenOptions, EventMap>['layout']
|
|
76
77
|
) => {
|
|
77
78
|
const configs = React.Children.toArray(children).reduce<
|
|
78
79
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>[]
|
|
@@ -95,6 +96,7 @@ const getRouteConfigsFromChildren = <
|
|
|
95
96
|
acc.push({
|
|
96
97
|
keys: [groupKey, child.props.navigationKey],
|
|
97
98
|
options: groupOptions,
|
|
99
|
+
layout: groupLayout,
|
|
98
100
|
props: child.props as RouteConfig<
|
|
99
101
|
ParamListBase,
|
|
100
102
|
string,
|
|
@@ -103,6 +105,7 @@ const getRouteConfigsFromChildren = <
|
|
|
103
105
|
EventMap
|
|
104
106
|
>,
|
|
105
107
|
});
|
|
108
|
+
|
|
106
109
|
return acc;
|
|
107
110
|
}
|
|
108
111
|
|
|
@@ -124,8 +127,9 @@ const getRouteConfigsFromChildren = <
|
|
|
124
127
|
child.type !== Group
|
|
125
128
|
? groupOptions
|
|
126
129
|
: groupOptions != null
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
? [...groupOptions, child.props.screenOptions]
|
|
131
|
+
: [child.props.screenOptions],
|
|
132
|
+
child.props.screenLayout ?? groupLayout
|
|
129
133
|
)
|
|
130
134
|
);
|
|
131
135
|
return acc;
|
|
@@ -146,8 +150,8 @@ const getRouteConfigsFromChildren = <
|
|
|
146
150
|
: ''
|
|
147
151
|
}`
|
|
148
152
|
: typeof child === 'object'
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
? JSON.stringify(child)
|
|
154
|
+
: `'${String(child)}'`
|
|
151
155
|
}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`
|
|
152
156
|
);
|
|
153
157
|
}, []);
|
|
@@ -259,7 +263,15 @@ export function useNavigationBuilder<
|
|
|
259
263
|
| NavigatorRoute
|
|
260
264
|
| undefined;
|
|
261
265
|
|
|
262
|
-
const {
|
|
266
|
+
const {
|
|
267
|
+
children,
|
|
268
|
+
layout,
|
|
269
|
+
screenOptions,
|
|
270
|
+
screenLayout,
|
|
271
|
+
screenListeners,
|
|
272
|
+
...rest
|
|
273
|
+
} = options;
|
|
274
|
+
|
|
263
275
|
const { current: router } = React.useRef<Router<State, any>>(
|
|
264
276
|
createRouter(rest as unknown as RouterOptions)
|
|
265
277
|
);
|
|
@@ -679,7 +691,7 @@ export function useNavigationBuilder<
|
|
|
679
691
|
getStateListeners: keyedListeners.getState,
|
|
680
692
|
});
|
|
681
693
|
|
|
682
|
-
const descriptors = useDescriptors<
|
|
694
|
+
const { describe, descriptors } = useDescriptors<
|
|
683
695
|
State,
|
|
684
696
|
ActionHelpers,
|
|
685
697
|
ScreenOptions,
|
|
@@ -689,6 +701,7 @@ export function useNavigationBuilder<
|
|
|
689
701
|
screens,
|
|
690
702
|
navigation,
|
|
691
703
|
screenOptions,
|
|
704
|
+
screenLayout,
|
|
692
705
|
onAction,
|
|
693
706
|
getState,
|
|
694
707
|
setState,
|
|
@@ -727,6 +740,7 @@ export function useNavigationBuilder<
|
|
|
727
740
|
return {
|
|
728
741
|
state,
|
|
729
742
|
navigation,
|
|
743
|
+
describe,
|
|
730
744
|
descriptors,
|
|
731
745
|
NavigationContent,
|
|
732
746
|
};
|
|
@@ -29,22 +29,25 @@ type Options<
|
|
|
29
29
|
emitter: NavigationEventEmitter<EventMap>;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
type
|
|
32
|
+
type NavigationItem<
|
|
33
33
|
State extends NavigationState,
|
|
34
34
|
ScreenOptions extends {},
|
|
35
35
|
EventMap extends Record<string, any>,
|
|
36
|
-
> =
|
|
36
|
+
> = NavigationProp<
|
|
37
|
+
ParamListBase,
|
|
37
38
|
string,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
State,
|
|
43
|
-
ScreenOptions,
|
|
44
|
-
EventMap
|
|
45
|
-
>
|
|
39
|
+
string | undefined,
|
|
40
|
+
State,
|
|
41
|
+
ScreenOptions,
|
|
42
|
+
EventMap
|
|
46
43
|
>;
|
|
47
44
|
|
|
45
|
+
type NavigationCache<
|
|
46
|
+
State extends NavigationState,
|
|
47
|
+
ScreenOptions extends {},
|
|
48
|
+
EventMap extends Record<string, any>,
|
|
49
|
+
> = Record<string, NavigationItem<State, ScreenOptions, EventMap>>;
|
|
50
|
+
|
|
48
51
|
/**
|
|
49
52
|
* Hook to cache navigation objects for each screen in the navigator.
|
|
50
53
|
* It's important to cache them to make sure navigation objects don't change between renders.
|
|
@@ -54,6 +57,7 @@ export function useNavigationCache<
|
|
|
54
57
|
State extends NavigationState,
|
|
55
58
|
ScreenOptions extends {},
|
|
56
59
|
EventMap extends Record<string, any>,
|
|
60
|
+
ActionHelpers extends Record<string, () => void>,
|
|
57
61
|
>({
|
|
58
62
|
state,
|
|
59
63
|
getState,
|
|
@@ -64,20 +68,72 @@ export function useNavigationCache<
|
|
|
64
68
|
}: Options<State, ScreenOptions, EventMap>) {
|
|
65
69
|
const { stackRef } = React.useContext(NavigationBuilderContext);
|
|
66
70
|
|
|
71
|
+
const base = React.useMemo((): NavigationItem<
|
|
72
|
+
State,
|
|
73
|
+
ScreenOptions,
|
|
74
|
+
EventMap
|
|
75
|
+
> &
|
|
76
|
+
ActionHelpers => {
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
78
|
+
const { emit, ...rest } = navigation;
|
|
79
|
+
|
|
80
|
+
const actions = {
|
|
81
|
+
...router.actionCreators,
|
|
82
|
+
...CommonActions,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const dispatch = () => {
|
|
86
|
+
throw new Error(
|
|
87
|
+
'Actions cannot be dispatched from a placeholder screen.'
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
|
|
92
|
+
(acc, name) => {
|
|
93
|
+
acc[name] = dispatch;
|
|
94
|
+
|
|
95
|
+
return acc;
|
|
96
|
+
},
|
|
97
|
+
{}
|
|
98
|
+
) as ActionHelpers;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...rest,
|
|
102
|
+
...helpers,
|
|
103
|
+
addListener: () => {
|
|
104
|
+
// Event listeners are not supported for placeholder screens
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
// Empty function
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
removeListener: () => {
|
|
111
|
+
// Event listeners are not supported for placeholder screens
|
|
112
|
+
},
|
|
113
|
+
dispatch,
|
|
114
|
+
getParent: (id?: string) => {
|
|
115
|
+
if (id !== undefined && id === rest.getId()) {
|
|
116
|
+
return base;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return rest.getParent(id);
|
|
120
|
+
},
|
|
121
|
+
setOptions: () => {
|
|
122
|
+
throw new Error('Options cannot be set from a placeholder screen.');
|
|
123
|
+
},
|
|
124
|
+
isFocused: () => false,
|
|
125
|
+
};
|
|
126
|
+
}, [navigation, router.actionCreators]);
|
|
127
|
+
|
|
67
128
|
// Cache object which holds navigation objects for each screen
|
|
68
129
|
// We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
|
|
69
130
|
// In reality, these deps will rarely change, if ever
|
|
70
131
|
const cache = React.useMemo(
|
|
71
132
|
() => ({ current: {} as NavigationCache<State, ScreenOptions, EventMap> }),
|
|
72
133
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
73
|
-
[getState, navigation, setOptions,
|
|
134
|
+
[base, getState, navigation, setOptions, emitter]
|
|
74
135
|
);
|
|
75
136
|
|
|
76
|
-
const actions = {
|
|
77
|
-
...router.actionCreators,
|
|
78
|
-
...CommonActions,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
137
|
cache.current = state.routes.reduce<
|
|
82
138
|
NavigationCache<State, ScreenOptions, EventMap>
|
|
83
139
|
>((acc, route) => {
|
|
@@ -91,9 +147,6 @@ export function useNavigationCache<
|
|
|
91
147
|
// If a cached navigation object already exists, reuse it
|
|
92
148
|
acc[route.key] = previous;
|
|
93
149
|
} else {
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
95
|
-
const { emit, ...rest } = navigation;
|
|
96
|
-
|
|
97
150
|
const dispatch = (thunk: Thunk) => {
|
|
98
151
|
const action = typeof thunk === 'function' ? thunk(getState()) : thunk;
|
|
99
152
|
|
|
@@ -124,6 +177,11 @@ export function useNavigationCache<
|
|
|
124
177
|
}
|
|
125
178
|
};
|
|
126
179
|
|
|
180
|
+
const actions = {
|
|
181
|
+
...router.actionCreators,
|
|
182
|
+
...CommonActions,
|
|
183
|
+
};
|
|
184
|
+
|
|
127
185
|
const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
|
|
128
186
|
(acc, name) => {
|
|
129
187
|
acc[name] = (...args: any) =>
|
|
@@ -138,19 +196,19 @@ export function useNavigationCache<
|
|
|
138
196
|
);
|
|
139
197
|
|
|
140
198
|
acc[route.key] = {
|
|
141
|
-
...
|
|
199
|
+
...base,
|
|
142
200
|
...helpers,
|
|
143
201
|
// FIXME: too much work to fix the types for now
|
|
144
202
|
...(emitter.create(route.key) as any),
|
|
145
203
|
dispatch: (thunk: Thunk) => withStack(() => dispatch(thunk)),
|
|
146
204
|
getParent: (id?: string) => {
|
|
147
|
-
if (id !== undefined && id ===
|
|
205
|
+
if (id !== undefined && id === base.getId()) {
|
|
148
206
|
// If the passed id is the same as the current navigation id,
|
|
149
207
|
// we return the cached navigation object for the relevant route
|
|
150
208
|
return acc[route.key];
|
|
151
209
|
}
|
|
152
210
|
|
|
153
|
-
return
|
|
211
|
+
return base.getParent(id);
|
|
154
212
|
},
|
|
155
213
|
setOptions: (options: object) => {
|
|
156
214
|
setOptions((o) => ({
|
|
@@ -175,5 +233,8 @@ export function useNavigationCache<
|
|
|
175
233
|
return acc;
|
|
176
234
|
}, {});
|
|
177
235
|
|
|
178
|
-
return
|
|
236
|
+
return {
|
|
237
|
+
base,
|
|
238
|
+
navigations: cache.current,
|
|
239
|
+
};
|
|
179
240
|
}
|