This repository was archived by the owner on Feb 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 131
/
Copy pathTooltip.js
111 lines (101 loc) · 2.93 KB
/
Tooltip.js
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { PORTAL_TARGETS } from "constants/domNodes";
import { PALETTE, Z_INDEXES } from "constants/styles";
import { renderToPortal } from "helpers/renderToPortal";
// For the arrow, it should be 4px (.25rem) away from the edge. Since it's a box
// rotated 45 degrees, calculate the length of 1 side of the box.
// a^2 + b^2 = c^2
const hypotenuse = (0.25 ** 2 + 0.25 ** 2) ** 0.5;
const El = styled.div``;
const TooltipEl = styled.div`
position: absolute;
display: block;
border-radius: 4px;
overflow: visible;
font-size: 0.75rem;
padding: 0.625rem;
z-index: ${Z_INDEXES.tooltip};
color: ${PALETTE.white};
background-color: rgba(41, 45, 62, 1);
pointer-events: none;
opacity: 0;
transition: opacity ${(props) => props.duration}ms ease-in-out;
${({ isOpen }) =>
isOpen
? `
opacity: 1;
pointer-events: all;
`
: ""}
&::after {
content: "";
position: absolute;
width: ${hypotenuse}rem;
height: ${hypotenuse}rem;
right: 1rem;
bottom: -0.25rem;
background-color: rgba(41, 45, 62, 1);
transform: rotate(45deg);
// Not sure why but this makes it look nicer. Maybe fine units getting
// trimmed weirdly?? One of the square's corners appears to be cut off.
transform-origin: top;
}
`;
export const Tooltip = ({ duration = 150, message, children }) => {
const wrapperRef = React.useRef();
const [isOpen, setIsOpen] = React.useState(false);
const [size, setSize] = React.useState({
width: 0,
height: 0,
});
const tooltipRef = React.useRef();
const updateDimensions = React.useCallback(() => {
if (tooltipRef.current) {
const { width, height } = tooltipRef.current.getBoundingClientRect();
setSize({
width,
height,
});
}
}, []);
React.useLayoutEffect(() => {
if (!isOpen || !tooltipRef.current) {
return;
}
const { right, top } = wrapperRef.current.getBoundingClientRect();
const newRight = window.innerWidth - right;
// We need height + some because of the extra space for the little pointer
// bit. That's .25rem away
const newTop = window.pageYOffset + top - (size.height + 4);
tooltipRef.current.style.right = `${newRight}px`;
tooltipRef.current.style.top = `${newTop}px`;
}, [isOpen, size]);
return (
<El
ref={wrapperRef}
onMouseEnter={() => {
setIsOpen(true);
updateDimensions();
}}
onMouseLeave={() => {
setIsOpen(false);
updateDimensions();
}}
>
{children}
{renderToPortal(
<TooltipEl isOpen={isOpen} ref={tooltipRef} duration={duration}>
{message}
</TooltipEl>,
PORTAL_TARGETS.tooltip,
)}
</El>
);
};
Tooltip.propTypes = {
children: PropTypes.node.isRequired,
message: PropTypes.node.isRequired,
duration: PropTypes.number,
};