146 lines
4.7 KiB
JavaScript
146 lines
4.7 KiB
JavaScript
import { Motion } from "@legendapp/motion";
|
|
import * as Haptics from "expo-haptics";
|
|
import React, { useState } from "react";
|
|
import { Text, TouchableOpacity, View } from "react-native";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
import ItemContainer from "../components/ItemContainer/ItemContainer";
|
|
import useLayoutType from "../hooks/useLayoutType";
|
|
import { responsiveHeight } from "../actions/responsiveSizes";
|
|
import { gutters, Palette, Style } from "../styles";
|
|
import { FONT_FAMILY } from "../styles/Fonts";
|
|
|
|
const TabBar = ({ state = {}, descriptors = {}, navigation = {} }) => {
|
|
const { isWeb, isIOS } = useLayoutType();
|
|
const [pressedIndex, setPressedIndex] = useState(null);
|
|
const { bottom: bottomInset = 0 } = useSafeAreaInsets();
|
|
const fallbackBottomOffset = responsiveHeight(2, true);
|
|
const bottomOffset = isWeb
|
|
? gutters
|
|
: isIOS
|
|
? Math.max(bottomInset, fallbackBottomOffset)
|
|
: bottomInset + fallbackBottomOffset;
|
|
const visibleRoutesCount = Math.max(
|
|
1,
|
|
(state?.routes || []).reduce((count, route) => {
|
|
const descriptor = descriptors[route.key];
|
|
return descriptor?.options?.hide ? count : count + 1;
|
|
}, 0)
|
|
);
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
position: "absolute",
|
|
bottom: bottomOffset,
|
|
width: "90%",
|
|
maxWidth: 500,
|
|
alignSelf: "center",
|
|
zIndex: 1,
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<ItemContainer
|
|
height={64}
|
|
gradientProps={{
|
|
start: { x: 0, y: 0 },
|
|
end: { x: 1, y: 0 },
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
...Style.containerSpaceBetween,
|
|
paddingHorizontal: 4,
|
|
flex: 1,
|
|
}}
|
|
>
|
|
{state?.routes?.map((route, index) => {
|
|
const { options } = descriptors[route.key];
|
|
const isFocused = state?.index === index;
|
|
|
|
const label =
|
|
options.tabBarLabel !== undefined
|
|
? options.tabBarLabel
|
|
: options.title !== undefined
|
|
? options.title
|
|
: route.name;
|
|
|
|
const _onPressTabBar = () => {
|
|
if (!isWeb) {
|
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
}
|
|
|
|
const event = navigation.emit({
|
|
type: "tabPress",
|
|
target: route.key,
|
|
canPreventDefault: true,
|
|
});
|
|
|
|
if (!isFocused && !event.defaultPrevented) {
|
|
// The `merge: true` option makes sure that the params inside the tab screen are preserved
|
|
navigation.navigate({ name: route.name, merge: true });
|
|
}
|
|
};
|
|
|
|
const onLongPress = () => {
|
|
navigation.emit({
|
|
type: "tabLongPress",
|
|
target: route.key,
|
|
});
|
|
};
|
|
|
|
return (
|
|
!options?.hide && (
|
|
<TouchableOpacity
|
|
key={route.key}
|
|
style={{
|
|
alignSelf: "center",
|
|
width: `${100 / visibleRoutesCount}%`,
|
|
height: "100%",
|
|
...Style.containerCenter,
|
|
}}
|
|
accessibilityRole="button"
|
|
accessibilityState={isFocused ? { selected: true } : {}}
|
|
accessibilityLabel={options.tabBarAccessibilityLabel}
|
|
testID={options.tabBarTestID}
|
|
onPress={_onPressTabBar}
|
|
onLongPress={onLongPress}
|
|
onPressIn={() => setPressedIndex(index)}
|
|
onPressOut={() => setPressedIndex(null)}
|
|
>
|
|
<Motion.View
|
|
animate={{
|
|
scale: isFocused
|
|
? 1.12
|
|
: pressedIndex === index
|
|
? 1.05
|
|
: 1,
|
|
}}
|
|
transition={{ type: "spring", damping: 18, mass: 0.7 }}
|
|
style={{
|
|
...Style.containerCenter,
|
|
}}
|
|
>
|
|
{options?.tabBarIcon({ focused: isFocused })}
|
|
<Text
|
|
style={{
|
|
fontSize: isWeb ? 14 : 10,
|
|
color: isFocused ? Palette.white : "#cacacaff",
|
|
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
|
|
marginTop: 2,
|
|
}}
|
|
>
|
|
{label}
|
|
</Text>
|
|
</Motion.View>
|
|
</TouchableOpacity>
|
|
)
|
|
);
|
|
})}
|
|
</View>
|
|
</ItemContainer>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default TabBar;
|