/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format */ import type {____ViewStyle_Internal} from '../../StyleSheet/StyleSheetTypes'; import type { AnimatedComponentType, AnimatedProps, } from '../createAnimatedComponent'; import RefreshControl from '../../Components/RefreshControl/RefreshControl'; import ScrollView, { type ScrollViewProps, } from '../../Components/ScrollView/ScrollView'; import flattenStyle from '../../StyleSheet/flattenStyle'; import splitLayoutProps from '../../StyleSheet/splitLayoutProps'; import StyleSheet from '../../StyleSheet/StyleSheet'; import Platform from '../../Utilities/Platform'; import useMergeRefs from '../../Utilities/useMergeRefs'; import createAnimatedComponent from '../createAnimatedComponent'; import useAnimatedProps from '../useAnimatedProps'; import * as React from 'react'; import {cloneElement, useMemo} from 'react'; type AnimatedScrollViewInstance = React.ElementRef; /** * @see https://github.com/facebook/react-native/commit/b8c8562 */ const AnimatedScrollView: AnimatedComponentType< ScrollViewProps, AnimatedScrollViewInstance, > = function AnimatedScrollViewWithOrWithoutInvertedRefreshControl({ ref: forwardedRef, ...props }: { ref?: React.RefSetter, ...AnimatedProps, }) { // (Android only) When a ScrollView has a RefreshControl and // any `style` property set with an Animated.Value, the CSS // gets incorrectly applied twice. This is because ScrollView // swaps the parent/child relationship of itself and the // RefreshControl component (see ScrollView.js for more details). if ( Platform.OS === 'android' && props.refreshControl != null && props.style != null ) { return ( // $FlowFixMe - It should return an Animated ScrollView but it returns a ScrollView with Animated props applied. ); } else { return ( ); } }; const AnimatedScrollViewWithInvertedRefreshControl = function AnimatedScrollViewWithInvertedRefreshControl({ ref: forwardedRef, ...props }: { ref?: React.RefSetter, ...React.ElementConfig, // $FlowFixMe[unclear-type] Same Flow type as `refreshControl` in ScrollView refreshControl: ExactReactElement_DEPRECATED, }) { // Split `props` into the animate-able props for the parent (RefreshControl) // and child (ScrollView). const {intermediatePropsForRefreshControl, intermediatePropsForScrollView} = useMemo(() => { // $FlowFixMe[underconstrained-implicit-instantiation] // $FlowFixMe[incompatible-call] const {outer, inner} = splitLayoutProps(flattenStyle(props.style)); return { intermediatePropsForRefreshControl: {style: outer}, intermediatePropsForScrollView: {...props, style: inner}, }; }, [props]); // Handle animated props on `refreshControl`. const [refreshControlAnimatedProps, refreshControlRef] = useAnimatedProps< {style: ?____ViewStyle_Internal}, $FlowFixMe, >(intermediatePropsForRefreshControl); // NOTE: Assumes that refreshControl.ref` and `refreshControl.style` can be // safely clobbered. const refreshControl: ExactReactElement_DEPRECATED = cloneElement(props.refreshControl, { ...refreshControlAnimatedProps, ref: refreshControlRef, }); // Handle animated props on `NativeDirectionalScrollView`. const [scrollViewAnimatedProps, scrollViewRef] = useAnimatedProps< ScrollViewProps, AnimatedScrollViewInstance, >(intermediatePropsForScrollView); const ref = useMergeRefs( scrollViewRef, forwardedRef, ); return ( // $FlowFixMe[incompatible-use] Investigate useAnimatedProps return value ); }; const AnimatedScrollViewWithoutInvertedRefreshControl = createAnimatedComponent(ScrollView); export default AnimatedScrollView;