← back to Quadrille House Site
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
3ea5258a9e29696d0d7a36172281968287ba26d6 · 2026-08-12 08:33:44 -0700 · Steve
Files touched
Diff
commit 3ea5258a9e29696d0d7a36172281968287ba26d6
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:33:44 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
---
public/index.html | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 62 insertions(+), 5 deletions(-)
diff --git a/public/index.html b/public/index.html
index 562c28d..7792a28 100644
--- a/public/index.html
+++ b/public/index.html
@@ -87,8 +87,28 @@
<input type="range" id="density" min="2" max="20" step="1" value="4">
</div>
<button class="chip view-btn" id="view" type="button">▤ List view</button>
+ <div class="fieldmenu" style="position:relative">
+ <button class="chip view-btn" id="fieldsBtn" type="button" aria-haspopup="true" aria-expanded="false" style="font-family:var(--sans)">Fields ▾</button>
+ <div id="fpop" hidden role="menu" style="position:absolute;top:calc(100% + 8px);right:0;background:var(--paper);border:1px solid var(--line);border-radius:8px;box-shadow:0 14px 34px rgba(0,0,0,.16);padding:6px 4px;z-index:200;min-width:190px"></div>
+ </div>
</div>
</div>
+<style>
+/* Fields popover rows */
+#fpop label{display:flex;align-items:center;gap:10px;padding:7px 14px;font-family:var(--sans);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-soft);cursor:pointer;white-space:nowrap}
+#fpop label:hover{background:var(--bg)}
+#fpop input[type=checkbox]{accent-color:var(--accent)}
+/* Per-field hide on root.hf-<field> (HTML element, survives card re-renders) */
+html.hf-image .card .ph {display:none!important}
+html.hf-series .card .ser,
+html.hf-series [data-f="series"]{display:none!important}
+html.hf-name .card .nm,
+html.hf-name [data-f="name"] {display:none!important}
+html.hf-color .card .col,
+html.hf-color [data-f="color"] {display:none!important}
+html.hf-cta .card .pr,
+html.hf-cta [data-f="cta"] {display:none!important}
+</style>
<!-- COLOR FILTER -->
<div class="colorbar" id="colorbar"></div>
@@ -270,13 +290,13 @@ function render(){
$('#cnt').textContent=items.length;
grid.innerHTML = items.map(p=>`
<div class="card" data-h="${p.handle}">
- <img class="ph" loading="lazy" decoding="async" width="400" height="400" alt="${(p.title||'').replace(/"/g,'"')} — ${p.brand}" src="${p.swatch||p.room||''}">
+ <img class="ph" data-f="image" loading="lazy" decoding="async" width="400" height="400" alt="${(p.title||'').replace(/"/g,'"')} — ${p.brand}" src="${p.swatch||p.room||''}">
<div class="body">
- <div class="ser">${p.series?drill('series',p.series,p.display_eyebrow||p.series):(p.display_eyebrow||'')}</div>
- <div class="nm">${p.display_name||p.color||p.title}</div>
+ <div class="ser" data-f="series">${p.series?drill('series',p.series,p.display_eyebrow||p.series):(p.display_eyebrow||'')}</div>
+ <div class="nm" data-f="name">${p.display_name||p.color||p.title}</div>
<div class="meta">
- <span class="col">${p.hex?`<span class="dot" style="background:${p.hex}"></span>`:''}${p.brand}</span>
- <span class="pr view">${p.cta_mode==='live'?'View →':p.cta_mode==='sample'?('Sample $'+Number(p.sample_price||4.25).toFixed(2)+' →'):'Request →'}</span>
+ <span class="col" data-f="color">${p.hex?`<span class="dot" style="background:${p.hex}"></span>`:''}${p.brand}</span>
+ <span class="pr view" data-f="cta">${p.cta_mode==='live'?'View →':p.cta_mode==='sample'?('Sample $'+Number(p.sample_price||4.25).toFixed(2)+' →'):'Request →'}</span>
</div>
${COLS.map(c=>lcCell(p,c)).join('')}
</div>
@@ -422,6 +442,43 @@ async function loadBrand(){
startHero(ALL.length?ALL:[]);
loadLookbook();
})();
+
+// ── Fields menu ────────────────────────────────────────────────────────────────
+const QH_FIELDS = [
+ ['image', 'Image', true],
+ ['series', 'Pattern', true],
+ ['name', 'Name', true],
+ ['color', 'Color', true],
+ ['cta', 'CTA', true],
+];
+function qhLoadFields(){
+ const def={}; QH_FIELDS.forEach(([k,,d])=>def[k]=d);
+ try{return Object.assign(def,JSON.parse(localStorage.getItem('qhouse.fields')||'{}'));}
+ catch(e){return def;}
+}
+const qhFields=qhLoadFields();
+function qhApplyFields(){
+ QH_FIELDS.forEach(([k])=>document.documentElement.classList.toggle('hf-'+k,!qhFields[k]));
+}
+function qhBuildFieldMenu(){
+ const pop=$('#fpop'); if(!pop)return;
+ pop.innerHTML=QH_FIELDS.map(([k,label])=>
+ `<label><input type="checkbox" data-field="${k}"${qhFields[k]?' checked':''}> ${label}</label>`).join('');
+ pop.querySelectorAll('input').forEach(cb=>cb.addEventListener('change',()=>{
+ qhFields[cb.dataset.field]=cb.checked;
+ localStorage.setItem('qhouse.fields',JSON.stringify(qhFields));
+ qhApplyFields();
+ }));
+}
+(function qhFieldsInit(){
+ qhApplyFields();
+ qhBuildFieldMenu();
+ const fb=$('#fieldsBtn'), fp=$('#fpop');
+ if(fb&&fp){
+ fb.addEventListener('click',e=>{e.stopPropagation();const open=fp.hidden;fp.hidden=!open;fb.setAttribute('aria-expanded',String(open));});
+ document.addEventListener('click',e=>{if(!fp.hidden&&!e.target.closest('.fieldmenu')){fp.hidden=true;fb.setAttribute('aria-expanded','false');}});
+ }
+})();
</script>
</body>
</html>
← 9127fe1 SEO: rel=nofollow sponsored on designerwallcoverings.com lin
·
back to Quadrille House Site
·
Add search and AI discovery endpoints 6c6b2d8 →