152 lines
4.0 KiB
TypeScript
152 lines
4.0 KiB
TypeScript
import React from "react";
|
|
import { View, StyleSheet, TouchableOpacity, Text } from "react-native";
|
|
import { BottomTabBarProps } from "@react-navigation/bottom-tabs";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
import { useTheme } from "../contexts/ThemeContext";
|
|
|
|
export function CustomTabBar({
|
|
state,
|
|
descriptors,
|
|
navigation,
|
|
}: BottomTabBarProps) {
|
|
const { colors } = useTheme();
|
|
const insets = useSafeAreaInsets();
|
|
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.container,
|
|
{
|
|
backgroundColor: colors.surface,
|
|
borderTopColor: colors.border,
|
|
paddingBottom: insets.bottom,
|
|
},
|
|
]}
|
|
>
|
|
{state.routes.map((route, index) => {
|
|
const { options } = descriptors[route.key];
|
|
const isFocused = state.index === index;
|
|
|
|
const onPress = () => {
|
|
const event = navigation.emit({
|
|
type: "tabPress",
|
|
target: route.key,
|
|
canPreventDefault: true,
|
|
});
|
|
|
|
if (!isFocused && !event.defaultPrevented) {
|
|
navigation.navigate(route.name);
|
|
}
|
|
};
|
|
|
|
const getIconName = (
|
|
routeName: string,
|
|
focused: boolean,
|
|
): keyof typeof Ionicons.glyphMap => {
|
|
switch (routeName) {
|
|
case "index":
|
|
return focused ? "home" : "home-outline";
|
|
case "goals":
|
|
return focused ? "trophy" : "trophy-outline";
|
|
case "attendance":
|
|
return focused ? "calendar" : "calendar-outline";
|
|
case "recommendations":
|
|
return focused ? "sparkles" : "sparkles-outline";
|
|
case "profile":
|
|
return focused ? "person" : "person-outline";
|
|
default:
|
|
return "ellipse-outline";
|
|
}
|
|
};
|
|
|
|
const getLabel = (routeName: string) => {
|
|
switch (routeName) {
|
|
case "index":
|
|
return "Home";
|
|
case "goals":
|
|
return "Goals";
|
|
case "attendance":
|
|
return "Attendance";
|
|
case "recommendations":
|
|
return "Plans";
|
|
case "profile":
|
|
return "Profile";
|
|
default:
|
|
return "";
|
|
}
|
|
};
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
key={index}
|
|
accessibilityRole="button"
|
|
accessibilityState={isFocused ? { selected: true } : {}}
|
|
accessibilityLabel={options.tabBarAccessibilityLabel}
|
|
testID={(options as any).tabBarTestID}
|
|
onPress={onPress}
|
|
style={styles.tabItem}
|
|
activeOpacity={0.7}
|
|
>
|
|
<View style={styles.iconWrapper}>
|
|
<Ionicons
|
|
name={getIconName(route.name, isFocused)}
|
|
size={26}
|
|
color={isFocused ? colors.primary : colors.textTertiary}
|
|
/>
|
|
{isFocused && (
|
|
<View
|
|
style={[
|
|
styles.indicator,
|
|
{ backgroundColor: colors.primary },
|
|
]}
|
|
/>
|
|
)}
|
|
</View>
|
|
<Text
|
|
style={[
|
|
styles.label,
|
|
{
|
|
color: isFocused ? colors.primary : colors.textTertiary,
|
|
fontWeight: isFocused ? "700" : "500",
|
|
},
|
|
]}
|
|
>
|
|
{getLabel(route.name)}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: "row",
|
|
height: 70,
|
|
borderTopWidth: 1,
|
|
paddingTop: 8,
|
|
},
|
|
tabItem: {
|
|
flex: 1,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
height: "100%",
|
|
},
|
|
iconWrapper: {
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
},
|
|
indicator: {
|
|
width: 20,
|
|
height: 4,
|
|
borderRadius: 2,
|
|
marginTop: 4,
|
|
},
|
|
label: {
|
|
fontSize: 11,
|
|
marginTop: 4,
|
|
},
|
|
});
|