learning_ai_invt_trdg/mobile/components/CustomTabBar.tsx

132 lines
3.4 KiB
TypeScript

import React from 'react';
import { View, Text, Pressable, StyleSheet, Platform } from 'react-native';
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
import { Activity, Layers, Clock, Cpu, FileSliders as Sliders } from 'lucide-react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Colors, Fonts, FontSize } from '@/constants/theme';
import { triggerHaptic } from '@/utils/haptics';
const TAB_ICONS = [Activity, Layers, Clock, Cpu, Sliders];
const TAB_LABELS = ['Dashboard', 'Positions', 'History', 'Strategies', 'Settings'];
function TabItem({
index,
isFocused,
onPress,
}: {
index: number;
isFocused: boolean;
onPress: () => void;
}) {
const scale = useSharedValue(1);
const Icon = TAB_ICONS[index];
const label = TAB_LABELS[index];
const animStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
React.useEffect(() => {
scale.value = withSpring(isFocused ? 1.1 : 1, { damping: 15, stiffness: 150 });
}, [isFocused]);
return (
<Pressable
style={styles.tabItem}
onPress={() => {
triggerHaptic('light');
onPress();
}}
>
<Animated.View style={[styles.iconContainer, animStyle]}>
<Icon
size={22}
color={isFocused ? Colors.accent.green : Colors.text.secondary}
strokeWidth={isFocused ? 2.5 : 1.8}
/>
{isFocused && <View style={styles.glowDot} />}
</Animated.View>
{isFocused && (
<Text style={styles.activeLabel}>{label}</Text>
)}
</Pressable>
);
}
export default function CustomTabBar({ state, navigation }: BottomTabBarProps) {
const insets = useSafeAreaInsets();
return (
<View style={[styles.container, { paddingBottom: Math.max(insets.bottom, 12) }]}>
{state.routes.map((route, index) => {
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);
}
};
return (
<TabItem
key={route.key}
index={index}
isFocused={isFocused}
onPress={onPress}
/>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
backgroundColor: Colors.background.primary,
borderTopWidth: 1,
borderTopColor: Colors.border.default,
paddingTop: 10,
},
tabItem: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 4,
},
iconContainer: {
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
},
glowDot: {
position: 'absolute',
bottom: -8,
width: 4,
height: 4,
borderRadius: 2,
backgroundColor: Colors.accent.green,
shadowColor: Colors.accent.green,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.8,
shadowRadius: 4,
elevation: 4,
},
activeLabel: {
fontFamily: Fonts.inter.extraBold,
fontSize: 9,
color: Colors.accent.green,
textTransform: 'uppercase',
letterSpacing: 1,
marginTop: 6,
},
});