feat: column visibility toggle UI for collection table
This commit is contained in:
1 parent
77c92d569d
commit
f199bcfa34
2 files changed
+186
-7
No files matched your search
@@ -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
@@ -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,
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user