Size-aware chart tooltip positioning (#980)
This commit is contained in:
parent
2f3ae5192e
commit
2f1221f094
|
@ -16,6 +16,7 @@ import dayjs from 'dayjs'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
|
import { useMeasureSize } from 'web/hooks/use-measure-size'
|
||||||
|
|
||||||
export type Point<X, Y, T = unknown> = { x: X; y: Y; obj?: T }
|
export type Point<X, Y, T = unknown> = { x: X; y: Y; obj?: T }
|
||||||
|
|
||||||
|
@ -135,6 +136,7 @@ export const SVGChart = <X, TT>(props: {
|
||||||
}) => {
|
}) => {
|
||||||
const { children, w, h, xAxis, yAxis, onMouseOver, onSelect, Tooltip } = props
|
const { children, w, h, xAxis, yAxis, onMouseOver, onSelect, Tooltip } = props
|
||||||
const [mouse, setMouse] = useState<{ x: number; y: number; data: TT }>()
|
const [mouse, setMouse] = useState<{ x: number; y: number; data: TT }>()
|
||||||
|
const tooltipMeasure = useMeasureSize()
|
||||||
const overlayRef = useRef<SVGGElement>(null)
|
const overlayRef = useRef<SVGGElement>(null)
|
||||||
const innerW = w - MARGIN_X
|
const innerW = w - MARGIN_X
|
||||||
const innerH = h - MARGIN_Y
|
const innerH = h - MARGIN_Y
|
||||||
|
@ -188,10 +190,18 @@ export const SVGChart = <X, TT>(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative overflow-hidden">
|
||||||
{mouse && Tooltip && (
|
{mouse && Tooltip && (
|
||||||
<TooltipContainer
|
<TooltipContainer
|
||||||
pos={getTooltipPosition(mouse.x, mouse.y, innerW, innerH)}
|
setElem={tooltipMeasure.setElem}
|
||||||
|
pos={getTooltipPosition(
|
||||||
|
mouse.x,
|
||||||
|
mouse.y,
|
||||||
|
innerW,
|
||||||
|
innerH,
|
||||||
|
tooltipMeasure.width,
|
||||||
|
tooltipMeasure.height
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
xScale={xAxis.scale()}
|
xScale={xAxis.scale()}
|
||||||
|
@ -227,31 +237,31 @@ export const SVGChart = <X, TT>(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TooltipPosition = {
|
export type TooltipPosition = { left: number; bottom: number }
|
||||||
top?: number
|
|
||||||
right?: number
|
|
||||||
bottom?: number
|
|
||||||
left?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getTooltipPosition = (
|
export const getTooltipPosition = (
|
||||||
mouseX: number,
|
mouseX: number,
|
||||||
mouseY: number,
|
mouseY: number,
|
||||||
w: number,
|
containerWidth: number,
|
||||||
h: number
|
containerHeight: number,
|
||||||
|
tooltipWidth?: number,
|
||||||
|
tooltipHeight?: number
|
||||||
) => {
|
) => {
|
||||||
const result: TooltipPosition = {}
|
let left = mouseX + 12
|
||||||
if (mouseX <= (3 * w) / 4) {
|
let bottom = containerHeight - mouseY + 12
|
||||||
result.left = mouseX + 10 // in the left three quarters
|
if (tooltipWidth != null) {
|
||||||
} else {
|
const overflow = left + tooltipWidth - containerWidth
|
||||||
result.right = w - mouseX + 10 // in the right quarter
|
if (overflow > 0) {
|
||||||
|
left -= overflow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mouseY <= h / 4) {
|
if (tooltipHeight != null) {
|
||||||
result.top = mouseY + 10 // in the top quarter
|
const overflow = tooltipHeight - mouseY
|
||||||
} else {
|
if (overflow > 0) {
|
||||||
result.bottom = h - mouseY + 10 // in the bottom three quarters
|
bottom -= overflow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return { left, bottom }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TooltipProps<X, T> = {
|
export type TooltipProps<X, T> = {
|
||||||
|
@ -260,15 +270,18 @@ export type TooltipProps<X, T> = {
|
||||||
xScale: ContinuousScale<X>
|
xScale: ContinuousScale<X>
|
||||||
data: T
|
data: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TooltipComponent<X, T> = React.ComponentType<TooltipProps<X, T>>
|
export type TooltipComponent<X, T> = React.ComponentType<TooltipProps<X, T>>
|
||||||
export const TooltipContainer = (props: {
|
export const TooltipContainer = (props: {
|
||||||
|
setElem: (e: HTMLElement | null) => void
|
||||||
pos: TooltipPosition
|
pos: TooltipPosition
|
||||||
className?: string
|
className?: string
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const { pos, className, children } = props
|
const { setElem, pos, className, children } = props
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={setElem}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
className,
|
className,
|
||||||
'pointer-events-none absolute z-10 whitespace-pre rounded border border-gray-200 bg-white/80 p-2 px-4 py-2 text-xs sm:text-sm'
|
'pointer-events-none absolute z-10 whitespace-pre rounded border border-gray-200 bg-white/80 p-2 px-4 py-2 text-xs sm:text-sm'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user