All files / Rindu/hooks useDynamicFontSize.ts

3.22% Statements 2/62
0% Branches 0/19
0% Functions 0/6
3.38% Lines 2/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 112 113 114 115 116 117 118 119 120 121 122 12319x   19x                                                                                                                                                                                                                                                
import { useLayoutEffect } from "react";
 
import { useOnSmallScreen } from "./useOnSmallScreen";
 
interface UseDynamicFontSizeProps {
  element: HTMLHeadingElement | null;
  maxFontSize: number;
  minFontSize: number;
  maxHeight: number;
  lineNum?: number;
}
 
function measureText(
  pText: string,
  pFontSize: number,
  width: number,
  maxHeight: number,
  lineNum: number
) {
  let lDiv: HTMLDivElement | null = document.createElement("div");
 
  document.body.appendChild(lDiv);
  lDiv.style.fontSize = `${pFontSize}px`;
  lDiv.style.position = "absolute";
  lDiv.style.left = "-1000";
  lDiv.style.top = "-1000";
  lDiv.style.maxWidth = `${width}px`;
  lDiv.style.maxHeight = `${maxHeight}px`;
  lDiv.style.webkitLineClamp = `${lineNum}`;
  lDiv.style.textOverflow = "ellipsis";
  lDiv.style.overflow = "hidden";
  lDiv.style.webkitBoxOrient = "vertical";
  lDiv.style.display = "-webkit-box";
  lDiv.style.lineHeight = "1";
  lDiv.textContent = pText;
  const lResult = {
    width: lDiv.scrollWidth,
    height: lDiv.scrollHeight,
  };
 
  document.body.removeChild(lDiv);
  lDiv = null;
 
  return lResult;
}
 
export function fitText(
  el: HTMLHeadingElement,
  maxFontSize: number,
  minFontSize: number,
  maxHeight: number,
  lineNum: number
): void {
  const text = el.textContent;
  Iif (!text) return;
  const computedStyles = getComputedStyle(el);
  let fsize = parseInt(computedStyles.fontSize);
  fsize = Math.max(fsize, minFontSize);
  const { width } = el.getBoundingClientRect();
  const measured = measureText(text, fsize, width, maxHeight, lineNum);
  const letsBeTrue = true;
  if (measured.width > width || measured.height > maxHeight) {
    while (letsBeTrue) {
      fsize = parseInt(computedStyles.fontSize);
      const m = measureText(text, fsize, width, maxHeight, lineNum);
      if ((m.width > width && fsize > minFontSize) || m.height > maxHeight) {
        el.style.fontSize = `${--fsize}px`;
      } else {
        break;
      }
    }
  } else {
    while (letsBeTrue) {
      fsize = parseInt(computedStyles.fontSize);
      const m = measureText(text, fsize, width, maxHeight, lineNum);
      if (m.width < width - 4 && fsize < maxFontSize) {
        el.style.fontSize = `${++fsize}px`;
      } else {
        break;
      }
    }
  }
}
 
export function useDynamicFontSize({
  element,
  maxFontSize,
  minFontSize,
  maxHeight,
  lineNum = 1,
}: UseDynamicFontSizeProps): void {
  const isSmallScreen = useOnSmallScreen();
  useLayoutEffect(() => {
    Iif (isSmallScreen) {
      Iif (!element) return;
      element.style.fontSize = `${minFontSize}px`;
      return;
    }
    const handleResize = () => {
      Iif (element) {
        fitText(element, maxFontSize, minFontSize, maxHeight, lineNum);
      }
    };
 
    window.addEventListener("resize", handleResize);
    handleResize();
 
    return () => {
      window.removeEventListener("resize", handleResize);
      Iif (!element) return;
      element.style.fontSize = "";
    };
  }, [
    element,
    maxFontSize,
    element?.textContent,
    minFontSize,
    maxHeight,
    lineNum,
    isSmallScreen,
  ]);
}