[object Object]

← 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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
 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 →