import { useEffect, useRef, useState } from "react"; import { lineHeights } from "../../theme"; import { typographyVariants } from "../text"; import { useIsomorphicLayoutEffect } from "../../utils"; function isValidFontSizeKey(key: number): key is keyof typeof lineHeights { return Object.keys(lineHeights).includes(key.toString()); } const lineHeightsByTextVariant = Object.entries(typographyVariants).reduce>( (acc, [key, value]) => { if (!value.fontSize) { throw new Error(`${key} is missing fontSize`); } const fontSizeIndex = parseInt(value.fontSize.toString().slice(1)); if (!isValidFontSizeKey(fontSizeIndex)) { throw new Error(`${fontSizeIndex} is not a valid fontSize key`); } acc[key as keyof typeof typographyVariants] = lineHeights[fontSizeIndex]; return acc; }, {} ) as Record; // eslint-disable-next-line max-lines-per-function export const useIsTextOverflow = ({ rows, trigger, textVariant, }: { rows: number; trigger?: unknown; textVariant: keyof typeof lineHeightsByTextVariant; }) => { const [isOverflow, setIsOverflow] = useState(false); const [width, setWidth] = useState(0); const textRef = useRef(null); const observerRef = useRef( typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver((entries) => { const firstEntry = entries[0]; const firstEntryContentRect = firstEntry?.contentRect; setWidth(firstEntryContentRect?.width); }) ); useEffect(() => { const current = textRef.current; const observer = observerRef.current; if (current && observer) { observer.observe(current); } return () => { current && observer?.unobserve(current); }; }, [textRef]); useIsomorphicLayoutEffect(() => { const { current } = textRef; if (current) { const currentOverflow = current.style.overflow; const currentHeight = current.style.height; if (!currentOverflow || currentOverflow !== "hidden") { current.style.overflow = "hidden"; current.style.height = expectedHeightWhenClosed; } const hasOverflow = current.scrollHeight > current.clientHeight; setIsOverflow(hasOverflow); current.style.overflow = currentOverflow; current.style.height = currentHeight; } }, [width, ...(Array.isArray(trigger) ? trigger : [trigger])]); const expectedHeightWhenClosed = `calc(${rows} * ${lineHeightsByTextVariant[textVariant]})`; return { isOverflow, textRef, expectedHeightWhenClosed }; };