UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

134 lines (133 loc) 5.72 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 { 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 ?? [], { onlyMatching: true, protectedScreens }); if (!sorted.length) { return null; } return /* @__PURE__ */jsx(Nav, { ...options?.props, ...props, id: contextKey, ref, children: sorted }); }), { Screen }); } export { useFilterScreenChildren, useResolvedGuardedRedirects, withLayoutContext }; //# sourceMappingURL=withLayoutContext.mjs.map