Skip to content

Commit

Permalink
Improve HoverTip placement logic (jlfwong#395)
Browse files Browse the repository at this point in the history
This changes the HoverTip placement logic to use measurements from the actual DOM node rather than basing everything on the maximum sizes.

This avoids some counter-intuitive behaviour, most importantly situations where the label would overflow off the left side of the screen for no obvious reason.

Fixes jlfwong#394
Fixes jlfwong#256
  • Loading branch information
jlfwong authored and jackerghan committed Jul 28, 2023
1 parent 217ca07 commit bc11d4b
Showing 1 changed file with 41 additions and 16 deletions.
57 changes: 41 additions & 16 deletions src/views/hovertip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Sizes, FontSize, FontFamily, ZIndex} from './style'
import {css, StyleSheet} from 'aphrodite'
import {ComponentChildren, h} from 'preact'
import {useTheme, withTheme} from './themes/theme'
import { useCallback } from 'preact/hooks'

interface HovertipProps {
containerSize: Vec2
Expand All @@ -14,26 +15,50 @@ export function Hovertip(props: HovertipProps) {
const style = getStyle(useTheme())

const {containerSize, offset} = props
const width = containerSize.x
const height = containerSize.y

const positionStyle: {[key: string]: number} = {}
const containerWidth = containerSize.x
const containerHeight = containerSize.y

const OFFSET_FROM_MOUSE = 7
if (offset.x + OFFSET_FROM_MOUSE + Sizes.TOOLTIP_WIDTH_MAX < width) {
positionStyle.left = offset.x + OFFSET_FROM_MOUSE
} else {
positionStyle.right = width - offset.x + 1
}

if (offset.y + OFFSET_FROM_MOUSE + Sizes.TOOLTIP_HEIGHT_MAX < height) {
positionStyle.top = offset.y + OFFSET_FROM_MOUSE
} else {
positionStyle.bottom = height - offset.y + 1
}

const updateLocation = useCallback((el: HTMLDivElement | null) => {
if (!el) return

const clientRect = el.getBoundingClientRect()

// Place the hovertip to the right of the cursor.
let leftEdgeX = offset.x + OFFSET_FROM_MOUSE

// If this would cause it to overflow the container, align the right
// edge of the hovertip with the right edge of the container.
if (leftEdgeX + clientRect.width > containerWidth - 1) {
leftEdgeX = containerWidth - clientRect.width - 1

// If aligning the right edge overflows the container, align the left edge
// of the hovertip with the left edge of the container.
if (leftEdgeX < 1) { leftEdgeX = 1 }
}
el.style.left = `${leftEdgeX}px`

// Place the tooltip below the cursor
let topEdgeY = offset.y + OFFSET_FROM_MOUSE

// If this would cause it to overflow the container, place the hovertip
// above the cursor instead. This intentionally differs from the horizontal
// axis logic to avoid the cursor being in the middle of a hovertip when
// possible.
if (topEdgeY + clientRect.height > containerHeight - 1) {
topEdgeY = offset.y - clientRect.height - 1

// If placing the hovertip above the cursor overflows the container, align
// the top edge of the hovertip with the top edge of the container.
if (topEdgeY < 1) { topEdgeY = 1 }
}
el.style.top = `${topEdgeY}px`

}, [containerWidth, containerHeight, offset.x, offset.y])

return (
<div className={css(style.hoverTip)} style={positionStyle}>
<div className={css(style.hoverTip)} ref={updateLocation}>
<div className={css(style.hoverTipRow)}>{props.children}</div>
</div>
)
Expand Down

0 comments on commit bc11d4b

Please sign in to comment.