expo-router
Version:
Expo Router is a file-based router for React Native and web applications.
280 lines (278 loc) • 14.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
StackRouter: null,
stackRouterOverride: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get StackRouter () {
return StackRouter;
},
get stackRouterOverride () {
return stackRouterOverride;
}
});
const _navigationParams = require("../navigationParams");
const _native = require("../react-navigation/native");
const _StackRouter = require("../react-navigation/routers/StackRouter");
const _attachRouteState = require("../react-navigation/routers/attachRouteState");
const _ensureStateType = require("../react-navigation/routers/ensureStateType");
const _stateKeys = require("../react-navigation/routers/stateKeys");
const _utils = require("../react-navigation/routers/utils");
const _getSingularId = require("../utils/getSingularId");
function isStackAction(action) {
return action.type === 'PUSH' || action.type === 'NAVIGATE' || action.type === 'POP' || action.type === 'POP_TO_TOP' || action.type === 'REPLACE' || action.type === 'PRELOAD';
}
const getPreviewKey = (action)=>{
const payload = action.payload;
return payload && '__internal__PreviewKey' in payload && typeof payload.__internal__PreviewKey === 'string' ? payload.__internal__PreviewKey : undefined;
};
const getZoomTransitionIdFromAction = (action)=>{
const allParams = !!action.payload && 'params' in action.payload && typeof action.payload.params === 'object' ? action.payload.params : undefined;
const internalParams = (0, _navigationParams.getInternalExpoRouterParams)(allParams ?? undefined);
const val = internalParams[_navigationParams.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME];
if (val && typeof val === 'string') {
return val;
}
return undefined;
};
const stackRouterOverride = (original)=>{
return {
getStateForAction: (state, action, options)=>{
state = (0, _ensureStateType.ensureStateType)(state, 'stack');
if (action.target && action.target !== state.key) {
return _native.RouterActionHandling.Unhandled;
}
if (!isStackAction(action)) {
return original.getStateForAction(state, action, options);
}
// The dynamic getId added to an action, `router.push('screen', { singular: true })`
const actionSingularOptions = action.payload && 'singular' in action.payload ? action.payload.singular : undefined;
// Handle if 'getID' or 'singular' is set.
function getIdFunction() {
// Actions can be fired by the user, so we do need to validate their structure.
if (!('payload' in action) || !action.payload || !('name' in action.payload) || typeof action.payload.name !== 'string') {
return;
}
const actionName = action.payload.name;
return(// The dynamic singular added to an action, `router.push('screen', { singular: () => 'id' })`
getActionSingularIdFn(actionSingularOptions, actionName) || // The static getId added as a prop to `<Screen singular />` or `<Screen getId={} />`
options.routeGetIdList[actionName]);
}
switch(action.type){
case 'PUSH':
case 'NAVIGATE':
{
if (!state.routeNames.includes(action.payload.name)) {
return _native.RouterActionHandling.Unhandled;
}
const getId = getIdFunction();
const id = getId?.({
params: action.payload.params
});
const activeRoutes = state.routes.slice(0, state.index + 1);
const preloadedRoutes = state.routes.slice(state.index + 1);
const previewKey = getPreviewKey(action);
const previewRoute = preloadedRoutes.find((route)=>route.key === previewKey && route.name === action.payload.name);
const currentRoute = activeRoutes[state.index];
const shouldCreateSingularRoute = action.type === 'NAVIGATE' && !previewRoute && id === undefined && currentRoute.name === action.payload.name && (0, _getSingularId.getSingularId)(currentRoute.name, {
params: currentRoute.params
}) !== (0, _getSingularId.getSingularId)(action.payload.name, {
params: action.payload.params
});
const routeGetIdList = {
...options.routeGetIdList
};
if (getId) {
routeGetIdList[action.payload.name] = getId;
}
let actionResult;
if (previewRoute) {
// Native committed this exact React child. Name/getId matching can select a
// different preload after reopen, or an active route in a nested navigator.
const route = (0, _attachRouteState.attachRouteState)({
...previewRoute,
path: (action.type === 'NAVIGATE' ? action.payload.path : undefined) ?? previewRoute.path,
params: {
...action.type === 'NAVIGATE' && action.payload.merge ? previewRoute.params : {},
...action.payload.params,
[_navigationParams.INTERNAL_EXPO_ROUTER_IS_PREVIEW_NAVIGATION_PARAM_NAME]: true
}
}, action);
actionResult = {
state: {
...state,
index: activeRoutes.length,
routes: activeRoutes.concat(route, preloadedRoutes.filter((r)=>r.key !== previewKey))
},
affectedRouteKey: route.key
};
} else if (shouldCreateSingularRoute) {
const minter = (0, _stateKeys.createRouteKeyMinter)(state);
const params = action.payload.merge ? {
...currentRoute.params,
...action.payload.params
} : action.payload.params;
const route = (0, _attachRouteState.attachRouteState)({
key: minter.mint(action.payload.name),
name: currentRoute.name,
path: action.payload.path ?? currentRoute.path,
params
}, action);
actionResult = {
state: {
...state,
routeKeySeq: minter.routeKeySeq,
index: activeRoutes.length,
routes: activeRoutes.concat(route, preloadedRoutes)
},
affectedRouteKey: route.key,
browserHistory: {
type: 'push'
}
};
} else {
actionResult = original.getStateForAction(state, action, {
...options,
routeGetIdList
});
}
if (!(0, _utils.isActionHandled)(actionResult)) {
return actionResult;
}
const affectedRouteKey = actionResult.affectedRouteKey;
const result = actionResult.state;
if (actionSingularOptions) {
const filteredState = filterSingular(result, getId);
return {
...actionResult,
state: filteredState,
browserHistory: (0, _StackRouter.getStackBrowserHistoryAction)(state, filteredState, action)
};
}
const zoomTransitionId = getZoomTransitionIdFromAction(action);
if (zoomTransitionId) {
const lastRoute = result.routes[result.index];
const modifiedLastRoute = {
...lastRoute,
params: {
...lastRoute.params,
[_navigationParams.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME]: affectedRouteKey
}
};
return {
...actionResult,
state: {
...result,
routes: result.routes.map((route, index)=>index === result.index ? modifiedLastRoute : route)
},
affectedRouteKey
};
}
return actionResult;
}
case 'PRELOAD':
{
if (!state.routeNames.includes(action.payload.name)) {
return _native.RouterActionHandling.Unhandled;
}
const actionResult = original.getStateForAction(state, action, options);
if (!(0, _utils.isActionHandled)(actionResult)) {
return actionResult;
}
const affectedRouteKey = actionResult.affectedRouteKey;
const affectedIndex = actionResult.state.routes.findIndex((route)=>route.key === affectedRouteKey);
const preloadZoomTransitionId = getZoomTransitionIdFromAction(action);
let routes = actionResult.state.routes;
if (affectedIndex > state.index + 1) {
const affectedRoute = routes[affectedIndex];
routes = routes.toSpliced(affectedIndex, 1).toSpliced(state.index + 1, 0, affectedRoute);
}
if (preloadZoomTransitionId) {
routes = routes.map((route)=>route.key === affectedRouteKey ? {
...route,
params: {
...route.params,
[_navigationParams.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME]: affectedRouteKey
}
} : route);
}
return {
...actionResult,
state: routes === actionResult.state.routes ? actionResult.state : {
...actionResult.state,
routes
},
affectedRouteKey
};
}
default:
{
return original.getStateForAction(state, action, options);
}
}
}
};
};
function getActionSingularIdFn(actionGetId, name) {
if (typeof actionGetId === 'function') {
return (options)=>actionGetId(name, options.params ?? {});
} else if (actionGetId === true) {
return (options)=>(0, _getSingularId.getSingularId)(name, options);
}
return undefined;
}
/**
* If there is a dynamic singular on an action, then we need to filter the state to only have singular screens.
* As multiples may have been added before we did the singular navigation.
*/ function filterSingular(state, getId) {
if (!state) {
return state;
}
if (!state.routes) {
return state;
}
// TODO(@kitten): This looks wrong as it's defaulting `index === 0`
const currentIndex = state.index ?? state.routes.length - 1;
const activeRoutes = state.routes.slice(0, currentIndex + 1);
const preloadedRoutes = state.routes.slice(currentIndex + 1);
const current = activeRoutes[currentIndex];
const name = current.name;
const id = getId?.({
params: current.params
});
if (!id) {
return state;
}
// TypeScript needs a type assertion here for the filter to work.
let routes = activeRoutes;
routes = routes.filter((route, index)=>{
// If the route is the current route, keep it.
if (index === currentIndex) {
return true;
}
// Remove all other routes with the same name and id.
return name !== route.name || id !== getId?.({
params: route.params
});
});
return {
...state,
index: routes.length - 1,
// Filtering preserves the input state's route type even though TypeScript widens it to partial routes.
routes: [
...routes,
...preloadedRoutes
]
};
}
const StackRouter = (0, _native.extendRouter)(_native.StackRouter, ({ baseRouter })=>stackRouterOverride(baseRouter));
//# sourceMappingURL=stack-router.js.map