import React, { ComponentType, useEffect, useRef, useState } from "react" import { useContextSelector } from "use-context-selector" import { useTimeout } from "usehooks-ts" import { Tooltip, TooltipProps } from "../../components/Tooltip" import { useCallbackRef } from "../../hooks/useCallbackRef" import { ChartContext } from "./ChartContext" export type RenderTooltipFn = ComponentType< { item: T point: Highcharts.Point } & S > // Extra y-offset required to align tooltip better to different types of highchart points const POINT_TYPE_TO_Y_OFFSET = { marker: 7, column: -2, } as const type PointType = keyof typeof POINT_TYPE_TO_Y_OFFSET type ChartTooltipProps = Pick & { pointType: PointType delay?: number } const ChartTooltipBase = ({ content, pointType, delay }: ChartTooltipProps) => { const [open, setOpen] = useState(false) const [isPanning, setHoveredPoint, hoveredPoint] = useContextSelector( ChartContext, v => [v.isPanning, v.setHoveredPoint, v.hoveredPoint], ) const [ref, setRef] = useCallbackRef() const hasOpened = useRef(false) useEffect(() => { if (open) { hasOpened.current = true } }, [open]) useTimeout(() => setOpen(true), delay ?? 0) useEffect(() => { const close = () => setHoveredPoint(undefined) window.addEventListener("scroll", close) return () => { window.removeEventListener("scroll", close) } }, [setHoveredPoint]) if (isPanning || !hoveredPoint?.series) { return <> } const seriesLeft = "left" in hoveredPoint.series.xAxis ? (hoveredPoint.series.xAxis.left as number) : 0 const left = seriesLeft + hoveredPoint.series.chart.plotLeft + (hoveredPoint.plotX ?? 0) const top = hoveredPoint.series.chart.plotTop + hoveredPoint.series.yAxis.toPixels( hoveredPoint.total ?? hoveredPoint.y ?? 0, true, ) - POINT_TYPE_TO_Y_OFFSET[pointType] return ( {content}} disablePointerEvents hoverable={false} key={`${left}-${top}`} open={delay ? open : true} >
) } export const ChartTooltip = (props: ChartTooltipProps) => { const hoveredPoint = useContextSelector(ChartContext, v => v.hoveredPoint) return ( ) }