← back to Designersalesreps
viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
7accefe71840c0f97bdaebc2b7f9bbf2b74954ca · 2026-08-12 08:53:19 -0700 · Steve
Files touched
Diff
commit 7accefe71840c0f97bdaebc2b7f9bbf2b74954ca
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 | 83 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 75 insertions(+), 8 deletions(-)
diff --git a/public/index.html b/public/index.html
index e2c47ac..868eb2a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -59,7 +59,33 @@ select.filter:focus{border-color:var(--accent)}
.sort-select{background:var(--card-bg);border:1px solid var(--line);color:var(--ink);padding:.5rem .75rem;border-radius:6px;font-size:.85rem;cursor:pointer;font-family:inherit}
.density-wrap{display:flex;align-items:center;gap:.5rem;color:var(--ink-soft);font-size:.85rem}
.density-wrap input[type=range]{accent-color:var(--accent);width:80px}
+/* view toggle */
+.view-seg{display:flex;border:1px solid var(--line);border-radius:6px;overflow:hidden}
+.view-seg button{padding:.4rem .85rem;background:var(--card-bg);border:none;font-size:.78rem;letter-spacing:.07em;text-transform:uppercase;cursor:pointer;color:var(--ink-soft);font-family:inherit}
+.view-seg button.on{background:var(--accent);color:#0a0a0a}
+/* fields menu */
+.dsr-fieldmenu{position:relative}
+.dsr-fbtn{padding:.45rem .9rem;border:1px solid var(--line);background:var(--card-bg);font-size:.78rem;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-soft);cursor:pointer;border-radius:6px;font-family:inherit}
+.dsr-fbtn:hover{border-color:var(--accent);color:var(--ink)}
+.dsr-fpop{position:absolute;top:calc(100% + 6px);right:0;background:var(--bg);border:1px solid var(--line);border-radius:8px;box-shadow:0 14px 34px rgba(0,0,0,.25);padding:8px 4px;z-index:30;min-width:180px}
+.dsr-fpop[hidden]{display:none}
+.dsr-fpop label{display:flex;align-items:center;gap:9px;padding:7px 14px;font-size:.8rem;letter-spacing:.06em;color:var(--ink-soft);cursor:pointer;white-space:nowrap}
+.dsr-fpop label:hover{color:var(--ink)}
+.dsr-fpop input{accent-color:var(--accent);width:14px;height:14px;margin:0;padding:0}
+/* grid view */
.grid{max-width:1200px;margin:0 auto 4rem;padding:0 2rem;display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--card-min,300px),1fr));gap:1.25rem}
+/* list view */
+.grid.list{display:flex;flex-direction:column;gap:.65rem}
+.grid.list .card{flex-direction:row;align-items:stretch;padding:0}
+.grid.list .card-accent{width:100%;height:4px;position:static;border-radius:4px 4px 0 0}
+.grid.list .card-inner{flex:1;padding:1rem 1.25rem;display:flex;flex-direction:row;align-items:center;gap:1.5rem;flex-wrap:wrap}
+/* field visibility */
+body.dsr-hf-agency [data-f="agency"]{display:none!important}
+body.dsr-hf-city [data-f="city"]{display:none!important}
+body.dsr-hf-lines [data-f="lines"]{display:none!important}
+body.dsr-hf-territories [data-f="territories"]{display:none!important}
+body.dsr-hf-vertical [data-f="vertical"]{display:none!important}
+body.dsr-hf-claim [data-f="claim"]{display:none!important}
.card{background:var(--card-bg);backdrop-filter:blur(12px);border:1px solid var(--line);border-radius:12px;padding:1.5rem;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;position:relative;overflow:hidden}
.card:hover{transform:translateY(-2px);box-shadow:0 8px 32px rgba(212,160,74,0.12);border-color:rgba(212,160,74,0.3)}
.card-accent{width:4px;height:100%;position:absolute;left:0;top:0;background:linear-gradient(to bottom,var(--accent),var(--accent-dark));border-radius:4px 0 0 4px}
@@ -139,11 +165,20 @@ select.filter:focus{border-color:var(--accent)}
<option value="agency">Sort: Agency A–Z</option>
<option value="city">Sort: City A–Z</option>
<option value="territory">Sort: Territory</option>
+ <option value="vertical">Sort: Vertical</option>
</select>
<div class="density-wrap">
Grid:
<input type="range" id="density" min="220" max="420" step="20" value="300">
</div>
+ <div class="view-seg">
+ <button id="dsrVGrid" class="on" title="Grid view">⊞ Grid</button>
+ <button id="dsrVList" title="List view">☰ List</button>
+ </div>
+ <div class="dsr-fieldmenu">
+ <button class="dsr-fbtn" id="dsrFieldsBtn" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div class="dsr-fpop" id="dsrFpop" hidden role="menu"></div>
+ </div>
</div>
<div class="grid" id="grid"></div>
@@ -237,16 +272,14 @@ function load(){
<div class="card" onclick="showModal(${r.id})" data-id="${r.id}">
<div class="card-accent"></div>
<div class="card-name">${esc(r.name)}</div>
- <div class="card-agency">${drill('q', r.agency, r.agency, 'card-agency-drill')}</div>
+ <div class="card-agency" data-f="agency">${drill('q', r.agency, r.agency, 'card-agency-drill')}</div>
<div class="card-meta">
- <div class="meta-row"><span class="meta-label">City</span>${drill('q', r.city, r.city)}</div>
- <div class="meta-row"><span class="meta-label">Lines</span>${r.lines.slice(0,2).map(l=>drill('q',l,l)).join(', ')}${r.lines.length>2?' +'+( r.lines.length-2)+' more':''}</div>
- </div>
- <div class="tags">
- ${r.territories.map(t=>drill('territory',t,t,'tag territory')).join('')}
- ${r.vertical.map(v=>drill('vertical',v,v,'tag')).join('')}
+ <div class="meta-row" data-f="city"><span class="meta-label">City</span>${drill('q', r.city, r.city)}</div>
+ <div class="meta-row" data-f="lines"><span class="meta-label">Lines</span>${r.lines.slice(0,2).map(l=>drill('q',l,l)).join(', ')}${r.lines.length>2?' +'+( r.lines.length-2)+' more':''}</div>
</div>
- <button class="claim-btn" onclick="event.stopPropagation();document.getElementById('claim').scrollIntoView({behavior:'smooth'})">Claim / Update Listing</button>
+ <div class="tags" data-f="territories">${r.territories.map(t=>drill('territory',t,t,'tag territory')).join('')}</div>
+ <div class="tags" data-f="vertical">${r.vertical.map(v=>drill('vertical',v,v,'tag')).join('')}</div>
+ <button class="claim-btn" data-f="claim" onclick="event.stopPropagation();document.getElementById('claim').scrollIntoView({behavior:'smooth'})">Claim / Update Listing</button>
</div>
`).join('');
});
@@ -263,6 +296,40 @@ densityEl.addEventListener('input',()=>{document.querySelector('.grid').style.se
const sv=localStorage.getItem('dsr-sort'); if(sv) sortEl.value=sv;
const dv=localStorage.getItem('dsr-density'); if(dv){densityEl.value=dv;grid.style.setProperty('--card-min',dv+'px');}
+// ── Grid / List toggle ──
+function setDsrView(v){
+ grid.classList.toggle('list',v==='list');
+ document.getElementById('dsrVGrid').classList.toggle('on',v==='grid');
+ document.getElementById('dsrVList').classList.toggle('on',v==='list');
+ localStorage.setItem('dsr-view',v);
+}
+document.getElementById('dsrVGrid').onclick=()=>setDsrView('grid');
+document.getElementById('dsrVList').onclick=()=>setDsrView('list');
+setDsrView(localStorage.getItem('dsr-view')||'grid');
+
+// ── Fields menu ──
+const DSR_FIELDS=[['agency','Agency'],['city','City'],['lines','Lines'],['territories','Territories'],['vertical','Verticals'],['claim','Claim Button']];
+const DSR_FIELDS_DEF=Object.fromEntries(DSR_FIELDS.map(([k])=>[k,true]));
+let dsrFieldVis=Object.assign({},DSR_FIELDS_DEF,JSON.parse(localStorage.getItem('dsr-fields')||'{}'));
+function applyDsrFields(){
+ DSR_FIELDS.forEach(([k])=>document.body.classList.toggle('dsr-hf-'+k,!dsrFieldVis[k]));
+ document.querySelectorAll('#dsrFpop input[data-field]').forEach(cb=>cb.checked=!!dsrFieldVis[cb.dataset.field]);
+ localStorage.setItem('dsr-fields',JSON.stringify(dsrFieldVis));
+}
+function buildDsrFieldMenu(){
+ const fp=document.getElementById('dsrFpop');
+ fp.innerHTML=DSR_FIELDS.map(([k,lbl])=>`<label><input type="checkbox" data-field="${k}"${dsrFieldVis[k]?' checked':''}> ${lbl}</label>`).join('');
+ fp.querySelectorAll('input').forEach(cb=>cb.addEventListener('change',()=>{dsrFieldVis[cb.dataset.field]=cb.checked;localStorage.setItem('dsr-fields',JSON.stringify(dsrFieldVis));applyDsrFields();}));
+}
+buildDsrFieldMenu();
+applyDsrFields();
+(function(){
+ const btn=document.getElementById('dsrFieldsBtn'), pop=document.getElementById('dsrFpop');
+ btn.addEventListener('click',e=>{e.stopPropagation();const h=pop.hidden;pop.hidden=!h;btn.setAttribute('aria-expanded',String(h));});
+ document.addEventListener('click',()=>{if(!pop.hidden){pop.hidden=true;btn.setAttribute('aria-expanded','false');}});
+ pop.addEventListener('click',e=>e.stopPropagation());
+})();
+
// deep-link: hydrate filter controls from the URL before first load
readURL();
← 3c253cc designersalesreps: adopt href-to-deeper-data primitives (TK-
·
back to Designersalesreps
·
GA4+Meta pixel: inject gtag + fbq into homepage c2aa478 →