Files
Fluxup_PAP/node_modules/expo-router/build/useScreens.js
2026-03-10 16:18:05 +00:00

354 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
'use client';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useSortedScreens = useSortedScreens;
exports.getQualifiedRouteComponent = getQualifiedRouteComponent;
exports.screenOptionsFactory = screenOptionsFactory;
exports.routeToScreen = routeToScreen;
exports.getSingularId = getSingularId;
const native_1 = require("@react-navigation/native");
const react_1 = __importStar(require("react"));
const Route_1 = require("./Route");
const storeContext_1 = require("./global-state/storeContext");
const utils_1 = require("./global-state/utils");
const import_mode_1 = __importDefault(require("./import-mode"));
const ZoomTransitionEnabler_1 = require("./link/zoom/ZoomTransitionEnabler");
const zoom_transition_context_providers_1 = require("./link/zoom/zoom-transition-context-providers");
const navigationEvents_1 = require("./navigationEvents");
const utils_2 = require("./navigationEvents/utils");
const navigationParams_1 = require("./navigationParams");
const primitives_1 = require("./primitives");
const EmptyRoute_1 = require("./views/EmptyRoute");
const SuspenseFallback_1 = require("./views/SuspenseFallback");
const Try_1 = require("./views/Try");
function getSortedChildren(children, order = [], initialRouteName) {
if (!order?.length) {
return children
.sort((0, Route_1.sortRoutesWithInitial)(initialRouteName))
.map((route) => ({ route, props: {} }));
}
const entries = [...children];
const ordered = order
.map(({ name, redirect, initialParams, listeners, options, getId, dangerouslySingular: singular, }) => {
if (!entries.length) {
console.warn(`[Layout children]: Too many screens defined. Route "${name}" is extraneous.`);
return null;
}
const matchIndex = entries.findIndex((child) => child.route === name);
if (matchIndex === -1) {
console.warn(`[Layout children]: No route named "${name}" exists in nested children:`, children.map(({ route }) => route));
return null;
}
else {
// Get match and remove from entries
const match = entries[matchIndex];
entries.splice(matchIndex, 1);
// Ensure to return null after removing from entries.
if (redirect) {
if (typeof redirect === 'string') {
throw new Error(`Redirecting to a specific route is not supported yet.`);
}
return null;
}
if (getId) {
console.warn(`Deprecated: prop 'getId' on screen ${name} is deprecated. Please rename the prop to 'dangerouslySingular'`);
if (singular) {
console.warn(`Screen ${name} cannot use both getId and dangerouslySingular together.`);
}
}
else if (singular) {
// If singular is set, use it as the getId function.
if (typeof singular === 'string') {
getId = () => singular;
}
else if (typeof singular === 'function' && name) {
getId = (options) => singular(name, options.params || {});
}
else if (singular === true && name) {
getId = (options) => getSingularId(name, options);
}
}
return {
route: match,
props: { initialParams, listeners, options, getId },
};
}
})
.filter(Boolean);
// Add any remaining children
ordered.push(...entries.sort((0, Route_1.sortRoutesWithInitial)(initialRouteName)).map((route) => ({ route, props: {} })));
return ordered;
}
/**
* @returns React Navigation screens sorted by the `route` property.
*/
function useSortedScreens(order, protectedScreens, useOnlyUserDefinedScreens = false) {
const node = (0, Route_1.useRouteNode)();
const nodeChildren = node?.children ?? [];
const children = useOnlyUserDefinedScreens
? nodeChildren.filter((child) => order.some((userDefinedScreen) => userDefinedScreen.name === child.route))
: nodeChildren;
const sorted = children.length ? getSortedChildren(children, order, node?.initialRouteName) : [];
return react_1.default.useMemo(() => sorted
.filter((item) => !protectedScreens.has(item.route.route))
.map((value) => {
return routeToScreen(value.route, value.props);
}), [sorted, protectedScreens]);
}
function fromImport(value, { ErrorBoundary, ...component }) {
// If possible, add a more helpful display name for the component stack to improve debugging of React errors such as `Text strings must be rendered within a <Text> component.`.
if (component?.default && __DEV__) {
component.default.displayName ??= `${component.default.name ?? 'Route'}(${value.contextKey})`;
}
if (ErrorBoundary) {
const Wrapped = react_1.default.forwardRef((props, ref) => {
const children = react_1.default.createElement(component.default || EmptyRoute_1.EmptyRoute, {
...props,
ref,
});
return <Try_1.Try catch={ErrorBoundary}>{children}</Try_1.Try>;
});
if (__DEV__) {
Wrapped.displayName = `ErrorBoundary(${value.contextKey})`;
}
return {
default: Wrapped,
};
}
if (process.env.NODE_ENV !== 'production') {
if (typeof component.default === 'object' &&
component.default &&
Object.keys(component.default).length === 0) {
return { default: EmptyRoute_1.EmptyRoute };
}
}
return { default: component.default };
}
function fromLoadedRoute(value, res) {
if (!(res instanceof Promise)) {
return fromImport(value, res);
}
return res.then(fromImport.bind(null, value));
}
// TODO: Maybe there's a more React-y way to do this?
// Without this store, the process enters a recursive loop.
const qualifiedStore = new WeakMap();
/** Wrap the component with various enhancements and add access to child routes. */
function getQualifiedRouteComponent(value) {
if (qualifiedStore.has(value)) {
return qualifiedStore.get(value);
}
let ScreenComponent;
// TODO: This ensures sync doesn't use React.lazy, but it's not ideal.
if (import_mode_1.default === 'lazy') {
ScreenComponent = react_1.default.lazy(async () => {
const res = value.loadRoute();
return fromLoadedRoute(value, res);
});
if (__DEV__) {
ScreenComponent.displayName = `AsyncRoute(${value.route})`;
}
}
else {
const res = value.loadRoute();
ScreenComponent = fromImport(value, res).default;
}
const WrappedScreenComponent = (props) => {
(0, utils_1.useColorSchemeChangesIfNeeded)();
return <ScreenComponent {...props}/>;
};
function BaseRoute({
// Remove these React Navigation props to
// enforce usage of expo-router hooks (where the query params are correct).
route, navigation,
// Pass all other props to the component
...props }) {
const stateForPath = (0, native_1.useStateForPath)();
const isFocused = navigation.isFocused();
const store = (0, storeContext_1.useExpoRouterStore)();
if (isFocused) {
const state = navigation.getState();
const isLeaf = !(state && 'state' in state.routes[state.index]);
if (isLeaf && stateForPath)
store.setFocusedState(stateForPath);
}
(0, react_1.useEffect)(() => navigation.addListener('focus', () => {
const state = navigation.getState();
const isLeaf = !(state && 'state' in state.routes[state.index]);
// Because setFocusedState caches the route info, this call will only trigger rerenders
// if the component itself didnt rerender and the route info changed.
// Otherwise, the update from the `if` above will handle it,
// and this wont cause a redundant second update.
if (isLeaf && stateForPath)
store.setFocusedState(stateForPath);
}), [navigation]);
(0, react_1.useEffect)(() => {
return navigation.addListener('transitionEnd', (e) => {
if (!e?.data?.closing) {
// When navigating to a screen, remove the no animation param to re-enable animations
// Otherwise the navigation back would also have no animation
if ((0, navigationParams_1.hasParam)(route?.params, navigationParams_1.INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME)) {
navigation.replaceParams((0, navigationParams_1.removeParams)(route?.params, [navigationParams_1.INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME]));
}
}
});
}, [navigation]);
const isRouteType = value.type === 'route';
const hasRouteKey = !!route?.key;
return (<Route_1.Route node={value} params={route?.params}>
{navigationEvents_1.unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && (<AnalyticsListeners navigation={navigation} screenId={route.key}/>)}
<zoom_transition_context_providers_1.ZoomTransitionTargetContextProvider route={route}>
<ZoomTransitionEnabler_1.ZoomTransitionEnabler route={route}/>
<react_1.default.Suspense fallback={<SuspenseFallback_1.SuspenseFallback route={value}/>}>
<WrappedScreenComponent {...props}
// Expose the template segment path, e.g. `(home)`, `[foo]`, `index`
// the intention is to make it possible to deduce shared routes.
segment={value.route}/>
</react_1.default.Suspense>
</zoom_transition_context_providers_1.ZoomTransitionTargetContextProvider>
</Route_1.Route>);
}
if (__DEV__) {
BaseRoute.displayName = `Route(${value.route})`;
}
qualifiedStore.set(value, BaseRoute);
return BaseRoute;
}
function AnalyticsListeners({ navigation, screenId, }) {
const stateForPath = (0, native_1.useStateForPath)();
const isFirstRenderRef = react_1.default.useRef(true);
const hasBlurredRef = react_1.default.useRef(true);
const stringUrl = (0, react_1.useMemo)(() => (0, utils_2.generateStringUrlForState)(stateForPath), [stateForPath]);
if (isFirstRenderRef.current) {
isFirstRenderRef.current = false;
if (stringUrl) {
navigationEvents_1.unstable_navigationEvents.emit('pageWillRender', {
...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
screenId,
});
}
}
(0, react_1.useEffect)(() => {
if (stringUrl) {
return () => {
navigationEvents_1.unstable_navigationEvents.emit('pageRemoved', {
...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
screenId,
});
};
}
return () => { };
}, [stringUrl, screenId]);
const isFocused = navigation.isFocused();
if (isFocused && stringUrl) {
navigationEvents_1.unstable_navigationEvents.emit('pageFocused', {
...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
screenId,
});
hasBlurredRef.current = false;
}
(0, react_1.useEffect)(() => {
if (stringUrl) {
const cleanFocus = navigation.addListener('focus', () => {
// If the screen was not blurred, don't emit focused again
// hasBlurredRef will be false when the screen was initially focused
if (hasBlurredRef.current) {
navigationEvents_1.unstable_navigationEvents.emit('pageFocused', {
...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
screenId,
});
hasBlurredRef.current = false;
}
});
const cleanBlur = navigation.addListener('blur', () => {
navigationEvents_1.unstable_navigationEvents.emit('pageBlurred', {
...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
screenId,
});
hasBlurredRef.current = true;
});
return () => {
cleanFocus();
cleanBlur();
};
}
return () => { };
}, [navigation, stringUrl, screenId]);
return null;
}
function screenOptionsFactory(route, options) {
return (args) => {
// Only eager load generated components
const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;
const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions;
const dynamicResult = typeof options === 'function' ? options?.(args) : options;
const output = {
...staticResult,
...dynamicResult,
};
// Prevent generated screens from showing up in the tab bar.
if (route.internal) {
output.tabBarItemStyle = { display: 'none' };
output.tabBarButton = () => null;
// TODO: React Navigation doesn't provide a way to prevent rendering the drawer item.
output.drawerItemStyle = { height: 0, display: 'none' };
}
return output;
};
}
function routeToScreen(route, { options, getId, ...props } = {}) {
return (<primitives_1.Screen {...props} name={route.route} key={route.route} getId={getId} options={screenOptionsFactory(route, options)} getComponent={() => getQualifiedRouteComponent(route)}/>);
}
function getSingularId(name, options = {}) {
return name
.split('/')
.map((segment) => {
if (segment.startsWith('[...')) {
return options.params?.[segment.slice(4, -1)]?.join('/') || segment;
}
else if (segment.startsWith('[') && segment.endsWith(']')) {
return options.params?.[segment.slice(1, -1)] || segment;
}
else {
return segment;
}
})
.join('/');
}
//# sourceMappingURL=useScreens.js.map