Trim down both versions
This commit is contained in:
@@ -19,7 +19,6 @@ import Animated, {
|
|||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {TabBar} from './TabBar'
|
import {TabBar} from './TabBar'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
|
|
||||||
@@ -246,7 +245,6 @@ let PagerTabBar = ({
|
|||||||
onCurrentPageSelected?: (index: number) => void
|
onCurrentPageSelected?: (index: number) => void
|
||||||
onSelect?: (index: number) => void
|
onSelect?: (index: number) => void
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const {isMobile} = useWebMediaQueries()
|
|
||||||
const headerTransform = useAnimatedStyle(() => ({
|
const headerTransform = useAnimatedStyle(() => ({
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{
|
||||||
@@ -257,10 +255,7 @@ let PagerTabBar = ({
|
|||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents="box-none"
|
pointerEvents="box-none"
|
||||||
style={[
|
style={[styles.tabBarMobile, headerTransform]}>
|
||||||
isMobile ? styles.tabBarMobile : styles.tabBarDesktop,
|
|
||||||
headerTransform,
|
|
||||||
]}>
|
|
||||||
<View onLayout={onHeaderOnlyLayout} pointerEvents="box-none">
|
<View onLayout={onHeaderOnlyLayout} pointerEvents="box-none">
|
||||||
{renderHeader?.()}
|
{renderHeader?.()}
|
||||||
</View>
|
</View>
|
||||||
@@ -333,14 +328,6 @@ const styles = StyleSheet.create({
|
|||||||
left: 0,
|
left: 0,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
tabBarDesktop: {
|
|
||||||
position: 'absolute',
|
|
||||||
zIndex: 1,
|
|
||||||
top: 0,
|
|
||||||
// @ts-ignore Web only -prf
|
|
||||||
left: 'calc(50% - 299px)',
|
|
||||||
width: 598,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function noop() {
|
function noop() {
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import {
|
import {
|
||||||
LayoutChangeEvent,
|
|
||||||
FlatList,
|
FlatList,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
NativeScrollEvent,
|
NativeScrollEvent,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import Animated, {
|
import {useSharedValue, runOnJS, useAnimatedRef} from 'react-native-reanimated'
|
||||||
useAnimatedStyle,
|
|
||||||
useSharedValue,
|
|
||||||
runOnJS,
|
|
||||||
runOnUI,
|
|
||||||
scrollTo,
|
|
||||||
useAnimatedRef,
|
|
||||||
AnimatedRef,
|
|
||||||
SharedValue,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {TabBar} from './TabBar'
|
import {TabBar} from './TabBar'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {isWeb} from '#/platform/detection'
|
|
||||||
|
|
||||||
const SCROLLED_DOWN_LIMIT = 200
|
const SCROLLED_DOWN_LIMIT = 200
|
||||||
|
|
||||||
@@ -52,7 +40,6 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||||||
children,
|
children,
|
||||||
testID,
|
testID,
|
||||||
items,
|
items,
|
||||||
isHeaderReady,
|
|
||||||
renderHeader,
|
renderHeader,
|
||||||
initialPage,
|
initialPage,
|
||||||
onPageSelected,
|
onPageSelected,
|
||||||
@@ -61,92 +48,25 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const [currentPage, setCurrentPage] = React.useState(0)
|
const [currentPage, setCurrentPage] = React.useState(0)
|
||||||
const [tabBarHeight, setTabBarHeight] = React.useState(0)
|
|
||||||
const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
|
|
||||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||||
const scrollY = useSharedValue(0)
|
const scrollY = useSharedValue(0)
|
||||||
const headerHeight = headerOnlyHeight + tabBarHeight
|
|
||||||
|
|
||||||
// capture the header bar sizing
|
|
||||||
const onTabBarLayout = React.useCallback(
|
|
||||||
(evt: LayoutChangeEvent) => {
|
|
||||||
const height = evt.nativeEvent.layout.height
|
|
||||||
if (height > 0) {
|
|
||||||
// The rounding is necessary to prevent jumps on iOS
|
|
||||||
setTabBarHeight(Math.round(height))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setTabBarHeight],
|
|
||||||
)
|
|
||||||
const onHeaderOnlyLayout = React.useCallback(
|
|
||||||
(evt: LayoutChangeEvent) => {
|
|
||||||
const height = evt.nativeEvent.layout.height
|
|
||||||
if (height > 0) {
|
|
||||||
// The rounding is necessary to prevent jumps on iOS
|
|
||||||
setHeaderOnlyHeight(Math.round(height))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setHeaderOnlyHeight],
|
|
||||||
)
|
|
||||||
|
|
||||||
const renderTabBar = React.useCallback(
|
const renderTabBar = React.useCallback(
|
||||||
(props: RenderTabBarFnProps) => {
|
(props: RenderTabBarFnProps) => {
|
||||||
return (
|
return (
|
||||||
<PagerTabBar
|
<PagerTabBar
|
||||||
headerOnlyHeight={headerOnlyHeight}
|
|
||||||
items={items}
|
items={items}
|
||||||
isHeaderReady={isHeaderReady}
|
|
||||||
renderHeader={renderHeader}
|
renderHeader={renderHeader}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
onCurrentPageSelected={onCurrentPageSelected}
|
onCurrentPageSelected={onCurrentPageSelected}
|
||||||
onTabBarLayout={onTabBarLayout}
|
|
||||||
onHeaderOnlyLayout={onHeaderOnlyLayout}
|
|
||||||
onSelect={props.onSelect}
|
onSelect={props.onSelect}
|
||||||
scrollY={scrollY}
|
|
||||||
testID={testID}
|
testID={testID}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[
|
[items, renderHeader, currentPage, onCurrentPageSelected, testID],
|
||||||
headerOnlyHeight,
|
|
||||||
items,
|
|
||||||
isHeaderReady,
|
|
||||||
renderHeader,
|
|
||||||
currentPage,
|
|
||||||
onCurrentPageSelected,
|
|
||||||
onTabBarLayout,
|
|
||||||
onHeaderOnlyLayout,
|
|
||||||
scrollY,
|
|
||||||
testID,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const scrollRefs = useSharedValue<AnimatedRef<any>[]>([])
|
|
||||||
const registerRef = (scrollRef: AnimatedRef<any>, index: number) => {
|
|
||||||
scrollRefs.modify(refs => {
|
|
||||||
'worklet'
|
|
||||||
refs[index] = scrollRef
|
|
||||||
return refs
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastForcedScrollY = useSharedValue(0)
|
|
||||||
const adjustScrollForOtherPages = () => {
|
|
||||||
'worklet'
|
|
||||||
const currentScrollY = scrollY.value
|
|
||||||
const forcedScrollY = Math.min(currentScrollY, headerOnlyHeight)
|
|
||||||
if (lastForcedScrollY.value !== forcedScrollY) {
|
|
||||||
lastForcedScrollY.value = forcedScrollY
|
|
||||||
const refs = scrollRefs.value
|
|
||||||
for (let i = 0; i < refs.length; i++) {
|
|
||||||
if (i !== currentPage) {
|
|
||||||
// This needs to run on the UI thread.
|
|
||||||
scrollTo(refs[i], 0, forcedScrollY, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const throttleTimeout = React.useRef<ReturnType<typeof setTimeout> | null>(
|
const throttleTimeout = React.useRef<ReturnType<typeof setTimeout> | null>(
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
@@ -155,15 +75,13 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||||||
throttleTimeout.current = setTimeout(() => {
|
throttleTimeout.current = setTimeout(() => {
|
||||||
throttleTimeout.current = null
|
throttleTimeout.current = null
|
||||||
|
|
||||||
runOnUI(adjustScrollForOtherPages)()
|
|
||||||
|
|
||||||
const nextIsScrolledDown = scrollY.value > SCROLLED_DOWN_LIMIT
|
const nextIsScrolledDown = scrollY.value > SCROLLED_DOWN_LIMIT
|
||||||
if (isScrolledDown !== nextIsScrolledDown) {
|
if (isScrolledDown !== nextIsScrolledDown) {
|
||||||
React.startTransition(() => {
|
React.startTransition(() => {
|
||||||
setIsScrolledDown(nextIsScrolledDown)
|
setIsScrolledDown(nextIsScrolledDown)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, 80 /* Sync often enough you're unlikely to catch it unsynced */)
|
}, 80)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -201,17 +119,12 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||||||
{toArray(children)
|
{toArray(children)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((child, i) => {
|
.map((child, i) => {
|
||||||
const isReady =
|
|
||||||
isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0
|
|
||||||
return (
|
return (
|
||||||
<View key={i} collapsable={false}>
|
<View key={i} collapsable={false}>
|
||||||
<PagerItem
|
<PagerItem
|
||||||
headerHeight={headerHeight}
|
|
||||||
isReady={isReady}
|
|
||||||
isFocused={i === currentPage}
|
isFocused={i === currentPage}
|
||||||
isScrolledDown={isScrolledDown}
|
isScrolledDown={isScrolledDown}
|
||||||
onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
|
onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
|
||||||
registerRef={(r: AnimatedRef<any>) => registerRef(r, i)}
|
|
||||||
renderTab={child}
|
renderTab={child}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -224,62 +137,23 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
|||||||
|
|
||||||
let PagerTabBar = ({
|
let PagerTabBar = ({
|
||||||
currentPage,
|
currentPage,
|
||||||
headerOnlyHeight,
|
|
||||||
isHeaderReady,
|
|
||||||
items,
|
items,
|
||||||
scrollY,
|
|
||||||
testID,
|
testID,
|
||||||
renderHeader,
|
renderHeader,
|
||||||
onHeaderOnlyLayout,
|
|
||||||
onTabBarLayout,
|
|
||||||
onCurrentPageSelected,
|
onCurrentPageSelected,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: {
|
}: {
|
||||||
currentPage: number
|
currentPage: number
|
||||||
headerOnlyHeight: number
|
|
||||||
isHeaderReady: boolean
|
|
||||||
items: string[]
|
items: string[]
|
||||||
testID?: string
|
testID?: string
|
||||||
scrollY: SharedValue<number>
|
|
||||||
renderHeader?: () => JSX.Element
|
renderHeader?: () => JSX.Element
|
||||||
onHeaderOnlyLayout: (e: LayoutChangeEvent) => void
|
|
||||||
onTabBarLayout: (e: LayoutChangeEvent) => void
|
|
||||||
onCurrentPageSelected?: (index: number) => void
|
onCurrentPageSelected?: (index: number) => void
|
||||||
onSelect?: (index: number) => void
|
onSelect?: (index: number) => void
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const {isMobile} = useWebMediaQueries()
|
|
||||||
const headerTransform = useAnimatedStyle(() => ({
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateY: Math.min(Math.min(scrollY.value, headerOnlyHeight) * -1, 0),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View
|
<View style={styles.headerContainer}>{renderHeader?.()}</View>
|
||||||
onLayout={onHeaderOnlyLayout}
|
<View style={styles.tabBarContainer}>
|
||||||
pointerEvents="box-none"
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
width: 598,
|
|
||||||
}}>
|
|
||||||
{renderHeader?.()}
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
onLayout={onTabBarLayout}
|
|
||||||
style={[
|
|
||||||
{
|
|
||||||
// Render it immediately to measure it early since its size doesn't depend on the content.
|
|
||||||
// However, keep it invisible until the header above stabilizes in order to prevent jumps.
|
|
||||||
opacity: isHeaderReady ? 1 : 0,
|
|
||||||
pointerEvents: isHeaderReady ? 'auto' : 'none',
|
|
||||||
},
|
|
||||||
isWeb ? {position: 'sticky', top: 0, zIndex: 1} : null,
|
|
||||||
{width: '100%', marginLeft: 'auto', marginRight: 'auto', width: 598},
|
|
||||||
]}>
|
|
||||||
<TabBar
|
<TabBar
|
||||||
testID={testID}
|
testID={testID}
|
||||||
items={items}
|
items={items}
|
||||||
@@ -294,36 +168,26 @@ let PagerTabBar = ({
|
|||||||
PagerTabBar = React.memo(PagerTabBar)
|
PagerTabBar = React.memo(PagerTabBar)
|
||||||
|
|
||||||
function PagerItem({
|
function PagerItem({
|
||||||
headerHeight,
|
|
||||||
isReady,
|
|
||||||
isFocused,
|
isFocused,
|
||||||
isScrolledDown,
|
isScrolledDown,
|
||||||
onScrollWorklet,
|
onScrollWorklet,
|
||||||
renderTab,
|
renderTab,
|
||||||
registerRef,
|
|
||||||
}: {
|
}: {
|
||||||
headerHeight: number
|
|
||||||
isFocused: boolean
|
isFocused: boolean
|
||||||
isReady: boolean
|
|
||||||
isScrolledDown: boolean
|
isScrolledDown: boolean
|
||||||
registerRef: (scrollRef: AnimatedRef<any>) => void
|
|
||||||
onScrollWorklet: (e: NativeScrollEvent) => void
|
onScrollWorklet: (e: NativeScrollEvent) => void
|
||||||
renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null
|
renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null
|
||||||
}) {
|
}) {
|
||||||
const scrollElRef = useAnimatedRef()
|
const scrollElRef = useAnimatedRef()
|
||||||
registerRef(scrollElRef)
|
|
||||||
|
|
||||||
const scrollHandler = React.useMemo(
|
const scrollHandler = React.useMemo(
|
||||||
() => ({onScroll: onScrollWorklet}),
|
() => ({onScroll: onScrollWorklet}),
|
||||||
[onScrollWorklet],
|
[onScrollWorklet],
|
||||||
)
|
)
|
||||||
|
if (renderTab == null) {
|
||||||
if (!isReady || renderTab == null) {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderTab({
|
return renderTab({
|
||||||
headerHeight: isWeb ? 0 : headerHeight,
|
headerHeight: 0,
|
||||||
isFocused,
|
isFocused,
|
||||||
isScrolledDown,
|
isScrolledDown,
|
||||||
onScroll: scrollHandler,
|
onScroll: scrollHandler,
|
||||||
@@ -334,16 +198,19 @@ function PagerItem({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
tabBarMobile: {
|
headerContainer: {
|
||||||
zIndex: 1,
|
marginLeft: 'auto',
|
||||||
top: 0,
|
marginRight: 'auto',
|
||||||
left: 0,
|
width: 598,
|
||||||
width: '100%',
|
|
||||||
},
|
},
|
||||||
tabBarDesktop: {
|
tabBarContainer: {
|
||||||
zIndex: 1,
|
// @ts-ignore web-only
|
||||||
|
position: 'sticky',
|
||||||
top: 0,
|
top: 0,
|
||||||
// @ts-ignore Web only -prf
|
zIndex: 1,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
width: 598,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user