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