diff --git a/src/NavigationContext.tsx b/src/NavigationContext.tsx index 4c2342c3..65f5bd02 100644 --- a/src/NavigationContext.tsx +++ b/src/NavigationContext.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import { NavigationProp } from './types'; +import { NavigationProp, ParamListBase } from './types'; -const NavigationContext = React.createContext( - undefined -); +const NavigationContext = React.createContext< + NavigationProp | undefined +>(undefined); export default NavigationContext; diff --git a/src/SceneView.tsx b/src/SceneView.tsx index 70aaf0e3..48f5b138 100644 --- a/src/SceneView.tsx +++ b/src/SceneView.tsx @@ -5,16 +5,17 @@ import StaticContainer from './StaticContainer'; import EnsureSingleNavigator from './EnsureSingleNavigator'; import { Route, + ParamListBase, NavigationState, NavigationProp, RouteConfig, TargetRoute, } from './types'; -type Props = { - screen: RouteConfig; - navigation: NavigationProp; - route: Route & { state?: NavigationState }; +type Props = { + screen: RouteConfig; + navigation: NavigationProp; + route: Route & { state?: NavigationState }; getState: () => NavigationState; setState: (state: NavigationState) => void; setOptions: ( @@ -22,14 +23,14 @@ type Props = { ) => void; }; -export default function SceneView({ +export default function SceneView({ screen, route, navigation: helpers, getState, setState, setOptions, -}: Props) { +}: Props) { const { performTransaction } = React.useContext(NavigationStateContext); const navigation = React.useMemo( diff --git a/src/Screen.tsx b/src/Screen.tsx index 955d18a1..bc92b471 100644 --- a/src/Screen.tsx +++ b/src/Screen.tsx @@ -1,6 +1,10 @@ -import { RouteConfig } from './types'; +import { RouteConfig, ParamListBase } from './types'; -export default function Screen(_: RouteConfig) { +export default function Screen< + ParamList extends ParamListBase, + RouteName extends keyof ParamList, + ScreenOptions extends object +>(_: RouteConfig) { /* istanbul ignore next */ return null; } diff --git a/src/types.tsx b/src/types.tsx index 79ce2561..44f5c399 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -23,7 +23,7 @@ export type NavigationState = { /** * List of rendered routes. */ - routes: Array; + routes: Array & { state?: NavigationState }>; }; export type PartialState = Omit, 'key'> & { @@ -32,7 +32,7 @@ export type PartialState = Omit, 'key'> & { state?: PartialState; }; -export type Route = { +export type Route = { /** * Unique key for the route. */ @@ -51,11 +51,11 @@ export type NavigationAction = { type: string; }; -export type ActionCreators = { +export type ActionCreators = { [key: string]: (...args: any) => Action; }; -export type Router = { +export type Router = { /** * Initialize the navigation state. * @@ -150,8 +150,8 @@ class PrivateValueStore { } export type NavigationProp< - ParamList extends ParamListBase = ParamListBase, - ScreenOptions extends object = object + ParamList extends ParamListBase, + ScreenOptions extends object = {} > = { /** * Dispatch an action or an update function to the router. @@ -224,7 +224,7 @@ export type NavigationProp< export type RouteProp< ParamList extends ParamListBase, RouteName extends keyof ParamList -> = Omit, 'params'> & +> = Omit>, 'params'> & (ParamList[RouteName] extends undefined ? {} : { @@ -258,9 +258,9 @@ export type Descriptor = { }; export type RouteConfig< - ParamList extends ParamListBase = ParamListBase, - RouteName extends keyof ParamList = string, - ScreenOptions extends object = object + ParamList extends ParamListBase, + RouteName extends keyof ParamList, + ScreenOptions extends object > = { /** * Route name of this screen. @@ -274,7 +274,7 @@ export type RouteConfig< | ScreenOptions | ((props: { route: RouteProp; - navigation: NavigationProp; + navigation: NavigationProp; }) => ScreenOptions); /** diff --git a/src/useDescriptors.tsx b/src/useDescriptors.tsx index 47865ca0..d5edf625 100644 --- a/src/useDescriptors.tsx +++ b/src/useDescriptors.tsx @@ -13,9 +13,9 @@ import NavigationBuilderContext, { ChildActionListener, } from './NavigationBuilderContext'; -type Options = { +type Options = { state: NavigationState | PartialState; - screens: { [key: string]: RouteConfig }; + screens: { [key: string]: RouteConfig }; navigation: NavigationProp; onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean; getState: () => NavigationState; @@ -35,7 +35,7 @@ export default function useDescriptors({ addActionListener, removeActionListener, onRouteFocus, -}: Options) { +}: Options) { const [options, setOptions] = React.useState<{ [key: string]: object }>({}); const context = React.useMemo( () => ({ @@ -74,13 +74,13 @@ export default function useDescriptors({ ); }, options: { - ...(typeof screen.options === 'function' - ? screen.options({ + ...(typeof screen.options === 'object' || screen.options == null + ? screen.options + : screen.options({ // @ts-ignore route, navigation, - }) - : screen.options), + })), ...options[route.key], }, }; diff --git a/src/useNavigationBuilder.tsx b/src/useNavigationBuilder.tsx index 8f6e89c0..314a2716 100644 --- a/src/useNavigationBuilder.tsx +++ b/src/useNavigationBuilder.tsx @@ -5,7 +5,7 @@ import useRegisterNavigator from './useRegisterNavigator'; import useDescriptors from './useDescriptors'; import useNavigationHelpers from './useNavigationHelpers'; import useOnAction from './useOnAction'; -import { Router, NavigationState, RouteConfig } from './types'; +import { Router, NavigationState, RouteConfig, ParamListBase } from './types'; import useOnRouteFocus from './useOnRouteFocus'; import useChildActionListeners from './useChildActionListeners'; @@ -17,16 +17,26 @@ type Options = { const isArrayEqual = (a: any[], b: any[]) => a.length === b.length && a.every((it, index) => it === b[index]); -const getRouteConfigsFromChildren = (children: React.ReactNode) => - React.Children.toArray(children).reduce((acc, child) => { +const getRouteConfigsFromChildren = ( + children: React.ReactNode +) => + React.Children.toArray(children).reduce< + RouteConfig[] + >((acc, child) => { if (React.isValidElement(child)) { if (child.type === Screen) { - acc.push(child.props as RouteConfig); + acc.push(child.props as RouteConfig< + ParamListBase, + string, + ScreenOptions + >); return acc; } if (child.type === React.Fragment) { - acc.push(...getRouteConfigsFromChildren(child.props.children)); + acc.push( + ...getRouteConfigsFromChildren(child.props.children) + ); return acc; } } @@ -45,12 +55,14 @@ export default function useNavigationBuilder( ) { useRegisterNavigator(); - const screens = getRouteConfigsFromChildren(options.children).reduce( + const screens = getRouteConfigsFromChildren( + options.children + ).reduce( (acc, curr) => { acc[curr.name] = curr; return acc; }, - {} as { [key: string]: RouteConfig } + {} as { [key: string]: RouteConfig } ); const routeNames = Object.keys(screens); diff --git a/src/useNavigationHelpers.tsx b/src/useNavigationHelpers.tsx index 9cd704ac..c7801564 100644 --- a/src/useNavigationHelpers.tsx +++ b/src/useNavigationHelpers.tsx @@ -7,25 +7,26 @@ import { NavigationAction, NavigationState, ActionCreators, + ParamListBase, } from './types'; -type Options = { +type Options = { onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean; getState: () => NavigationState; setState: (state: NavigationState) => void; - actionCreators?: ActionCreators; + actionCreators?: ActionCreators; }; -export default function useNavigationHelpers({ +export default function useNavigationHelpers({ onAction, getState, setState, actionCreators, -}: Options) { +}: Options) { const parentNavigationHelpers = React.useContext(NavigationContext); const { performTransaction } = React.useContext(NavigationStateContext); - return React.useMemo((): NavigationProp => { + return React.useMemo((): NavigationProp => { const dispatch = ( action: NavigationAction | ((state: NavigationState) => NavigationState) ) => { diff --git a/src/useOnRouteFocus.tsx b/src/useOnRouteFocus.tsx index f4564d3b..664c140c 100644 --- a/src/useOnRouteFocus.tsx +++ b/src/useOnRouteFocus.tsx @@ -2,21 +2,21 @@ import * as React from 'react'; import { NavigationAction, NavigationState, Router } from './types'; import NavigationBuilderContext from './NavigationBuilderContext'; -type Options = { - router: Router; +type Options = { + router: Router; onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean; getState: () => NavigationState; setState: (state: NavigationState) => void; key?: string; }; -export default function useOnRouteFocus({ +export default function useOnRouteFocus({ router, onAction, getState, key: sourceNavigatorKey, setState, -}: Options) { +}: Options) { const { onRouteFocus: onRouteFocusParent, addActionListener: addActionListenerParent,