feat: column visibility toggle UI for collection table

This commit is contained in:
KoalaTrapLord authored and GitHub committed 2026-03-25 08:31:12 -04:00
1 parent 77c92d569d
commit f199bcfa34
2 files changed
+186 -7

No files matched your search

+95
View File
@@ -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);
}
+91 -7
View File
@@ -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 = (() => {
<button class="coll-autoclassify-btn" onclick="Collection.autoClassify()" title="Auto-detect categories (Ramp, Draw, Removal, etc.) for uncategorized cards">Auto-Classify</button>
<button class="coll-reenrich-btn" onclick="Collection.reEnrich()" title="Re-fetch all card data from Scryfall">Refresh Data</button>
<button class="coll-stats-btn" onclick="Collection.toggleStats()" title="Show collection statistics dashboard">📊 Stats</button>
<a href="deckbuilder.html" class="coll-nav-link" style="color:var(--lab-purple);border-color:var(--lab-purple)">⚔ Deck Builder</a>
<div style="position:relative">
<button class="coll-col-toggle-btn" onclick="Collection.openColumnPanel()" title="Show/hide table columns">☰ Columns</button>
<div id="coll-col-panel" class="coll-col-panel" style="display:none"></div>
</div>
<a href="deckbuilder.html" class="coll-nav-link" style="color:var(--lab-purple);border-color:var(--lab-purple)">⚔ Deck Builder</a>
<a href="deckgenerator.html" class="coll-nav-link" style="color:var(--lab-warning);border-color:var(--lab-warning)">⚡ Auto Gen</a>
</div>
</div>
@@ -966,6 +971,79 @@ const Collection = (() => {
</div>`;
}
// ── 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 `<label class="coll-col-toggle-item">
<input type="checkbox" ${checked} onchange="Collection.toggleColumn('${col.key}')">
${escapeHtml(col.label)}
</label>`;
}).join('');
panel.innerHTML = `
<div class="coll-col-panel-inner">
<div class="coll-col-panel-hdr">Columns <button class="coll-col-close" onclick="Collection.closeColumnPanel()">×</button></div>
${cols}
<button class="coll-col-reset" onclick="Collection.resetColumns()">Reset Defaults</button>
</div>`;
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 `<th class="coll-th coll-th-${col.key}">${col.label}</th>`;
}
const isActive = state.sortField === col.key;
@@ -1018,12 +1096,12 @@ const Collection = (() => {
}
if (state.loading) {
tbody.innerHTML = `<tr><td colspan="${COLUMNS.length}" class="coll-empty">Loading…</td></tr>`;
tbody.innerHTML = `<tr><td colspan="${state.preferences.visibleColumns.length}" class="coll-empty">Loading…</td></tr>`;
return;
}
if (state.items.length === 0) {
tbody.innerHTML = `<tr><td colspan="${COLUMNS.length}" class="coll-empty">
tbody.innerHTML = `<tr><td colspan="${state.preferences.visibleColumns.length}" class="coll-empty">
<div class="coll-empty-state">
<div class="coll-empty-icon">🃏</div>
<div>No cards in collection</div>
@@ -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,
};
})();