← back to Rentv 2026
re-search: click items expand INLINE + left detail panel (no modal, no navigate-away) — Steve directive
caa3fa7c783b7cce566c6c57a126b70226a57e27 · 2026-08-05 14:51:13 -0700 · Steve Abrams
onclick any result → expands inline in the row (all fields, source link); '↖ Open in panel'
slides a LEFT detail drawer for a roomier view (list stays on the right). Title no longer
navigates away — the item opens in place, per Steve's 'keep all inline' rule. Verified via
real Chrome: inline expand + left panel + detail all PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit caa3fa7c783b7cce566c6c57a126b70226a57e27
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 5 14:51:13 2026 -0700
re-search: click items expand INLINE + left detail panel (no modal, no navigate-away) — Steve directive
onclick any result → expands inline in the row (all fields, source link); '↖ Open in panel'
slides a LEFT detail drawer for a roomier view (list stays on the right). Title no longer
navigates away — the item opens in place, per Steve's 'keep all inline' rule. Verified via
real Chrome: inline expand + left panel + detail all PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/re-search.html | 59 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 5 deletions(-)
diff --git a/public/re-search.html b/public/re-search.html
index 9a2f17d7..54854a6d 100644
--- a/public/re-search.html
+++ b/public/re-search.html
@@ -29,6 +29,25 @@
.type{font-size:10px;color:var(--sub);border:1px solid var(--ln);border-radius:5px;padding:1px 6px}
.empty{padding:60px;text-align:center;color:var(--sub)}
#more{text-align:center;margin-top:14px} #more a{color:var(--a)}
+ /* clickable rows — INLINE expand (no modal, no navigate-away) */
+ .res{cursor:pointer} .res.open{border-color:var(--a);box-shadow:0 2px 10px rgba(200,16,46,.08)}
+ .res .caret{flex:0 0 auto;color:var(--sub);font-size:12px;transition:transform .15s;align-self:center}
+ .res.open .caret{transform:rotate(90deg);color:var(--a)}
+ .res-detail{display:none;margin-top:10px;padding-top:10px;border-top:1px dashed var(--ln)}
+ .res.open .res-detail{display:block}
+ .res-detail dl{display:grid;grid-template-columns:120px 1fr;gap:4px 12px;margin:0;font-size:12.5px}
+ .res-detail dt{color:var(--sub)} .res-detail dd{margin:0;color:var(--ink);word-break:break-word}
+ .res-detail dd a{color:var(--a);text-decoration:none} .res-detail dd a:hover{text-decoration:underline}
+ .res-detail .actions{margin-top:9px;display:flex;gap:10px} .res-detail .actions button,.res-detail .actions a{font-size:12px;border:1px solid var(--ln);background:var(--wa);border-radius:7px;padding:5px 10px;cursor:pointer;color:var(--ink);text-decoration:none}
+ .res-detail .actions button:hover,.res-detail .actions a:hover{border-color:var(--a);color:var(--a)}
+ /* LEFT detail panel (opens "to the left" when more room is needed) */
+ body.panel-open .wrap{margin-left:400px}
+ #detailPanel{position:fixed;top:0;left:0;width:380px;height:100vh;background:var(--bg);border-right:2px solid var(--a);box-shadow:4px 0 24px rgba(0,0,0,.10);z-index:40;transform:translateX(-100%);transition:transform .2s;overflow-y:auto;padding:16px 18px}
+ body.panel-open #detailPanel{transform:translateX(0)}
+ #detailPanel .pclose{position:absolute;top:12px;right:12px;background:none;border:0;font-size:20px;cursor:pointer;color:var(--sub)}
+ #detailPanel h3{margin:0 24px 4px 0;font:700 16px/1.3 'Playfair Display',Georgia,serif}
+ #detailPanel .pbadge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 8px;border-radius:6px;color:#fff;display:inline-block;margin-bottom:8px}
+ @media(max-width:900px){ body.panel-open .wrap{margin-left:0} #detailPanel{width:88vw} }
</style>
</head>
<body>
@@ -40,6 +59,7 @@
<div class="searchbar"><input type="text" id="q" placeholder="Search all RE content — articles, deals, addresses, firms, brokers, cities… (space = AND)" autofocus></div>
<div class="facets" id="fBuild"></div>
<div class="facets" id="fType" style="padding-bottom:8px"></div>
+<aside id="detailPanel"><button class="pclose" aria-label="Close" onclick="closePanel()">✕</button><div id="pbody"></div></aside>
<div class="wrap"><div id="results"><p class="empty">Type to search across RENTV articles, CRCP + usre deals, and more.</p></div><div id="more"></div></div>
<script>
@@ -47,6 +67,18 @@ const $=s=>document.querySelector(s), $$=s=>Array.from(document.querySelectorAll
const esc=s=>String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
const BUILDLBL={rentv:'RENTV',crcp:'CRCP',usre:'usre',homesonspec:'HomesOnSpec'};
const F={q:'',build:'',type:''}; let offset=0, total=0, loading=false, lastFacets={build:{},type:{}};
+const byId={}; // id -> full record, for the left detail panel
+function srcLink(x){ return (x.url&&/^https?:/.test(x.url))?`<a href="${esc(x.url)}" target="_blank" rel="noopener noreferrer">view source ↗</a>`:''; }
+// Shared detail renderer — used INLINE (expanded row) AND in the LEFT panel. Every field, no dead-ends.
+function detailHTML(x){
+ const rows=[
+ ['Build', BUILDLBL[x.build]||x.build], ['Type', x.type], ['Amount', x.amount_label],
+ ['Date', x.date], ['City', x.city], ['Region', x.region], ['Parties', x.parties],
+ ['Subtitle', x.subtitle], ['ID', x.id],
+ ].filter(([,v])=>v!=null&&v!=='');
+ const src=srcLink(x);
+ return `<dl>${rows.map(([k,v])=>`<dt>${esc(k)}</dt><dd>${esc(v)}</dd>`).join('')}${src?`<dt>Source</dt><dd>${src}</dd>`:''}</dl>`;
+}
function chips(host,current,counts,labels){
const items=Object.entries(counts).sort((a,b)=>b[1]-a[1]);
@@ -55,14 +87,16 @@ function chips(host,current,counts,labels){
: $('#fType').innerHTML='<span class="lbl">Type</span>'+all+items.map(([k,n])=>`<span class="chip ${current===k?'on':''}" data-v="${esc(k)}">${esc(k)}<span class="c">${n}</span></span>`).join('');
}
function resultRow(x){
- const url=x.url&&/^https?:/.test(x.url);
- const title=url?`<a href="${esc(x.url)}" target="_blank" rel="noopener noreferrer">${esc(x.title)} ↗</a>`:esc(x.title);
+ byId[x.id]=x;
const loc=[x.city,x.region].filter(Boolean).join(', ');
- return `<div class="res"><span class="badge b-${esc(x.build)}">${esc(BUILDLBL[x.build]||x.build)}</span><div class="body">
- <div class="t">${title} <span class="type">${esc(x.type)}</span></div>
+ return `<div class="res" data-id="${esc(x.id)}"><span class="badge b-${esc(x.build)}">${esc(BUILDLBL[x.build]||x.build)}</span><div class="body">
+ <div class="t">${esc(x.title)} <span class="type">${esc(x.type)}</span></div>
${x.subtitle?`<div class="sub">${esc(x.subtitle)}</div>`:''}
<div class="meta2">${x.amount_label?`<span class="amt">${esc(x.amount_label)}</span>`:''}${x.date?`<span>🕓 ${esc(x.date)}</span>`:''}${loc?`<span>📍 ${esc(loc)}</span>`:''}${x.parties?`<span>👤 ${esc(x.parties)}</span>`:''}</div>
- </div></div>`;
+ <div class="res-detail">${detailHTML(x)}
+ <div class="actions"><button class="openpanel" data-id="${esc(x.id)}">↖ Open in panel</button>${srcLink(x)}</div>
+ </div>
+ </div><span class="caret">▶</span></div>`;
}
async function run(reset){
if(loading) return; loading=true;
@@ -83,6 +117,21 @@ async function run(reset){
$('#q').addEventListener('input',()=>{ clearTimeout(window._t); window._t=setTimeout(()=>{F.q=$('#q').value.trim();run(true);},200); });
$('#fBuild').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;F.build=c.dataset.v;run(true);});
$('#fType').addEventListener('click',e=>{const c=e.target.closest('.chip');if(!c)return;F.type=c.dataset.v;run(true);});
+// Row click → INLINE expand (no modal, no navigate-away). "Open in panel" → LEFT drawer. Links pass through.
+$('#results').addEventListener('click',e=>{
+ const op=e.target.closest('.openpanel'); if(op){ e.stopPropagation(); openPanel(op.dataset.id); return; }
+ if(e.target.closest('a')) return; // let source links work
+ const row=e.target.closest('.res'); if(!row) return;
+ row.classList.toggle('open');
+});
+function openPanel(id){ const x=byId[id]; if(!x) return;
+ $('#pbody').innerHTML=`<span class="pbadge b-${esc(x.build)}">${esc(BUILDLBL[x.build]||x.build)} · ${esc(x.type)}</span><h3>${esc(x.title)}</h3>${detailHTML(x)}`;
+ document.body.classList.add('panel-open');
+ // reflect selection: also expand the row inline
+ const row=$(`.res[data-id="${CSS.escape(id)}"]`); if(row) row.classList.add('open');
+}
+function closePanel(){ document.body.classList.remove('panel-open'); }
+document.addEventListener('keydown',e=>{ if(e.key==='Escape') closePanel(); });
run(true);
</script>
</body>
← dfe922fe Cody-gate: buyer/seller/broker are now admin-editable (added
·
back to Rentv 2026
·
deals-log: change-history opens as a LEFT drawer, not a cent 8d2c596e →