| import type { ReactNode } from "react"; |
| import { useLayoutEffect, useRef, useState } from "react"; |
|
|
| type TooltipLabelProps = { |
| value: string; |
| className?: string; |
| children?: ReactNode; |
| }; |
|
|
| export function TooltipLabel({ value, className, children }: TooltipLabelProps) { |
| const classes = ["tooltipAnchor", className].filter(Boolean).join(" "); |
| const labelRef = useRef<HTMLSpanElement>(null); |
| const measureRef = useRef<HTMLSpanElement>(null); |
| const [isCollapsed, setIsCollapsed] = useState(false); |
|
|
| useLayoutEffect(() => { |
| const labelElement = labelRef.current; |
| const measureElement = measureRef.current; |
| if (!labelElement || !measureElement) { |
| return undefined; |
| } |
|
|
| const updateCollapsedState = () => { |
| const styles = window.getComputedStyle(labelElement); |
| const horizontalPadding = |
| Number.parseFloat(styles.paddingLeft) + Number.parseFloat(styles.paddingRight); |
| const availableWidth = labelElement.clientWidth - horizontalPadding; |
| const fullTextWidth = measureElement.getBoundingClientRect().width; |
|
|
| setIsCollapsed(fullTextWidth > availableWidth + 1); |
| }; |
|
|
| updateCollapsedState(); |
|
|
| const resizeObserver = new ResizeObserver(updateCollapsedState); |
| resizeObserver.observe(labelElement); |
| resizeObserver.observe(measureElement); |
| window.addEventListener("resize", updateCollapsedState); |
|
|
| return () => { |
| resizeObserver.disconnect(); |
| window.removeEventListener("resize", updateCollapsedState); |
| }; |
| }, [value, children]); |
|
|
| return ( |
| <span |
| ref={labelRef} |
| className={classes} |
| data-tooltip={isCollapsed ? value : undefined} |
| tabIndex={isCollapsed ? 0 : undefined} |
| > |
| <span className="truncateText">{children ?? value}</span> |
| <span ref={measureRef} className="tooltipMeasure" aria-hidden="true"> |
| {value} |
| </span> |
| </span> |
| ); |
| } |
|
|