← back to Embroideredwallpaper
viewer standard: add per-field on/off menu (Fields ▾) to product grid
6bbf59a8cc1dab040e565be12fec74b46e448ac7 · 2026-08-12 08:10:54 -0700 · Steve
Files touched
Diff
commit 6bbf59a8cc1dab040e565be12fec74b46e448ac7
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:10:54 2026 -0700
viewer standard: add per-field on/off menu (Fields ▾) to product grid
---
public/index.html | 92 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 83 insertions(+), 9 deletions(-)
diff --git a/public/index.html b/public/index.html
index d0d7187..44cdf03 100644
--- a/public/index.html
+++ b/public/index.html
@@ -188,6 +188,22 @@ textarea:focus-visible,
outline: 2px solid var(--accent);
outline-offset: 2px;
}
+/* ===== Fields on/off menu ===== */
+.fieldmenu { position:relative; flex-shrink:0 }
+.fbtn { padding:5px 11px; border:1px solid var(--line); background:transparent; font-family:inherit; font-size:10px; letter-spacing:0.22em; text-transform:uppercase; font-weight:700; color:var(--muted); cursor:pointer; transition:all .15s }
+.fbtn:hover { border-color:var(--paper); color:var(--paper) }
+.fpop { position:absolute; top:calc(100% + 8px); right:0; background:var(--bg); border:1px solid var(--line); box-shadow:0 14px 34px rgba(0,0,0,.22); padding:6px 4px; z-index:50; min-width:176px }
+.fpop[hidden] { display:none }
+.fpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-family:inherit; font-size:10px; letter-spacing:0.18em; text-transform:uppercase; font-weight:600; color:var(--paper); cursor:pointer; white-space:nowrap }
+.fpop label:hover { background:var(--bg-soft) }
+.fpop input[type=checkbox] { accent-color:var(--accent); width:13px; height:13px; cursor:pointer }
+/* Per-field hide rules — keyed on html.hf-<field> (survives re-renders, no re-apply needed) */
+html.hf-image [data-f="image"] { display:none !important }
+html.hf-pattern [data-f="pattern"] { display:none !important }
+html.hf-vendor [data-f="vendor"] { display:none !important }
+html.hf-color [data-f="color"] { display:none !important }
+html.hf-price [data-f="price"] { display:none !important }
+html.hf-sample [data-f="sample"] { display:none !important }
</style>
<script>
(function(){ try { var t = localStorage.getItem('emb_theme') || 'light'; document.documentElement.dataset.theme = t; } catch(e){} })();
@@ -446,6 +462,10 @@ textarea:focus-visible,
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5.5-5.5"/></svg>
<input type="text" id="searchInput" placeholder="Search by pattern, color…" autocomplete="off">
</div>
+ <div class="fieldmenu">
+ <button class="fbtn" id="fieldsBtn" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div class="fpop" id="fpop" hidden role="menu"></div>
+ </div>
</div>
<div class="stat-line" id="statLine">Loading…</div>
@@ -622,12 +642,12 @@ function cleanSku(s){
function safeImg(u) { u = u || ''; return (/^\/img\//.test(u) || /^https:\/\/(?:cdn\.shopify\.com|designerwallcoverings\.com)\//.test(u)) ? u : '/hero-bg.jpg'; }
function cardHTML(p) {
const eager = state.page === 1;
- return '<img loading="' + (eager ? 'eager' : 'lazy') + '"' + (eager ? ' fetchpriority="high"' : '') + ' src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
+ return '<img data-f="image" loading="' + (eager ? 'eager' : 'lazy') + '"' + (eager ? ' fetchpriority="high"' : '') + ' src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
+ '<div class="overlay">'
- + '<div class="pat">' + escAttr(p.pattern_name || p.title) + '</div>'
- + '<div class="ven">Designer Wallcoverings' + (cleanSku(p.sku || p.handle_display || p.handle) ? ' · ' + escAttr(cleanSku(p.sku || p.handle_display || p.handle)) : '') + '</div>'
+ + '<div class="pat" data-f="pattern">' + escAttr(p.pattern_name || p.title) + '</div>'
+ + '<div class="ven" data-f="vendor">Designer Wallcoverings' + (cleanSku(p.sku || p.handle_display || p.handle) ? ' · ' + escAttr(cleanSku(p.sku || p.handle_display || p.handle)) : '') + '</div>'
+ '<div class="actions">'
- + '<button class="sample-btn" onclick="event.stopPropagation();dwmOpen(\'Sample\',' + JSON.stringify({sku:p.sku||p.handle_display||p.handle, title:p.title, image_url:safeImg(p.image_url)}).replace(/"/g,'"') + ')">Sample</button>'
+ + '<button class="sample-btn" data-f="sample" onclick="event.stopPropagation();dwmOpen(\'Sample\',' + JSON.stringify({sku:p.sku||p.handle_display||p.handle, title:p.title, image_url:safeImg(p.image_url)}).replace(/"/g,'"') + ')">Sample</button>'
+ '</div></div>';
}
@@ -637,11 +657,11 @@ function rowHTML(p) {
const mp = Number(p.max_price);
const price = (p.max_price != null && !isNaN(mp) && mp > 0) ? '$' + mp.toFixed(2) : '—';
const aes = LABELS[p.aesthetic] || p.aesthetic || '—';
- return '<img loading="lazy" src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
- + '<div class="r-pat">' + escAttr(p.pattern_name || p.title) + '</div>'
- + '<div class="r-col r-sku">' + escAttr(cleanSku(p.sku || p.handle_display || p.handle) || '—') + '</div>'
- + '<div class="r-col r-aes">' + (p.aesthetic ? sfDrill(p.aesthetic, String(aes).replace(/-/g, ' '), 'span') : escAttr(String(aes).replace(/-/g, ' '))) + '</div>'
- + '<div class="r-col r-price">' + price + '</div>';
+ return '<img data-f="image" loading="lazy" src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
+ + '<div class="r-pat" data-f="pattern">' + escAttr(p.pattern_name || p.title) + '</div>'
+ + '<div class="r-col r-sku" data-f="vendor">' + escAttr(cleanSku(p.sku || p.handle_display || p.handle) || '—') + '</div>'
+ + '<div class="r-col r-aes" data-f="color">' + (p.aesthetic ? sfDrill(p.aesthetic, String(aes).replace(/-/g, ' '), 'span') : escAttr(String(aes).replace(/-/g, ' '))) + '</div>'
+ + '<div class="r-col r-price" data-f="price">' + price + '</div>';
}
// Click-to-open-full-details — every card opens a rich detail modal showing
@@ -857,6 +877,60 @@ function setTheme(t){ document.documentElement.dataset.theme = t; try { localSto
setTheme(document.documentElement.dataset.theme || 'light');
if (tb) tb.addEventListener('click', () => setTheme(document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'));
+
+// ===== Fields on/off menu =====
+// [key, label, defaultVisible] — color=OFF by default; image-only achievable by turning rest off.
+const FIELDS = [
+ ['image', 'Image', true],
+ ['pattern', 'Pattern', true],
+ ['vendor', 'Vendor', true],
+ ['color', 'Color', false],
+ ['price', 'Price', true],
+ ['sample', 'Sample', true],
+];
+function loadFields() {
+ const def = {}; FIELDS.forEach(([k,,d]) => def[k] = d);
+ try { return Object.assign(def, JSON.parse(localStorage.getItem(HOSTKEY + '_fields') || '{}')); } catch(e) { return def; }
+}
+let _fields = loadFields();
+function applyFields() {
+ FIELDS.forEach(([k]) => document.documentElement.classList.toggle('hf-' + k, !_fields[k]));
+}
+function buildFieldMenu() {
+ const fpop = document.getElementById('fpop');
+ if (!fpop) return;
+ fpop.innerHTML = FIELDS.map(([k, label]) =>
+ '<label><input type="checkbox" data-field="' + k + '"' + (_fields[k] ? ' checked' : '') + '> ' + label + '</label>'
+ ).join('');
+ fpop.querySelectorAll('input[type=checkbox]').forEach(function(cb) {
+ cb.addEventListener('change', function() {
+ _fields[cb.dataset.field] = cb.checked;
+ try { localStorage.setItem(HOSTKEY + '_fields', JSON.stringify(_fields)); } catch(e) {}
+ applyFields();
+ });
+ });
+}
+// Fields popover open/close
+(function() {
+ var btn = document.getElementById('fieldsBtn');
+ var pop = document.getElementById('fpop');
+ if (!btn || !pop) return;
+ btn.addEventListener('click', function(e) {
+ e.stopPropagation();
+ var open = pop.hidden;
+ pop.hidden = !open;
+ btn.setAttribute('aria-expanded', String(open));
+ });
+ document.addEventListener('click', function(e) {
+ if (!pop.hidden && !e.target.closest('.fieldmenu')) {
+ pop.hidden = true;
+ btn.setAttribute('aria-expanded', 'false');
+ }
+ });
+})();
+buildFieldMenu();
+applyFields();
+
sfReadURL();
loadFacets();
loadGridPage();
← 84246da fix: take infinite-scroll .loading indicator out of flow (ki
·
back to Embroideredwallpaper
·
front-page-steward: ux-primitives bundle (silent) TK-12031 503f5aa →