Skip to content

Commit 0c7ed4a

Browse files
committed
Release v1.12.0: searchable column chooser with Hide all, export skips hidden columns (#18)
1 parent f919e46 commit 0c7ed4a

7 files changed

Lines changed: 80 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to CSV Grid Editor are documented here.
44

5+
## [1.12.0] - 2026-06-16
6+
7+
### Added
8+
- **Search and Hide all in the column chooser** - The **Show / hide columns** menu now has a search box that filters the column list by name, plus a **Hide all** button next to the existing **Show all** (requested in [#18](https://github.com/Robin-Reiche/csv-grid-editor/issues/18)). On a wide file the flow becomes: Hide all, then type to find the few columns you want and check them, instead of unchecking dozens one by one.
9+
10+
### Changed
11+
- **Export now leaves out hidden columns** - Exporting to JSON, JSON Lines or Markdown used to still include columns you had hidden in the column chooser. It now exports only the visible columns, which matches how copy already behaved, so the output is exactly what you see (follow-up to [#18](https://github.com/Robin-Reiche/csv-grid-editor/issues/18)).
12+
513
## [1.11.1] - 2026-06-15
614

715
### Fixed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ For files larger than 10 MB you get a quick menu to open the full file, preview
8888
- Right-click → **Copy with header** to include the column headers in the copy
8989
- `Delete` / `Backspace` clears every cell in the selection
9090
- The status bar shows the selection size plus live `Count / Sum / Avg / Min / Max`
91-
- **Export as JSON** - Convert the current filtered and sorted view to a JSON array of objects via the native VS Code save dialog. Column headers become the keys and numbers and booleans come out typed, while values that would lose information (IDs with leading zeros, very large numbers) stay strings.
91+
- **Export as JSON** - Convert the current filtered and sorted view to a JSON array of objects via the native VS Code save dialog. Column headers become the keys and numbers and booleans come out typed, while values that would lose information (IDs with leading zeros, very large numbers) stay strings. Columns you have hidden in the column chooser are left out, the same as copy.
9292
- **Export as JSON Lines** - The same view as JSON Lines (NDJSON), one object per line, handy for streaming tools and data pipelines
9393
- **Export as Markdown table** - The same view as a GitHub-flavored Markdown table, ready to paste into a README, issue or pull request
9494

@@ -106,7 +106,7 @@ For files larger than 10 MB you get a quick menu to open the full file, preview
106106
- **Rename** - Right-click a column header → **Rename column** to rename it. The new name is written to the CSV header row and is fully undoable.
107107

108108
### Show / Hide Columns
109-
- **Column chooser** - Click the checklist icon in the toolbar to open a list of all columns with checkboxes. Uncheck a column to hide it, re-check to show it, or **Show all** to reset. A view aid, hidden columns are still included on export.
109+
- **Column chooser** - Click the checklist icon in the toolbar to open a searchable list of all columns with checkboxes. Type to filter the list by column name, uncheck a column to hide it, re-check to show it, or use **Show all** / **Hide all** to flip every column at once (Hide all, then search and re-check the few you want). Hidden columns are excluded from copy and export, so the output matches exactly what you see.
110110

111111
### Sort & Filter
112112

media/webview.css

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,17 +1151,6 @@ body.vscode-high-contrast #grid-container.cm-on {
11511151
border-bottom: 1px solid var(--vscode-menu-border, #454545);
11521152
margin-bottom: 4px;
11531153
}
1154-
.col-chooser-showall {
1155-
border: 1px solid transparent;
1156-
border-radius: 3px;
1157-
padding: 2px 8px;
1158-
font-size: 11px;
1159-
font-family: inherit;
1160-
cursor: pointer;
1161-
background: var(--vscode-button-secondaryBackground, #3a3d41);
1162-
color: var(--vscode-button-secondaryForeground, #ccc);
1163-
}
1164-
.col-chooser-showall:hover { background: var(--vscode-button-secondaryHoverBackground, #45494e); }
11651154
.col-chooser-list { max-height: 320px; overflow-y: auto; }
11661155
.col-chooser-list::-webkit-scrollbar { width: 6px; }
11671156
.col-chooser-list::-webkit-scrollbar-thumb { background: var(--vscode-scrollbarSlider-background, #424242); border-radius: 3px; }

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "csv-grid-editor",
33
"displayName": "CSV Grid Editor: CSV & TSV Viewer",
44
"description": "Open, view and edit CSV and TSV files in a fast sortable filterable grid. Inline editing, find and replace, freeze rows, column stats, Excel copy and paste and export to JSON. Free.",
5-
"version": "1.11.1",
5+
"version": "1.12.0",
66
"publisher": "RobinReiche",
77
"license": "MIT",
88
"icon": "icon.png",

src/webview.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,11 @@ export function getWebviewContent(
172172
<div id="col-chooser-popover" class="col-chooser-popover hidden">
173173
<div class="col-chooser-head">
174174
<span class="goto-label" style="margin:0;">Columns</span>
175-
<button id="col-chooser-showall" class="col-chooser-showall">Show all</button>
175+
</div>
176+
<input id="col-chooser-search" class="csv-filter-input" type="text" placeholder="Search columns…" spellcheck="false">
177+
<div class="csv-filter-actions">
178+
<button id="col-chooser-showall" type="button" class="csv-filter-link">Show all</button>
179+
<button id="col-chooser-hideall" type="button" class="csv-filter-link">Hide all</button>
176180
</div>
177181
<div id="col-chooser-list" class="col-chooser-list"></div>
178182
</div>

src/webview/features/column-chooser.ts

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import { closeAllPopups } from './popups';
33

44
// ── Column chooser (show / hide columns) ────────────────────────────────────
55
// A toolbar dropdown listing every data column with a checkbox to toggle its
6-
// visibility. Visibility is applied via AG Grid (setColumnsVisible) and mirrored
7-
// into state.hiddenCols (0-based data-column indices) so it survives a grid
8-
// rebuild — e.g. a paged-view page change re-runs buildGrid, which re-applies
9-
// `hide` from this set. In-memory only (not persisted across reload), matching
10-
// the freeze features. Column insert/delete clears the set (see delete-row-col).
6+
// visibility. A search box filters the list by column name, and Show all / Hide
7+
// all flip every column at once (Hide all, then search and re-check the few you
8+
// want). Visibility is applied via AG Grid (setColumnsVisible) and mirrored into
9+
// state.hiddenCols (0-based data-column indices) so it survives a grid rebuild —
10+
// e.g. a paged-view page change re-runs buildGrid, which re-applies `hide` from
11+
// this set. In-memory only (not persisted across reload), matching the freeze
12+
// features. Column insert/delete clears the set (see delete-row-col).
13+
14+
let searchQuery = '';
1115

1216
function setColHidden(colIndex: number, hidden: boolean): void {
1317
if (hidden) state.hiddenCols.add(colIndex);
@@ -16,22 +20,46 @@ function setColHidden(colIndex: number, hidden: boolean): void {
1620
updateButton();
1721
}
1822

19-
function showAll(): void {
23+
function allColIds(): string[] {
2024
const n = (state.data[0] ?? []).length;
2125
const ids: string[] = [];
2226
for (let c = 0; c < n; c++) ids.push('col_' + c);
27+
return ids;
28+
}
29+
30+
function showAll(): void {
31+
state.hiddenCols.clear();
32+
state.gridApi?.setColumnsVisible(allColIds(), true);
33+
buildList();
34+
updateButton();
35+
}
36+
37+
function hideAll(): void {
38+
const n = (state.data[0] ?? []).length;
2339
state.hiddenCols.clear();
24-
state.gridApi?.setColumnsVisible(ids, true);
40+
for (let c = 0; c < n; c++) state.hiddenCols.add(c);
41+
state.gridApi?.setColumnsVisible(allColIds(), false);
2542
buildList();
2643
updateButton();
2744
}
2845

46+
function colLabel(header: string[], c: number): string {
47+
const name = header[c] ?? '';
48+
return name !== '' ? name : '(column ' + (c + 1) + ')';
49+
}
50+
2951
function buildList(): void {
3052
const list = document.getElementById('col-chooser-list');
3153
if (!list) return;
3254
list.innerHTML = '';
3355
const header = state.data[0] ?? [];
56+
const q = searchQuery.trim().toLowerCase();
57+
let shown = 0;
3458
for (let c = 0; c < header.length; c++) {
59+
const label = colLabel(header, c);
60+
if (q && !label.toLowerCase().includes(q)) continue;
61+
shown++;
62+
3563
const row = document.createElement('label');
3664
row.className = 'col-chooser-item';
3765

@@ -43,13 +71,19 @@ function buildList(): void {
4371

4472
const span = document.createElement('span');
4573
span.className = 'col-chooser-label';
46-
const name = header[c] ?? '';
47-
span.textContent = name !== '' ? name : '(column ' + (c + 1) + ')';
74+
span.textContent = label;
4875

4976
row.appendChild(cb);
5077
row.appendChild(span);
5178
list.appendChild(row);
5279
}
80+
81+
if (shown === 0) {
82+
const empty = document.createElement('div');
83+
empty.className = 'csv-filter-empty';
84+
empty.textContent = 'No matching columns';
85+
list.appendChild(empty);
86+
}
5387
}
5488

5589
function updateButton(): void {
@@ -60,13 +94,17 @@ function openChooser(): void {
6094
const pop = document.getElementById('col-chooser-popover');
6195
const btn = document.getElementById('btn-columns');
6296
if (!pop || !btn) return;
97+
searchQuery = '';
98+
const search = document.getElementById('col-chooser-search') as HTMLInputElement | null;
99+
if (search) search.value = '';
63100
buildList();
64101
pop.classList.remove('hidden');
65102
const r = btn.getBoundingClientRect();
66103
const pw = pop.offsetWidth || 220;
67104
const vw = window.innerWidth;
68105
pop.style.top = (r.bottom + 4) + 'px';
69106
pop.style.left = Math.max(4, Math.min(r.left, vw - pw - 4)) + 'px';
107+
search?.focus();
70108
}
71109

72110
function closeChooser(): void {
@@ -84,6 +122,13 @@ export function setupColumnChooser(): void {
84122
});
85123

86124
document.getElementById('col-chooser-showall')?.addEventListener('click', showAll);
125+
document.getElementById('col-chooser-hideall')?.addEventListener('click', hideAll);
126+
127+
const search = document.getElementById('col-chooser-search') as HTMLInputElement | null;
128+
search?.addEventListener('input', () => {
129+
searchQuery = search.value;
130+
buildList();
131+
});
87132

88133
document.addEventListener('mousedown', (evt) => {
89134
const pop = document.getElementById('col-chooser-popover');

src/webview/features/export.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { closeAllPopups } from './popups';
77
// The toolbar Export button opens a small dropdown (JSON / JSON Lines /
88
// Markdown table). Every format exports the CURRENT VIEW: active filters and
99
// sort order are applied, columns appear in their current (possibly reordered)
10-
// order with their current (possibly renamed) headers. Hidden columns are still
11-
// included — the column chooser is documented as a view aid (see README). A
12-
// frozen reference row is exported first, matching where the user sees it.
10+
// order with their current (possibly renamed) headers. Hidden columns are
11+
// EXCLUDED, matching the Excel-style copy behavior — export reflects exactly the
12+
// visible view. A frozen reference row is exported first, matching where the
13+
// user sees it.
1314
// Saving the file itself already writes CSV, so there is no CSV entry here.
1415

1516
type ExportFormat = 'json' | 'jsonl' | 'md';
@@ -22,7 +23,12 @@ const FORMAT_EXT: Record<ExportFormat, string> = {
2223

2324
function collectView(): { headers: string[]; rows: string[][]; types: ColType[] } {
2425
const colDefs = (state.gridApi.getColumnDefs() as any[])
25-
.filter(c => c.colId !== 'row-index' && (c.field ?? c.colId));
26+
.filter(c => {
27+
const id = String(c.field ?? c.colId ?? '');
28+
if (c.colId === 'row-index' || id === '') return false;
29+
const ci = parseInt(id.replace('col_', ''), 10);
30+
return !state.hiddenCols.has(ci); // skip hidden columns — export the visible view only
31+
});
2632
const fields = colDefs.map(c => String(c.field ?? c.colId));
2733
const headers = colDefs.map(c => String(c.headerName ?? ''));
2834
const types = fields.map(f => {

0 commit comments

Comments
 (0)