From 9a748b8178bfef78c69bca3a7e45be07d496ea97 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Thu, 17 Sep 2026 14:02:59 -0400 Subject: [PATCH] Let headers and row labels size their own column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fit worked on the values and not on the headings. Auto-fit measures cells with getBoundingClientRect() and sets each column's min-width from the result, but the datagrid's CSS wraps and clips header text — so a clipped th measures at the width it was *allotted*, not the width of its content, and a column can never grow to fit its own heading. Row labels are th elements in tbody and clip for the same reason. white-space: nowrap on those cells, and nothing else: no width, no overflow. The measurement then sees the full text and the existing sizing logic does the rest. Folded into the stylesheet already being injected for the grand-total column, and renamed from pf-hide-split-total to pf-grid-css now that it does more than the one thing. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/views/Forecast.jsx | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx index 7caa0c5..d59b030 100644 --- a/ui/src/views/Forecast.jsx +++ b/ui/src/views/Forecast.jsx @@ -1007,7 +1007,23 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio // survives adding measures or rearranging the pivot, unlike hiding by position. // The rule has to live inside the plugin's shadow root; a document stylesheet // cannot reach it. - const HIDE_SPLIT_TOTAL_CSS = ` + const GRID_CSS = ` + /* Let headers and row labels size their own column. + * + * Auto-fit measures cells with getBoundingClientRect() and sets each column's + * min-width from the result — but the datagrid's own CSS wraps and clips + * header text, so a clipped th measures at the width it was *allotted*, not + * the width of its content. The column can therefore never grow to fit its + * own heading, which is why the values fit and the headings did not. Row + * labels are th elements in tbody and clip for the same reason. + * + * nowrap alone: no width or overflow is touched, so the measurement sees the + * full text and the existing sizing logic does the rest. */ + thead th, + tbody th { + white-space: nowrap !important; + } + th.psp-split-total:not(.psp-split-subtotal), td.psp-split-total:not(.psp-split-subtotal) { visibility: hidden !important; @@ -1045,10 +1061,10 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio // Retried from the config-update handler, and once on a short delay. if (!grid) return false const root = grid.getRootNode() - if (!root || root.querySelector?.('#pf-hide-split-total')) return true + if (!root || root.querySelector?.('#pf-grid-css')) return true const style = document.createElement('style') - style.id = 'pf-hide-split-total' - style.textContent = HIDE_SPLIT_TOTAL_CSS + style.id = 'pf-grid-css' + style.textContent = GRID_CSS ;(root.appendChild ? root : document.head).appendChild(style) return true }