← back to All Designerwallcoverings
microsites.html: add list/grid toggle + per-field on/off menu (full viewer control standard)
95446fa377c96d83fba14007aa9f7a37db086a62 · 2026-08-11 14:07:30 -0700 · Steve
Files touched
Diff
commit 95446fa377c96d83fba14007aa9f7a37db086a62
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Aug 11 14:07:30 2026 -0700
microsites.html: add list/grid toggle + per-field on/off menu (full viewer control standard)
---
public/microsites.html | 113 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 105 insertions(+), 8 deletions(-)
diff --git a/public/microsites.html b/public/microsites.html
index fd6ac26..2acec69 100644
--- a/public/microsites.html
+++ b/public/microsites.html
@@ -44,7 +44,43 @@
.dens { display:flex; align-items:center; gap:10px; font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#7d766a; }
.dens input { accent-color: var(--gold); width: 130px; }
+ /* Fields on/off menu — toggle any field on the card (down to image-only). */
+ .fieldmenu { position:relative; }
+ .fbtn { padding:9px 14px; border:1px solid var(--line); background:#fff; font-size:12px; letter-spacing:.1em;
+ text-transform:uppercase; color:#7d766a; cursor:pointer; border-radius:2px; }
+ .fbtn:hover { border-color:var(--gold); color:var(--ink); }
+ .fpop { position:absolute; top:calc(100% + 8px); right:0; background:#fff; border:1px solid var(--line);
+ border-radius:2px; box-shadow:0 14px 34px rgba(22,20,18,.16); padding:10px 6px; z-index:30; min-width:190px; }
+ .fpop[hidden] { display:none; }
+ .fpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:12px; letter-spacing:.08em;
+ text-transform:uppercase; color:#4b463d; cursor:pointer; white-space:nowrap; }
+ .fpop label:hover { background:var(--paper); }
+ .fpop input { accent-color: var(--gold); }
+
.grid { display:grid; grid-template-columns: repeat(var(--cols), 1fr); gap: 26px; padding: 34px; }
+
+ /* List view — cards become horizontal rows (thumbnail left, meta right). */
+ .grid.list { display:block; padding: 20px 34px; }
+ .grid.list .card { flex-direction:row; align-items:stretch; margin-bottom:14px; }
+ .grid.list .card:hover { transform:none; }
+ .grid.list .collage { flex:0 0 clamp(120px, 22vw, 210px); aspect-ratio:4/3; }
+ .grid.list .meta { flex:1; display:flex; flex-direction:column; justify-content:center; }
+ .grid.list .badge { display:none; }
+
+ /* Extra record fields (off by default) — surfaced as labeled lines so each is toggleable. */
+ .card .fline { margin-top:6px; font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:#9a927f; }
+ .card .fline b { color:#6b6459; font-weight:600; }
+
+ /* Per-field hide switches driven by body.hf-<field> classes (set from the Fields menu). */
+ body.hf-image .collage,
+ body.hf-image .card:not(.list) .collage { display:none; }
+ body.hf-name [data-f="name"] { display:none; }
+ body.hf-type [data-f="type"] { display:none; }
+ body.hf-slug [data-f="slug"] { display:none; }
+ body.hf-count [data-f="count"] { display:none; }
+ body.hf-status [data-f="status"] { display:none; }
+ body.hf-cta [data-f="cta"] { display:none; }
+ body.hf-inquiry [data-f="inquiry"] { display:none; }
.card { background:#fff; border:1px solid var(--line); overflow:hidden; display:flex; flex-direction:column;
transition: box-shadow .25s, transform .25s; position:relative; }
.card:hover { box-shadow: 0 14px 34px rgba(22,20,18,.14); transform: translateY(-3px); }
@@ -139,6 +175,14 @@
<option value="gated">Trade-Gated First</option>
</select>
<label class="dens">Density <input type="range" id="density" min="1" max="30" step="1" value="4"><span id="densN">4</span></label>
+ <div class="seg" role="tablist" aria-label="View">
+ <button id="vGrid" class="on">Grid</button>
+ <button id="vList">List</button>
+ </div>
+ <div class="fieldmenu">
+ <button id="fieldsBtn" class="fbtn" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div class="fpop" id="fpop" hidden role="menu"></div>
+ </div>
</div>
<main class="grid" id="grid"></main>
@@ -168,12 +212,31 @@
<script>
const grid = document.getElementById('grid'), sentinel = document.getElementById('sentinel');
const menu = document.getElementById('menu');
+// Every field on a card is user-toggleable; [key, label, default-visible].
+const FIELDS = [
+ ['image', 'Image', true],
+ ['name', 'Name', true],
+ ['type', 'Type', false],
+ ['slug', 'Slug', false],
+ ['count', 'Products', true],
+ ['status', 'Status', false],
+ ['cta', 'Visit link', true],
+ ['inquiry', 'Inquiry', true],
+];
+function loadFields(){
+ const def = {}; FIELDS.forEach(([k,,d]) => def[k] = d);
+ try { return Object.assign(def, JSON.parse(localStorage.getItem('adwm_fields') || '{}')); }
+ catch(e){ return def; }
+}
+
const state = {
all: [], view: [], rendered: 0, BATCH: 40,
q: '',
filter: localStorage.getItem('adwm_filter') || 'feeds',
sort: localStorage.getItem('adwm_sort') || 'products',
cols: localStorage.getItem('adwm_cols') || '4',
+ viewMode: localStorage.getItem('adwm_view') || 'grid',
+ fields: loadFields(),
};
document.documentElement.style.setProperty('--cols', state.cols);
@@ -181,6 +244,27 @@ density.value = state.cols;
densN.textContent = state.cols;
sort.value = state.sort;
setSeg();
+setView();
+buildFieldMenu();
+applyFields();
+
+function setView(){
+ grid.classList.toggle('list', state.viewMode === 'list');
+ vGrid.classList.toggle('on', state.viewMode === 'grid');
+ vList.classList.toggle('on', state.viewMode === 'list');
+}
+function applyFields(){
+ FIELDS.forEach(([k]) => document.body.classList.toggle('hf-' + k, !state.fields[k]));
+}
+function buildFieldMenu(){
+ fpop.innerHTML = FIELDS.map(([k,label]) =>
+ `<label><input type="checkbox" data-field="${k}"${state.fields[k] ? ' checked' : ''}> ${label}</label>`).join('');
+ fpop.querySelectorAll('input').forEach(cb => cb.addEventListener('change', () => {
+ state.fields[cb.dataset.field] = cb.checked;
+ localStorage.setItem('adwm_fields', JSON.stringify(state.fields));
+ applyFields();
+ }));
+}
function setSeg(){
fFeeds.classList.toggle('on', state.filter==='feeds');
@@ -258,24 +342,28 @@ function card(s){
// Only when we have a real vendor name that maps to the product grid's vendor facet.
const aggUrl = s.vendor ? '/?vendors=' + encodeURIComponent(s.vendor) : '';
const countTxt = s.productCount.toLocaleString() + ' designs';
- const badge = gated ? `<span class="badge gated">Trade Access</span>`
+ const badge = gated ? `<span class="badge gated" data-f="status">Trade Access</span>`
: (s.productCount>0
- ? (aggUrl ? `<span class="badge countdrill" data-agg="${aggUrl.replace(/"/g,'"')}" title="See all ${countTxt} in the catalog aggregator">${countTxt}</span>`
- : `<span class="badge">${countTxt}</span>`)
+ ? (aggUrl ? `<span class="badge countdrill" data-f="count" data-agg="${aggUrl.replace(/"/g,'"')}" title="See all ${countTxt} in the catalog aggregator">${countTxt}</span>`
+ : `<span class="badge" data-f="count">${countTxt}</span>`)
: '');
const sub = gated ? 'By trade inquiry'
: (s.productCount>0
? (aggUrl ? `<span class="countdrill" data-agg="${aggUrl.replace(/"/g,'"')}" title="See all ${countTxt} in the catalog aggregator">${countTxt} →</span>`
: countTxt)
: 'Editorial lookbook');
+ const clean = v => String(v == null ? '' : v).replace(/[<>]/g,'');
el.innerHTML = `
${badge}
- <div class="collage${pool.length<=1?' single':''}">${cells}</div>
+ <div class="collage${pool.length<=1?' single':''}" data-f="image">${cells}</div>
<div class="meta">
- <h3></h3>
- <div class="sub">${sub}</div>
- <div class="cta">Visit Microsite →</div>
- <button type="button" class="mailv" title="Compose a stock & price inquiry to this vendor — saves a Gmail DRAFT, never auto-sends">✉ Stock & Price</button>
+ <h3 data-f="name"></h3>
+ <div class="sub" data-f="count">${sub}</div>
+ <div class="fline" data-f="type"><b>Type</b> ${clean(s.type) || '—'}</div>
+ <div class="fline" data-f="slug"><b>Slug</b> ${clean(s.slug) || '—'}</div>
+ <div class="fline" data-f="status"><b>Status</b> ${gated ? 'Trade-Gated' : 'Live'}</div>
+ <div class="cta" data-f="cta">Visit Microsite →</div>
+ <button type="button" class="mailv" data-f="inquiry" title="Compose a stock & price inquiry to this vendor — saves a Gmail DRAFT, never auto-sends">✉ Stock & Price</button>
</div>`;
el.querySelector('h3').textContent = name;
// The card itself is an <a> — the inquiry button must not navigate.
@@ -352,6 +440,15 @@ density.addEventListener('input', e=>{ state.cols=e.target.value; localStorage.s
densN.textContent=state.cols; document.documentElement.style.setProperty('--cols', state.cols); });
fFeeds.onclick=()=>{ state.filter='feeds'; localStorage.setItem('adwm_filter','feeds'); setSeg(); apply(); };
fAll.onclick=()=>{ state.filter='all'; localStorage.setItem('adwm_filter','all'); setSeg(); apply(); };
+
+vGrid.onclick=()=>{ state.viewMode='grid'; localStorage.setItem('adwm_view','grid'); setView(); };
+vList.onclick=()=>{ state.viewMode='list'; localStorage.setItem('adwm_view','list'); setView(); };
+
+// Fields menu open/close (click-away closes).
+fieldsBtn.addEventListener('click', (e)=>{ e.stopPropagation();
+ const open = fpop.hidden; fpop.hidden = !open; fieldsBtn.setAttribute('aria-expanded', String(open)); });
+document.addEventListener('click', (e)=>{ if (!fpop.hidden && !e.target.closest('.fieldmenu')) {
+ fpop.hidden = true; fieldsBtn.setAttribute('aria-expanded','false'); } });
</script>
</body>
</html>
← 47d2616 all.: add greenland to BANNED (private-label leak) + drop ro
·
back to All Designerwallcoverings
·
viewer control standard: add Fields on/off menu to index.htm 7f2ae6c →