import React from 'react'; import { Group, Path, Skia, Text, type SkFont, } from '@shopify/react-native-skia'; import { useAnimatedReaction, useDerivedValue, useSharedValue, withSpring, withTiming, type DerivedValue, type SharedValue, } from 'react-native-reanimated'; import { useCandlestickChart } from '../CandlestickChartContext'; import type { CandlestickChartDataPoint } from '../interfaces'; export interface CandlestickChartTooltipOHLCPropsInterface { backgroundColor?: string; textColor?: string; offsetY?: number; font?: SkFont; format?: (item: CandlestickChartDataPoint) => string; } const CandlestickChartTooltipOHLC: React.FC< CandlestickChartTooltipOHLCPropsInterface > = ({ backgroundColor = '#111827', textColor = '#F9FAFB', offsetY = 8, font: tooltipFont, format, }) => { const { layoutData, selectedIndex, scrollX, canvasWidth, height, font: globalFont, } = useCandlestickChart(); const opacity = useSharedValue(0); const worldX = useSharedValue(0); const text = useSharedValue(''); const tooltipWidth = useSharedValue(0); const font = tooltipFont || globalFont; useAnimatedReaction( () => selectedIndex.value, (current) => { if (current === -1) { opacity.value = withTiming(0); return; } const item = layoutData[current]; if (!item) return; const displayString = format ? format(item.data) : `O ${item.data.open.toFixed(2)} H ${item.data.high.toFixed( 2 )} L ${item.data.low.toFixed(2)} C ${item.data.close.toFixed(2)}`; text.value = displayString; if (font) { tooltipWidth.value = font.getTextWidth(displayString) + 20; } if (opacity.value === 0) { worldX.value = item.centerX; opacity.value = withTiming(1); } else { worldX.value = withSpring(item.centerX); } }, [selectedIndex, layoutData, font, format] ); const shiftX = useDerivedValue(() => { const anchorScreenX = worldX.value - scrollX.value; const halfWidth = tooltipWidth.value / 2; const leftEdge = anchorScreenX - halfWidth; const rightEdge = anchorScreenX + halfWidth; let shift = 0; if (leftEdge < 0) shift = -leftEdge + 4; else if (rightEdge > canvasWidth) shift = canvasWidth - rightEdge - 4; const maxShift = Math.max(halfWidth - 10, 0); return Math.max(-maxShift, Math.min(maxShift, shift)); }, [worldX, scrollX, tooltipWidth, canvasWidth]); const transform = useDerivedValue(() => { return [ { translateX: worldX.value - scrollX.value }, { translateY: Math.max(Math.min(offsetY, height - 28), 0) }, ]; }, [worldX, scrollX, offsetY, height]); if (!font) return null; return ( ); }; const TooltipBubble: React.FC<{ text: DerivedValue; font: SkFont; width: SharedValue; bgColor: string; textColor: string; shiftX: DerivedValue; }> = ({ text, font, width, bgColor, textColor, shiftX }) => { const path = useDerivedValue(() => { const bubbleHeight = 28; const radius = 6; const bubblePath = Skia.Path.Make(); bubblePath.addRRect( Skia.RRectXY( Skia.XYWHRect( shiftX.value - width.value / 2, 0, width.value, bubbleHeight ), radius, radius ) ); return bubblePath; }); const textX = useDerivedValue(() => { return shiftX.value - width.value / 2 + 10; }); return ( ); }; export default CandlestickChartTooltipOHLC;