← back to Rentv Sheet Enrich Refine
Live console: click a field in the left rail to open its full value lookup in the main table area (search + multi-select to filter)
1bff2a8eeb01809f24f134fd5d788961a50ed34e · 2026-08-13 16:01:49 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 1bff2a8eeb01809f24f134fd5d788961a50ed34e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 13 16:01:49 2026 -0700
Live console: click a field in the left rail to open its full value lookup in the main table area (search + multi-select to filter)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public_live/index.html | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/public_live/index.html b/public_live/index.html
index 17c15f9..574914c 100644
--- a/public_live/index.html
+++ b/public_live/index.html
@@ -82,6 +82,17 @@
a.iconbtn svg,a.callbtn svg{width:1.05em;height:1.05em;display:block}
a.callbtn{color:#16a34a} a.iconbtn.web{color:var(--accent)} a.iconbtn.li{color:#0a66c2}
.linkval{color:inherit}
+ /* FIELD LOOKUP: click a field in the left rail -> its full value list opens here in the main area */
+ .lkhead{display:flex;align-items:center;gap:10px;padding:8px 10px;position:sticky;top:0;background:var(--panel2);border-bottom:1px solid var(--line);z-index:6}
+ .lkhead b{font-size:14px;color:var(--accent)} .lksub{color:var(--dim);font-size:12px}
+ .lkback{cursor:pointer;color:var(--accent);font-weight:700;white-space:nowrap} .lkback:hover{text-decoration:underline}
+ .lkclose{cursor:pointer;color:var(--dim);font-weight:700} .lkclose:hover{color:#fff}
+ #lksearch{margin-left:auto;background:var(--panel);border:1px solid var(--line);color:#e6edf3;border-radius:6px;padding:3px 8px;font-size:12px;min-width:150px}
+ table.lktbl{table-layout:auto;border-collapse:collapse;width:100%}
+ .lktbl th{position:sticky;top:39px;text-align:left;background:var(--panel);color:var(--dim);font-size:11px;padding:6px 10px;border-bottom:1px solid var(--line);z-index:5}
+ .lkrow{cursor:pointer;border-bottom:1px solid var(--line)} .lkrow:hover{background:#1a212b}
+ .lkrow td{padding:6px 10px} .lkv{max-width:680px} .lkn{color:var(--dim);text-align:right;white-space:nowrap}
+ .lkrow .lkbx{width:18px;color:transparent} .lkrow.on{background:rgba(123,192,67,.12)} .lkrow.on .lkbx{color:#7bc043}
.rowbtn{background:var(--accent);color:#04121f;border:0;border-radius:6px;padding:3px 8px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap}
.grouphdr td{background:#12161c;color:var(--accent);font-weight:700;font-size:12px;padding:6px 9px;position:sticky;top:26px}
/* cards */
@@ -159,6 +170,7 @@ let VIEW=LS.get('view','table'), GID=LS.get('gid',null), editing=false, curRow=n
let SELECTED=new Set(LS.get('sheets',[])); // selected sheet gids (1..all) shown combined
function curKey(){ return SELECTED.size===1 ? String([...SELECTED][0]) : ('combo.'+[...SELECTED].sort().join('-')); }
let FILTERS={}, SEARCH='', SORT=LS.get('sort',null), GROUP=LS.get('group',''), HIDDEN=new Set(LS.get('hidden.'+(GID||''),[])), COLW={}, ORDER=[], dragCol=null;
+let LOOKUP=null; // when set to a column index, the main area shows that field's full value lookup
const MAXROWS=2500;
function pk(){return 'p'+GID} // per-tab localStorage namespace
@@ -241,6 +253,7 @@ function filtered(){ let rows=DATA.rows.map((r,i)=>[r,i]).filter(([r])=>passes(r
// ---- LEFT RAIL: search + sort + group + density + field toggles + per-field FILTERS ----
function buildRail(){
const rail=$('#rail'); if(rail.classList.contains('hide')){return}
+ LOOKUP=null; // rebuilding the rail (sheet change / reset) drops any open field lookup
const H=DATA.headers, opts=H.map((h,i)=>h?`<option value="${i}">${esc(h)}</option>`:'').join('');
let html=`<details class="sec" open><summary>Sheets (${META?META.tabs.length:0}) · <span class="mini" id="alls">all</span> / <span class="mini" id="nones">none</span></summary>
<div id="sheets"></div></details>
@@ -279,6 +292,39 @@ function buildFacets(){
});
host.innerHTML=html||'<div class="hint" style="padding:6px">No filterable fields</div>';
host.querySelectorAll('.val').forEach(el=>el.onclick=()=>toggleFilter(+el.closest('.field').dataset.c, el.dataset.v));
+ // click a FIELD (not the inline disclosure) -> open its full value lookup in the main table area on the right
+ host.querySelectorAll('details.field > summary').forEach(sm=>sm.addEventListener('click',e=>{
+ e.preventDefault(); openLookup(+sm.closest('.field').dataset.c);
+ }));
+}
+function openLookup(ci){ LOOKUP=ci; render(); document.querySelector('#wrap').scrollTop=0; }
+function lookupHTML(ci){
+ const h=DATA.headers[ci]||'', tally={}; // full contents of THIS field across the loaded rows
+ for(const r of DATA.rows){ const raw=ci<r.length?r[ci]:''; const v=norm(raw)||'(blank)'; tally[v]=(tally[v]||0)+1; }
+ const vals=Object.entries(tally).sort((a,b)=>b[1]-a[1]||a[0].localeCompare(b[0]));
+ const act=FILTERS[ci];
+ const body=vals.map(([v,n])=>`<tr class="lkrow${act&&act.has(v)?' on':''}" data-v="${esc(v)}">`
+ +`<td class="lkbx">✓</td><td class="lkv" title="${esc(v)}">${esc(v)}</td><td class="lkn">${n}</td></tr>`).join('');
+ return `<div class="lookup"><div class="lkhead">`
+ +`<span class="lkback" title="back to the table">‹ Table</span><b>${esc(h)}</b>`
+ +`<span class="lksub">${vals.length} distinct · click a value to filter</span>`
+ +`<input id="lksearch" placeholder="Filter values…" autocomplete="off"><span class="lkclose" title="close">✕</span></div>`
+ +`<table class="lktbl"><thead><tr><th class="lkbx"></th><th>Value</th><th class="lkn">Rows</th></tr></thead>`
+ +`<tbody id="lkbody">${body}</tbody></table></div>`;
+}
+function wireLookup(){
+ const w=$('#wrap'), done=()=>{LOOKUP=null;render()};
+ w.querySelector('.lkback')&&(w.querySelector('.lkback').onclick=done);
+ w.querySelector('.lkclose')&&(w.querySelector('.lkclose').onclick=done);
+ // toggle a value's filter IN PLACE (no full re-render) so scroll + search text survive multi-select
+ w.querySelectorAll('.lkrow').forEach(tr=>tr.onclick=()=>{ const ci=LOOKUP,v=tr.dataset.v;
+ FILTERS[ci]=FILTERS[ci]||new Set();
+ if(FILTERS[ci].has(v)){FILTERS[ci].delete(v);tr.classList.remove('on');}else{FILTERS[ci].add(v);tr.classList.add('on');}
+ if(!FILTERS[ci].size)delete FILTERS[ci];
+ renderChips(); });
+ const s=w.querySelector('#lksearch');
+ if(s)s.oninput=e=>{const q=e.target.value.toLowerCase();
+ w.querySelectorAll('#lkbody .lkrow').forEach(tr=>{tr.style.display=tr.querySelector('.lkv').textContent.toLowerCase().includes(q)?'':'none';});};
}
function toggleFilter(ci,v){ FILTERS[ci]=FILTERS[ci]||new Set(); FILTERS[ci].has(v)?FILTERS[ci].delete(v):FILTERS[ci].add(v); if(!FILTERS[ci].size)delete FILTERS[ci]; buildFacets(); renderChips(); render(); }
function renderChips(){ const c=$('#chips'); let h='';
@@ -354,6 +400,7 @@ function resetLayout(){ COLW={}; ORDER=[]; LS.set('colw.'+curKey(),{}); LS.set('
ORDER=LS.get('order.'+curKey(),[]); buildRail(); render(); }
function render(){
const wrap=$('#wrap'); $$('#viewseg button').forEach(b=>b.classList.toggle('on',b.dataset.v===VIEW));
+ if(LOOKUP!=null && DATA.headers[LOOKUP]){ wrap.innerHTML=lookupHTML(LOOKUP); wireLookup(); setSync('ok'); return; }
const rows=filtered();
setSync('ok');
if(!rows.length){wrap.innerHTML='<div class="empty">No rows match the current filters.</div>';return}
← 44d2687 Live console: icons open Website/LinkedIn in a popup window
·
back to Rentv Sheet Enrich Refine
·
Live console: Website/LinkedIn/phone cells are now WHOLE-CEL 4ff3c02 →