[object Object]

← 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 →