What I have so far:
I’m working on a very generic table based on a configuration. This table is generated based on a graph, the final calculated data structure is
type TableMatrix = Cell[][];
interface Cell {
isCoveredByPreviousCell: boolean;
rowspan: number;
offset: number;
hasContent: boolean;
columnIndex: number;
}
I grabbed data from a correct table to test with ( Playground with test data )
Sidenote: Based on this data I know there will be 14 columns to render
When it comes to rendering I decided to use VueJs ( Playground Link )
<script setup>
import { tableMatrix } from './data.ts'
</script>
<template>
<table>
<thead>
<tr>
<th v-for="headerIndex in 14">
Header {{ headerIndex }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in tableMatrix" :key="rowIndex">
<!-- Inser line number cell here -->
<template v-for="(cell, columnIndex) in row" :key="columnIndex">
<td v-if="cell.isCoveredByPreviousCell" :style="{ 'display': 'none' }"/>
<td v-else :rowspan="cell.rowspan">
<template v-if="cell.hasContent">
<div>I belong to col {{ cell.columnIndex + 1 }}</div>
</template>
</td>
</template>
</tr>
</tbody>
</table>
</template>
<style>
table, th, td {
border: 1px solid black;
}
</style>
What I want to achieve:
I want to add line numbers to every leading row.
A row is a leading row if every cell in that row is not covered by a previous cell. Pseudo example:
(row /* Cell[] */ ).every((cell) => !cell.isCoveredByPreviousCell);
So for this example ( the first leading row in the first data column might have multiple cells but not in this example ) the desired output would be
What I’ve tried so far:
I thought I could create a structure that knows the correct row index for a line number cell and the required rowspan.
const lineNumbersMatrix = computed(() => {
interface LineNumberInfo {
index: number;
rowspan: number;
}
const matrix = new Map<number, LineNumberInfo>();
let currentLineIndex = 0;
for (let rowIndex = 0; rowIndex < tableMatrix.value.length; rowIndex++) {
const row = tableMatrix.value[rowIndex];
// skip if not a leading row
if (row.some((cell) => cell.isCoveredByPreviousCell)) {
continue;
}
let lineNumberRowspan = 1;
for (const cell of row) {
if (lineNumberRowspan >= cell.rowspan) {
continue;
}
lineNumberRowspan = cell.rowspan;
}
matrix.set(rowIndex, {
index: currentLineIndex,
rowspan: lineNumberRowspan
});
currentLineIndex ++;
}
return matrix;
});
After that I thought I could add a new table header
<th>Line</th>
and replace the comment
<!-- Inser line number cell here -->
with
<td v-if="lineNumbersMatrix.has(rowIndex)" :rowspan="lineNumbersMatrix.get(rowIndex)!.rowspan">{{ lineNumbersMatrix.get(rowIndex)!.index }}</td>
to get this Result, which seems to work as expected.
Unfortunately my real project renders an additional blank column at the end of the table
and I can’t reproduce it in the sandbox…
I’m assuming my approach is not 100% correct and there might be better ways. Do you have any ideas how to solve this?