import { useTheme } from '@emotion/react'; import React from 'react'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { FlatList, Platform, TouchableWithoutFeedback } from 'react-native'; import type { ItemType, TabType } from '..'; import Icon from '../../Icon'; import { isHeroIcon } from '../../Icon/utils'; import Typography from '../../Typography'; import { HeaderTabItem, HeaderTabItemIndicator, HeaderTabItemOutline, HeaderTabItemOutlineWrapper, HeaderTabItemWrapper, HeaderTabWrapper, } from '../StyledScrollableTabs'; import TabWithBadge from '../TabWithBadge'; import useInitHighlightedAnimation from './hooks/useInitHighlightedAnimation'; import useInitUnderlinedAnimation from './hooks/useInitUnderlinedAnimation'; const getTabItem = ({ item, color, active, }: { item: ItemType; color: string; active: boolean; }) => { if (isHeroIcon(item)) { return ( ); } if (typeof item === 'string') { return ( {item} ); } return item({ color }); }; export interface ScrollableTabHeaderProps extends ViewProps { /** * Callback which is called on tab press, receiving key of upcoming active Tab. */ onTabPress: (key: string) => void; /** * Current selected tab key. */ selectedIndex?: number; /** * List of Tabs to be rendered. Each Tab must have an unique key. */ tabs: TabType[]; /** * Style for the tab navigation bar. */ barStyle?: StyleProp; /** * Testing id of the component. */ testID?: string; /** * component insets */ insets?: { top: number; bottom: number; right: number; left: number; }; /** * Variant of the tab header */ variant?: 'underlined' | 'highlighted'; } const ScrollableTabHeader = ({ onTabPress, selectedIndex, tabs, barStyle, testID, insets = { top: 0, bottom: 0, right: 0, left: 0 }, variant = 'highlighted', }: ScrollableTabHeaderProps) => { const theme = useTheme(); const flatListRef = React.useRef(null); // Init underlined animation data const { underlinedTranslateX, underlinedOpacity } = useInitUnderlinedAnimation({ tabsLength: tabs.length, selectedIndex, variant, }); // Init highlighted animation data const { tabsAnims } = useInitHighlightedAnimation({ selectedIndex, tabsLength: tabs.length, variant, }); React.useEffect(() => { const timeoutHandle: number | null = null; if (selectedIndex !== undefined && selectedIndex !== -1) { flatListRef.current?.scrollToIndex({ index: selectedIndex, viewPosition: 0.5, }); } return () => { if (timeoutHandle) { clearTimeout(timeoutHandle); } }; }, [selectedIndex]); return ( testID={testID} ref={flatListRef} horizontal data={tabs} keyExtractor={(tab) => String(tab.key)} showsHorizontalScrollIndicator={false} onScrollToIndexFailed={({ index }) => { setTimeout( () => flatListRef.current?.scrollToIndex({ index, viewPosition: 0.5, }), 100 ); }} style={{ borderBottomColor: theme.__hd__.tabs.colors.headerBottom, borderBottomWidth: theme.__hd__.tabs.sizes.indicator, }} contentContainerStyle={{ paddingHorizontal: theme.__hd__.tabs.space.flatListHorizontalPadding, // Specify it here again or the indicator won't show ...(Platform.OS === 'android' && { borderBottomColor: theme.__hd__.tabs.colors.headerBottom, borderBottomWidth: theme.__hd__.tabs.sizes.indicator, }), }} renderItem={({ item: tab, index }) => { const { key, testID: tabItemTestID, activeItem, inactiveItem: originalInactiveItem, badge, } = tab; const active = selectedIndex === index; const activeAnimated = tabsAnims[index]; const outlineScale = activeAnimated.interpolate({ inputRange: [0, 1], outputRange: [0.5, 1], }); const inactiveItem = originalInactiveItem ?? activeItem; const tabItem = getTabItem({ item: active ? activeItem : inactiveItem, color: active ? theme.__hd__.tabs.colors.active : theme.__hd__.tabs.colors.inactive, active, }); return ( { onTabPress(key); }} testID={tabItemTestID} > {variant === 'highlighted' && ( )} {variant === 'underlined' && ( )} ); }} /> ); }; export default ScrollableTabHeader;