/* eslint-disable react-native/no-inline-styles */ import * as React from 'react'; import shortid from 'shortid'; import { useNavigationBuilder, NavigationProp, CommonAction, ParamListBase, Router, createNavigator, } from '../src/index'; type Props = { initialRouteName?: string; children: React.ReactNode; }; type Action = | { type: 'PUSH'; payload: { name: string; params?: object }; } | { type: 'POP'; payload: { count: number }; } | { type: 'POP_TO_TOP' }; export type StackNavigationProp< ParamList extends ParamListBase, RouteName extends keyof ParamList = string > = NavigationProp & { /** * Push a new screen onto the stack. * * @param name Name of the route for the tab. * @param [params] Params object for the route. */ push( ...args: ParamList[RouteName] extends void ? [RouteName] : [RouteName, ParamList[RouteName]] ): void; /** * Pop a screen from the stack. */ pop(count?: number): void; /** * Pop to the first route in the stack, dismissing all other screens. */ popToTop(): void; }; const StackRouter: Router = { getInitialState({ routeNames, initialRouteName = routeNames[0], initialParamsList, }) { const index = routeNames.indexOf(initialRouteName); return { key: `stack-${shortid()}`, index, routeNames, routes: routeNames.slice(0, index + 1).map(name => ({ name, key: `${name}-${shortid()}`, params: initialParamsList[name], })), }; }, getRehydratedState({ routeNames, partialState }) { let state = partialState; if (state.routeNames === undefined || state.key === undefined) { state = { ...state, routeNames, key: `stack-${shortid()}`, }; } return state; }, getStateForAction(state, action) { switch (action.type) { case 'PUSH': if (state.routeNames.includes(action.payload.name)) { return { ...state, index: state.index + 1, routes: [ ...state.routes, { key: `${action.payload.name}-${shortid()}`, name: action.payload.name, params: action.payload.params, }, ], }; } return null; case 'POP': if (state.index > 0) { return { ...state, index: state.index - 1, routes: state.routes.slice( 0, Math.max(state.routes.length - action.payload.count, 1) ), }; } return null; case 'POP_TO_TOP': return StackRouter.getStateForAction(state, { type: 'POP', payload: { count: state.routes.length - 1 }, }); case 'NAVIGATE': if (state.routeNames.includes(action.payload.name)) { // If the route already exists, navigate to that let index = -1; if (state.routes[state.index].name === action.payload.name) { index = state.index; } else { for (let i = state.routes.length - 1; i >= 0; i--) { if (state.routes[i].name === action.payload.name) { index = i; break; } } } if (index === -1) { return StackRouter.getStateForAction(state, { type: 'PUSH', payload: action.payload, }); } return { ...state, index, routes: [ ...state.routes.slice(0, index), action.payload.params !== undefined ? { ...state.routes[index], params: { ...state.routes[index].params, ...action.payload.params, }, } : state.routes[index], ], }; } return null; case 'REPLACE': { return { ...state, routes: state.routes.map((route, i) => i === state.index ? { key: `${action.payload.name}-${shortid()}`, name: action.payload.name, params: action.payload.params, } : route ), }; } case 'GO_BACK': return StackRouter.getStateForAction(state, { type: 'POP', payload: { count: 1 }, }); case 'RESET': { if ( action.payload.key === undefined || action.payload.key === state.key ) { return { ...action.payload, key: state.key, routeNames: state.routeNames, }; } return null; } default: return null; } }, getStateForChildUpdate(state, { update, focus, key }) { const index = state.routes.findIndex(r => r.key === key); if (index === -1) { return state; } return { ...state, index: focus ? index : state.index, routes: focus ? [ ...state.routes.slice(0, index), { ...state.routes[index], state: update }, ] : state.routes.map((route, i) => i === index ? { ...route, state: update } : route ), }; }, shouldActionPropagateToChildren(action) { return action.type === 'NAVIGATE'; }, shouldActionChangeFocus(action) { return action.type === 'NAVIGATE'; }, actionCreators: { push(name: string, params?: object) { return { type: 'PUSH', payload: { name, params } }; }, pop(count: number = 1) { return { type: 'POP', payload: { count } }; }, popToTop() { return { type: 'POP_TO_TOP' }; }, }, }; export function StackNavigator(props: Props) { const { navigation, descriptors } = useNavigationBuilder(StackRouter, props); return (
{navigation.state.routes.map((route, i) => (
{descriptors[route.key].render()}
))}
{ descriptors[navigation.state.routes[navigation.state.index].key] .options.title }
); } export default createNavigator(StackNavigator);