-
Notifications
You must be signed in to change notification settings - Fork 308
Expand file tree
/
Copy pathhovertip.tsx
More file actions
93 lines (77 loc) · 2.91 KB
/
hovertip.tsx
File metadata and controls
93 lines (77 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import {Vec2} from '../lib/math'
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
offset: Vec2
children?: ComponentChildren
}
export function Hovertip(props: HovertipProps) {
const style = getStyle(useTheme())
const {containerSize, offset} = props
const containerWidth = containerSize.x
const containerHeight = containerSize.y
const OFFSET_FROM_MOUSE = 20
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)} ref={updateLocation}>
<div className={css(style.hoverTipRow)}>{props.children}</div>
</div>
)
}
const HOVERTIP_PADDING = 2
const getStyle = withTheme(theme =>
StyleSheet.create({
hoverTip: {
position: 'absolute',
background: theme.bgPrimaryColor,
border: '1px solid black',
maxWidth: Sizes.TOOLTIP_WIDTH_MAX,
paddingTop: HOVERTIP_PADDING,
paddingBottom: HOVERTIP_PADDING,
pointerEvents: 'none',
userSelect: 'none',
fontSize: FontSize.LABEL,
fontFamily: FontFamily.MONOSPACE,
zIndex: ZIndex.HOVERTIP,
},
hoverTipRow: {
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflowX: 'hidden',
paddingLeft: HOVERTIP_PADDING,
paddingRight: HOVERTIP_PADDING,
maxWidth: Sizes.TOOLTIP_WIDTH_MAX,
},
}),
)