import React, { type ComponentProps, forwardRef, useCallback, useRef, } from 'react'; import type { LayoutChangeEvent, ViewProps } from 'react-native'; import type { SimultaneousGesture } from 'react-native-gesture-handler'; import Animated from 'react-native-reanimated'; import { INITIAL_LAYOUT_VALUE } from '../../constants'; import { useBottomSheetInternal } from '../../hooks'; import { BottomSheetDraggableScrollable } from './BottomSheetDraggableScrollable'; interface ScrollableContainerProps { nativeGesture: SimultaneousGesture; setContentSize: (contentHeight: number) => void; // biome-ignore lint/suspicious/noExplicitAny: 🤷‍♂️ ScrollableComponent: any; onLayout: ViewProps['onLayout']; } /** * Detect if the current browser is Safari or not. */ const isWebkit = () => { // @ts-expect-error return navigator.userAgent.indexOf('Safari') > -1; }; export const ScrollableContainer = forwardRef< never, ScrollableContainerProps & { animatedProps: never } >(function ScrollableContainer( { nativeGesture, ScrollableComponent, animatedProps, setContentSize, onLayout, ...rest }, ref ) { //#region refs const isInitialContentHeightCaptured = useRef(false); //#endregion //#region hooks const { animatedLayoutState } = useBottomSheetInternal(); //#endregion //#region callbacks const renderScrollComponent = useCallback( (props: ComponentProps) => ( ), [animatedProps] ); /** * A workaround a bug in React Native Web [#1502](https://github.com/necolas/react-native-web/issues/1502), * where the `onContentSizeChange` won't be call on initial render. */ const handleOnLayout = useCallback( (event: LayoutChangeEvent) => { if (onLayout) { onLayout(event); } if (!isInitialContentHeightCaptured.current) { isInitialContentHeightCaptured.current = true; if (!isWebkit()) { return; } /** * early exit if the content height been calculated. */ if (animatedLayoutState.get().contentHeight !== INITIAL_LAYOUT_VALUE) { return; } // @ts-expect-error window.requestAnimationFrame(() => { // @ts-expect-error setContentSize(event.nativeEvent.target.clientHeight); }); } }, [onLayout, setContentSize, animatedLayoutState] ); //#endregion return ( ); });