import type { LayoutChangeEvent, } from 'react-native' import React, { useEffect, useRef, useState } from 'react' import { ScrollView, Text, TouchableWithoutFeedback, View, } from 'react-native' interface TabItem { label: string key: string | number content?: React.ReactNode } interface TabProps { data?: TabItem[] defaultKey?: TabItem['key'] onChange?: (key: TabItem['key']) => void } export default function Tab({ data, defaultKey, onChange, }: TabProps) { if (!data || data.length === 0) return null /* ================= state ================= */ const initialKey = defaultKey ?? data[0].key const [selectedKey, setSelectedKey] = useState(initialKey) /* ================= refs ================= */ const scrollRef = useRef(null) // 每个 tab 的 layout 信息 const itemLayouts = useRef< Record< TabItem['key'], { x: number, width: number } > >({}) const containerWidth = useRef(0) /* ================= handlers ================= */ const handlePress = (key: TabItem['key']) => { setSelectedKey(key) onChange?.(key) scrollToCenter(key) } const scrollToCenter = (key: TabItem['key']) => { const layout = itemLayouts.current[key] if (!layout || !scrollRef.current) return const offset = layout.x + layout.width / 2 - containerWidth.current / 2 scrollRef.current.scrollTo({ x: Math.max(0, offset), animated: true, }) } /* ================= effects ================= */ useEffect(() => { // 首次挂载后,让默认选中项居中 requestAnimationFrame(() => { scrollToCenter(selectedKey) }) }, []) /* ================= render ================= */ return ( { containerWidth.current = e.nativeEvent.layout.width }} contentContainerStyle={{ flexDirection: 'row', gap: 8, }} > {data.map((item) => { const isActive = item.key === selectedKey return ( handlePress(item.key)} > { const { x, width } = e.nativeEvent.layout itemLayouts.current[item.key] = { x, width } }} className={ isActive ? 'items-center justify-center rounded-md px-4 py-2 bg-gray-800' : 'items-center justify-center rounded-md px-4 py-2 bg-gray-200' } > {item.label} ) })} ) }