Sync scroll to swipe
This commit is contained in:
@@ -62,6 +62,7 @@ export function HomeHeader(
|
||||
testID={props.testID}
|
||||
items={items}
|
||||
indicatorColor={pal.colors.link}
|
||||
dragGesture={props.dragGesture}
|
||||
/>
|
||||
</HomeHeaderLayout>
|
||||
)
|
||||
|
||||
@@ -99,6 +99,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
|
||||
onPageSelected(e: PagerViewOnPageSelectedEventData) {
|
||||
'worklet'
|
||||
dragPage.set(e.position)
|
||||
dragProgress.set(0)
|
||||
runOnJS(onPageSelectedJSThread)(e.position)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import {useCallback, useMemo, useRef} from 'react'
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {ScrollView, StyleSheet, View} from 'react-native'
|
||||
import Animated, {
|
||||
scrollTo,
|
||||
useAnimatedReaction,
|
||||
useAnimatedRef,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {PressableWithHover} from '../util/PressableWithHover'
|
||||
@@ -21,9 +28,10 @@ export function TabBar({
|
||||
indicatorColor,
|
||||
onSelect,
|
||||
onPressSelected,
|
||||
dragGesture,
|
||||
}: TabBarProps) {
|
||||
const pal = usePalette('default')
|
||||
const scrollElRef = useRef<ScrollView>(null)
|
||||
const scrollElRef = useAnimatedRef()
|
||||
const indicatorStyle = useMemo(
|
||||
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
||||
[indicatorColor, pal],
|
||||
@@ -39,6 +47,30 @@ export function TabBar({
|
||||
[onSelect, selectedPage, onPressSelected],
|
||||
)
|
||||
|
||||
const contentSize = useSharedValue(0)
|
||||
const containerSize = useSharedValue(0)
|
||||
const itemsLength = items.length
|
||||
const {dragPage, dragProgress} = dragGesture
|
||||
|
||||
useAnimatedReaction(
|
||||
() => dragPage.get(),
|
||||
(page, prevPage) => {
|
||||
if (page !== prevPage) {
|
||||
const offsetPerPage = contentSize.get() - containerSize.get()
|
||||
const offset = (dragPage.get() / (itemsLength - 1)) * offsetPerPage
|
||||
scrollTo(scrollElRef, offset, 0, false)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const contentStyle = useAnimatedStyle(() => {
|
||||
const offsetPerPage = contentSize.get() - containerSize.get()
|
||||
const offset = (dragProgress.get() / (itemsLength - 1)) * offsetPerPage
|
||||
return {
|
||||
transform: [{translateX: -offset}],
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<View
|
||||
testID={testID}
|
||||
@@ -49,32 +81,46 @@ export function TabBar({
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
ref={scrollElRef}
|
||||
contentContainerStyle={styles.contentContainer}>
|
||||
{items.map((item, i) => {
|
||||
const selected = i === selectedPage
|
||||
return (
|
||||
<PressableWithHover
|
||||
testID={`${testID}-selector-${i}`}
|
||||
key={`${item}-${i}`}
|
||||
style={styles.item}
|
||||
hoverStyle={pal.viewLight}
|
||||
onPress={() => onPressItem(i)}
|
||||
accessibilityRole="tab">
|
||||
<View style={[styles.itemInner, selected && indicatorStyle]}>
|
||||
<Text
|
||||
emoji
|
||||
type="lg-bold"
|
||||
testID={testID ? `${testID}-${item}` : undefined}
|
||||
style={[
|
||||
selected ? pal.text : pal.textLight,
|
||||
{lineHeight: 20},
|
||||
]}>
|
||||
{item}
|
||||
</Text>
|
||||
</View>
|
||||
</PressableWithHover>
|
||||
)
|
||||
})}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
onLayout={e => {
|
||||
containerSize.set(e.nativeEvent.layout.width)
|
||||
}}>
|
||||
<Animated.View
|
||||
onLayout={e => {
|
||||
contentSize.set(e.nativeEvent.layout.width)
|
||||
}}
|
||||
style={[
|
||||
contentStyle,
|
||||
{
|
||||
flexDirection: 'row',
|
||||
},
|
||||
]}>
|
||||
{items.map((item, i) => {
|
||||
const selected = i === selectedPage
|
||||
return (
|
||||
<PressableWithHover
|
||||
testID={`${testID}-selector-${i}`}
|
||||
key={`${item}-${i}`}
|
||||
style={styles.item}
|
||||
hoverStyle={pal.viewLight}
|
||||
onPress={() => onPressItem(i)}
|
||||
accessibilityRole="tab">
|
||||
<View style={[styles.itemInner, selected && indicatorStyle]}>
|
||||
<Text
|
||||
emoji
|
||||
type="lg-bold"
|
||||
testID={testID ? `${testID}-${item}` : undefined}
|
||||
style={[
|
||||
selected ? pal.text : pal.textLight,
|
||||
{lineHeight: 20},
|
||||
]}>
|
||||
{item}
|
||||
</Text>
|
||||
</View>
|
||||
</PressableWithHover>
|
||||
)
|
||||
})}
|
||||
</Animated.View>
|
||||
</ScrollView>
|
||||
<View style={[pal.border, styles.outerBottomBorder]} />
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user