import { useThree } from '@react-three/fiber' ; import type { ThreeEvent } from '@react-three/fiber/dist/declarations/src/core/events' ; import { Line } from '@visx/shape' ; import { TooltipWithBounds , useTooltip } from '@visx/tooltip' ; import { useCallback } from 'react' ; import type { ReactElement } from 'react' ; import type { Coords } from '../models' ; import { projectCameraToHtml } from '../utils' ; import { useAxisSystemContext } from './AxisSystemContext' ; import Html from './Html' ; import styles from './TooltipMesh.module.css' ; import VisMesh from './VisMesh' ; interface Props { guides ? : 'horizontal' | 'vertical' | 'both' ; renderTooltip : ( x : number , y : number ) => ReactElement | undefined ; } function TooltipMesh ( props ) { const { guides , renderTooltip } = props ; const camera = useThree ( ( state ) => state . camera ) ; const { width , height } = useThree ( ( state ) => state . size ) ; const { abscissaScale , ordinateScale } = useAxisSystemContext ( ) ; const { tooltipOpen , tooltipTop , tooltipLeft , tooltipData , showTooltip , hideTooltip , } = useTooltip < Coords > ( ) ; const onPointerMove = useCallback ( ( evt ) => { const abscissaCoord = abscissaScale . invert ( evt . unprojectedPoint . x ) ; const ordinateCoord = ordinateScale . invert ( evt . unprojectedPoint . y ) ; const cameraPoint = evt . unprojectedPoint . clone ( ) . project ( camera ) ; const htmlPoint = projectCameraToHtml ( cameraPoint , width , height ) ; showTooltip ( { tooltipLeft : htmlPoint . x , tooltipTop : htmlPoint . y , tooltipData : [ abscissaCoord , ordinateCoord ] , } ) ; } , [ camera , abscissaScale , ordinateScale , width , height , showTooltip ] ) ; const onPointerOut = useCallback ( hideTooltip , [ hideTooltip ] ) ; const onPointerDown = useCallback ( hideTooltip , [ hideTooltip ] ) ; const onPointerUp = useCallback ( ( evt ) => { const { sourceEvent } = evt ; const { offsetX : x , offsetY : y } = sourceEvent ; if ( x >= 0 && x <= width && y >= 0 && y <= height ) { onPointerMove ( evt ) ; } } , [ height , onPointerMove , width ] ) ; const content = tooltipData && renderTooltip ( ... tooltipData ) ; return ( < > < VisMesh { ... { onPointerMove , onPointerOut , onPointerDown , onPointerUp } } > < meshBasicMaterial opacity = { 0 } transparent / > < /VisMesh> < Html style = { { width , height } } > { tooltipOpen && content && ( < > < TooltipWithBounds key = { Math . random ( ) } className = { styles . tooltip } top = { tooltipTop } left = { tooltipLeft } unstyled applyPositionStyle > { content } < /TooltipWithBounds> { guides && ( < svg className = { styles . guides } > { guides !== 'horizontal' && ( < Line from = { { x : tooltipLeft , y : 0 } } to = { { x : tooltipLeft , y : height } } />)} { guides !== 'vertical' && ( < Line from = { { x : 0 , y : tooltipTop } } to = { { x : width , y : tooltipTop } } />)} < /svg>)} < />)} < /Html> < />); } export type { Props as TooltipMeshProps } ; export default TooltipMesh ;