UNPKG

expo-router

Version:

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

180 lines (178 loc) • 9.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "NativeTabsView", { enumerable: true, get: function() { return NativeTabsView; } }); function _jsxruntime() { const data = require("react/jsx-runtime"); _jsxruntime = function() { return data; }; return data; } function _react() { const data = require("react"); _react = function() { return data; }; return data; } function _reactnativesafeareacontext() { const data = require("react-native-safe-area-context"); _reactnativesafeareacontext = function() { return data; }; return data; } function _reactnativescreens() { const data = require("react-native-screens"); _reactnativescreens = function() { return data; }; return data; } const _useDeprecated = require("../useDeprecated"); const _children = require("../utils/children"); const _NativeTabsViewshared = require("./NativeTabsView.shared"); const _appearance = require("./appearance"); const _elements = require("./common/elements"); const _types = require("./types"); const _bottomAccessory = require("./utils/bottomAccessory"); const _optionsIconConverter = require("./utils/optionsIconConverter"); function NativeTabsView(props) { const { minimizeBehavior, tabs, sidebarAdaptable, preferredPlacement, nonTriggerChildren, unstable_nativeProps } = props; // `ios`/`android` are the only platform-nested keys on `TabsHostProps`. We drop the inactive // platform's slice so users writing universal code don't pass Android-only props to the iOS host. const { ios: rawIosProps, android: _ignoredRawAndroidProps, ...rawHostRestProps } = unstable_nativeProps ?? {}; const { selectedScreenKey, provenance } = (0, _NativeTabsViewshared.useSelectedScreenKey)(props); const onTabSelected = (0, _NativeTabsViewshared.useOnTabSelectedHandler)(props.onTabChange); const onTabSelectionPrevented = (0, _NativeTabsViewshared.useOnTabSelectionPreventedHandler)(props.onTabChange); const prominentScreenKey = useProminentScreenKey(tabs); const iosAppearances = tabs.map((tab)=>({ standardAppearance: (0, _appearance.createStandardAppearanceFromOptions)(tab.options), scrollEdgeAppearance: (0, _appearance.createScrollEdgeAppearanceFromOptions)(tab.options) })); const bottomAccessory = (0, _react().useMemo)(()=>(0, _children.getFirstChildOfType)(nonTriggerChildren, _elements.NativeTabsBottomAccessory), [ nonTriggerChildren ]); const bottomAccessoryFn = (0, _bottomAccessory.useBottomAccessoryFunctionFromBottomAccessories)(bottomAccessory); (0, _useDeprecated.useWarnOnce)('NativeTabs ignores `preferredPlacement` because `sidebarAdaptable` is `false`. ' + 'The placement has an effect only when the tab bar can turn into a sidebar. ' + 'Remove `sidebarAdaptable={false}` to use the placement, or remove `preferredPlacement`.', sidebarAdaptable === false && preferredPlacement !== undefined); if (process.env.NODE_ENV !== 'production' && bottomAccessory && rawIosProps && 'bottomAccessory' in rawIosProps) { console.warn('<NativeTabs.BottomAccessory> is being overridden by `unstable_nativeProps.ios.bottomAccessory`. ' + 'Either remove the `<NativeTabs.BottomAccessory>` child or stop passing `ios.bottomAccessory` via `unstable_nativeProps`.'); } const children = tabs.map((tab, index)=>/*#__PURE__*/ (0, _jsxruntime().jsx)(Screen, { name: tab.name, routeKey: tab.routeKey, options: tab.options, isFocused: selectedScreenKey === tab.name, standardAppearance: iosAppearances[index].standardAppearance, scrollEdgeAppearance: iosAppearances[index].scrollEdgeAppearance, contentRenderer: tab.contentRenderer }, tab.name)); if (children.length === 0) { return null; } // The placement works only in `tabSidebar` mode, so setting it enables that mode. const tabBarControllerMode = sidebarAdaptable === false ? 'tabBar' : sidebarAdaptable || preferredPlacement ? 'tabSidebar' : 'automatic'; return /*#__PURE__*/ (0, _jsxruntime().jsx)(TabsHostWrapper, { ios: { tabBarTintColor: props.tintColor, tabBarMinimizeBehavior: minimizeBehavior, tabBarControllerMode, tabBarSidebarPreferredPlacement: preferredPlacement, bottomAccessory: bottomAccessoryFn, tabBarHiddenAnimationEnabled: props.disableHiddenAnimation ? false : undefined, prominentScreenKey, ...rawIosProps }, tabBarHidden: props.hidden, ...rawHostRestProps, navStateRequest: { selectedScreenKey, baseProvenance: provenance }, onTabSelected: onTabSelected, onTabSelectionPrevented: onTabSelectionPrevented, children: children }); } // `tabs` holds only visible tabs, so a hidden tab can never be prominent. function useProminentScreenKey(tabs) { const [prominentTab, ...otherProminentTabs] = tabs.filter((tab)=>tab.options.prominent); (0, _useDeprecated.useWarnOnce)(`NativeTabs supports only one prominent tab, so only the "${prominentTab?.name}" tab is prominent. ` + `Remove \`prominent\` from these tabs: ${otherProminentTabs.map((tab)=>`"${tab.name}"`).join(', ')}.`, otherProminentTabs.length > 0); return prominentTab?.name; } function Screen(props) { const { options, standardAppearance, scrollEdgeAppearance, contentRenderer } = props; const shared = (0, _NativeTabsViewshared.useSharedScreenProps)(props); const selectedIcon = shared.selectedIcon ?? shared.icon; // React Native Screens requires `icon` and `selectedIcon` to resolve to the same icon type and // throws otherwise, so both states have to share a rendering mode. The two can disagree when a // color is set for only one of the states, or when `renderingMode` is set explicitly. const normalRenderingMode = (0, _optionsIconConverter.resolveIconRenderingMode)(shared.icon, standardAppearance?.stacked?.normal?.tabBarItemIconColor); const selectedRenderingMode = (0, _optionsIconConverter.resolveIconRenderingMode)(selectedIcon, standardAppearance?.stacked?.selected?.tabBarItemIconColor); if (process.env.NODE_ENV !== 'production' && normalRenderingMode && selectedRenderingMode && normalRenderingMode !== selectedRenderingMode) { console.warn(`NativeTabs does not currently support rendering icons in different modes, so the "${props.name}" tab renders both icons with the default icon's mode. ` + 'The modes disagree when an icon color applies to only one of the states — `tintColor`, `iconColor={{ selected }}`, or the `Icon` `selectedColor` prop — or when `renderingMode` is set for only one state. ' + 'To render both the same way, set a color for both states (for example `iconColor` rather than only `tintColor`), or set `renderingMode` on the `Icon`.'); } // The normal state wins, so the selected icon follows the mode the tab bar shows most of the time. const effectiveRenderingMode = normalRenderingMode ?? selectedRenderingMode; const iosIcon = (0, _optionsIconConverter.convertOptionsIconToScreensPropsIcon)(shared.icon, effectiveRenderingMode); const iosSelectedIcon = (0, _optionsIconConverter.convertOptionsIconToScreensPropsIcon)(selectedIcon, effectiveRenderingMode); const content = /*#__PURE__*/ (0, _jsxruntime().jsx)(_NativeTabsViewshared.ScreenContent, { options: options, contentRenderer: contentRenderer }); const wrappedContent = (0, _react().useMemo)(()=>/*#__PURE__*/ (0, _jsxruntime().jsx)(_reactnativesafeareacontext().SafeAreaProvider, { children: content }), [ content ]); return /*#__PURE__*/ (0, _jsxruntime().jsx)(_NativeTabsViewshared.TabsScreen, { ...shared.options, pointerEvents: shared.pointerEvents, ios: { icon: iosIcon?.platform === 'ios' ? iosIcon.icon : undefined, selectedIcon: iosSelectedIcon?.platform === 'ios' ? iosSelectedIcon.icon : undefined, standardAppearance, scrollEdgeAppearance, systemItem: options.role, // On by default: on iOS 27, UIKit detaches the search tab only while activation is on. ...options.role === 'search' ? { role: 'search', automaticallyActivatesSearch: !options.disableAutomaticSearchActivation } : {}, overrideScrollViewContentInsetAdjustmentBehavior: !options.disableAutomaticContentInsets, ...shared.nativeIosOverrides }, title: shared.title, preventNativeSelection: options.disabled, ...shared.nativeRestOverrides, screenKey: shared.screenKey, nativeID: props.routeKey ? `expo-router-tab:${props.routeKey}` : undefined, children: wrappedContent }); } const supportedTabBarMinimizeBehaviorsSet = new Set(_types.SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS); function TabsHostWrapper(props) { // TODO(@ubax): add function for validation let validatedIos = props.ios; if (validatedIos?.tabBarMinimizeBehavior) { if (!supportedTabBarMinimizeBehaviorsSet.has(validatedIos.tabBarMinimizeBehavior)) { console.warn(`Unsupported minimizeBehavior: ${validatedIos.tabBarMinimizeBehavior}. Supported values are: ${_types.SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS.map((behavior)=>`"${behavior}"`).join(', ')}`); validatedIos = { ...validatedIos, tabBarMinimizeBehavior: undefined }; } } return /*#__PURE__*/ (0, _jsxruntime().jsx)(_reactnativescreens().Tabs.Host, { ...props, ios: validatedIos }); } //# sourceMappingURL=NativeTabsView.ios.js.map