|
1 | 1 | import { Tabs } from 'expo-router'; |
2 | 2 | import React from 'react'; |
3 | 3 | import { Ionicons } from '@expo/vector-icons'; |
| 4 | +import { Platform, StyleSheet, TouchableOpacity, View, Text } from 'react-native'; |
| 5 | +import { BlurView } from 'expo-blur'; |
| 6 | +import { useSafeAreaInsets } from 'react-native-safe-area-context'; |
| 7 | +import * as Haptics from 'expo-haptics'; |
4 | 8 |
|
5 | | -import { HapticTab } from '@/components/haptic-tab'; |
6 | | -import { Colors } from '@/constants/theme'; |
7 | | -import { useColorScheme } from '@/hooks/use-color-scheme'; |
| 9 | +import { useTheme } from '@/constants/ThemeContext'; |
| 10 | + |
| 11 | +function GlassTabBar({ state, descriptors, navigation }: any) { |
| 12 | + const theme = useTheme(); |
| 13 | + const cc = theme.colors; |
| 14 | + const insets = useSafeAreaInsets(); |
| 15 | + |
| 16 | + return ( |
| 17 | + <BlurView |
| 18 | + intensity={theme.isDark ? 40 : 60} |
| 19 | + tint={theme.isDark ? 'dark' : 'light'} |
| 20 | + style={[ |
| 21 | + styles.tabBar, |
| 22 | + { |
| 23 | + paddingBottom: Math.max(insets.bottom, 8), |
| 24 | + borderColor: cc.border, |
| 25 | + }, |
| 26 | + ]} |
| 27 | + > |
| 28 | + {state.routes.map((route: any, index: number) => { |
| 29 | + const { options } = descriptors[route.key]; |
| 30 | + const isFocused = state.index === index; |
| 31 | + const label = options.title ?? route.name; |
| 32 | + const Icon = options.tabBarIcon; |
| 33 | + |
| 34 | + const onPress = () => { |
| 35 | + if (Platform.OS === 'ios') { |
| 36 | + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); |
| 37 | + } |
| 38 | + const event = navigation.emit({ type: 'tabPress', target: route.key, canPreventDefault: true }); |
| 39 | + if (!isFocused && !event.defaultPrevented) { |
| 40 | + navigation.navigate(route.name); |
| 41 | + } |
| 42 | + }; |
| 43 | + |
| 44 | + return ( |
| 45 | + <TouchableOpacity |
| 46 | + key={route.key} |
| 47 | + onPress={onPress} |
| 48 | + style={[ |
| 49 | + styles.tabItem, |
| 50 | + isFocused && { backgroundColor: cc.accentSoft }, |
| 51 | + ]} |
| 52 | + activeOpacity={0.7} |
| 53 | + > |
| 54 | + {Icon && <Icon size={22} color={isFocused ? cc.accent : cc.textMuted} />} |
| 55 | + <Text |
| 56 | + style={[ |
| 57 | + styles.tabLabel, |
| 58 | + { color: isFocused ? cc.accent : cc.textMuted }, |
| 59 | + ]} |
| 60 | + > |
| 61 | + {label} |
| 62 | + </Text> |
| 63 | + </TouchableOpacity> |
| 64 | + ); |
| 65 | + })} |
| 66 | + </BlurView> |
| 67 | + ); |
| 68 | +} |
| 69 | + |
| 70 | +const styles = StyleSheet.create({ |
| 71 | + tabBar: { |
| 72 | + flexDirection: 'row', |
| 73 | + borderTopWidth: 1, |
| 74 | + paddingTop: 6, |
| 75 | + }, |
| 76 | + tabItem: { |
| 77 | + flex: 1, |
| 78 | + alignItems: 'center', |
| 79 | + justifyContent: 'center', |
| 80 | + gap: 3, |
| 81 | + paddingVertical: 6, |
| 82 | + borderRadius: 12, |
| 83 | + }, |
| 84 | + tabLabel: { |
| 85 | + fontSize: 11, |
| 86 | + fontWeight: '500', |
| 87 | + }, |
| 88 | +}); |
8 | 89 |
|
9 | 90 | export default function TabLayout() { |
10 | | - const colorScheme = useColorScheme(); |
| 91 | + const theme = useTheme(); |
11 | 92 |
|
12 | 93 | return ( |
13 | 94 | <Tabs |
| 95 | + tabBar={(props) => <GlassTabBar {...props} />} |
14 | 96 | screenOptions={{ |
15 | | - tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, |
16 | 97 | headerShown: false, |
17 | | - tabBarButton: HapticTab, |
18 | | - }}> |
| 98 | + }} |
| 99 | + > |
19 | 100 | <Tabs.Screen |
20 | 101 | name="index" |
21 | 102 | options={{ |
|
0 commit comments