Source

utils/processing/columnsMinWidthCalc/columnsMinWidthCalc.ts

import {
  HeadingsElementInterface,
  DataElementInterface,
} from '../../types/types';
import { StyleInterface } from '../../types/StyleInterface';
import { getCellInterTextLength } from '../getCellInterTextLength/getCellInterTextLength';
import { resolveCellFontStyle } from '../resolveCellFontStyle/resolveCellFontStyle';

/**
 * Calculate the min-width for each column (heading + data).
 * Uses the Canvas API to measure the length of the text of each cell and return the max for each column
 * Takes into account of the font style, cell inter text length and the sorting image of column headers.
 * @param {array} headings - the headings array.
 * @param {array} data - the data array.
 * @param {HTMLElement} ref - the ref to the pluggin html element, to recover font size and font family.
 * @param {object} style - the style state from the store.
 * @memberof utils
 * @function
 * @returns {array} - minwidth value for each column (in px) in the same sequence as the headings array.
 */
export const columnsMinWidthCalc = (
  headings: HeadingsElementInterface[],
  data: DataElementInterface[],
  ref: HTMLElement,
  style: StyleInterface
): number[] | undefined => {
  if (!ref) return;

  const { bodyStyle, headStyle } = resolveCellFontStyle(ref, style);

  //create canvas and test each body cell text length with applied style to find the longer
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  if (!context) return;
  //console.log(bodyStyle); //------------------------------------------------------------------------------------------------------------------
  context.font = `${bodyStyle.fontWeight} ${bodyStyle.fontSize} ${bodyStyle.fontFamily}`;
  const bodyColumnMinWidth: number[] = headings.map((heading) =>
    data
      .map((item) => {
        const value = item[heading.key];
        if (typeof value === 'string') return value;
        else return value.toString();
      })
      .reduce<number>(
        (acc: number, cur: string): number =>
          Math.max(acc, context.measureText(cur).width),
        0
      )
  );
  //compare with the column header cell text length and keep the longer, then add the intertextLength (padding+border)
  context.font = `${headStyle.fontWeight} ${headStyle.fontSize} ${headStyle.fontFamily}`;
  const cellInterTextLength = getCellInterTextLength(style);
  const columnMinWidth: number[] = headings.map(
    (heading, index): number =>
      Math.ceil(
        Math.max(
          bodyColumnMinWidth[index],
          context.measureText(heading.label).width +
            parseInt(headStyle.fontSize) // is the sort icon in column header (1em width)
        )
      ) + cellInterTextLength
  );
  canvas.remove();
  return columnMinWidth;
};