← back to Atomic50 Onboard
viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
412128ba40c26537c0978aa0d4a9ad2791306187 · 2026-08-12 08:53:18 -0700 · Steve
Files touched
Diff
commit 412128ba40c26537c0978aa0d4a9ad2791306187
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:53:18 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
---
public/index.html | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/public/index.html b/public/index.html
index 6e03d6a..0d6e973 100644
--- a/public/index.html
+++ b/public/index.html
@@ -85,6 +85,35 @@ a.drill.kind:hover{color:var(--gold)}
footer.house{background:var(--ink);color:#cfc9bb;padding:34px 30px;font-family:-apple-system,sans-serif;font-size:12.5px;text-align:center;letter-spacing:.04em}
footer.house a{color:#fff;text-decoration:none;border-bottom:1px solid #ffffff40}
@media(max-width:820px){.grid{grid-template-columns:repeat(2,1fr)}.ctl.dens{display:none}nav.house{display:none}}
+/* ── List view ── */
+.grid.list{display:flex;flex-direction:column;gap:8px;padding:16px 26px 70px;max-width:1500px;margin:0 auto}
+.grid.list .card{flex-direction:row;align-items:stretch;border-radius:3px}
+.grid.list .card:hover{transform:none}
+.grid.list .imgwrap{flex:0 0 110px;width:110px;aspect-ratio:1/1}
+.grid.list .card .b{flex:1;padding:12px 16px;flex-direction:row;align-items:center;gap:18px;flex-wrap:wrap}
+.grid.list .card .kind{font-size:10px}
+.grid.list .card .name{font-size:14px;flex:1;min-width:180px}
+.grid.list .card .sub{font-size:11px}
+.grid.list .card .cta{font-size:11px;white-space:nowrap}
+/* ── View toggle ── */
+.view-btn{background:var(--paper);border:1px solid var(--line);color:var(--taupe);border-radius:4px;padding:6px 11px;cursor:pointer;font-size:12px;font-family:-apple-system,sans-serif;letter-spacing:.03em}
+.view-btn:hover{border-color:var(--accent)}
+.view-btn.active{border-color:var(--accent);color:var(--accent)}
+/* ── Fields ▾ popover ── */
+.fieldmenu{position:relative}
+.fbtn{background:var(--paper);border:1px solid var(--line);color:var(--taupe);border-radius:4px;padding:6px 11px;cursor:pointer;font-size:12px;font-family:-apple-system,sans-serif;white-space:nowrap}
+.fbtn:hover{border-color:var(--accent);color:var(--ink)}
+.fpop{position:absolute;top:calc(100% + 6px);left:0;background:var(--paper);border:1px solid var(--line);border-radius:3px;box-shadow:0 10px 28px rgba(22,20,18,.14);padding:8px 4px;z-index:30;min-width:180px}
+.fpop[hidden]{display:none}
+.fpop label{display:flex;align-items:center;gap:9px;padding:7px 14px;font-size:12px;font-family:-apple-system,sans-serif;letter-spacing:.04em;color:var(--taupe);cursor:pointer;white-space:nowrap}
+.fpop label:hover{background:var(--bg)}
+.fpop input{accent-color:var(--accent);margin:0}
+/* ── field hide classes (grid only) ── */
+body.hf-img .grid:not(.list) .imgwrap{display:none}
+body.hf-kind .grid:not(.list) .card .kind{display:none}
+body.hf-name .grid:not(.list) .card .name{display:none}
+body.hf-sub .grid:not(.list) .card .sub{display:none}
+body.hf-cta .grid:not(.list) .card .cta{display:none}
</style></head>
<body>
<header class="house">
@@ -114,12 +143,26 @@ footer.house a{color:#fff;text-decoration:none;border-bottom:1px solid #ffffff40
<option value="pattern">Pattern A→Z</option>
<option value="sku">SKU A→Z</option>
<option value="name">Name A→Z</option>
+ <option value="collection">Collection A→Z</option>
+ <option value="finish">Finish A→Z</option>
</select></div>
<div class="ctl dens"><label>Density</label>
<input type="range" id="density" min="2" max="8" step="1"></div>
<input type="text" id="q" placeholder="Search pattern / sku…" style="min-width:180px">
<div class="chipf" id="typef"></div>
<div class="chipf" id="colorf"></div>
+ <button class="view-btn" id="viewgrid" title="Grid view">⊞ Grid</button>
+ <button class="view-btn" id="viewlist" title="List view">☰ List</button>
+ <div class="fieldmenu">
+ <button class="fbtn" id="fieldbtn">Fields ▾</button>
+ <div class="fpop" id="fpop" hidden>
+ <label><input type="checkbox" data-af="img" checked> Image</label>
+ <label><input type="checkbox" data-af="kind" checked> Type / Collection</label>
+ <label><input type="checkbox" data-af="name" checked> Name</label>
+ <label><input type="checkbox" data-af="sub" checked> Style · Pattern · Color</label>
+ <label><input type="checkbox" data-af="cta" checked> CTA</label>
+ </div>
+ </div>
</div>
<div class="grid" id="grid"></div>
@@ -293,6 +336,8 @@ function apply(){
pattern:(a,b)=>String(a.pattern||'~').localeCompare(String(b.pattern||'~')),
sku:(a,b)=>String(a.sku||a.handle).localeCompare(String(b.sku||b.handle),undefined,{numeric:true}),
name:(a,b)=>String(a.display_name||'').localeCompare(String(b.display_name||''),undefined,{numeric:true}),
+ collection:(a,b)=>String(a.collection||'~').localeCompare(String(b.collection||'~')),
+ finish:(a,b)=>String(a.finish||'~').localeCompare(String(b.finish||'~')),
}[s];
if(cmp)VIEW=[...VIEW].sort(cmp);
grid.innerHTML=VIEW.map(card).join('');
@@ -323,6 +368,35 @@ function renderChips(){
});
}
+// ── VIEW TOGGLE ────────────────────────────────────────────────────
+function setA50View(v){
+ grid.classList.toggle('list',v==='list');
+ $('#viewgrid').classList.toggle('active',v==='grid');
+ $('#viewlist').classList.toggle('active',v==='list');
+ $('#density').parentElement.style.opacity=v==='list'?'.35':'1';
+ $('#density').disabled=v==='list';
+ try{localStorage.setItem('a50.view',v);}catch(_){}
+}
+$('#viewgrid').addEventListener('click',()=>setA50View('grid'));
+$('#viewlist').addEventListener('click',()=>setA50View('list'));
+setA50View(localStorage.getItem('a50.view')||'grid');
+
+// ── FIELDS MENU ───────────────────────────────────────────────────
+const A50F_DEF={img:true,kind:true,name:true,sub:true,cta:true};
+let A50F=Object.assign({},A50F_DEF);
+try{const s=localStorage.getItem('a50.fields');if(s)A50F=Object.assign({},A50F_DEF,JSON.parse(s));}catch(_){}
+function applyA50Fields(){
+ for(const k of Object.keys(A50F_DEF)){document.body.classList.toggle('hf-'+k,!A50F[k]);}
+ document.querySelectorAll('#fpop input[data-af]').forEach(cb=>{cb.checked=!!A50F[cb.dataset.af];});
+ try{localStorage.setItem('a50.fields',JSON.stringify(A50F));}catch(_){}
+}
+document.querySelectorAll('#fpop input[data-af]').forEach(cb=>{
+ cb.addEventListener('change',()=>{A50F[cb.dataset.af]=cb.checked;applyA50Fields();});
+});
+applyA50Fields();
+$('#fieldbtn').addEventListener('click',e=>{e.stopPropagation();$('#fpop').hidden=!$('#fpop').hidden;});
+document.addEventListener('click',e=>{if(!e.target.closest('.fieldmenu'))$('#fpop').hidden=true;});
+
async function boot(){
const [cfg,pd,fc]=await Promise.all([
fetch(location.origin+'/api/config').then(r=>r.json()),
← c9b3f52 auto-save: 2026-07-31T14:58:42 (1 files) — public/index.html
·
back to Atomic50 Onboard
·
auto-data-snapshot: 2026-08-25T11:28:19 (1 data files) — cad 49d6179 →