wip
This commit is contained in:
@@ -15,38 +15,99 @@ export function DenseTable(props: {
|
||||
const cols = props.table.columns;
|
||||
const rows = props.table.rows;
|
||||
|
||||
const cfgPct = cols.length >= 12 ? 0.28 : 0.32;
|
||||
const dataPct = 1 - cfgPct;
|
||||
const each = cols.length ? dataPct / cols.length : dataPct;
|
||||
const cfgW = `${Math.round(cfgPct * 100)}%`;
|
||||
const dataW = `${Math.round(clamp(each, 0.03, 0.12) * 1000) / 10}%`;
|
||||
const noWrapHeader = (label: string): string => {
|
||||
const raw = String(label || '').trim();
|
||||
if (!raw) return '';
|
||||
|
||||
// Ensure the header never wraps into a second line.
|
||||
// - Remove whitespace break opportunities (NBSP)
|
||||
// NOTE: Avoid inserting zero-width joiners between letters.
|
||||
// Some PDF viewers render them with spacing/odd glyph behavior.
|
||||
// This is intentionally aggressive because broken headers destroy scanability.
|
||||
return raw.replace(/\s+/g, '\u00A0');
|
||||
};
|
||||
|
||||
// Column widths: use explicit percentages (no rounding gaps) so the table always
|
||||
// consumes the full content width.
|
||||
// Goal:
|
||||
// - keep the designation column *not too wide*
|
||||
// - guarantee enough width for data headers when there is available space
|
||||
const cfgMin = 0.18;
|
||||
const cfgMax = 0.30;
|
||||
|
||||
let cfgPct = cols.length >= 14 ? 0.22 : cols.length >= 12 ? 0.24 : cols.length >= 10 ? 0.26 : 0.30;
|
||||
cfgPct = clamp(cfgPct, cfgMin, cfgMax);
|
||||
|
||||
const minDataPct = cols.length >= 14 ? 0.045 : cols.length >= 12 ? 0.05 : cols.length >= 10 ? 0.055 : 0.06;
|
||||
// If the initial cfgPct leaves too little width per data column, reduce cfgPct.
|
||||
const cfgPctMaxForMinData = 1 - cols.length * minDataPct;
|
||||
if (Number.isFinite(cfgPctMaxForMinData)) {
|
||||
cfgPct = Math.min(cfgPct, cfgPctMaxForMinData);
|
||||
cfgPct = clamp(cfgPct, cfgMin, cfgMax);
|
||||
}
|
||||
|
||||
const cfgW = `${(cfgPct * 100).toFixed(4)}%`;
|
||||
const dataTotal = 1 - cfgPct;
|
||||
const each = cols.length ? dataTotal / cols.length : dataTotal;
|
||||
const dataWs = cols.map((_, idx) => {
|
||||
// Keep the last column as the remainder so percentages sum to exactly 100%.
|
||||
if (idx === cols.length - 1) {
|
||||
const used = each * Math.max(0, cols.length - 1);
|
||||
const remainder = Math.max(0, dataTotal - used);
|
||||
return `${(remainder * 100).toFixed(4)}%`;
|
||||
}
|
||||
return `${(each * 100).toFixed(4)}%`;
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.tableWrap}>
|
||||
<View style={styles.tableHeader}>
|
||||
<View style={styles.tableHeader} wrap={false}>
|
||||
<View style={{ width: cfgW }}>
|
||||
<Text style={styles.tableHeaderCell}>{props.firstColLabel}</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.tableHeaderCell,
|
||||
styles.tableHeaderCellCfg,
|
||||
cols.length ? styles.tableHeaderCellDivider : null,
|
||||
]}
|
||||
wrap={false}
|
||||
>
|
||||
{noWrapHeader(props.firstColLabel)}
|
||||
</Text>
|
||||
</View>
|
||||
{cols.map(c => (
|
||||
<View key={c.key} style={{ width: dataW }}>
|
||||
<Text style={styles.tableHeaderCell}>{c.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
{cols.map((c, idx) => {
|
||||
const isLast = idx === cols.length - 1;
|
||||
return (
|
||||
<View key={c.key} style={{ width: dataWs[idx] }}>
|
||||
<Text
|
||||
style={[styles.tableHeaderCell, !isLast ? styles.tableHeaderCellDivider : null]}
|
||||
wrap={false}
|
||||
>
|
||||
{noWrapHeader(c.label)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
{rows.map((r, ri) => (
|
||||
<View key={`${r.configuration}-${ri}`} style={[styles.tableRow, ri % 2 === 0 ? styles.tableRowAlt : null]}>
|
||||
<View
|
||||
key={`${r.configuration}-${ri}`}
|
||||
style={[styles.tableRow, ri % 2 === 0 ? styles.tableRowAlt : null]}
|
||||
wrap={false}
|
||||
>
|
||||
<View style={{ width: cfgW }}>
|
||||
<Text style={styles.tableCell}>{r.configuration}</Text>
|
||||
<Text style={[styles.tableCell, styles.tableCellCfg, cols.length ? styles.tableCellDivider : null]}>{r.configuration}</Text>
|
||||
</View>
|
||||
{r.cells.map((cell, ci) => (
|
||||
<View key={`${cols[ci]?.key || ci}`} style={{ width: dataW }}>
|
||||
<Text style={styles.tableCell}>{cell}</Text>
|
||||
</View>
|
||||
))}
|
||||
{r.cells.map((cell, ci) => {
|
||||
const isLast = ci === r.cells.length - 1;
|
||||
return (
|
||||
<View key={`${cols[ci]?.key || ci}`} style={{ width: dataWs[ci] }}>
|
||||
<Text style={[styles.tableCell, !isLast ? styles.tableCellDivider : null]}>{cell}</Text>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user