← back to Catalog Crawl Atlas
viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
883813948df6c85affbbd565f30b686f4b7fe89c · 2026-08-12 08:53:19 -0700 · Steve
Files touched
Diff
commit 883813948df6c85affbbd565f30b686f4b7fe89c
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:53:19 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
---
public/index.html | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index 48613d3..31831bd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -93,7 +93,31 @@ svg{width:100%;height:100%;display:block}
.pctl .mono{font-family:'Share Tech Mono',monospace;letter-spacing:0}
.pctl select{font:inherit;font-size:12px;color:var(--cream);background:#14151a;border:1px solid var(--edge);border-radius:8px;padding:5px 9px}
.pctl input[type=range]{width:150px;accent-color:var(--amber)}
+/* view toggle */
+.vbtn{font-size:11px;letter-spacing:.5px;padding:4px 10px;border-radius:6px;border:1px solid var(--edge);background:#14151a;color:var(--muted);cursor:pointer}
+.vbtn.on{background:var(--amber);border-color:var(--amber);color:#0c0d10}
+/* fields menu */
+.ca-fieldmenu{position:relative}
+.ca-fbtn{font-size:11px;letter-spacing:.5px;padding:4px 10px;border-radius:6px;border:1px solid var(--edge);background:#14151a;color:var(--muted);cursor:pointer}
+.ca-fbtn:hover{border-color:var(--amber);color:var(--cream)}
+.ca-fpop{position:absolute;top:calc(100% + 6px);right:0;background:#14151a;border:1px solid var(--edge);border-radius:10px;box-shadow:0 10px 28px rgba(0,0,0,.7);padding:8px 4px;z-index:30;min-width:160px}
+.ca-fpop[hidden]{display:none}
+.ca-fpop label{display:flex;align-items:center;gap:8px;padding:6px 12px;font-size:11px;letter-spacing:.4px;color:var(--muted);cursor:pointer;white-space:nowrap}
+.ca-fpop label:hover{color:var(--cream)}
+.ca-fpop input{accent-color:var(--amber);width:13px;height:13px;margin:0;padding:0}
+/* grid view */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--card,150px),1fr));gap:12px;padding:16px 22px 40px}
+/* list view */
+.grid.list{display:flex;flex-direction:column;gap:6px;padding:12px 22px 40px}
+.grid.list .card{flex-direction:row;align-items:center;border-radius:8px;min-height:54px}
+.grid.list .card img{width:54px;min-width:54px;height:54px;aspect-ratio:auto;object-fit:cover;border-radius:6px 0 0 6px}
+.grid.list .card .cap{flex:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:8px 12px}
+.grid.list .card .ct{max-height:none;font-size:13px;flex:1;min-width:120px}
+.grid.list .card .tags{margin-top:0}
+/* field visibility */
+body.ca-hf-image .card img{display:none!important}
+body.ca-hf-title [data-f="title"]{display:none!important}
+body.ca-hf-tags [data-f="tags"]{display:none!important}
.card{background:#121318;border:1px solid var(--edge);border-radius:12px;overflow:hidden;text-decoration:none;color:var(--cream);transition:.14s;display:flex;flex-direction:column}
.card:hover{border-color:var(--amber);transform:translateY(-2px);box-shadow:0 8px 22px rgba(0,0,0,.6)}
.card img{width:100%;aspect-ratio:1;object-fit:cover;background:#1c1d22;display:block}
@@ -149,6 +173,13 @@ svg{width:100%;height:100%;display:block}
<span class="pl">Density</span>
<input type="range" id="densRange" min="100" max="340" step="10">
<span class="pl mono" id="densVal"></span>
+ <span class="pl" style="margin-left:10px">View</span>
+ <button class="vbtn on" id="caVGrid" title="Grid view">⊞ Grid</button>
+ <button class="vbtn" id="caVList" title="List view">☰ List</button>
+ <div class="ca-fieldmenu">
+ <button class="ca-fbtn" id="caFieldsBtn" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div class="ca-fpop" id="caFpop" hidden role="menu"></div>
+ </div>
</div>
<div class="grid" id="grid"></div>
</div>
@@ -272,6 +303,26 @@ function sortRows(rows){
const idx={title:1,color:3,style:4,motif:5,material:6}[sortMode];
return rows.slice().sort((a,b)=>String(a[idx]||'~').localeCompare(String(b[idx]||'~')));
}
+// ── Grid / List + Fields ──
+const CA_FIELDS=[['image','Image'],['title','Title'],['tags','Tags']];
+const CA_FIELDS_DEF=Object.fromEntries(CA_FIELDS.map(([k])=>[k,true]));
+let caFieldVis=Object.assign({},CA_FIELDS_DEF,JSON.parse(localStorage.getItem('ca_fields')||'{}'));
+function applyCaFields(){
+ CA_FIELDS.forEach(([k])=>document.body.classList.toggle('ca-hf-'+k,!caFieldVis[k]));
+ document.querySelectorAll('#caFpop input[data-field]').forEach(cb=>cb.checked=!!caFieldVis[cb.dataset.field]);
+ localStorage.setItem('ca_fields',JSON.stringify(caFieldVis));
+}
+function buildCaFieldMenu(){
+ const fp=document.getElementById('caFpop');
+ fp.innerHTML=CA_FIELDS.map(([k,lbl])=>`<label><input type="checkbox" data-field="${k}"${caFieldVis[k]?' checked':''}> ${lbl}</label>`).join('');
+ fp.querySelectorAll('input').forEach(cb=>cb.addEventListener('change',()=>{caFieldVis[cb.dataset.field]=cb.checked;localStorage.setItem('ca_fields',JSON.stringify(caFieldVis));applyCaFields();}));
+}
+function setCaView(v){
+ document.getElementById('grid').classList.toggle('list',v==='list');
+ document.getElementById('caVGrid').classList.toggle('on',v==='grid');
+ document.getElementById('caVList').classList.toggle('on',v==='list');
+ localStorage.setItem('ca_view',v);
+}
function initControls(){
const ss=document.getElementById('sortSel'), dr=document.getElementById('densRange'), dv=document.getElementById('densVal');
ss.value=sortMode;
@@ -280,6 +331,17 @@ function initControls(){
const applyDens=v=>{ document.getElementById('grid').style.setProperty('--card',v+'px'); dv.textContent=v+'px'; };
dr.value=dens; applyDens(dens);
dr.oninput=()=>{ applyDens(+dr.value); localStorage.setItem('ca_dens',dr.value); };
+ // view + fields
+ document.getElementById('caVGrid').onclick=()=>setCaView('grid');
+ document.getElementById('caVList').onclick=()=>setCaView('list');
+ setCaView(localStorage.getItem('ca_view')||'grid');
+ buildCaFieldMenu();
+ applyCaFields();
+ // fields popover open/close
+ const fb=document.getElementById('caFieldsBtn'), fp=document.getElementById('caFpop');
+ fb.addEventListener('click',e=>{e.stopPropagation();const h=fp.hidden;fp.hidden=!h;fb.setAttribute('aria-expanded',String(h));});
+ document.addEventListener('click',()=>{fp.hidden=true;fb.setAttribute('aria-expanded','false');});
+ fp.addEventListener('click',e=>e.stopPropagation());
}
function colorHex(v){ const n=G.nodes['color:'+v]; return n?n.hex:'#888'; }
function applyFilter(){
@@ -318,7 +380,7 @@ function renderMore(){
const admin=`https://admin.shopify.com/store/designer-laboratory-sandbox/products/${g}`;
return `<a class="card" href="${admin}" target="_blank" rel="noopener">
<img loading="lazy" src="${img(im,240)}" alt="">
- <div class="cap"><div class="ct">${esc(t)}</div><div class="tags">${tags}</div></div></a>`;
+ <div class="cap"><div class="ct" data-f="title">${esc(t)}</div><div class="tags" data-f="tags">${tags}</div></div></a>`;
}).join('');
grid.insertAdjacentHTML('beforeend',html);
gridState.shown+=slice.length;
← 69a677b Atlas refine: filter-aware neighbors (spokes show +N within
·
back to Catalog Crawl Atlas
·
nav-agent: universal grid-controls drop-in on internal dashb b4b5a8d →