1"use strict"; 2var __importDefault = (this && this.__importDefault) || function (mod) { 3 return (mod && mod.__esModule) ? mod : { "default": mod }; 4}; 5Object.defineProperty(exports, "__esModule", { value: true }); 6exports.createGetIdForRoute = exports.getQualifiedRouteComponent = exports.useSortedScreens = void 0; 7const react_1 = __importDefault(require("react")); 8const Route_1 = require("./Route"); 9const import_mode_1 = __importDefault(require("./import-mode")); 10const primitives_1 = require("./primitives"); 11const EmptyRoute_1 = require("./views/EmptyRoute"); 12const SuspenseFallback_1 = require("./views/SuspenseFallback"); 13const Try_1 = require("./views/Try"); 14function getSortedChildren(children, order, initialRouteName) { 15 if (!order?.length) { 16 return children 17 .sort((0, Route_1.sortRoutesWithInitial)(initialRouteName)) 18 .map((route) => ({ route, props: {} })); 19 } 20 const entries = [...children]; 21 const ordered = order 22 .map(({ name, redirect, initialParams, listeners, options, getId }) => { 23 if (!entries.length) { 24 console.warn(`[Layout children]: Too many screens defined. Route "${name}" is extraneous.`); 25 return null; 26 } 27 const matchIndex = entries.findIndex((child) => child.route === name); 28 if (matchIndex === -1) { 29 console.warn(`[Layout children]: No route named "${name}" exists in nested children:`, children.map(({ route }) => route)); 30 return null; 31 } 32 else { 33 // Get match and remove from entries 34 const match = entries[matchIndex]; 35 entries.splice(matchIndex, 1); 36 // Ensure to return null after removing from entries. 37 if (redirect) { 38 if (typeof redirect === 'string') { 39 throw new Error(`Redirecting to a specific route is not supported yet.`); 40 } 41 return null; 42 } 43 return { 44 route: match, 45 props: { initialParams, listeners, options, getId }, 46 }; 47 } 48 }) 49 .filter(Boolean); 50 // Add any remaining children 51 ordered.push(...entries.sort((0, Route_1.sortRoutesWithInitial)(initialRouteName)).map((route) => ({ route, props: {} }))); 52 return ordered; 53} 54/** 55 * @returns React Navigation screens sorted by the `route` property. 56 */ 57function useSortedScreens(order) { 58 const node = (0, Route_1.useRouteNode)(); 59 const sorted = node?.children?.length 60 ? getSortedChildren(node.children, order, node.initialRouteName) 61 : []; 62 return react_1.default.useMemo(() => sorted.map((value) => routeToScreen(value.route, value.props)), [sorted]); 63} 64exports.useSortedScreens = useSortedScreens; 65function fromImport({ ErrorBoundary, ...component }) { 66 if (ErrorBoundary) { 67 return { 68 default: react_1.default.forwardRef((props, ref) => { 69 const children = react_1.default.createElement(component.default || EmptyRoute_1.EmptyRoute, { 70 ...props, 71 ref, 72 }); 73 return react_1.default.createElement(Try_1.Try, { catch: ErrorBoundary }, children); 74 }), 75 }; 76 } 77 if (process.env.NODE_ENV !== 'production') { 78 if (typeof component.default === 'object' && 79 component.default && 80 Object.keys(component.default).length === 0) { 81 return { default: EmptyRoute_1.EmptyRoute }; 82 } 83 } 84 return { default: component.default || EmptyRoute_1.EmptyRoute }; 85} 86function fromLoadedRoute(res) { 87 if (!(res instanceof Promise)) { 88 return fromImport(res); 89 } 90 return res.then(fromImport); 91} 92// TODO: Maybe there's a more React-y way to do this? 93// Without this store, the process enters a recursive loop. 94const qualifiedStore = new WeakMap(); 95/** Wrap the component with various enhancements and add access to child routes. */ 96function getQualifiedRouteComponent(value) { 97 if (qualifiedStore.has(value)) { 98 return qualifiedStore.get(value); 99 } 100 let getLoadable; 101 // TODO: This ensures sync doesn't use React.lazy, but it's not ideal. 102 if (import_mode_1.default === 'lazy') { 103 const AsyncComponent = react_1.default.lazy(async () => { 104 const res = value.loadRoute(); 105 return fromLoadedRoute(res); 106 }); 107 getLoadable = (props, ref) => (react_1.default.createElement(react_1.default.Suspense, { fallback: react_1.default.createElement(SuspenseFallback_1.SuspenseFallback, { route: value }) }, 108 react_1.default.createElement(AsyncComponent, { ...props, 109 ref, 110 // Expose the template segment path, e.g. `(home)`, `[foo]`, `index` 111 // the intention is to make it possible to deduce shared routes. 112 segment: value.route }))); 113 } 114 else { 115 const res = value.loadRoute(); 116 const Component = fromImport(res).default; 117 const SyncComponent = react_1.default.forwardRef((props, ref) => { 118 return react_1.default.createElement(Component, { ...props, ref: ref }); 119 }); 120 getLoadable = (props, ref) => (react_1.default.createElement(SyncComponent, { ...props, 121 ref, 122 // Expose the template segment path, e.g. `(home)`, `[foo]`, `index` 123 // the intention is to make it possible to deduce shared routes. 124 segment: value.route })); 125 } 126 const QualifiedRoute = react_1.default.forwardRef(({ 127 // Remove these React Navigation props to 128 // enforce usage of expo-router hooks (where the query params are correct). 129 route, navigation, 130 // Pass all other props to the component 131 ...props }, ref) => { 132 const loadable = getLoadable(props, ref); 133 return react_1.default.createElement(Route_1.Route, { node: value }, loadable); 134 }); 135 QualifiedRoute.displayName = `Route(${value.route})`; 136 qualifiedStore.set(value, QualifiedRoute); 137 return QualifiedRoute; 138} 139exports.getQualifiedRouteComponent = getQualifiedRouteComponent; 140/** @returns a function which provides a screen id that matches the dynamic route name in params. */ 141function createGetIdForRoute(route) { 142 if (!route.dynamic?.length) { 143 return undefined; 144 } 145 return ({ params }) => { 146 const getPreferredId = (segment) => { 147 // Params can be undefined when there are no params in the route. 148 const preferredId = params?.[segment.name]; 149 // If the route has a dynamic segment, use the matching parameter 150 // as the screen id. This enables pushing a screen like `/[user]` multiple times 151 // when the user is different. 152 if (preferredId) { 153 if (!Array.isArray(preferredId)) { 154 return preferredId; 155 } 156 else if (preferredId.length) { 157 // Deep dynamic routes will return as an array, so we'll join them to create a 158 // fully qualified string. 159 return preferredId.join('/'); 160 } 161 // Empty arrays... 162 } 163 return segment.deep ? `[...${segment.name}]` : `[${segment.name}]`; 164 }; 165 return route.dynamic?.map((segment) => getPreferredId(segment)).join('/'); 166 }; 167} 168exports.createGetIdForRoute = createGetIdForRoute; 169function routeToScreen(route, { options, ...props } = {}) { 170 return (react_1.default.createElement(primitives_1.Screen 171 // Users can override the screen getId function. 172 , { 173 // Users can override the screen getId function. 174 getId: createGetIdForRoute(route), ...props, name: route.route, key: route.route, options: (args) => { 175 // Only eager load generated components 176 const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null; 177 const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions; 178 const dynamicResult = typeof options === 'function' ? options?.(args) : options; 179 const output = { 180 ...staticResult, 181 ...dynamicResult, 182 }; 183 // Prevent generated screens from showing up in the tab bar. 184 if (route.generated) { 185 output.tabBarButton = () => null; 186 // TODO: React Navigation doesn't provide a way to prevent rendering the drawer item. 187 output.drawerItemStyle = { height: 0, display: 'none' }; 188 } 189 return output; 190 }, getComponent: () => getQualifiedRouteComponent(route) })); 191} 192//# sourceMappingURL=useScreens.js.map