Scroll restore for tabs
This commit is contained in:
@@ -20,6 +20,7 @@ export const Pager = React.forwardRef(function PagerImpl(
|
||||
children,
|
||||
tabBarPosition = 'top',
|
||||
initialPage = 0,
|
||||
headerOnlyHeight = 0,
|
||||
renderTabBar,
|
||||
onPageSelected,
|
||||
onPageSelecting,
|
||||
@@ -27,6 +28,7 @@ export const Pager = React.forwardRef(function PagerImpl(
|
||||
ref,
|
||||
) {
|
||||
const [selectedPage, setSelectedPage] = React.useState(initialPage)
|
||||
const scrollYs = React.useRef([])
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
setPage: (index: number) => setSelectedPage(index),
|
||||
@@ -34,11 +36,25 @@ export const Pager = React.forwardRef(function PagerImpl(
|
||||
|
||||
const onTabBarSelect = React.useCallback(
|
||||
(index: number) => {
|
||||
const scrollY = window.scrollY
|
||||
scrollYs.current[selectedPage] = scrollY
|
||||
setSelectedPage(index)
|
||||
onPageSelected?.(index)
|
||||
onPageSelecting?.(index)
|
||||
if (scrollY >= headerOnlyHeight) {
|
||||
window.scrollTo(
|
||||
0,
|
||||
Math.max(headerOnlyHeight, scrollYs.current[index] ?? 0),
|
||||
)
|
||||
}
|
||||
},
|
||||
[setSelectedPage, onPageSelected, onPageSelecting],
|
||||
[
|
||||
selectedPage,
|
||||
setSelectedPage,
|
||||
onPageSelected,
|
||||
onPageSelecting,
|
||||
headerOnlyHeight,
|
||||
],
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -39,6 +39,17 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
ref,
|
||||
) {
|
||||
const [currentPage, setCurrentPage] = React.useState(0)
|
||||
const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
|
||||
|
||||
const onHeaderOnlyLayout = React.useCallback(
|
||||
(evt: LayoutChangeEvent) => {
|
||||
const height = evt.nativeEvent.layout.height
|
||||
if (height > 0) {
|
||||
setHeaderOnlyHeight(Math.round(height))
|
||||
}
|
||||
},
|
||||
[setHeaderOnlyHeight],
|
||||
)
|
||||
|
||||
const renderTabBar = React.useCallback(
|
||||
(props: RenderTabBarFnProps) => {
|
||||
@@ -48,12 +59,20 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
renderHeader={renderHeader}
|
||||
currentPage={currentPage}
|
||||
onCurrentPageSelected={onCurrentPageSelected}
|
||||
onHeaderOnlyLayout={onHeaderOnlyLayout}
|
||||
onSelect={props.onSelect}
|
||||
testID={testID}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[items, renderHeader, currentPage, onCurrentPageSelected, testID],
|
||||
[
|
||||
items,
|
||||
renderHeader,
|
||||
currentPage,
|
||||
onCurrentPageSelected,
|
||||
onHeaderOnlyLayout,
|
||||
testID,
|
||||
],
|
||||
)
|
||||
|
||||
const onPageSelectedInner = React.useCallback(
|
||||
@@ -76,6 +95,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
onPageSelected={onPageSelectedInner}
|
||||
onPageSelecting={onPageSelecting}
|
||||
renderTabBar={renderTabBar}
|
||||
headerOnlyHeight={headerOnlyHeight}
|
||||
tabBarPosition="top">
|
||||
{toArray(children)
|
||||
.filter(Boolean)
|
||||
@@ -97,6 +117,7 @@ let PagerTabBar = ({
|
||||
testID,
|
||||
renderHeader,
|
||||
onCurrentPageSelected,
|
||||
onHeaderOnlyLayout,
|
||||
onSelect,
|
||||
}: {
|
||||
currentPage: number
|
||||
@@ -110,7 +131,9 @@ let PagerTabBar = ({
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
return (
|
||||
<>
|
||||
<View style={[!isMobile && styles.headerContainerDesktop, pal.border]}>
|
||||
<View
|
||||
style={[!isMobile && styles.headerContainerDesktop, pal.border]}
|
||||
onLayout={onHeaderOnlyLayout}>
|
||||
{renderHeader?.()}
|
||||
</View>
|
||||
<View
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import React, {useCallback, useEffect} from 'react'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
import {NativeScrollEvent} from 'react-native'
|
||||
import {useSetMinimalShellMode, useMinimalShellMode} from '#/state/shell'
|
||||
@@ -18,6 +19,15 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||
const startDragOffset = useSharedValue<number | null>(null)
|
||||
const startMode = useSharedValue<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isWeb) {
|
||||
return listenToForcedWindowScroll(() => {
|
||||
startDragOffset.value = null
|
||||
startMode.value = null
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const onBeginDrag = useCallback(
|
||||
(e: NativeScrollEvent) => {
|
||||
'worklet'
|
||||
@@ -95,3 +105,26 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||
</ScrollProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
|
||||
if (isWeb) {
|
||||
const originalScroll = window.scroll
|
||||
window.scroll = function () {
|
||||
emitter.emit('forced-scroll')
|
||||
return originalScroll.apply(this, arguments as any)
|
||||
}
|
||||
|
||||
const originalScrollTo = window.scrollTo
|
||||
window.scrollTo = function () {
|
||||
emitter.emit('forced-scroll')
|
||||
return originalScrollTo.apply(this, arguments as any)
|
||||
}
|
||||
}
|
||||
|
||||
function listenToForcedWindowScroll(listener: () => void) {
|
||||
emitter.addListener('forced-scroll', listener)
|
||||
return () => {
|
||||
emitter.removeListener('forced-scroll', listener)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user