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;
}