Files
musicland/src/navigation/TabBar.js
T
2025-11-05 10:58:34 +01:00

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;