mirror of
https://github.com/facebook/react.git
synced 2026-02-22 03:42:05 +00:00
This update was a bit more involved. - `React$Component` was removed, I replaced it with Flow component types. - Flow removed shipping the standard library. This adds the environment libraries back from `flow-typed` which seemed to have changed slightly (probably got more precise and less `any`s). Suppresses some new type errors.
121 lines
3.2 KiB
JavaScript
121 lines
3.2 KiB
JavaScript
/** @flow */
|
|
|
|
import * as React from 'react';
|
|
import {useRef} from 'react';
|
|
|
|
import styles from './Tooltip.css';
|
|
import typeof {SyntheticMouseEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
|
|
|
|
const initialTooltipState = {height: 0, mouseX: 0, mouseY: 0, width: 0};
|
|
|
|
export default function Tooltip({
|
|
children,
|
|
className,
|
|
label,
|
|
style,
|
|
}: any): React.Node {
|
|
const containerRef = useRef(null);
|
|
const tooltipRef = useRef(null);
|
|
|
|
// update the position of the tooltip based on current mouse position
|
|
const updateTooltipPosition = (event: SyntheticMouseEvent) => {
|
|
const element = tooltipRef.current;
|
|
if (element != null) {
|
|
// first find the mouse position
|
|
const mousePosition = getMousePosition(containerRef.current, event);
|
|
// use the mouse position to find the position of tooltip
|
|
const {left, top} = getTooltipPosition(element, mousePosition);
|
|
// update tooltip position
|
|
element.style.left = left;
|
|
element.style.top = top;
|
|
}
|
|
};
|
|
|
|
const onMouseMove = (event: SyntheticMouseEvent) => {
|
|
updateTooltipPosition(event);
|
|
};
|
|
|
|
const tooltipClassName = label === null ? styles.hidden : '';
|
|
|
|
return (
|
|
<div
|
|
className={styles.Container}
|
|
onMouseMove={onMouseMove}
|
|
ref={containerRef}>
|
|
<div
|
|
className={`${styles.Tooltip} ${tooltipClassName} ${className || ''}`}
|
|
ref={tooltipRef}
|
|
style={style}>
|
|
{label}
|
|
</div>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const TOOLTIP_OFFSET = 5;
|
|
|
|
// Method used to find the position of the tooltip based on current mouse position
|
|
function getTooltipPosition(
|
|
element: empty,
|
|
mousePosition: {
|
|
height: number,
|
|
mouseX: number,
|
|
mouseY: number,
|
|
width: number,
|
|
},
|
|
) {
|
|
const {height, mouseX, mouseY, width} = mousePosition;
|
|
let top: number | string = 0;
|
|
let left: number | string = 0;
|
|
|
|
if (mouseY + TOOLTIP_OFFSET + element.offsetHeight >= height) {
|
|
if (mouseY - TOOLTIP_OFFSET - element.offsetHeight > 0) {
|
|
top = `${mouseY - element.offsetHeight - TOOLTIP_OFFSET}px`;
|
|
} else {
|
|
top = '0px';
|
|
}
|
|
} else {
|
|
top = `${mouseY + TOOLTIP_OFFSET}px`;
|
|
}
|
|
|
|
if (mouseX + TOOLTIP_OFFSET + element.offsetWidth >= width) {
|
|
if (mouseX - TOOLTIP_OFFSET - element.offsetWidth > 0) {
|
|
left = `${mouseX - element.offsetWidth - TOOLTIP_OFFSET}px`;
|
|
} else {
|
|
left = '0px';
|
|
}
|
|
} else {
|
|
left = `${mouseX + TOOLTIP_OFFSET * 2}px`;
|
|
}
|
|
|
|
return {left, top};
|
|
}
|
|
|
|
// method used to find the current mouse position inside the container
|
|
function getMousePosition(
|
|
relativeContainer: null,
|
|
mouseEvent: SyntheticMouseEvent,
|
|
) {
|
|
if (relativeContainer !== null) {
|
|
// Position within the nearest position:relative container.
|
|
let targetContainer = relativeContainer;
|
|
while (targetContainer.parentElement != null) {
|
|
if (targetContainer.style.position === 'relative') {
|
|
break;
|
|
} else {
|
|
targetContainer = targetContainer.parentElement;
|
|
}
|
|
}
|
|
|
|
const {height, left, top, width} = targetContainer.getBoundingClientRect();
|
|
|
|
const mouseX = mouseEvent.clientX - left;
|
|
const mouseY = mouseEvent.clientY - top;
|
|
|
|
return {height, mouseX, mouseY, width};
|
|
} else {
|
|
return initialTooltipState;
|
|
}
|
|
}
|