import React, { useEffect } from 'react'; import { View, TouchableOpacity, StyleSheet, Dimensions, Platform, } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import * as LucideIcons from 'lucide-react-native'; import Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming, interpolate, Extrapolation } from 'react-native-reanimated'; import Screens from '../Screens'; import NavigationString from '../Constant/NavigationString'; import { useTheme } from '../Hooks/useTheme'; import { typography } from '../theme/typography'; // import EditableExcel from '../Screens/Tender/Component/EnvelopeCExcelPreview'; const Tab = createBottomTabNavigator(); const TabButton = ({ route, isFocused, onPress }: any) => { const { colors } = useTheme(); const icons: Record = { Home: { outline: "House", filled: "House" }, Auction: { outline: "Gavel", filled: "Gavel" }, Tender: { outline: "FileText", filled: "FileText" }, Profile: { outline: "User", filled: "User" }, }; // Lucide icons don't always have 'filled' variants in the set, so we might use the same icon // or rely on color/strokeWidth to indicate state. const iconKey = icons[route.name]?.outline || "HelpCircle"; // Fallback const IconName = LucideIcons[iconKey] as React.ElementType; const scale = useSharedValue(0); const translateY = useSharedValue(0); useEffect(() => { if (isFocused) { scale.value = withSpring(1, { damping: 12, stiffness: 100 }); translateY.value = withSpring(-4, { damping: 12, stiffness: 100 }); } else { scale.value = withTiming(0, { duration: 200 }); translateY.value = withSpring(0, { damping: 12, stiffness: 100 }); } // Intentionally runs only when isFocused changes; other deps are stable or recreated each render. // eslint-disable-next-line react-hooks/exhaustive-deps }, [isFocused]); useAnimatedStyle(() => { return { transform: [{ scale: scale.value }], opacity: interpolate(scale.value, [0, 1], [0, 1], Extrapolation.CLAMP), }; }); const animatedIconStyle = useAnimatedStyle(() => { return { transform: [{ scale: interpolate(scale.value, [0, 1], [1, 0.9]) }], // Slight shrink on press }; }); const animatedTextStyle = useAnimatedStyle(() => { const opacity = interpolate(scale.value, [0, 1], [0, 1]); const translateX = interpolate(scale.value, [0, 1], [20, 0]); // Slide in from right return { opacity, transform: [{ translateX }], display: scale.value === 0 ? 'none' : 'flex', // Hide when inactive marginLeft: 4, // Reduced space }; }); return ( {IconName && ( )} {isFocused && ( {route.name === 'BrandDay' ? 'Coupons' : route.name} )} ); }; const CustomTabBar = ({ state, navigation }: any) => { const { colors } = useTheme(); const { width: windowWidth } = Dimensions.get('window'); const TAB_BAR_WIDTH = windowWidth - 40; // 20 margin each side const TAB_PADDING_H = 10; // 5 padding left + 5 padding right const TAB_WIDTH = (TAB_BAR_WIDTH - TAB_PADDING_H) / state.routes.length; const translateX = useSharedValue(0); useEffect(() => { translateX.value = withSpring(state.index * TAB_WIDTH, { damping: 15, stiffness: 150, mass: 0.5, }); // Intentionally runs only when the selected tab index changes; other deps are stable or recreated each render. // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.index]); const animatedIndicatorStyle = useAnimatedStyle(() => { return { transform: [{ translateX: translateX.value }], }; }); return ( {state.routes.map((route: any, index: number) => { const isFocused = state.index === index; return ( navigation.navigate(route.name)} /> ); })} ); }; export default function BottomTab() { const { colors } = useTheme(); return ( } screenOptions={{ headerShown: false, tabBarHideOnKeyboard: true, // Smooth cross-fade when switching tabs instead of an instant cut. animation: 'shift', }} > {/* */} ); } const styles = StyleSheet.create({ container: { flex: 1, }, tabBar: { position: 'absolute', bottom: 20, left: 20, right: 20, flexDirection: 'row', height: 70, borderRadius: 35, elevation: 20, // stronger Android elevation shadowColor: '#000', // iOS shadow color shadowOffset: { width: 0, height: 10 }, shadowOpacity: Platform.OS === 'ios' ? 0.25 : 0.2, shadowRadius: 12, overflow: 'visible', // allow shadow to render outside rounded corners zIndex: 9999, paddingBottom: 0, justifyContent: 'space-around', alignItems: 'center', paddingHorizontal: 5, }, tabButton: { flex: 1, justifyContent: 'center', alignItems: 'center', height: '100%', }, tabContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingHorizontal: 4, // Drastically reduced from 12 paddingVertical: 4, borderRadius: 20, }, slidingIndicator: { position: 'absolute', top: 12, left: 5, height: 40, borderRadius: 30, zIndex: 0, }, label: { fontSize: 11, // Slightly smaller text textAlign: 'left', textTransform: 'capitalize', letterSpacing: 0.2, // Tighter spacing }, });