@@ -16,24 +16,17 @@ import React, {
1616 useRef ,
1717 useState ,
1818} from 'react' ;
19- import { LayoutChangeEvent , StyleSheet , View } from 'react-native' ;
20- import Animated , {
21- useAnimatedScrollHandler ,
22- useAnimatedStyle ,
23- useSharedValue ,
24- } from 'react-native-reanimated' ;
19+ import { View } from 'react-native' ;
20+ import Animated from 'react-native-reanimated' ;
2521import { Atoms } from '../theme' ;
22+ import { HidingHeader , renderNode , useHidingHeader } from './HidingHeader' ;
2623
2724// A reanimated-compatible FlashList.
2825const AnimatedFlashList = Animated . createAnimatedComponent ( FlashList ) ;
2926
3027const WEB_INITIAL_VISIBLE = 12 ;
3128const WEB_PAGE_SIZE = 12 ;
3229
33- // Keep the sticky header fully visible until the user has scrolled past
34- // this distance; only then does scroll-driven hiding kick in.
35- const HEADER_HIDE_THRESHOLD = 50 ;
36-
3730export type { FlashListProps , ListRenderItem , ListRenderItemInfo } ;
3831
3932export type ListProps < T > = FlashListProps < T > & {
@@ -78,59 +71,8 @@ function NativeList<T>({
7871 } ) ,
7972 [ ] ,
8073 ) ;
81- const lastScrollY = useSharedValue ( 0 ) ;
82- const headerTranslate = useSharedValue ( 0 ) ;
83- const headerHeightShared = useSharedValue ( 0 ) ;
84- const isDragging = useSharedValue ( false ) ;
85- const isMomentum = useSharedValue ( false ) ;
86- const [ headerHeight , setHeaderHeight ] = useState ( 0 ) ;
87-
88- const onScroll = useAnimatedScrollHandler ( {
89- onBeginDrag : ( ) => {
90- isDragging . value = true ;
91- } ,
92- onEndDrag : ( ) => {
93- isDragging . value = false ;
94- } ,
95- onMomentumBegin : ( ) => {
96- isMomentum . value = true ;
97- } ,
98- onMomentumEnd : ( ) => {
99- isMomentum . value = false ;
100- } ,
101- onScroll : ( event ) => {
102- const currentY = event . contentOffset . y ;
103- const h = headerHeightShared . value ;
104-
105- if ( currentY <= HEADER_HIDE_THRESHOLD ) {
106- headerTranslate . value = 0 ;
107- } else if ( isDragging . value || isMomentum . value ) {
108- // Compute delta relative to the threshold so movement past it
109- // starts the hide from translate 0 instead of snapping.
110- const lastEffective = Math . max (
111- 0 ,
112- lastScrollY . value - HEADER_HIDE_THRESHOLD ,
113- ) ;
114- const currentEffective = currentY - HEADER_HIDE_THRESHOLD ;
115- const delta = currentEffective - lastEffective ;
116- const next = headerTranslate . value - delta ;
117- headerTranslate . value = Math . min ( 0 , Math . max ( - h , next ) ) ;
118- }
119- // Always update so the next user-driven event computes the
120- // correct delta — even if we skipped animating this tick.
121- lastScrollY . value = currentY ;
122- } ,
123- } ) ;
124-
125- const headerAnimatedStyle = useAnimatedStyle ( ( ) => ( {
126- transform : [ { translateY : headerTranslate . value } ] ,
127- } ) ) ;
128-
129- const onHeaderLayout = ( event : LayoutChangeEvent ) => {
130- const next = event . nativeEvent . layout . height ;
131- headerHeightShared . value = next ;
132- if ( next !== headerHeight ) setHeaderHeight ( next ) ;
133- } ;
74+ const { onScroll, headerHeight, headerAnimatedStyle, onHeaderLayout } =
75+ useHidingHeader ( ) ;
13476
13577 const renderedHeader = renderNode ( HeaderComponent ) ;
13678
@@ -149,12 +91,9 @@ function NativeList<T>({
14991 return (
15092 < View style = { [ Atoms . flex_1 ] } >
15193 { renderedHeader ? (
152- < Animated . View
153- onLayout = { onHeaderLayout }
154- style = { [ Atoms . absolute , styles . stickyHeader , headerAnimatedStyle ] }
155- >
94+ < HidingHeader style = { headerAnimatedStyle } onLayout = { onHeaderLayout } >
15695 { renderedHeader }
157- </ Animated . View >
96+ </ HidingHeader >
15897 ) : null }
15998
16099 < AnimatedFlashList
@@ -261,25 +200,3 @@ function WebFeedViewer<T>({
261200 </ View >
262201 ) ;
263202}
264-
265- type ReactNodeOrComponent =
266- | React . ReactElement
267- | React . ComponentType
268- | null
269- | undefined ;
270-
271- function renderNode ( node : ReactNodeOrComponent ) {
272- if ( node == null ) return null ;
273- if ( isValidElement ( node ) ) return node ;
274- const Component = node as React . ComponentType ;
275- return < Component /> ;
276- }
277-
278- const styles = StyleSheet . create ( {
279- stickyHeader : {
280- top : 0 ,
281- left : 0 ,
282- right : 0 ,
283- zIndex : 1 ,
284- } ,
285- } ) ;
0 commit comments