jquintozamora/react-d3-treemap

View on GitHub
src/components/Node/helpers.ts

Summary

Maintainability
A
2 hrs
Test Coverage
let canvas;
export const getTextDimensions = (
  text: string,
  style: React.CSSProperties = {
    fontVariant: "normal",
    fontWeight: "normal",
    fontSize: 14,
    fontFamily: "Arial",
  }
): { width: number; height: number } => {
  // re-use canvas object for better performance
  if (!canvas) {
    canvas = document.createElement("canvas");
  }
  const context = canvas.getContext("2d");
  const { fontVariant, fontWeight, fontSize, fontFamily } = style;
  const heightSpacingAround = 2;
  const height = Number(fontSize) + heightSpacingAround;
  if (context) {
    context.font = `${fontVariant} ${fontWeight} ${fontSize}px '${fontFamily}'`;
    return {
      width: Number(context.measureText(text).width),
      height,
    };
  } else {
    return { width: 0, height };
  }
};

const charWidthCache: Record<string, number> = {};
export const truncateText = (
  text: string,
  style: React.CSSProperties,
  maxWidth: number,
  ellipsis = "..."
): string => {
  const cachedCharWidth = (char: string) => {
    const cached = charWidthCache[char];
    if (cached !== undefined) {
      return cached;
    }
    const charWidth = getTextDimensions(char, style).width;
    charWidthCache[char] = charWidth;
    return charWidth;
  };

  const truncatedChars: string[] = [];
  const charArray = Array.from(text);

  const ellipsisWidth = cachedCharWidth(ellipsis);
  if (maxWidth - ellipsisWidth < 0) {
    return text.charAt(0);
  }

  let currentWidth = ellipsisWidth;
  let didTruncate = false;
  for (let i = 0; i < charArray.length; i++) {
    const charWidth = cachedCharWidth(charArray[i]);
    if (currentWidth + charWidth <= maxWidth) {
      truncatedChars[i] = charArray[i];
      currentWidth += charWidth;
    } else {
      truncatedChars[i] = ellipsis;
      didTruncate = true;
      break;
    }
  }

  if (didTruncate) {
    return truncatedChars.join("");
  }

  return text;
};

export const getNumberItemsWidthByNumberOfChars = (
  fontSize: number,
  numberOfChars: number
): number => {
  return (fontSize / 2) * numberOfChars + 5;
};