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/PagerWithHeader.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/PagerWithHeader.web.tsx')
-rw-r--r-- | src/view/com/pager/PagerWithHeader.web.tsx | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/view/com/pager/PagerWithHeader.web.tsx b/src/view/com/pager/PagerWithHeader.web.tsx new file mode 100644 index 000000000..0a18a9e7d --- /dev/null +++ b/src/view/com/pager/PagerWithHeader.web.tsx @@ -0,0 +1,194 @@ +import * as React from 'react' +import {FlatList, ScrollView, StyleSheet, View} from 'react-native' +import {useAnimatedRef} from 'react-native-reanimated' +import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' +import {TabBar} from './TabBar' +import {usePalette} from '#/lib/hooks/usePalette' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {ListMethods} from '../util/List' + +export interface PagerWithHeaderChildParams { + headerHeight: number + isFocused: boolean + scrollElRef: React.MutableRefObject<FlatList<any> | ScrollView | null> +} + +export interface PagerWithHeaderProps { + testID?: string + children: + | (((props: PagerWithHeaderChildParams) => JSX.Element) | null)[] + | ((props: PagerWithHeaderChildParams) => JSX.Element) + items: string[] + isHeaderReady: boolean + renderHeader?: () => JSX.Element + initialPage?: number + onPageSelected?: (index: number) => void + onCurrentPageSelected?: (index: number) => void +} +export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( + function PageWithHeaderImpl( + { + children, + testID, + items, + renderHeader, + initialPage, + onPageSelected, + onCurrentPageSelected, + }: PagerWithHeaderProps, + ref, + ) { + const [currentPage, setCurrentPage] = React.useState(0) + + const renderTabBar = React.useCallback( + (props: RenderTabBarFnProps) => { + return ( + <PagerTabBar + items={items} + renderHeader={renderHeader} + currentPage={currentPage} + onCurrentPageSelected={onCurrentPageSelected} + onSelect={props.onSelect} + tabBarAnchor={props.tabBarAnchor} + testID={testID} + /> + ) + }, + [items, renderHeader, currentPage, onCurrentPageSelected, testID], + ) + + const onPageSelectedInner = React.useCallback( + (index: number) => { + setCurrentPage(index) + onPageSelected?.(index) + }, + [onPageSelected, setCurrentPage], + ) + + const onPageSelecting = React.useCallback((index: number) => { + setCurrentPage(index) + }, []) + + return ( + <Pager + ref={ref} + testID={testID} + initialPage={initialPage} + onPageSelected={onPageSelectedInner} + onPageSelecting={onPageSelecting} + renderTabBar={renderTabBar} + tabBarPosition="top"> + {toArray(children) + .filter(Boolean) + .map((child, i) => { + return ( + <View key={i} collapsable={false}> + <PagerItem isFocused={i === currentPage} renderTab={child} /> + </View> + ) + })} + </Pager> + ) + }, +) + +let PagerTabBar = ({ + currentPage, + items, + testID, + renderHeader, + onCurrentPageSelected, + onSelect, + tabBarAnchor, +}: { + currentPage: number + items: string[] + testID?: string + renderHeader?: () => JSX.Element + onCurrentPageSelected?: (index: number) => void + onSelect?: (index: number) => void + tabBarAnchor?: JSX.Element | null | undefined +}): React.ReactNode => { + const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() + return ( + <> + <View style={[!isMobile && styles.headerContainerDesktop, pal.border]}> + {renderHeader?.()} + </View> + {tabBarAnchor} + <View + style={[ + styles.tabBarContainer, + isMobile + ? styles.tabBarContainerMobile + : styles.tabBarContainerDesktop, + pal.border, + ]}> + <TabBar + testID={testID} + items={items} + selectedPage={currentPage} + onSelect={onSelect} + onPressSelected={onCurrentPageSelected} + /> + </View> + </> + ) +} +PagerTabBar = React.memo(PagerTabBar) + +function PagerItem({ + isFocused, + renderTab, +}: { + isFocused: boolean + renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null +}) { + const scrollElRef = useAnimatedRef() + if (renderTab == null) { + return null + } + return renderTab({ + headerHeight: 0, + isFocused, + scrollElRef: scrollElRef as React.MutableRefObject< + ListMethods | ScrollView | null + >, + }) +} + +const styles = StyleSheet.create({ + headerContainerDesktop: { + marginLeft: 'auto', + marginRight: 'auto', + width: 600, + borderLeftWidth: 1, + borderRightWidth: 1, + }, + tabBarContainer: { + // @ts-ignore web-only + position: 'sticky', + overflow: 'hidden', + top: 0, + zIndex: 1, + }, + tabBarContainerDesktop: { + marginLeft: 'auto', + marginRight: 'auto', + width: 600, + borderLeftWidth: 1, + borderRightWidth: 1, + }, + tabBarContainerMobile: { + paddingLeft: 14, + paddingRight: 14, + }, +}) + +function toArray<T>(v: T | T[]): T[] { + if (Array.isArray(v)) { + return v + } + return [v] +} |