← back to Wallsandfabrics
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
8e3604a732987e22de3a9f3f004ca1613df55674 · 2026-08-12 08:33:43 -0700 · Steve
Files touched
Diff
commit 8e3604a732987e22de3a9f3f004ca1613df55674
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:33:43 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
---
public/index.html | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 84 insertions(+), 3 deletions(-)
diff --git a/public/index.html b/public/index.html
index 750d1bc..2314e9d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -115,6 +115,25 @@ footer.site .grid-foot{display:grid;grid-template-columns:2fr 1fr 1fr;gap:30px}
footer.site h4{font-size:11px;text-transform:uppercase;letter-spacing:.18em;color:var(--fg);margin:0 0 10px}
footer.site a{color:var(--accent2)}
.legal{margin-top:18px;font-size:12px;color:var(--muted)}
+
+/* ── Grid / List view ── */
+html.waf-list #results.grid { display:block; padding-bottom:60px; }
+html.waf-list .card { display:flex; flex-direction:row; align-items:stretch; border-radius:8px; margin-bottom:10px; }
+html.waf-list .card .img { flex:0 0 clamp(72px,14vw,140px); width:clamp(72px,14vw,140px); min-height:72px; aspect-ratio:auto; border-radius:8px 0 0 8px; }
+html.waf-list .card .body { flex:1; display:flex; flex-direction:column; justify-content:center; }
+html.waf-list .card:hover { transform:none; }
+
+/* ── Per-field hide rules (html.hf-<field>) ── */
+html.hf-image .card .img { display:none; }
+html.hf-surface [data-f="surface"] { display:none; }
+html.hf-title [data-f="title"] { display:none; }
+html.hf-meta [data-f="meta"] { display:none; }
+html.hf-sample [data-f="sample"] { display:none; }
+
+/* ── Fields popover labels ── */
+#wafFpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:12px; letter-spacing:.05em; text-transform:uppercase; color:var(--fg); cursor:pointer; white-space:nowrap; font-family:inherit; }
+#wafFpop label:hover { background:var(--bg); border-radius:4px; }
+#wafFpop input[type=checkbox] { accent-color:var(--accent); width:13px; height:13px; cursor:pointer; }
</style>
<!-- Meta Pixel — INERT until set-fb-pixel.sh is run (does NOT load fbevents.js or hit Meta until flipped) -->
<script>
@@ -284,6 +303,16 @@ footer.site a{color:var(--accent2)}
<div class="group"><label for="cols">Density</label>
<input id="cols" type="range" min="3" max="8" step="1" value="5">
</div>
+ <!-- Grid/List view toggle -->
+ <div class="group" role="group" aria-label="View mode" style="gap:0;border:1px solid var(--line);border-radius:8px;overflow:hidden;">
+ <button id="wafVGrid" style="padding:9px 14px;background:var(--fg);color:var(--bg);border:0;font-size:12px;letter-spacing:.05em;text-transform:uppercase;cursor:pointer;font-family:inherit;" title="Grid view">Grid</button>
+ <button id="wafVList" style="padding:9px 14px;background:transparent;color:var(--muted);border:0;border-left:1px solid var(--line);font-size:12px;letter-spacing:.05em;text-transform:uppercase;cursor:pointer;font-family:inherit;" title="List view">List</button>
+ </div>
+ <!-- Fields ▾ menu -->
+ <div class="waf-fieldmenu" style="position:relative;">
+ <button id="wafFieldsBtn" style="padding:10px 14px;border:1px solid var(--line);background:var(--paper);border-radius:8px;font:inherit;font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);cursor:pointer;" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div id="wafFpop" hidden style="position:absolute;top:calc(100% + 8px);right:0;background:var(--paper);border:1px solid var(--line);box-shadow:0 12px 28px rgba(0,0,0,.12);padding:6px 4px;z-index:40;min-width:180px;border-radius:8px;" role="menu"></div>
+ </div>
</div>
<div id="results" class="grid"></div>
@@ -420,9 +449,9 @@ footer.site a{color:var(--accent2)}
var surfaceAtom='<span class="surface drill" data-filter="surface" data-surface="'+surface+'" data-v="'+surface+'" title="See all '+label+'">'+label+'</span>';
return '<a href="'+url+'" target="_blank" rel="noopener noreferrer" class="card" data-sku="'+(p.sku||p.handle||'')+'">'
+img
- +'<div class="body">'+surfaceAtom+'<div class="ttl">'+ttl+'</div>'
- +'<div class="meta"><span>'+brand+'</span><span>'+fmt$(p)+'</span></div>'
- +'<span class="smp">Request memo →</span></div></a>';
+ +'<div class="body">'+surfaceAtom+'<div class="ttl" data-f="title">'+ttl+'</div>'
+ +'<div class="meta" data-f="meta"><span>'+brand+'</span><span>'+fmt$(p)+'</span></div>'
+ +'<span class="smp" data-f="sample">Request memo →</span></div></a>';
}
function renderEmpty(){
@@ -451,6 +480,58 @@ footer.site a{color:var(--accent2)}
sfReadURL(); // deep-link / back-button: hydrate surface + q + sort + page from the URL
reload(state.page); // render at the URL-provided state
sfWriteURL(false); // normalize the URL to the canonical filter-set
+
+ // ── Grid/List view toggle ─────────────────────────────────────────────────
+ const WAF_FIELDS = [
+ ['image', 'Image', true],
+ ['surface', 'Surface', true],
+ ['title', 'Title', true],
+ ['meta', 'Price', true],
+ ['sample', 'Sample', true],
+ ];
+ function wafLoadFields(){
+ const def = {}; WAF_FIELDS.forEach(([k,,d]) => def[k] = d);
+ try { return Object.assign(def, JSON.parse(localStorage.getItem('waf-fields') || '{}')); } catch(e){ return def; }
+ }
+ var wafFieldState = wafLoadFields();
+
+ function wafApplyView(){
+ var v = localStorage.getItem('waf-view') || 'grid';
+ document.documentElement.classList.toggle('waf-list', v === 'list');
+ var gBtn = document.getElementById('wafVGrid'), lBtn = document.getElementById('wafVList');
+ if(gBtn){ gBtn.style.background = v==='grid'?'var(--fg)':'transparent'; gBtn.style.color = v==='grid'?'var(--bg)':'var(--muted)'; }
+ if(lBtn){ lBtn.style.background = v==='list'?'var(--fg)':'transparent'; lBtn.style.color = v==='list'?'var(--bg)':'var(--muted)'; }
+ }
+ var wafVG = document.getElementById('wafVGrid'), wafVL = document.getElementById('wafVList');
+ if(wafVG) wafVG.addEventListener('click', function(){ localStorage.setItem('waf-view','grid'); wafApplyView(); });
+ if(wafVL) wafVL.addEventListener('click', function(){ localStorage.setItem('waf-view','list'); wafApplyView(); });
+ wafApplyView();
+
+ // ── Fields ▾ menu ─────────────────────────────────────────────────────────
+ function wafApplyFields(){
+ WAF_FIELDS.forEach(function(f){ document.documentElement.classList.toggle('hf-' + f[0], !wafFieldState[f[0]]); });
+ }
+ function wafBuildFieldMenu(){
+ var pop = document.getElementById('wafFpop'); if(!pop) return;
+ pop.innerHTML = WAF_FIELDS.map(function(f){ return '<label><input type="checkbox" data-field="'+f[0]+'"'+(wafFieldState[f[0]]?' checked':'')+'> '+f[1]+'</label>'; }).join('');
+ pop.querySelectorAll('input').forEach(function(cb){ cb.addEventListener('change', function(){
+ wafFieldState[cb.dataset.field] = cb.checked;
+ localStorage.setItem('waf-fields', JSON.stringify(wafFieldState));
+ wafApplyFields();
+ }); });
+ }
+ var wafFBtn = document.getElementById('wafFieldsBtn'), wafFpop = document.getElementById('wafFpop');
+ if(wafFBtn && wafFpop){
+ wafFBtn.addEventListener('click', function(e){
+ e.stopPropagation(); var open = wafFpop.hidden; wafFpop.hidden = !open;
+ wafFBtn.setAttribute('aria-expanded', String(open));
+ });
+ document.addEventListener('click', function(e){
+ if(!wafFpop.hidden && !e.target.closest('.waf-fieldmenu')){ wafFpop.hidden = true; wafFBtn.setAttribute('aria-expanded','false'); }
+ });
+ }
+ wafBuildFieldMenu();
+ wafApplyFields();
})();
</script>
← 97292cb Wire DW Meta Pixel 1431180262113856 + DW-network ad slot [ad
·
back to Wallsandfabrics
·
TK-11341: add AdSense Auto-Ads loader + ads.txt (revert to r 5fcb3de →