Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions webview-ui/src/components/DataGrid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@
const selectedRange = $derived(gridStore.selectedRange);
const frozenCols = $derived(gridStore.frozenCols);

// Visible position of the column that stretches to absorb any leftover
// horizontal space, so the grid spans the full pane with no blank
// "phantom column" on the right. We grow the last visible column unless it's
// frozen (sticky positioning + flex-grow don't mix cleanly). When the columns
// already overflow the pane, flex-grow has nothing to add and the column keeps
// its natural width (horizontal scroll kicks in as before).
const fillColPos = $derived.by(() => {
const cols = gridStore.visibleSchema;
const last = cols.length - 1;
if (last < 0) return -1;
return frozenCols.has(cols[last].index) ? -1 : last;
});

// Returns the sticky `left` offset (in px) for a frozen column at visual position `colPos`.
// Row-number column is 56px + 1px border = 57px. Sums widths of all frozen cols before this one.
function getFrozenLeft(col: import('@shared/schema.js').ColumnSchema, colPos: number): number {
Expand Down Expand Up @@ -465,6 +478,7 @@
class="header-cell"
class:header-cell-selected={colSel}
class:col-frozen={isFrozen}
class:col-fill={colPos === fillColPos}
class:drag-over={dragOverPos === colPos && dragFromPos !== colPos}
role="columnheader"
tabindex="-1"
Expand Down Expand Up @@ -519,6 +533,7 @@
<!-- svelte-ignore a11y_autofocus -->
<input
class="cell-input"
class:col-fill={colPos === fillColPos}
style="width: {colWidth(col.name, col.inferredType)}px"
bind:value={editValue}
onblur={commitEdit}
Expand All @@ -535,6 +550,7 @@
class:cell-cross={isCrossPivot(row, col.index)}
class:cell-in-range={isInRange(row, col.index)}
class:col-frozen={isFrozen}
class:col-fill={colPos === fillColPos}
style="width: {colWidth(col.name, col.inferredType)}px; {colBgStyle(col.index)}{isFrozen ? ' left: ' + getFrozenLeft(col, colPos) + 'px;' : ''}"
onclick={(e) => onCellClick(e, row, col.index)}
ondblclick={() => startEdit(row, col.index)}
Expand Down Expand Up @@ -827,6 +843,17 @@
box-sizing: border-box;
}

/* The last visible column stretches to absorb leftover horizontal space so the
grid fills the pane with no blank phantom column on the right. The inline
`width` acts as the flex-basis and flex-shrink stays 0, so the column only
ever grows past its natural width — when columns overflow the pane there's
nothing to absorb and it keeps its set width (horizontal scroll as before). */
.header-cell.col-fill,
.cell.col-fill,
.cell-input.col-fill {
flex-grow: 1;
}

/* ── Coloured mode ──────────────────────────────────────────────────────
When the user enables column colours, two things change:
1. Column separators get a slightly stronger border so the pastels
Expand Down
14 changes: 14 additions & 0 deletions webview-ui/src/stores/grid.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ class GridStore {

// Auto-fitted column widths, keyed by column name. Computed once after data load.
private _autoWidths = new Map<string, number>();
// Guards the one-shot auto-width pass for worker-backed formats (JSON/Parquet/
// Arrow/Excel), which never populate _csvAllRows and so miss the CSV/raw-rows
// auto-width path. Reset on every worker READY so a new dataset (or Excel sheet
// switch) re-fits its columns.
private _autoWidthsDone = false;

// Viewport
visibleStartRow = $state(0);
Expand Down Expand Up @@ -1523,6 +1528,7 @@ class GridStore {
this.schema = msg.payload.schema;
this.totalRows = msg.payload.totalRows;
this.filteredRows = msg.payload.totalRows;
this._autoWidthsDone = false;
if (msg.payload.availableSheets) {
this.availableSheets = msg.payload.availableSheets;
this.selectedSheet = msg.payload.selectedSheet ?? msg.payload.availableSheets[0] ?? '';
Expand All @@ -1534,6 +1540,14 @@ class GridStore {

case 'CHUNK': {
const { requestId, rows, startRow, filteredTotal } = msg.payload;
// Worker-backed formats (JSON/Parquet/Arrow/Excel) never populate
// _csvAllRows, so the CSV/raw-rows auto-width path never runs for them.
// Fit widths once from the first chunk (natural order, no filter/sort on
// initial load) so columns size to their content like CSV does.
if (startRow === 0 && !this._autoWidthsDone && rows.length > 0 && this.schema.length > 0) {
this._computeAutoWidths(this.schema, rows);
this._autoWidthsDone = true;
}
this._storeChunk(startRow, rows, requestId);
this.filteredRows = filteredTotal;
const resolve = this._pendingRequests.get(requestId);
Expand Down
Loading