export function tableRenderer()

in frontend/libs/spreadsheet/src/lib/utils/renderers/tableRenderer/tableRenderer.ts [80:393]


export function tableRenderer(
  cell: GridCell,
  width: number,
  height: number,
  zoom: number,
  maxRows: number
) {
  const container = document.createElement('div');
  let contentWrapper;

  container.dataset.row = cell.row.toString();
  container.dataset.col = cell.col.toString();

  if (!cell.table)
    throw new Error("[tableRenderer] cell doesn't contain table");

  const { startRow, startCol, endRow, endCol, tableName, isTableHorizontal } =
    cell.table;
  const { col, row, field } = cell;

  const isHeader = !!cell.isTableHeader;
  const isField = !!cell.isFieldHeader;
  const isCell = !isHeader && !isField;
  const isTotalCell = !!cell.totalIndex;
  const isTopBorder = isHeader || row === startRow;
  const isLeftBorder = isHeader || col === startCol;
  const isRightBorder =
    isHeader || field ? cell.endCol === endCol : col === endCol;
  const isBottomBorder = row === endRow;
  const fieldSize =
    cell.field && !isTableHorizontal ? cell.endCol - cell.startCol + 1 : 1;
  const valueIndex = cell.dataIndex;
  const isFieldError = cell.field?.hasError && cell.field?.errorMessage;
  const isCellError = cell.hasError && cell.errorMessage;
  const isTableFieldsHeaderHidden = cell.table.isTableFieldsHeaderHidden;

  // Do not add styles to no width cells
  if (width === 0) {
    container.classList.add('zero-width');

    return container;
  }

  container.style.zIndex = cell.zIndex?.toString() || defaultZIndex.toString();

  // Highlight even columns or rows
  if (
    !isHeader &&
    !isField &&
    !isTotalCell &&
    valueIndex &&
    valueIndex % 2 === 1
  ) {
    container.classList.add(styles.colored);
  }

  if (cell.table.isNewAdded) {
    container.classList.add(styles.diff);
  }

  // handle table header styles
  if (cell.value && (!isHeader || (isHeader && col === startCol))) {
    contentWrapper = document.createElement('div');
    contentWrapper.classList.add(styles.contentWrapper);

    const isUrl = isCell && cell.isUrl;
    const contentTag = isUrl ? 'a' : 'div';
    const content = document.createElement(contentTag);
    content.classList.add(styles.content);

    // To show full table name
    if (isHeader) {
      contentWrapper.classList.add(tableHeaderTitleClass);
    }

    if (cell.isRightAligned) {
      contentWrapper.classList.add(styles.justifyRight);
    }

    if (isUrl && content instanceof HTMLAnchorElement) {
      content.href = cell.value;
      content.target = '_blank';
      content.draggable = false;
      content.innerHTML = cell.value;
    } else {
      let value;
      if (isHeader) {
        value = unescapeTableName(cell.value);
      } else if (isField) {
        value = unescapeFieldName(cell.value);
      } else {
        value = cell.value;
      }

      if (
        cell.field?.type === ColumnDataType.DATE &&
        !field?.isNested &&
        !isField
      ) {
        try {
          content.innerHTML = formatDate(value);
        } catch {
          content.innerHTML = value || '';
        }
      } else {
        content.innerHTML = value || '';
      }
    }

    contentWrapper.dataset.row = cell.row.toString();
    contentWrapper.dataset.col = cell.col.toString();
    content.dataset.row = cell.row.toString();
    content.dataset.col = cell.col.toString();

    contentWrapper.appendChild(content);
    container.appendChild(contentWrapper);

    if (
      cell.isOverride &&
      cell.field?.expression &&
      cell.field.expression !== naExpression &&
      !isCellError
    ) {
      container.classList.add(styles.overrideBorder);
    }
  }

  container.classList.add(styles.container);

  // Header rendered only once for visible cell
  if (isHeader) {
    container.classList.add(styles.header);
    container.classList.add(tableHeaderClass);
    container.style.width = `${width}px`;
    container.classList.add(styles.rightBorder);
    container.id = getTableHeaderId(cell);

    const cmButton = setIconButton(cmButtonInit, height, cell);
    const closeButton = setIconButton(closeButtonInit, height, cell);

    closeButton.classList.add(styles.closeButton);
    closeButton.dataset.tableName = tableName;
    cmButton.classList.add(styles.cmButton);

    const buttonsContainer = document.createElement('div');
    buttonsContainer.classList.add(styles.headerButtonsContainer);
    buttonsContainer.classList.add(tableHeaderButtonsContainerClass);

    buttonsContainer.appendChild(cmButton);
    buttonsContainer.appendChild(closeButton);

    container.appendChild(buttonsContainer);
  }

  // Handle table field header styles
  if (isField) {
    container.classList.add(styles.field);

    if (cell.field?.isKey) {
      container.classList.add(styles.keyField);
    }

    if (cell.field?.note) {
      const commentIndicator = document.createElement('div');
      commentIndicator.classList.add(styles.hasComment);
      const size = getPx(10 * zoom);
      commentIndicator.style.width = size;
      commentIndicator.style.height = size;
      commentIndicator.style.borderWidth = `0 ${size} ${size} 0`;
      commentIndicator.classList.add(commentTextClass);
      container.appendChild(commentIndicator);
    }

    if (!cell.field?.isDynamic) {
      const applyButton = getApplyButton(cell, height);
      container.appendChild(applyButton);
    }
  }

  if ((isField && isFieldError) || isCellError) {
    // For current implementation we need to calc zIndex to show tooltip trigger above all cells
    const baseZIndex = parseInt(container.style.zIndex);
    container.style.zIndex = (
      baseZIndex +
      endCol -
      col +
      maxRows -
      row +
      normalizedZIndex
    ).toString();

    const tooltipTrigger = document.createElement('span');
    tooltipTrigger.classList.add(styles.errorTooltipTrigger);
    tooltipTrigger.classList.add(errorTooltipTriggerClass);

    if (isField) {
      container.classList.add(
        isTableHorizontal
          ? styles.errorFieldBorderHorizontal
          : styles.errorFieldBorder
      );
    }

    if (isCellError) {
      container.classList.add(styles.errorBorder);
    }

    container.appendChild(tooltipTrigger);
  }

  if (isFieldError) {
    if (
      isTableFieldsHeaderHidden &&
      (cell.totalIndex === 1 ||
        (cell.table.totalSize === 0 && valueIndex === 0))
    ) {
      container.classList.add(
        isTableHorizontal ? styles.errorLeftBorder : styles.errorTopBorder
      );
    }

    if (isTableHorizontal && isRightBorder) {
      container.classList.add(styles.errorRightBorder);
    }
    if (isBottomBorder) {
      container.classList.add(styles.errorBottomBorder);
    }

    if (isCell) {
      container.classList.add(
        isTableHorizontal ? styles.errorYBorder : styles.errorXBorder
      );
    }
  }

  // Show right resize for field
  if (!isHeader && !cell.field?.isDynamic && !isTableHorizontal && cell.field) {
    const resizeTrigger = document.createElement('span');
    resizeTrigger.classList.add(resizeCellTriggerClass);
    resizeTrigger.classList.add(styles.resizeTrigger);
    if (fieldSize > 1) {
      resizeTrigger.classList.add(styles.resizeFullTrigger);
    } else {
      resizeTrigger.classList.add(styles.resizeLeftTrigger);
    }
    resizeTrigger.dataset.row = cell.row.toString();
    resizeTrigger.dataset.col = cell.col.toString();
    container.appendChild(resizeTrigger);
  }

  // total cell
  if (isTotalCell) {
    container.classList.add(styles.totalCell);

    if (cell.totalIndex === 1) {
      container.classList.add(
        isTableHorizontal ? styles.firstColTotalCell : styles.firstRowTotalCell
      );
    }

    if (cell.totalIndex === cell.table.totalSize) {
      container.classList.add(
        isTableHorizontal ? styles.lastColTotalCell : styles.lastRowTotalCell
      );
    }
  }

  // handle table cells

  // handle different types of column, cells has icons depending on type of column
  if (cell.value !== undefined) {
    const icon = getCellIcon(cell, isCell, isField, height);

    if (icon) {
      container.insertAdjacentElement('afterbegin', icon);
    }
  }

  if (isCell && cell?.field?.isKey) {
    container.classList.add(styles.keyCell);
  }

  if (isCell && cell.field?.isDynamic && cell.value === undefined) {
    const content = document.createElement('div');
    content.innerHTML = 'loading...';
    content.dataset.row = cell.row.toString();
    content.dataset.col = cell.col.toString();
    content.classList.add(styles.content);
    content.style.color = 'gray';
    container.appendChild(content);
  }

  if (isTopBorder) {
    container.classList.add(styles.topBorder);
  }

  if (isLeftBorder) {
    container.classList.add(styles.leftBorder);

    container.prepend(createShadow(height, zoom, 'left'));
  }

  if (isRightBorder) {
    container.classList.add(styles.rightBorder);

    container.append(createShadow(height, zoom, 'right'));
  }

  if (isBottomBorder) {
    container.classList.add(styles.bottomBorder);
  }

  return container;
}