umami/components/common/OverflowText.js

67 lines
1.7 KiB
JavaScript
Raw Normal View History

import PropTypes from 'prop-types';
import { useCallback, useEffect, useRef, useState } from 'react';
import ReactTooltip from 'react-tooltip';
import styles from './OverflowText.module.css';
const OverflowText = ({ children, tooltipId }) => {
const measureEl = useRef();
const [isOverflown, setIsOverflown] = useState(false);
const measure = useCallback(
el => {
if (!el) return;
setIsOverflown(el.scrollWidth > el.clientWidth);
},
[setIsOverflown],
);
// Do one measure on mount
useEffect(() => {
measure(measureEl.current);
}, [measure]);
// Set up resize listener for subsequent measures
useEffect(() => {
if (!measureEl.current) return;
// Destructure ref in case it changes out from under us
const el = measureEl.current;
if ('ResizeObserver' in global) {
// Ideally, we have access to ResizeObservers
const observer = new ResizeObserver(() => {
measure(el);
});
observer.observe(el);
return () => observer.unobserve(el);
} else {
// Otherwise, fall back to measuring on window resizes
const handler = () => measure(el);
window.addEventListener('resize', handler, { passive: true });
return () => window.removeEventListener('resize', handler, { passive: true });
}
});
return (
<span
ref={measureEl}
data-tip={children.toString()}
data-effect="solid"
data-for={tooltipId}
className={styles.root}
>
{children}
{isOverflown && <ReactTooltip id={tooltipId}>{children}</ReactTooltip>}
</span>
);
};
OverflowText.propTypes = {
children: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
tooltipId: PropTypes.string.isRequired,
};
export default OverflowText;