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;