[object Object]

← back to Rigo Line Viewer

viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer

4721c6df961c7e12bbf91285cfd1b0bc823a84e9 · 2026-08-12 08:53:17 -0700 · Steve

Files touched

Diff

commit 4721c6df961c7e12bbf91285cfd1b0bc823a84e9
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 12 08:53:17 2026 -0700

    viewer standard: add Grid/List toggle + per-field on/off menu to internal record-grid viewer
---
 public/index.html | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/public/index.html b/public/index.html
index 4dae6c0..e7b0685 100644
--- a/public/index.html
+++ b/public/index.html
@@ -35,6 +35,33 @@ label.tog { font-size:12px; color:var(--dim); display:flex; gap:5px; align-items
 /* ── drill atom: every data point is an href to its filtered view (deeper data) ── */
 .drill { color:inherit; text-decoration:none; cursor:pointer; border-bottom:1px dotted transparent; }
 .drill:hover { color:var(--teal); border-bottom-color:var(--teal); }
+
+/* ── Grid / List toggle ── */
+.vtoggle { display:flex; border:1px solid var(--line); border-radius:6px; overflow:hidden; }
+.vtoggle button { background:var(--card); color:var(--dim); border:none; padding:5px 11px; font:12px/1 inherit; cursor:pointer; letter-spacing:.04em; }
+.vtoggle button.on { background:var(--teal); color:#0a1412; font-weight:700; }
+
+/* ── List view ── */
+#grid.list { display:block; }
+#grid.list .card { display:flex; flex-direction:row; align-items:stretch; margin-bottom:6px; border-radius:6px; }
+#grid.list .card img { width:64px; min-width:64px; height:64px; aspect-ratio:auto; object-fit:cover; }
+#grid.list .card > div[style*="aspect-ratio"] { width:64px; min-width:64px; height:64px; aspect-ratio:auto; }
+#grid.list .card .meta { flex:1; padding:6px 10px; font-size:12px; display:flex; flex-direction:column; justify-content:center; gap:2px; }
+
+/* ── Fields menu ── */
+.fieldmenu { position:relative; }
+.fbtn { background:var(--card); color:var(--dim); border:1px solid var(--line); border-radius:6px; padding:5px 11px; font:12px/1 inherit; cursor:pointer; }
+.fbtn:hover { border-color:var(--teal); color:var(--fg); }
+.fpop { position:absolute; top:calc(100% + 6px); right:0; background:var(--card); border:1px solid var(--line); border-radius:8px; box-shadow:0 10px 28px #0006; padding:8px 4px; z-index:30; min-width:168px; }
+.fpop[hidden] { display:none; }
+.fpop label { display:flex; align-items:center; gap:8px; padding:5px 12px; font-size:12px; color:var(--dim); cursor:pointer; white-space:nowrap; }
+.fpop label:hover { color:var(--fg); }
+.fpop input { accent-color:var(--teal); }
+
+/* ── Field-hide rules (grid mode only; list shows all) ── */
+#grid:not(.list).hf-pr    .card .pr    { display:none; }
+#grid:not(.list).hf-internal .card .internal { display:none; }
+#grid:not(.list).hf-when  .card .when  { display:none; }
 </style>
 </head>
 <body>
@@ -49,8 +76,24 @@ label.tog { font-size:12px; color:var(--dim); display:flex; gap:5px; align-items
       <option value="pr">PR Name A→Z</option>
       <option value="sku">SKU A→Z</option>
       <option value="color">Color</option>
+      <option value="collection">Collection A→Z</option>
+      <option value="weight">Weight</option>
+      <option value="backing">Backing A→Z</option>
+      <option value="status">Status A→Z</option>
     </select>
     <input type="range" id="density" min="2" max="10" value="5" title="Density">
+    <div class="vtoggle" id="viewToggle">
+      <button id="vGrid" class="on" onclick="setView('grid')">▦ Grid</button>
+      <button id="vList" onclick="setView('list')">☰ List</button>
+    </div>
+    <div class="fieldmenu">
+      <button class="fbtn" id="fieldsBtn" onclick="toggleFieldsMenu(event)">Fields ▾</button>
+      <div class="fpop" id="fpop" hidden>
+        <label><input type="checkbox" data-rf="pr" checked> PR Name / Color</label>
+        <label><input type="checkbox" data-rf="internal" checked> Internal / SKU</label>
+        <label><input type="checkbox" data-rf="when" checked> Date</label>
+      </div>
+    </div>
     <label class="tog"><input type="checkbox" id="imgonly"> texture wall</label>
   </div>
 </header>
@@ -127,6 +170,10 @@ function render() {
     s==='pattern' ? (a.pattern_name||'').localeCompare(b.pattern_name||'') :
     s==='pr' ? (a.pr_pattern_name||'').localeCompare(b.pr_pattern_name||'') :
     s==='sku' ? (a.mfr_sku||'').localeCompare(b.mfr_sku||'') :
+    s==='collection' ? (a.collection||'').localeCompare(b.collection||'') :
+    s==='weight' ? (a.weight_type||'').localeCompare(b.weight_type||'') :
+    s==='backing' ? (a.backing||'').localeCompare(b.backing||'') :
+    s==='status' ? (a.status||'').localeCompare(b.status||'') :
     (a.color_name||'').localeCompare(b.color_name||''));
   $('#grid').innerHTML = rows.map(p => `
     <div class="card" title="${esc(p.mfr_sku)} · created ${p.created_at||''}">
@@ -155,6 +202,50 @@ $('#density').oninput = (e) => { document.documentElement.style.setProperty('--c
 $('#imgonly').onchange = (e) => document.body.classList.toggle('imgonly', e.target.checked);
 if (localStorage.getItem('rigo.cols')) { $('#density').value = localStorage.getItem('rigo.cols'); document.documentElement.style.setProperty('--cols', $('#density').value); }
 if (localStorage.getItem('rigo.sort')) $('#sort').value = localStorage.getItem('rigo.sort');
+
+// ── Grid / List view toggle ──
+const RIGO_FIELDS_DEFAULTS = { pr: true, internal: true, when: true };
+function setView(v) {
+  const g = $('#grid');
+  g.classList.toggle('list', v === 'list');
+  $('#vGrid').classList.toggle('on', v !== 'list');
+  $('#vList').classList.toggle('on', v === 'list');
+  localStorage.setItem('rigo.view', v);
+}
+(function initView() {
+  const v = localStorage.getItem('rigo.view') || 'grid';
+  setView(v);
+})();
+
+// ── Fields menu ──
+function getFieldPrefs() {
+  try { return Object.assign({}, RIGO_FIELDS_DEFAULTS, JSON.parse(localStorage.getItem('rigo.fields') || '{}')); }
+  catch(e) { return Object.assign({}, RIGO_FIELDS_DEFAULTS); }
+}
+function applyFieldPrefs(prefs) {
+  const g = $('#grid');
+  for (const [k, on] of Object.entries(prefs)) {
+    g.classList.toggle('hf-' + k, !on);
+  }
+  document.querySelectorAll('#fpop [data-rf]').forEach(cb => {
+    cb.checked = !!prefs[cb.dataset.rf];
+  });
+}
+function toggleFieldsMenu(e) {
+  e.stopPropagation();
+  const pop = $('#fpop');
+  pop.hidden = !pop.hidden;
+}
+document.addEventListener('click', () => { const p = $('#fpop'); if (p) p.hidden = true; });
+document.querySelectorAll('#fpop [data-rf]').forEach(cb => {
+  cb.addEventListener('change', () => {
+    const prefs = getFieldPrefs();
+    prefs[cb.dataset.rf] = cb.checked;
+    localStorage.setItem('rigo.fields', JSON.stringify(prefs));
+    applyFieldPrefs(prefs);
+  });
+});
+applyFieldPrefs(getFieldPrefs());
 addEventListener('popstate', () => { readURL(); buildSelected(); render(); }); // back/forward → re-read URL
 boot();
 </script>

← 43c92f1 rigo-line-viewer: adopt shared /drill.js href-to-deeper-data  ·  back to Rigo Line Viewer  ·  nav-agent: universal grid-controls drop-in on internal viewe 10a0a8d →