import React, {useLayoutEffect, useState} from 'react'; import {I18nManager, View} from 'react-native'; import Animated, { SharedValue, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming, } from 'react-native-reanimated'; import {useForceReRender} from '../utils'; import { ActivePointComponent, ActivePointComponentSharedValue, DataPoint, DataPointSharedValue, } from '../types'; import {scheduleOnRN} from 'react-native-worklets'; const ActivePointComponentWrapper = ({ activePointPositionX, activePointPositionY, pointOpacity, width, activePointSharedValue, activePointComponentWithSharedValue, activePointComponent, }: { activePointPositionX: SharedValue; activePointPositionY: SharedValue; pointOpacity: SharedValue; width: number; activePointSharedValue: DataPointSharedValue; activePointComponent?: ActivePointComponent; activePointComponentWithSharedValue?: ActivePointComponentSharedValue; }) => { const SPACE_BETWEEN_COMPONENT_AND_LINE = 15; const wrapperRef = React.useRef(null); const activeComponentWidthSV = useSharedValue(100); const [activeDataPointLocal, setActiveDataPointLocal] = useState< undefined | DataPoint >(undefined); const forceRerender = useForceReRender(); const calculateWidth = () => { wrapperRef.current?.measureInWindow((_x, _y, width) => { activeComponentWidthSV.value = width; }); }; useLayoutEffect(() => { calculateWidth(); }, [activePointComponent]); const componentPositionX = useDerivedValue(() => { const xPosition = activePointPositionX.value; if (I18nManager.isRTL) { if ( xPosition < activeComponentWidthSV.value + SPACE_BETWEEN_COMPONENT_AND_LINE ) { return ( xPosition - width + (activeComponentWidthSV.value + SPACE_BETWEEN_COMPONENT_AND_LINE) ); } return xPosition - width - SPACE_BETWEEN_COMPONENT_AND_LINE; } if ( width - xPosition < activeComponentWidthSV.value + SPACE_BETWEEN_COMPONENT_AND_LINE ) { return ( xPosition - activeComponentWidthSV.value - SPACE_BETWEEN_COMPONENT_AND_LINE ); } return xPosition + SPACE_BETWEEN_COMPONENT_AND_LINE; }, [activePointPositionX, activePointPositionY, activeComponentWidthSV]); const viewAnimatedStyle = useAnimatedStyle(() => { return { zIndex: 2, flexDirection: 'row', transform: [ { translateX: withTiming( componentPositionX.value, { duration: 100, }, finished => { if (finished) { scheduleOnRN(calculateWidth); } }, ), }, ], opacity: pointOpacity.value, }; }); useAnimatedReaction( () => { return activePointSharedValue.value; }, (current, previous) => { if (current !== undefined && previous === undefined) { scheduleOnRN(forceRerender); } if (activePointComponent !== undefined) { scheduleOnRN(setActiveDataPointLocal, current); } }, [activePointSharedValue], ); return ( { const {width: componentWidth} = event.nativeEvent.layout; activeComponentWidthSV.value = componentWidth; }} > {activePointComponentWithSharedValue !== undefined && activePointComponentWithSharedValue !== undefined && (activePointComponentWithSharedValue( activePointSharedValue, ) as React.ReactNode)} {activePointComponentWithSharedValue === undefined && activeDataPointLocal && activePointComponent !== undefined && (activePointComponent( activeDataPointLocal, ) as React.ReactNode)} ); }; export default ActivePointComponentWrapper;