import React, { useRef, useEffect, useCallback } from 'react' import { View, Text, Pressable, ScrollView, StyleSheet, ViewStyle, Dimensions, LayoutChangeEvent, } from 'react-native' import { DownArrowIcon } from '@/components/icon' const SCREEN_WIDTH = Dimensions.get('window').width const ARROW_WIDTH = 48 // 箭头按钮宽度 (padding 8 * 2 + icon ~24 + marginLeft 8) const HORIZONTAL_PADDING = 16 interface TabNavigationProps { tabs: string[] activeIndex: number onTabPress: (index: number) => void showArrow?: boolean onArrowPress?: () => void isSticky?: boolean wrapperStyle?: ViewStyle onLayout?: (y: number, height: number) => void } export function TabNavigation({ tabs, activeIndex, onTabPress, showArrow = false, onArrowPress, isSticky = false, wrapperStyle, onLayout, }: TabNavigationProps): React.ReactNode { const scrollViewRef = useRef(null) const tabLayouts = useRef<{ x: number; width: number }[]>([]) // 计算可用的滚动区域宽度 const scrollAreaWidth = SCREEN_WIDTH - HORIZONTAL_PADDING * 2 - (showArrow ? ARROW_WIDTH : 0) // 滚动到激活的 tab 使其居中 const scrollToActiveTab = useCallback((index: number) => { const layout = tabLayouts.current[index] if (!layout || !scrollViewRef.current) return // 计算让 tab 居中需要的滚动位置 const tabCenter = layout.x + layout.width / 2 const scrollX = tabCenter - scrollAreaWidth / 2 scrollViewRef.current.scrollTo({ x: Math.max(0, scrollX), animated: true, }) }, [scrollAreaWidth]) // 当 activeIndex 变化时滚动 useEffect(() => { // 延迟执行确保布局已完成 const timer = setTimeout(() => { scrollToActiveTab(activeIndex) }, 50) return () => clearTimeout(timer) }, [activeIndex, scrollToActiveTab]) // 记录每个 tab 的布局 const handleTabLayout = useCallback((index: number, event: LayoutChangeEvent) => { const { x, width } = event.nativeEvent.layout tabLayouts.current[index] = { x, width } }, []) return ( { const { y, height } = e.nativeEvent.layout onLayout?.(y, height) }} > {tabs.map((tab, index) => ( onTabPress(index)} onLayout={(e) => handleTabLayout(index, e)} > {tab} ))} {showArrow && ( )} ) } const styles = StyleSheet.create({ tabsWrapper: { paddingHorizontal: 16, paddingVertical: 8, backgroundColor: '#090A0B', }, stickyWrapper: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 100, }, tabsContainer: { flexDirection: 'row', alignItems: 'center', }, tabsScrollContent: { gap: 8, }, tab: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 20, backgroundColor: '#1C1E22', }, activeTab: { backgroundColor: '#F5F5F5', }, tabText: { color: '#F5F5F5', fontSize: 14, fontWeight: '500', }, activeTabText: { color: '#090A0B', }, tabArrow: { marginLeft: 8, padding: 8, backgroundColor: '#1C1E22', borderRadius: 20, }, })