import React from 'react'; import { StyleSheet, ViewProps } from 'react-native'; import { GestureDetector, LongPressGestureEvent, useLongPressGesture, } from 'react-native-gesture-handler'; import Animated, { useSharedValue, useAnimatedStyle, useAnimatedReaction, AnimatedProps, } from 'react-native-reanimated'; import { scheduleOnRN } from 'react-native-worklets'; import { clamp } from 'react-native-redash'; import { CandlestickChartDimensionsContext } from './Chart'; import { CandlestickChartLine, CandlestickChartLineProps } from './Line'; import { useCandlestickChart } from './useCandlestickChart'; import { CandlestickChartCrosshairTooltipContext } from './CrosshairTooltip'; type CandlestickChartCrosshairProps = { color?: string; children?: React.ReactNode; onCurrentXChange?: (value: number) => unknown; horizontalCrosshairProps?: AnimatedProps; verticalCrosshairProps?: AnimatedProps; lineProps?: Partial; minDurationMs?: number; }; export function CandlestickChartCrosshair({ color, onCurrentXChange, children, horizontalCrosshairProps = {}, verticalCrosshairProps = {}, lineProps = {}, minDurationMs = 0, }: CandlestickChartCrosshairProps) { const { width, height } = React.useContext(CandlestickChartDimensionsContext); const { currentX, currentY, step } = useCandlestickChart(); const tooltipPosition = useSharedValue<'left' | 'right'>('left'); const opacity = useSharedValue(0); const updatePosition = (x: number, y: number) => { 'worklet'; const boundedX = x <= width - 1 ? x : width - 1; if (boundedX < 100) { tooltipPosition.value = 'right'; } else { tooltipPosition.value = 'left'; } currentY.value = clamp(y, 0, height); currentX.value = boundedX - (boundedX % step) + step / 2; }; const longPressGesture = useLongPressGesture({ minDuration: minDurationMs ?? 0, maxDistance: 999999, onActivate: (event: LongPressGestureEvent) => { 'worklet'; opacity.value = 1; updatePosition(event.x, event.y); }, onTouchesMove: (event) => { 'worklet'; if (opacity.value === 1 && event.allTouches.length > 0) { updatePosition(event.allTouches[0]!.x, event.allTouches[0]!.y); } }, onDeactivate: () => { 'worklet'; opacity.value = 0; currentY.value = -1; currentX.value = -1; }, }); const horizontal = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ translateY: currentY.value }], })); const vertical = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ translateX: currentX.value }], })); useAnimatedReaction( () => currentX.value, (data, prevData) => { if (data !== -1 && data !== prevData && onCurrentXChange) { scheduleOnRN(onCurrentXChange, data); } } ); return ( {children} ); }