import * as React from 'react'; import { I18nManager, type LayoutChangeEvent, Platform, type StyleProp, StyleSheet, View, type ViewStyle, } from 'react-native'; import { Pager } from './Pager'; import { SceneView } from './SceneView'; import { TabBar } from './TabBar'; import type { Layout, LocaleDirection, NavigationState, PagerProps, Route, SceneRendererProps, TabDescriptor, } from './types'; export type Props = Omit & { onIndexChange: (index: number) => void; onTabSelect?: (props: { index: number }) => void; navigationState: NavigationState; renderLazyPlaceholder?: (props: { route: T }) => React.ReactNode; renderTabBar?: ( props: SceneRendererProps & { navigationState: NavigationState; options: Record> | undefined; } ) => React.ReactNode; tabBarPosition?: 'top' | 'bottom'; initialLayout?: Partial; lazy?: ((props: { route: T }) => boolean) | boolean; lazyPreloadDistance?: number; direction?: LocaleDirection; pagerStyle?: StyleProp; style?: StyleProp; renderScene: (props: SceneRendererProps & { route: T }) => React.ReactNode; options?: Record>; commonOptions?: TabDescriptor; }; const renderLazyPlaceholderDefault = () => null; export function TabView({ onIndexChange, onTabSelect, navigationState, renderScene, initialLayout, keyboardDismissMode = 'auto', lazy = false, lazyPreloadDistance = 0, onSwipeStart, onSwipeEnd, renderLazyPlaceholder = renderLazyPlaceholderDefault, // eslint-disable-next-line @eslint-react/no-unstable-default-props renderTabBar = (props) => , pagerStyle, style, direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr', swipeEnabled = true, tabBarPosition = 'top', animationEnabled = true, overScrollMode, options: sceneOptions, commonOptions, }: Props) { if ( Platform.OS !== 'web' && direction !== (I18nManager.getConstants().isRTL ? 'rtl' : 'ltr') ) { console.warn( `The 'direction' prop is set to '${direction}' but the effective value is '${ I18nManager.getConstants().isRTL ? 'rtl' : 'ltr' }'. This is not supported. Make sure to match the 'direction' prop with the writing direction of the app.` ); } const [layout, setLayout] = React.useState({ width: 0, height: 0, ...initialLayout, }); const jumpToIndex = (index: number) => { if (index !== navigationState.index) { onIndexChange(index); } }; const handleLayout = (e: LayoutChangeEvent) => { const { height, width } = e.nativeEvent.layout; setLayout((prevLayout) => { if (prevLayout.width === width && prevLayout.height === height) { return prevLayout; } return { height, width }; }); }; const options = Object.fromEntries( navigationState.routes.map((route) => [ route.key, { ...commonOptions, ...sceneOptions?.[route.key], }, ]) ); return ( {({ position, render, addEnterListener, jumpTo }) => { // All the props here must not change between re-renders // This is crucial to optimizing the routes with PureComponent const sceneRendererProps = { position, layout, jumpTo, }; return ( {tabBarPosition === 'top' && renderTabBar({ ...sceneRendererProps, options, navigationState, })} {render( navigationState.routes.map((route, i) => { const { sceneStyle } = options?.[route.key] ?? {}; return ( {({ loading }) => loading ? renderLazyPlaceholder({ route }) : renderScene({ ...sceneRendererProps, route, }) } ); }) )} {tabBarPosition === 'bottom' && renderTabBar({ ...sceneRendererProps, options, navigationState, })} ); }} ); } const styles = StyleSheet.create({ pager: { flex: 1, overflow: 'hidden', }, });