Pin the row-label columns by index, not through __ROW_PATH__
Row labels still did not fit, because the override was landing on the wrong column. restore_column_size_overrides maps the key "__ROW_PATH__" to index tree_header_offset - 1, which with two group_by levels is index 2 — the first data column. That column is the grand total, which the stylesheet hides, so pinning it changed nothing visible. The row labels are separate columns (rt-col-0 and rt-col-1 here, one per group_by level), so each is measured on its own and set by index through regular-table's saveColumnSizes / restoreColumnSizes, which are index-based and public. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e7c266a871
commit
9ccaabd4c1
@ -1069,7 +1069,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
||||
grid.resetAutoSize()
|
||||
await grid.draw({ invalid_columns: true })
|
||||
|
||||
await fitRowLabels(viewer, grid)
|
||||
await fitRowLabels(grid)
|
||||
|
||||
// Widths are not part of ViewConfig, so persist the layout to keep the
|
||||
// saved copy in step with what is on screen.
|
||||
@ -1081,47 +1081,53 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
||||
}
|
||||
}
|
||||
|
||||
// Pin the row-label column wide enough for the labels it shows.
|
||||
// Pin the row-label columns wide enough for the labels they show.
|
||||
//
|
||||
// Measured with a canvas rather than the DOM: the cell is clipped, so reading
|
||||
// its box back gives the width it was allotted, not the width of its text --
|
||||
// its box back gives the width it was allotted, not the width of its text —
|
||||
// the same circularity that stops headers sizing themselves.
|
||||
//
|
||||
// __ROW_PATH__ is the key restore_column_size_overrides special-cases for this
|
||||
// column, and an override is what survives every subsequent draw, which is
|
||||
// exactly why overrides were defeating resetAutoSize in the first place.
|
||||
async function fitRowLabels(viewer, grid) {
|
||||
// Set by column *index* through regular-table's own saveColumnSizes /
|
||||
// restoreColumnSizes, not as a plugin_config column_size_override. The override
|
||||
// path maps the key "__ROW_PATH__" to index tree_header_offset - 1, which with
|
||||
// two group_by levels is index 2 — the first data column, not a row label.
|
||||
// Pinning that was doing nothing visible because that column is the
|
||||
// grand-total one, which the stylesheet hides.
|
||||
async function fitRowLabels(grid) {
|
||||
const cells = [...grid.querySelectorAll('tbody th')]
|
||||
if (cells.length === 0) return
|
||||
|
||||
const probe = cells[0]
|
||||
const style = getComputedStyle(probe)
|
||||
const style = getComputedStyle(cells[0])
|
||||
const ctx = (fitRowLabels._ctx ||= document.createElement('canvas').getContext('2d'))
|
||||
ctx.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`
|
||||
|
||||
// Indentation is real width: a tree label sits inside a flex container with
|
||||
// the expand control beside it, so measure from the cell's left edge.
|
||||
const padding = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight)
|
||||
let widest = 0
|
||||
|
||||
// One width per row-header column, keyed by the rt-col-N the cell carries.
|
||||
const widest = new Map()
|
||||
for (const cell of cells) {
|
||||
const col = [...cell.classList]
|
||||
.map(c => /^rt-col-(\d+)$/.exec(c))
|
||||
.find(Boolean)
|
||||
if (!col) continue
|
||||
const idx = Number(col[1])
|
||||
const text = (cell.textContent || '').trim()
|
||||
if (!text) continue
|
||||
const indent = cell.querySelector('span.rt-tree-container')
|
||||
? (parseFloat(getComputedStyle(cell.firstElementChild).paddingLeft) || 0)
|
||||
: 0
|
||||
widest = Math.max(widest, ctx.measureText(text).width + indent)
|
||||
// Tree indentation occupies real width, so it counts toward the fit.
|
||||
const inner = cell.querySelector('span.rt-tree-container')
|
||||
const indent = inner ? (parseFloat(getComputedStyle(inner).paddingLeft) || 0) : 0
|
||||
const w = ctx.measureText(text).width + indent
|
||||
widest.set(idx, Math.max(widest.get(idx) ?? 0, w))
|
||||
}
|
||||
if (widest === 0) return
|
||||
if (widest.size === 0) return
|
||||
|
||||
// A few px over, since canvas metrics and rendered text differ slightly with
|
||||
// font fallback and letter-spacing.
|
||||
const width = Math.ceil(widest + padding + 8)
|
||||
|
||||
const { table: _t, ...cfg } = await viewer.save()
|
||||
const pc = { ...(cfg.plugin_config || {}) }
|
||||
pc.columns = { ...(pc.columns || {}) }
|
||||
pc.columns.__ROW_PATH__ = { ...(pc.columns.__ROW_PATH__ || {}), column_size_override: width }
|
||||
await viewer.restore({ ...cfg, plugin_config: pc })
|
||||
const sizes = { ...grid.saveColumnSizes() }
|
||||
for (const [idx, w] of widest) {
|
||||
// A few px over: canvas metrics and rendered text differ slightly with font
|
||||
// fallback and letter-spacing.
|
||||
sizes[idx] = Math.ceil(w + padding + 8)
|
||||
}
|
||||
grid.restoreColumnSizes(sizes)
|
||||
await grid.draw({ invalid_columns: true })
|
||||
}
|
||||
|
||||
const GRID_CSS = `
|
||||
|
||||
Loading…
Reference in New Issue
Block a user