Bluesky app fork with some witchin' additions 馃挮
at main 194 lines 4.7 kB view raw
1import * as React from 'react' 2import {type JSX} from 'react' 3import {type ScrollView, View} from 'react-native' 4import {useAnimatedRef} from 'react-native-reanimated' 5 6import { 7 Pager, 8 type PagerRef, 9 type RenderTabBarFnProps, 10} from '#/view/com/pager/Pager' 11import {atoms as a, web} from '#/alf' 12import * as Layout from '#/components/Layout' 13import {type ListMethods} from '../util/List' 14import {TabBar} from './TabBar' 15 16export interface PagerWithHeaderChildParams { 17 headerHeight: number 18 isFocused: boolean 19 scrollElRef: React.MutableRefObject<ListMethods | ScrollView | null> 20} 21 22export interface PagerWithHeaderProps { 23 testID?: string 24 children: 25 | (((props: PagerWithHeaderChildParams) => JSX.Element) | null)[] 26 | ((props: PagerWithHeaderChildParams) => JSX.Element) 27 items: string[] 28 isHeaderReady: boolean 29 renderHeader?: ({ 30 setMinimumHeight, 31 }: { 32 setMinimumHeight: () => void 33 }) => JSX.Element 34 initialPage?: number 35 onPageSelected?: (index: number) => void 36 onCurrentPageSelected?: (index: number) => void 37} 38export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( 39 function PageWithHeaderImpl( 40 { 41 children, 42 testID, 43 items, 44 isHeaderReady, 45 renderHeader, 46 initialPage, 47 onPageSelected, 48 onCurrentPageSelected, 49 }: PagerWithHeaderProps, 50 ref, 51 ) { 52 const [currentPage, setCurrentPage] = React.useState(0) 53 54 const renderTabBar = React.useCallback( 55 (props: RenderTabBarFnProps) => { 56 return ( 57 <PagerTabBar 58 items={items} 59 renderHeader={renderHeader} 60 isHeaderReady={isHeaderReady} 61 currentPage={currentPage} 62 onCurrentPageSelected={onCurrentPageSelected} 63 onSelect={props.onSelect} 64 tabBarAnchor={props.tabBarAnchor} 65 testID={testID} 66 /> 67 ) 68 }, 69 [ 70 items, 71 isHeaderReady, 72 renderHeader, 73 currentPage, 74 onCurrentPageSelected, 75 testID, 76 ], 77 ) 78 79 const onPageSelectedInner = React.useCallback( 80 (index: number) => { 81 setCurrentPage(index) 82 onPageSelected?.(index) 83 }, 84 [onPageSelected, setCurrentPage], 85 ) 86 87 return ( 88 <Pager 89 ref={ref} 90 testID={testID} 91 initialPage={initialPage} 92 onPageSelected={onPageSelectedInner} 93 renderTabBar={renderTabBar}> 94 {toArray(children) 95 .filter(Boolean) 96 .map((child, i) => { 97 const isReady = isHeaderReady 98 return ( 99 <View 100 key={i} 101 collapsable={false} 102 style={{ 103 display: isReady ? undefined : 'none', 104 }}> 105 <PagerItem isFocused={i === currentPage} renderTab={child} /> 106 </View> 107 ) 108 })} 109 </Pager> 110 ) 111 }, 112) 113 114let PagerTabBar = ({ 115 currentPage, 116 items, 117 isHeaderReady, 118 testID, 119 renderHeader, 120 onCurrentPageSelected, 121 onSelect, 122 tabBarAnchor, 123}: { 124 currentPage: number 125 items: string[] 126 testID?: string 127 renderHeader?: ({ 128 setMinimumHeight, 129 }: { 130 setMinimumHeight: () => void 131 }) => JSX.Element 132 isHeaderReady: boolean 133 onCurrentPageSelected?: (index: number) => void 134 onSelect?: (index: number) => void 135 tabBarAnchor?: JSX.Element | null | undefined 136}): React.ReactNode => { 137 return ( 138 <> 139 <Layout.Center>{renderHeader?.({setMinimumHeight: noop})}</Layout.Center> 140 {tabBarAnchor} 141 <Layout.Center 142 style={[ 143 a.z_10, 144 web([ 145 a.sticky, 146 { 147 top: 0, 148 display: isHeaderReady ? undefined : 'none', 149 }, 150 ]), 151 ]}> 152 <TabBar 153 testID={testID} 154 items={items} 155 selectedPage={currentPage} 156 onSelect={onSelect} 157 onPressSelected={onCurrentPageSelected} 158 dragProgress={undefined as any /* native-only */} 159 dragState={undefined as any /* native-only */} 160 /> 161 </Layout.Center> 162 </> 163 ) 164} 165PagerTabBar = React.memo(PagerTabBar) 166 167function PagerItem({ 168 isFocused, 169 renderTab, 170}: { 171 isFocused: boolean 172 renderTab: ((props: PagerWithHeaderChildParams) => JSX.Element) | null 173}) { 174 const scrollElRef = useAnimatedRef() 175 if (renderTab == null) { 176 return null 177 } 178 return renderTab({ 179 headerHeight: 0, 180 isFocused, 181 scrollElRef: scrollElRef as React.MutableRefObject< 182 ListMethods | ScrollView | null 183 >, 184 }) 185} 186 187function toArray<T>(v: T | T[]): T[] { 188 if (Array.isArray(v)) { 189 return v 190 } 191 return [v] 192} 193 194function noop() {}