diff --git a/ui/collection.css b/ui/collection.css index f16ec86..a2a0adc 100644 --- a/ui/collection.css +++ b/ui/collection.css @@ -3052,3 +3052,98 @@ } } + + +/* ── 18. Column Visibility Panel ───────────────────────────── */ + +.coll-col-toggle-btn { + background: rgba(139,148,158,0.1); + border: 1px solid var(--border, #30363d); + color: var(--text-secondary, #8b949e); + border-radius: 6px; + padding: 6px 12px; + font-size: 0.8rem; + cursor: pointer; + white-space: nowrap; + transition: background 0.15s; +} +.coll-col-toggle-btn:hover { + background: rgba(139,148,158,0.2); + color: var(--text, #e6edf3); +} + +.coll-col-panel { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 500; + background: var(--surface, #161b22); + border: 1px solid var(--border, #30363d); + border-radius: 10px; + box-shadow: 0 8px 24px rgba(0,0,0,0.4); + min-width: 180px; + animation: coll-fade-in 0.15s ease; +} + +.coll-col-panel-inner { + padding: 10px 12px 12px; +} + +.coll-col-panel-hdr { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.78rem; + font-weight: 600; + color: var(--text-secondary, #8b949e); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 8px; + padding-bottom: 6px; + border-bottom: 1px solid var(--border, #30363d); +} + +.coll-col-close { + background: none; + border: none; + color: var(--text-secondary, #8b949e); + cursor: pointer; + font-size: 1rem; + padding: 0 4px; + line-height: 1; +} +.coll-col-close:hover { color: var(--text, #e6edf3); } + +.coll-col-toggle-item { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; + font-size: 0.8rem; + color: var(--text, #e6edf3); + cursor: pointer; + user-select: none; +} +.coll-col-toggle-item input[type="checkbox"] { + accent-color: var(--accent, #6366f1); + cursor: pointer; +} +.coll-col-toggle-item:hover { + color: var(--accent, #6366f1); +} + +.coll-col-reset { + margin-top: 10px; + width: 100%; + background: rgba(99,102,241,0.1); + border: 1px solid rgba(99,102,241,0.3); + color: var(--accent, #6366f1); + border-radius: 5px; + padding: 5px 8px; + font-size: 0.75rem; + cursor: pointer; + transition: background 0.15s; +} +.coll-col-reset:hover { + background: rgba(99,102,241,0.25); +} diff --git a/ui/collection.js b/ui/collection.js index 1f957d8..fed7991 100644 --- a/ui/collection.js +++ b/ui/collection.js @@ -586,7 +586,8 @@ const Collection = (() => { // Pre-load set/keyword caches for filter autocomplete loadSetsCache(); loadKeywordsCache(); - await fetchCollection(); + applyColumnVisibility(); + await fetchCollection(); } /** Adjust table container height based on actual topbar height */ @@ -632,7 +633,11 @@ const Collection = (() => { - ⚔ Deck Builder +
+ + +
+ ⚔ Deck Builder ⚡ Auto Gen @@ -966,6 +971,79 @@ const Collection = (() => { `; } + // ── Column Visibility ────────────────────────────────────── + function colVis(key) { + return state.preferences.visibleColumns.includes(key) ? '' : 'display:none;'; + } + + function toggleColumn(key) { + const cols = state.preferences.visibleColumns; + const idx = cols.indexOf(key); + if (idx >= 0) { + if (cols.length <= 1) return; // keep at least 1 column + cols.splice(idx, 1); + } else { + cols.push(key); + } + savePreferences(); + renderTable(); + applyColumnVisibility(); + } + + function renderColumnPanel() { + const panel = document.getElementById('coll-col-panel'); + if (!panel) return; + const cols = COLUMNS.map(col => { + const checked = state.preferences.visibleColumns.includes(col.key) ? 'checked' : ''; + return ``; + }).join(''); + panel.innerHTML = ` +
+
Columns
+ ${cols} + +
`; + panel.style.display = 'block'; + } + + function openColumnPanel() { + const panel = document.getElementById('coll-col-panel'); + if (!panel) return; + if (panel.style.display === 'block') { + panel.style.display = 'none'; + } else { + renderColumnPanel(); + } + } + + function closeColumnPanel() { + const panel = document.getElementById('coll-col-panel'); + if (panel) panel.style.display = 'none'; + } + + function resetColumns() { + state.preferences.visibleColumns = ['quantity','name','mana_cost','type_line','color_identity','cmc','power_toughness','rarity','set_name','tcg_price','edhrec_rank','salt_score','category','finish']; + savePreferences(); + renderTable(); + renderColumnPanel(); + } + function applyColumnVisibility() { + let styleEl = document.getElementById('coll-col-vis-style'); + if (!styleEl) { + styleEl = document.createElement('style'); + styleEl.id = 'coll-col-vis-style'; + document.head.appendChild(styleEl); + } + const hidden = COLUMNS.filter(col => !state.preferences.visibleColumns.includes(col.key)); + styleEl.textContent = hidden.map(col => + `.coll-th-${col.key}, .coll-td-${col.key} { display: none !important; }` + ).join('\n'); + } + + // ── Table Header ──────────────────────────────────────── const COLUMNS = [ @@ -988,8 +1066,8 @@ const Collection = (() => { ]; function renderTableHeaderHtml() { - const headers = COLUMNS.map(col => { - if (!col.sortable) { + const visibleCols = COLUMNS.filter(col => state.preferences.visibleColumns.includes(col.key)); + const headers = visibleCols.map(col => { if (!col.sortable) { return `${col.label}`; } const isActive = state.sortField === col.key; @@ -1018,12 +1096,12 @@ const Collection = (() => { } if (state.loading) { - tbody.innerHTML = `Loading…`; + tbody.innerHTML = `Loading…`; return; } if (state.items.length === 0) { - tbody.innerHTML = ` + tbody.innerHTML = `
🃏
No cards in collection
@@ -2244,7 +2322,8 @@ const Collection = (() => { if (footer) footer.style.display = 'none'; // Refresh collection - await fetchCollection(); + applyColumnVisibility(); + await fetchCollection(); } catch (err) { if (statusEl) { @@ -2997,5 +3076,10 @@ const Collection = (() => { reEnrich, autoClassify, toggleStats, + // Column visibility + toggleColumn, + openColumnPanel, + closeColumnPanel, + resetColumns, }; })();