import { Ionicons } from '@expo/vector-icons'; import type { BottomTabBarProps } from '@react-navigation/bottom-tabs'; import * as Haptics from 'expo-haptics'; import { Platform, Pressable, StyleSheet, View } from 'react-native'; import { AppText } from './app-text'; import { colors, radius, shadow, size, spacing } from './tokens'; const tabConfig = { index: { label: 'Today', icon: 'today-outline', activeIcon: 'today' }, timetable: { label: 'Timetable', icon: 'calendar-outline', activeIcon: 'calendar' }, attendance: { label: 'Attendance', icon: 'pie-chart-outline', activeIcon: 'pie-chart' }, settings: { label: 'Settings', icon: 'settings-outline', activeIcon: 'settings' }, } as const; /** Floating, label-forward dock that keeps the active destination prominent without covering the page in a solid bar. */ export function BottomTabBar({ state, descriptors, navigation, insets }: BottomTabBarProps) { return {state.routes.map((route, index) => { const focused = state.index === index; const config = tabConfig[route.name as keyof typeof tabConfig]; if (!config) return null; const options = descriptors[route.key].options; const label = options.tabBarLabel === undefined || typeof options.tabBarLabel === 'string' ? options.tabBarLabel ?? config.label : config.label; const onPress = () => { const event = navigation.emit({ type: 'tabPress', target: route.key, canPreventDefault: true }); if (!focused && !event.defaultPrevented) { if (Platform.OS !== 'web') void Haptics.selectionAsync(); navigation.navigate(route.name); } }; const onLongPress = () => navigation.emit({ type: 'tabLongPress', target: route.key }); return [styles.tab, focused && styles.tabActive, pressed && styles.pressed]}> {label} ; })} ; } const styles = StyleSheet.create({ floatingArea: { position: 'absolute', left: 0, right: 0, bottom: 0, paddingHorizontal: spacing[5], paddingTop: spacing[3], backgroundColor: 'transparent' }, bar: { minHeight: size.touchTargetMin + spacing[3], flexDirection: 'row', alignItems: 'center', gap: spacing[1], padding: spacing[2], borderRadius: radius.sheet, borderWidth: 1, borderColor: colors.neutral.border, backgroundColor: colors.neutral.surface, ...shadow.floating }, tab: { minHeight: size.touchTargetMin, flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 1, borderRadius: radius.pill, paddingHorizontal: spacing[2] }, tabActive: { flexDirection: 'row', flexGrow: 1.28, gap: spacing[2], backgroundColor: colors.brand.cobalt, paddingHorizontal: spacing[4] }, label: { textAlign: 'center' }, pressed: { opacity: 0.76 }, });