UNPKG

expo-router

Version:

Expo Router is a file-based router for React Native and web applications.

196 lines 8.12 kB
"use strict"; 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 __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tabs = Tabs; exports.useTabsWithChildren = useTabsWithChildren; exports.useTabsWithTriggers = useTabsWithTriggers; const native_1 = require("@react-navigation/native"); const react_1 = require("react"); const react_native_1 = require("react-native"); const TabContext_1 = require("./TabContext"); const TabList_1 = require("./TabList"); const TabRouter_1 = require("./TabRouter"); const TabSlot_1 = require("./TabSlot"); const TabTrigger_1 = require("./TabTrigger"); const common_1 = require("./common"); const useComponent_1 = require("./useComponent"); const Route_1 = require("../Route"); const hooks_1 = require("../hooks"); const href_1 = require("../link/href"); const url_1 = require("../utils/url"); const Navigator_1 = require("../views/Navigator"); __exportStar(require("./TabContext"), exports); __exportStar(require("./TabList"), exports); __exportStar(require("./TabSlot"), exports); __exportStar(require("./TabTrigger"), exports); /** * Root component for the headless tabs. * * @see [`useTabsWithChildren`](#usetabswithchildrenoptions) for a hook version of this component. * @example * ```tsx * <Tabs> * <TabSlot /> * <TabList> * <TabTrigger name="home" href="/" /> * </TabList> * </Tabs> * ``` */ function Tabs(props) { const { children, asChild, options, ...rest } = props; const Comp = asChild ? common_1.SafeAreaViewSlot : react_native_1.View; const { NavigationContent } = useTabsWithChildren({ // asChild adds an extra layer, so we need to process the child's children children: asChild && (0, react_1.isValidElement)(children) && children.props && typeof children.props === 'object' && 'children' in children.props ? children.props.children : children, ...options, }); return (<Comp style={styles.tabsRoot} {...rest}> <NavigationContent>{children}</NavigationContent> </Comp>); } /** * Hook version of `Tabs`. The returned NavigationContent component * should be rendered. Using the hook requires using the `<TabList />` * and `<TabTrigger />` components exported from Expo Router. * * The `useTabsWithTriggers()` hook can be used for custom components. * * @see [`Tabs`](#tabs) for the component version of this hook. * @example * ```tsx * export function MyTabs({ children }) { * const { NavigationContent } = useTabsWithChildren({ children }) * * return <NavigationContent /> * } * ``` */ function useTabsWithChildren(options) { const { children, ...rest } = options; return useTabsWithTriggers({ triggers: parseTriggersFromChildren(children), ...rest }); } /** * Alternative hook version of `Tabs` that uses explicit triggers * instead of `children`. * * @see [`Tabs`](#tabs) for the component version of this hook. * @example * ```tsx * export function MyTabs({ children }) { * const { NavigationContent } = useTabsWithChildren({ triggers: [] }) * * return <NavigationContent /> * } * ``` */ function useTabsWithTriggers(options) { const { triggers, ...rest } = options; // Ensure we extend the parent triggers, so we can trigger them as well const parentTriggerMap = (0, react_1.use)(TabContext_1.TabTriggerMapContext); const routeNode = (0, Route_1.useRouteNode)(); const contextKey = (0, Route_1.useContextKey)(); const linking = (0, react_1.use)(native_1.LinkingContext).options; const routeInfo = (0, hooks_1.useRouteInfo)(); if (!routeNode || !linking) { throw new Error('No RouteNode. This is likely a bug in expo-router.'); } const initialRouteName = routeNode.initialRouteName; const { children, triggerMap } = (0, common_1.triggersToScreens)(triggers, routeNode, linking, initialRouteName, parentTriggerMap, routeInfo, contextKey); const navigatorContext = (0, native_1.useNavigationBuilder)(TabRouter_1.ExpoTabRouter, { children, ...rest, triggerMap, id: contextKey, initialRouteName, }); const { state, descriptors, navigation, describe, NavigationContent: RNNavigationContent, } = navigatorContext; const navigatorContextValue = (0, react_1.useMemo)(() => ({ ...navigatorContext, contextKey, router: TabRouter_1.ExpoTabRouter, }), [navigatorContext, contextKey, TabRouter_1.ExpoTabRouter]); const NavigationContent = (0, useComponent_1.useComponent)((children) => (<TabContext_1.TabTriggerMapContext.Provider value={triggerMap}> <Navigator_1.NavigatorContext.Provider value={navigatorContextValue}> <RNNavigationContent>{children}</RNNavigationContent> </Navigator_1.NavigatorContext.Provider> </TabContext_1.TabTriggerMapContext.Provider>)); return { state, descriptors, navigation, NavigationContent, describe }; } function parseTriggersFromChildren(children, screenTriggers = [], isInTabList = false) { react_1.Children.forEach(children, (child) => { if (!child || !(0, react_1.isValidElement)(child) || (0, TabSlot_1.isTabSlot)(child)) { return; } if (isFragment(child) && typeof child.props.children !== 'function') { return parseTriggersFromChildren(child.props.children, screenTriggers, isInTabList || (0, TabList_1.isTabList)(child)); } if ((0, TabList_1.isTabList)(child) && typeof child.props.children !== 'function') { let children = child.props.children; // <TabList asChild /> adds an extra layer. We need to parse the child's children if (child.props.asChild && (0, react_1.isValidElement)(children) && children.props && typeof children.props === 'object' && 'children' in children.props) { children = children.props.children; } return parseTriggersFromChildren(children, screenTriggers, isInTabList || (0, TabList_1.isTabList)(child)); } // We should only process TabTriggers within the TabList. All other components will be ignored if (!isInTabList || !(0, TabTrigger_1.isTabTrigger)(child)) { return; } const { href, name } = child.props; if (!href) { if (process.env.NODE_ENV === 'development') { console.warn(`<TabTrigger name={${name}}> does not have a 'href' prop. TabTriggers within a <TabList /> are required to have an href.`); } return; } const resolvedHref = (0, href_1.resolveHref)(href); if ((0, url_1.shouldLinkExternally)(resolvedHref)) { return screenTriggers.push({ type: 'external', name, href: resolvedHref, }); } if (!name) { if (process.env.NODE_ENV === 'development') { console.warn(`<TabTrigger> does not have a 'name' prop. TabTriggers within a <TabList /> are required to have a name.`); } return; } return screenTriggers.push({ type: 'internal', href: resolvedHref, name }); }); return screenTriggers; } function isFragment(child) { return child.type === react_1.Fragment; } const styles = react_native_1.StyleSheet.create({ tabsRoot: { flex: 1, }, }); //# sourceMappingURL=Tabs.js.map