import React, { useImperativeHandle } from 'react'; import { View, FlatListProps, StyleSheet } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Animated, { useAnimatedRef, AnimatedProps } from 'react-native-reanimated'; import FadingView from '../containers/FadingView'; import { useScrollContainerLogic } from './useScrollContainerLogic'; import type { SharedScrollContainerProps } from './types'; type AnimatedFlatListType = React.ComponentClass< AnimatedProps>, ItemT >; type AnimatedFlatListBaseProps = React.ComponentProps>; type AnimatedFlatListProps = AnimatedFlatListBaseProps; type FlatListWithHeadersProps = Omit< AnimatedFlatListProps & SharedScrollContainerProps, 'onScroll' >; const FlatListWithHeadersInputComp = ( { largeHeaderShown, containerStyle, LargeHeaderSubtitleComponent, LargeHeaderComponent, largeHeaderContainerStyle, HeaderComponent, onLargeHeaderLayout, onScrollBeginDrag, onScrollEndDrag, onScrollWorklet, onMomentumScrollBegin, onMomentumScrollEnd, ignoreLeftSafeArea, ignoreRightSafeArea, disableAutoFixScroll = false, // We use this to ensure that the onScroll property isn't accidentally used. // @ts-ignore onScroll: _unusedOnScroll, absoluteHeader = false, initialAbsoluteHeaderHeight = 0, contentContainerStyle, automaticallyAdjustsScrollIndicatorInsets, headerFadeInThreshold = 1, disableLargeHeaderFadeAnim = false, scrollIndicatorInsets = {}, inverted, CellRendererComponent, ...rest }: FlatListWithHeadersProps, ref: React.Ref | null> ) => { if (_unusedOnScroll) { throw new Error( "The 'onScroll' property is not supported. Please use onScrollWorklet to track the scroll container's state." ); } const insets = useSafeAreaInsets(); const scrollRef = useAnimatedRef>(); useImperativeHandle(ref, () => scrollRef.current); const { scrollY, showNavBar, largeHeaderHeight, largeHeaderOpacity, scrollHandler, debouncedFixScroll, onAbsoluteHeaderLayout, scrollViewAdjustments, } = useScrollContainerLogic({ scrollRef, largeHeaderShown, disableAutoFixScroll, largeHeaderExists: !!LargeHeaderComponent, absoluteHeader, initialAbsoluteHeaderHeight, headerFadeInThreshold, inverted: !!inverted, onScrollWorklet, }); return ( {!absoluteHeader && HeaderComponent({ showNavBar, scrollY })} { debouncedFixScroll.cancel(); if (onScrollBeginDrag) onScrollBeginDrag(e); }} onScrollEndDrag={(e) => { debouncedFixScroll(); if (onScrollEndDrag) onScrollEndDrag(e); }} onMomentumScrollBegin={(e) => { debouncedFixScroll.cancel(); if (onMomentumScrollBegin) onMomentumScrollBegin(e); }} onMomentumScrollEnd={(e) => { debouncedFixScroll(); if (onMomentumScrollEnd) onMomentumScrollEnd(e); }} contentContainerStyle={[ scrollViewAdjustments.contentContainerStyle, // @ts-ignore // Unfortunately there are issues with Reanimated typings, so will ignore for now. contentContainerStyle, ]} automaticallyAdjustsScrollIndicatorInsets={ automaticallyAdjustsScrollIndicatorInsets !== undefined ? automaticallyAdjustsScrollIndicatorInsets : !absoluteHeader } scrollIndicatorInsets={{ ...scrollViewAdjustments.scrollIndicatorInsets, ...scrollIndicatorInsets, }} ListHeaderComponent={ <> {LargeHeaderComponent && ( { largeHeaderHeight.value = e.nativeEvent.layout.height; if (onLargeHeaderLayout) onLargeHeaderLayout(e.nativeEvent.layout); }} > {!disableLargeHeaderFadeAnim ? ( {LargeHeaderComponent({ scrollY, showNavBar })} ) : ( {LargeHeaderComponent({ scrollY, showNavBar })} )} )} {LargeHeaderSubtitleComponent && LargeHeaderSubtitleComponent({ showNavBar, scrollY })} } inverted={inverted} CellRendererComponent={CellRendererComponent as any} {...rest} /> {absoluteHeader && ( {HeaderComponent({ showNavBar, scrollY })} )} ); }; // The typecast is needed to make the component generic. const FlatListWithHeaders = React.forwardRef(FlatListWithHeadersInputComp) as ( props: FlatListWithHeadersProps & { ref?: React.Ref>; } ) => React.ReactElement; export default FlatListWithHeaders; const styles = StyleSheet.create({ container: { flex: 1 }, absoluteHeader: { position: 'absolute', top: 0, right: 0, left: 0, }, });