u
This commit is contained in:
1 parent
7419021927
commit
49285f1df2
16 files changed
+1184
-283
No files matched your search
@@ -0,0 +1,130 @@
|
||||
import type { ViewStyle } from 'react-native'
|
||||
import * as React from 'react'
|
||||
import { Animated, Text, TouchableOpacity, View } from 'react-native'
|
||||
import PagerView from 'react-native-pager-view'
|
||||
|
||||
export interface PagerRoute {
|
||||
key: string
|
||||
title: string
|
||||
element?: React.ReactNode
|
||||
}
|
||||
|
||||
interface PagerTabsProps {
|
||||
routes: PagerRoute[]
|
||||
left?: React.ReactNode
|
||||
right?: React.ReactNode
|
||||
initialIndex?: number
|
||||
onChange?: (key: string) => void
|
||||
indicatorWidth?: number
|
||||
|
||||
className?: string
|
||||
style?: ViewStyle
|
||||
}
|
||||
|
||||
export default function PagerTabs({
|
||||
routes,
|
||||
left,
|
||||
right,
|
||||
initialIndex = 0,
|
||||
onChange,
|
||||
indicatorWidth = 38,
|
||||
className,
|
||||
style,
|
||||
}: PagerTabsProps) {
|
||||
const pagerRef = React.useRef<PagerView>(null)
|
||||
const [index, setIndex] = React.useState(initialIndex)
|
||||
const [tabWidth, setTabWidth] = React.useState(0)
|
||||
|
||||
const scrollX = React.useRef(new Animated.Value(0)).current
|
||||
|
||||
const onTabPress = (i: number) => {
|
||||
pagerRef.current?.setPage(i)
|
||||
}
|
||||
|
||||
const onPageScroll = (e: any) => {
|
||||
const { position, offset } = e.nativeEvent
|
||||
scrollX.setValue(position + offset)
|
||||
}
|
||||
|
||||
const onPageSelected = (position: number) => {
|
||||
setIndex(position)
|
||||
if (onChange && routes[position]) {
|
||||
onChange(routes[position].key)
|
||||
}
|
||||
}
|
||||
|
||||
const tabWidthPerTab = tabWidth / routes.length
|
||||
|
||||
const indicatorTranslateX = scrollX.interpolate({
|
||||
inputRange: routes.map((_, i) => i),
|
||||
outputRange: routes.map(
|
||||
(_, i) => i * tabWidthPerTab + (tabWidthPerTab - indicatorWidth) / 2,
|
||||
),
|
||||
extrapolate: 'clamp',
|
||||
})
|
||||
|
||||
return (
|
||||
<View
|
||||
className={`${className} flex-1`}
|
||||
style={[
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{/* Header */}
|
||||
<View className="flex flex-row items-center">
|
||||
{left}
|
||||
|
||||
{/* Tabs */}
|
||||
<View
|
||||
className="flex-1 flex-row relative"
|
||||
onLayout={(e) => {
|
||||
const w = e.nativeEvent.layout.width
|
||||
if (w !== tabWidth)
|
||||
setTabWidth(w)
|
||||
}}
|
||||
>
|
||||
{routes.map((r, i) => (
|
||||
<TouchableOpacity
|
||||
key={r.key}
|
||||
className="flex-1 items-center justify-center py-1"
|
||||
onPress={() => onTabPress(i)}
|
||||
>
|
||||
<Text
|
||||
className={`text-lg ${index === i ? 'text-[#333] font-bold' : 'text-gray-500'}`}
|
||||
>
|
||||
{r.title}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
|
||||
{tabWidth > 0 && (
|
||||
<Animated.View
|
||||
className="absolute bottom-0 h-0.5 bg-black"
|
||||
style={{
|
||||
width: indicatorWidth,
|
||||
transform: [{ translateX: indicatorTranslateX }],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{right}
|
||||
</View>
|
||||
|
||||
{/* Pager */}
|
||||
<PagerView
|
||||
ref={pagerRef}
|
||||
style={{ flex: 1 }}
|
||||
initialPage={initialIndex}
|
||||
onPageScroll={onPageScroll}
|
||||
onPageSelected={e => onPageSelected(e.nativeEvent.position)}
|
||||
>
|
||||
{routes.map(r => (
|
||||
<View key={r.key} className="flex-1">
|
||||
{r.element ?? null}
|
||||
</View>
|
||||
))}
|
||||
</PagerView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user