diff options
author | dan <dan.abramov@gmail.com> | 2024-01-22 22:46:32 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 14:46:32 -0800 |
commit | f015229acfef48084f9f9bbafd1d7fb787cbe00e (patch) | |
tree | 17c7f33b007d57bd1f5065029fb8701a5763ae9d /src/view/com/pager/Pager.web.tsx | |
parent | cd02922b031f38e90bdfa2e63091535ab28d34de (diff) | |
download | voidsky-f015229acfef48084f9f9bbafd1d7fb787cbe00e.tar.zst |
New Web Layout (#2126)
* Rip out virtualization on the web * Screw around with layout * onEndReached * scrollToOffset * Fix background * onScroll * Shell bars * More scroll * Fixes * position: sticky * Clean up 1 * Clean up 2 * Undo PagerWithHeader changes and fork it * Trim down both versions * Cleanup 3 * Memoize, lint * Don't scroll away modal or lightbox * Add content-visibility for rows * Fix composer * Fix types * Fix borked scroll animation * Fixes to layout * More FlatList parity * Layout fixes * Fix more layout * More layout * More layouts * Fix profile layout * Remove onScroll * Display: none inactive pages * Add an intermediate List component * Fix type * Add onScrolledDownChange * Port pager to use onScrolledDownChange * Fix on mobile * Don't pass down onScroll (replacement TBD) * Remove resetMainScroll * Replace onMainScroll with MainScrollProvider * Hook ScrollProvider to pager * Fix the remaining special case * Optimize a bit * Enforce that onScroll cannot be passed * Keep value updated even if no handler * Also memo it * Move the fork to List.web * Add scroll handler * Consolidate List props a bit * More stuff * Rm unused * Simplify * Make isScrolledDown work * Oops * Fixes * Hook up context scroll handlers * Scroll restore for tabs * Route scroll restoration POC * Fix some issues with restoration * Remove bad idea * Fix pager scroll restoration * Undo accidental locale changes * onContentSizeChange * Scroll to post * Better positioning * Layout fixes * Factor out navigation stuff * Cleanup * Oops * Cleanup * Fixes and types * Naming etc * Fix crash * Match FL semantics * Snap the header scroll on the web * Add body scroll lock * Scroll to top on search * Fix types * Typos * Fix Safari overflow * Fix search positioning * Add border * Patch react navigation * Revert "Patch react navigation" This reverts commit 62516ed9c20410d166e1582b43b656c819495ddc. * fixes * scroll * scrollbar * cleanup unrelated * undo unrel * flatter * Fix css * twk
Diffstat (limited to 'src/view/com/pager/Pager.web.tsx')
-rw-r--r-- | src/view/com/pager/Pager.web.tsx | 51 |
1 files changed, 35 insertions, 16 deletions
diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx index 3b5e9164a..dde799e42 100644 --- a/src/view/com/pager/Pager.web.tsx +++ b/src/view/com/pager/Pager.web.tsx @@ -1,10 +1,12 @@ import React from 'react' +import {flushSync} from 'react-dom' import {View} from 'react-native' import {s} from 'lib/styles' export interface RenderTabBarFnProps { selectedPage: number onSelect?: (index: number) => void + tabBarAnchor?: JSX.Element } export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element @@ -27,6 +29,8 @@ export const Pager = React.forwardRef(function PagerImpl( ref, ) { const [selectedPage, setSelectedPage] = React.useState(initialPage) + const scrollYs = React.useRef<Array<number | null>>([]) + const anchorRef = React.useRef(null) React.useImperativeHandle(ref, () => ({ setPage: (index: number) => setSelectedPage(index), @@ -34,11 +38,36 @@ export const Pager = React.forwardRef(function PagerImpl( const onTabBarSelect = React.useCallback( (index: number) => { - setSelectedPage(index) - onPageSelected?.(index) - onPageSelecting?.(index) + const scrollY = window.scrollY + // We want to determine if the tabbar is already "sticking" at the top (in which + // case we should preserve and restore scroll), or if it is somewhere below in the + // viewport (in which case a scroll jump would be jarring). We determine this by + // measuring where the "anchor" element is (which we place just above the tabbar). + let anchorTop = anchorRef.current + ? (anchorRef.current as Element).getBoundingClientRect().top + : -scrollY // If there's no anchor, treat the top of the page as one. + const isSticking = anchorTop <= 5 // This would be 0 if browser scrollTo() was reliable. + + if (isSticking) { + scrollYs.current[selectedPage] = window.scrollY + } else { + scrollYs.current[selectedPage] = null + } + flushSync(() => { + setSelectedPage(index) + onPageSelected?.(index) + onPageSelecting?.(index) + }) + if (isSticking) { + const restoredScrollY = scrollYs.current[index] + if (restoredScrollY != null) { + window.scrollTo(0, restoredScrollY) + } else { + window.scrollTo(0, scrollY + anchorTop) + } + } }, - [setSelectedPage, onPageSelected, onPageSelecting], + [selectedPage, setSelectedPage, onPageSelected, onPageSelecting], ) return ( @@ -46,21 +75,11 @@ export const Pager = React.forwardRef(function PagerImpl( {tabBarPosition === 'top' && renderTabBar({ selectedPage, + tabBarAnchor: <View ref={anchorRef} />, onSelect: onTabBarSelect, })} {React.Children.map(children, (child, i) => ( - <View - style={ - selectedPage === i - ? s.flex1 - : { - position: 'absolute', - pointerEvents: 'none', - // @ts-ignore web-only - visibility: 'hidden', - } - } - key={`page-${i}`}> + <View style={selectedPage === i ? s.flex1 : s.hidden} key={`page-${i}`}> {child} </View> ))} |