|
const navigators = { |
|
stack: createStackNavigator, |
|
tab: createBottomTabNavigator, |
|
} as const; |
|
|
|
type NavigatorType = keyof typeof navigators; |
|
|
|
type NavigatorProps<T extends NavigatorType> = ComponentProps< |
|
ReturnType<typeof navigators[T]>['Navigator'] |
|
>; |
|
|
|
type ScreenMap = Partial< |
|
{ |
|
[key in Navigator | Screen]: { |
|
screen: ComponentType<any>; |
|
options?: StackNavigationOptions; |
|
initialParams?: key extends keyof ScreenParameterMap ? ScreenParameterMap[key] : never; |
|
}; |
|
} |
|
>; |
|
|
|
interface PartialNavigatorConfiguration< |
|
TNavigatorType extends NavigatorType, |
|
TScreens extends ScreenMap |
|
> { |
|
name: Stacks; |
|
type: TNavigatorType; |
|
screens: TScreens; |
|
linking: { |
|
path?: string; |
|
initialRouteName?: Navigator | Screen; |
|
screens: Record<keyof TScreens, string | PathConfig>; |
|
}; |
|
options?: Omit<NavigatorProps<TNavigatorType>, 'children'>; |
|
render?: ComponentType<Omit<PartialNavigatorConfiguration<TNavigatorType, TScreens>, 'render'>>; |
|
} |
|
|
|
interface NavigatorConfiguration< |
|
TNavigatorType extends NavigatorType, |
|
TScreens extends ScreenMap |
|
> extends Omit<PartialNavigatorConfiguration<TNavigatorType, TScreens>, 'render'> { |
|
render: ComponentType<any>; |
|
} |
|
|
|
export function makeNavigator< |
|
TNavigatorType extends NavigatorType, |
|
TScreens extends ScreenMap |
|
>( |
|
config: PartialNavigatorConfiguration<TNavigatorType, TScreens>, |
|
): NavigatorConfiguration<TNavigatorType, TScreens> { |
|
if (config.render) { |
|
const Component = config.render; |
|
return { |
|
...config, |
|
render: () => <Component {...config} />, |
|
}; |
|
} |
|
|
|
const Parent = navigators[config.type](); |
|
|
|
return { |
|
...config, |
|
render: () => ( |
|
<Parent.Navigator> |
|
{renderScreens(Parent, config.screens)} |
|
</Parent.Navigator> |
|
); |
|
}; |
|
} |
|
|
|
export function renderScreens<TScreens extends ScreenMap>( |
|
Navigator: TypedNavigator<any, any, any, any, any>, |
|
screens: TScreens, |
|
mapper?: ( |
|
entry: TScreens[keyof TScreens], |
|
) => Omit<ComponentProps<typeof Navigator['Screen']>, 'children'>, |
|
) { |
|
return Object.entries(screens).map(([name, entry]) => ( |
|
// @ts-ignore due to an odd union type bug... |
|
<Navigator.Screen |
|
key={name} |
|
name={name} |
|
options={entry.options} |
|
component={entry.screen} |
|
initialParams={entry.initialParams} |
|
{...mapper?.((entry as unknown) as TScreens[keyof TScreens])} |
|
/> |
|
)); |
|
} |
|
|
|
export function mergeOptions<TOptions extends NavigatorProps<keyof typeof navigators>>( |
|
...xs: Array<Partial<TOptions>> |
|
): TOptions { |
|
return xs.reduce<TOptions>((output, x) => { |
|
return { |
|
...output, |
|
...x, |
|
screenOptions: { |
|
...output.screenOptions, |
|
...x.screenOptions, |
|
}, |
|
}; |
|
}, {} as TOptions); |
|
} |