UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

133 lines (130 loc) • 5.62 kB
import React from "react"; import { getContextKey, stripInvisibleSegmentsFromPath } from "../router/matchers.mjs"; import { useContextKey, useRouteNode } from "../router/Route.mjs"; import { registerProtectedRoutes, replace, resolveProtectedHref, routeInfo, unregisterProtectedRoutes } from "../router/router.mjs"; import { useSortedScreens } from "../router/useScreens.mjs"; import { sortRoutesWithInitial } from "../router/sortRoutes.mjs"; import { withStaticProperties } from "../utils/withStaticProperties.mjs"; import { isProtectedElement } from "../views/Protected.mjs"; import { Screen } from "../views/Screen.mjs"; import { StackScreen } from "./stack-utils/StackScreen.mjs"; import { IsWithinNativeNavigator } from "./NativeNavigatorContext.mjs"; import { jsx } from "react/jsx-runtime"; function useFilterScreenChildren(children, { isCustomNavigator, contextKey } = {}) { return React.useMemo(() => { const customChildren = []; const screens = []; const protectedScreens = /* @__PURE__ */ new Set(); const guardedRedirects = /* @__PURE__ */ new Map(); function flattenChild(child, exclude = false, redirectTo) { if (React.isValidElement(child) && (child.type === Screen || child.type === StackScreen)) { if (typeof child.props === "object" && child.props && "name" in child.props && !child.props.name) { throw new Error(`<Screen /> component in \`default export\` at \`app${contextKey}/_layout\` must have a \`name\` prop when used as a child of a Layout Route.`); } if (process.env.NODE_ENV !== "production") { if ([ "children", "component", "getComponent" ].some((key) => child.props && typeof child.props === "object" && key in child.props)) { throw new Error(`<Screen /> component in \`default export\` at \`app${contextKey}/_layout\` must not have a \`children\`, \`component\`, or \`getComponent\` prop when used as a child of a Layout Route`); } } const screenProps = child.props; if (exclude && screenProps.name) { protectedScreens.add(screenProps.name); guardedRedirects.set(screenProps.name, redirectTo); } else { screens.push(screenProps); } return; } if (isProtectedElement(child)) { const guardFails = !child.props.guard; const excludeChildren = exclude || guardFails; const childRedirectTo = guardFails ? child.props.redirectTo : redirectTo; React.Children.forEach(child.props.children, (nested) => { flattenChild(nested, excludeChildren, childRedirectTo); }); return; } if (isCustomNavigator) { customChildren.push(child); } else if (child != null && process.env.NODE_ENV === "development") { console.warn(`[one] a non-<Screen> child of the navigator at "app${contextKey}/_layout" renders on web only. On native this navigator is drawn by react-navigation and the child is ignored.`); } } React.Children.forEach(children, (child) => flattenChild(child)); if (process.env.NODE_ENV !== "production") { const names = screens.map((screen) => screen.name); if (names && new Set(names).size !== names.length) { throw new Error("Screen names must be unique: " + names); } } return { screens, children: customChildren, protectedScreens, guardedRedirects }; }, [ children, contextKey, isCustomNavigator ]); } function useResolvedGuardedRedirects(guardedRedirects) { const node = useRouteNode(); return React.useMemo(() => { if (!node) { return guardedRedirects; } const defaultRoute = [...node.children].sort(sortRoutesWithInitial(node.initialRouteName)).find((child) => { const normalized = child.route.replace(/\/index$/, ""); return !(guardedRedirects.has(child.route) || guardedRedirects.has(normalized)); }); const defaultHref = defaultRoute ? stripInvisibleSegmentsFromPath(getContextKey(defaultRoute.contextKey)) || "/" : void 0; return new Map(Array.from(guardedRedirects, ([name, redirectTo]) => [name, redirectTo ?? defaultHref])); }, [guardedRedirects, node]); } function withLayoutContext(Nav, processor, options) { return withStaticProperties(React.forwardRef((propsIn, ref) => { const { children, ...props } = propsIn; const contextKey = useContextKey(); const { screens, protectedScreens, guardedRedirects } = useFilterScreenChildren(children, { contextKey }); const resolvedGuardedRedirects = useResolvedGuardedRedirects(guardedRedirects); registerProtectedRoutes(contextKey, resolvedGuardedRedirects); const currentPathname = routeInfo?.pathname; const protectedHref = currentPathname ? resolveProtectedHref(currentPathname) : currentPathname; React.useEffect(() => { registerProtectedRoutes(contextKey, resolvedGuardedRedirects); return () => { unregisterProtectedRoutes(contextKey); }; }, [contextKey, resolvedGuardedRedirects]); React.useEffect(() => { if (currentPathname && protectedHref && protectedHref !== currentPathname) { replace(protectedHref); } }, [currentPathname, protectedHref]); const processed = processor ? processor(screens ?? []) : screens; const sorted = useSortedScreens(processed ?? [], { remaining: options?.undeclaredRoutes ? "authored" : "none", protectedScreens }); if (!sorted.length) { return null; } return /* @__PURE__ */ jsx(IsWithinNativeNavigator.Provider, { value: true, children: /* @__PURE__ */ jsx(Nav, { ...options?.props, ...props, ref, children: sorted }) }); }), { Screen }); } export { useFilterScreenChildren, useResolvedGuardedRedirects, withLayoutContext }; //# sourceMappingURL=withLayoutContext.mjs.map