← back to Nationalrealestate
USRE geo-search: recent searches (localStorage) shown on empty focus + clear (v0.15.0)
9ab361e885a68cb2b345fc47c8f1f19d75d837b5 · 2026-07-26 21:58:35 -0700 · Steve
Files touched
M package-lock.jsonM package.jsonM public/geo-search.cssM public/geo-search.js
Diff
commit 9ab361e885a68cb2b345fc47c8f1f19d75d837b5
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jul 26 21:58:35 2026 -0700
USRE geo-search: recent searches (localStorage) shown on empty focus + clear (v0.15.0)
---
package-lock.json | 4 ++--
package.json | 2 +-
public/geo-search.css | 5 +++++
public/geo-search.js | 41 +++++++++++++++++++++++++++++++++--------
4 files changed, 41 insertions(+), 11 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 7b9f6a3..4dfdf6f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "nationalrealestate",
- "version": "0.14.0",
+ "version": "0.15.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nationalrealestate",
- "version": "0.14.0",
+ "version": "0.15.0",
"dependencies": {
"better-sqlite3": "^13.0.1",
"dotenv": "^16.4.5",
diff --git a/package.json b/package.json
index 035c45f..c43e81d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "nationalrealestate",
- "version": "0.14.0",
+ "version": "0.15.0",
"private": true,
"type": "module",
"description": "USRealEstate — national U.S. residential market explorer. Free-data v1 (Redfin Data Center, Zillow Research, Census ACS, FHFA). Internal analyst tool behind basic auth.",
diff --git a/public/geo-search.css b/public/geo-search.css
index 3b5eac7..cc84a5f 100644
--- a/public/geo-search.css
+++ b/public/geo-search.css
@@ -28,6 +28,11 @@
.geo-badge{font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:var(--gold,#c8a24b);
border:1px solid var(--line,#222936);border-radius:10px;padding:2px 7px;white-space:nowrap;}
.geo-empty{padding:12px 14px;color:var(--muted,#8a93a3);font-size:13px;line-height:1.5;}
+.geo-rechdr{display:flex;justify-content:space-between;align-items:center;padding:8px 14px;
+ font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted,#8a93a3);
+ border-bottom:1px solid var(--line,#222936);}
+.geo-recclr{cursor:pointer;text-transform:none;letter-spacing:0;text-decoration:underline;}
+.geo-recclr:hover{color:var(--gold,#c8a24b);}
/* ── pill mode: fixed top-center (map + content pages) ── */
.geo-pill{position:fixed;top:12px;left:50%;transform:translateX(-50%);z-index:1100;width:min(440px,86vw);}
diff --git a/public/geo-search.js b/public/geo-search.js
index 5038f81..f73b64f 100644
--- a/public/geo-search.js
+++ b/public/geo-search.js
@@ -24,6 +24,30 @@
const close = () => { results.innerHTML = ''; results.classList.remove('open'); items = []; active = -1; };
const go = u => { if (u) location.href = u; };
+ // recent searches (client-side, per browser) — shown when the box is focused + empty
+ const RKEY = 'usre.recentSearches';
+ const readRecent = () => { try { return JSON.parse(localStorage.getItem(RKEY) || '[]'); } catch (e) { return []; } };
+ const remember = it => {
+ try {
+ if (!it || !it.url) return;
+ const a = readRecent().filter(x => x.url !== it.url);
+ a.unshift({ name: it.name, url: it.url, type: it.type, state: it.state || null, sub: it.sub || null });
+ localStorage.setItem(RKEY, JSON.stringify(a.slice(0, 6)));
+ } catch (e) {}
+ };
+ const rowHtml = (r, i) =>
+ `<a class="geo-item" data-i="${i}" href="${esc(r.url)}">` +
+ `<span class="geo-name">${esc(r.name)}${r.sub ? `<span class="geo-subline">${esc(r.sub)}</span>` : ''}</span>` +
+ (r.state ? `<span class="geo-state">${esc(r.state)}</span>` : '') +
+ `<span class="geo-badge">${badge(r.type)}</span></a>`;
+ function showRecent() {
+ const rec = readRecent();
+ if (!rec.length) { close(); return; }
+ items = rec; active = -1;
+ results.innerHTML = '<div class="geo-rechdr">Recent<span class="geo-recclr">clear</span></div>' + rec.map(rowHtml).join('');
+ results.classList.add('open');
+ }
+
function render(rows) {
items = rows;
if (!rows.length) {
@@ -34,11 +58,7 @@
: 'No matches.') + '</div>';
results.classList.add('open'); return;
}
- results.innerHTML = rows.map((r, i) =>
- `<a class="geo-item" data-i="${i}" href="${esc(r.url)}">` +
- `<span class="geo-name">${esc(r.name)}${r.sub ? `<span class="geo-subline">${esc(r.sub)}</span>` : ''}</span>` +
- (r.state ? `<span class="geo-state">${esc(r.state)}</span>` : '') +
- `<span class="geo-badge">${badge(r.type)}</span></a>`).join('');
+ results.innerHTML = rows.map(rowHtml).join('');
results.classList.add('open'); active = -1;
}
function setActive(n) {
@@ -57,16 +77,21 @@
const q = input.value.trim(); lastQ = q;
clear.style.display = q ? 'block' : 'none';
clearTimeout(t);
- if (q.length < 2) { close(); return; }
+ if (q.length < 2) { showRecent(); return; } // empty box shows recent searches
t = setTimeout(() => search(q), 180);
});
+ input.addEventListener('focus', () => { if (input.value.trim().length < 2) showRecent(); });
input.addEventListener('keydown', e => {
if (e.key === 'ArrowDown') { e.preventDefault(); setActive(active + 1); }
else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(active - 1); }
- else if (e.key === 'Enter') { if (active >= 0 && items[active]) go(items[active].url); else if (items[0]) go(items[0].url); }
+ else if (e.key === 'Enter') { const it = (active >= 0 && items[active]) ? items[active] : items[0]; if (it) { remember(it); go(it.url); } }
else if (e.key === 'Escape') { close(); input.blur(); }
});
- clear.addEventListener('click', () => { input.value = ''; clear.style.display = 'none'; close(); input.focus(); });
+ clear.addEventListener('click', () => { input.value = ''; clear.style.display = 'none'; input.focus(); showRecent(); });
+ results.addEventListener('click', e => {
+ if (e.target.closest('.geo-recclr')) { e.preventDefault(); try { localStorage.removeItem(RKEY); } catch (x) {} close(); return; }
+ const a = e.target.closest('.geo-item'); if (a) { const it = items[+a.dataset.i]; if (it) remember(it); } // store before the browser follows href
+ });
document.addEventListener('click', e => { if (!host.contains(e.target)) close(); });
}
← 7c0710f USRE: searched address in a parcel-covered county deep-links
·
back to Nationalrealestate
·
USRE Map: export matching counties to CSV (parity w/ grid) + 1499f0f →