← back to Commercialrealestate
sort options as a chip bar in the LEFT PANEL (Steve): shared sort-bar.js mirrors each page's sort <select> into a .rail 'Sort' chip section (inline fallback where no rail), proxying clicks to reuse existing sort logic; on 7 grids; reverted condos to a plain select for uniformity
3bd92017f9b1b60a74216a05358e5d392798d1b6 · 2026-08-03 13:05:06 -0700 · Steve Abrams
Files touched
M public/broker-grid.htmlM public/condos.htmlM public/just-listed.htmlM public/licensed.htmlM public/mls.htmlM public/rent-rolls.htmlM public/sales.htmlA public/sort-bar.js
Diff
commit 3bd92017f9b1b60a74216a05358e5d392798d1b6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 3 13:05:06 2026 -0700
sort options as a chip bar in the LEFT PANEL (Steve): shared sort-bar.js mirrors each page's sort <select> into a .rail 'Sort' chip section (inline fallback where no rail), proxying clicks to reuse existing sort logic; on 7 grids; reverted condos to a plain select for uniformity
---
public/broker-grid.html | 1 +
public/condos.html | 34 ++++++++---------------
public/just-listed.html | 1 +
public/licensed.html | 1 +
public/mls.html | 1 +
public/rent-rolls.html | 1 +
public/sales.html | 1 +
public/sort-bar.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 92 insertions(+), 22 deletions(-)
diff --git a/public/broker-grid.html b/public/broker-grid.html
index 4726faf..60d34e1 100644
--- a/public/broker-grid.html
+++ b/public/broker-grid.html
@@ -449,5 +449,6 @@ fetch('/api/brokers/all').then(r=>r.json()).then(d=>{DATA=d.brokers||[];autoCols
</script>
<script src="/col-resize.js" defer></script>
<script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body>
</html>
diff --git a/public/condos.html b/public/condos.html
index df6e954..ff85e28 100644
--- a/public/condos.html
+++ b/public/condos.html
@@ -35,12 +35,6 @@
.topbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:14px 24px;border-bottom:1px solid var(--line);position:sticky;top:53px;background:rgba(14,17,22,.95);backdrop-filter:blur(6px);z-index:20;}
.topbar input[type=text]{min-width:260px;}
.topbar .dens{font-size:12px;color:var(--mut);display:flex;align-items:center;gap:7px;}
- /* all sort options as a visible bar of chips (Steve 2026-08-03) */
- .sortbar{display:inline-flex;align-items:center;gap:4px;flex-wrap:wrap;}
- .sortbar .sortlabel{font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--mut);margin-right:2px;}
- .sortchip{background:var(--card,#141925);color:var(--mut,#8b949e);border:1px solid var(--line,#232a38);border-radius:7px;padding:4px 9px;font-size:12px;cursor:pointer;}
- .sortchip:hover{color:var(--ink);border-color:var(--mut);}
- .sortchip.on{border-color:var(--blue,#58a6ff);color:var(--blue,#58a6ff);background:rgba(88,166,255,.12);font-weight:600;}
.viewseg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;}
.viewseg button{background:var(--card);color:var(--mut);border:0;border-right:1px solid var(--line);padding:7px 12px;font-size:12px;cursor:pointer;}
.viewseg button:last-child{border-right:0;} .viewseg button.active{background:#1f6feb;color:#fff;}
@@ -141,17 +135,16 @@
<input type="text" id="q" placeholder="search all fields — project, city, zip, address, broker, firm, status… (space = AND)">
<span class="viewseg" id="viewseg"><button data-view="grid" class="active">▦ Grid</button><button data-view="list">☰ List</button></span>
<button id="shortbtn" title="Show only your shortlisted (starred) listings — shared with the Explorer & MLS">⭐ Shortlist (0)</button>
- <div class="sortbar" id="csort" title="Sort condos" role="group" aria-label="Sort">
- <span class="sortlabel">Sort</span>
- <button class="sortchip" data-sort="">Newest</button>
- <button class="sortchip" data-sort="price_desc">Price ↓</button>
- <button class="sortchip" data-sort="price_asc">Price ↑</button>
- <button class="sortchip" data-sort="pps_desc">$/SF ↓</button>
- <button class="sortchip" data-sort="year_desc">Year ↓</button>
- <button class="sortchip" data-sort="hoa_asc">HOA ↑</button>
- <button class="sortchip" data-sort="proj_az">Project A→Z</button>
- <button class="sortchip" data-sort="city_az">City A→Z</button>
- </div>
+ <select id="csort" title="Sort condos" style="background:var(--card,#141925);color:var(--ink,#E6E8EC);border:1px solid var(--line,#232a38);border-radius:7px;padding:5px 8px;font-size:12px">
+ <option value="">Newest</option>
+ <option value="price_desc">Price ↓</option>
+ <option value="price_asc">Price ↑</option>
+ <option value="pps_desc">$/SF ↓</option>
+ <option value="year_desc">Year ↓</option>
+ <option value="hoa_asc">HOA ↑</option>
+ <option value="proj_az">Project A→Z</option>
+ <option value="city_az">City A→Z</option>
+ </select>
<label class="dens">Density <input type="range" id="cols" min="1" max="20" step="1" value="3"></label>
<span class="count" id="count">…</span>
</div>
@@ -488,11 +481,7 @@ function sortRows(rows){
}[SORTKEY];
return cmp?rows.slice().sort(cmp):rows;
}
-(function(){ const bar=$('#csort'); if(!bar) return;
- const mark=()=>bar.querySelectorAll('.sortchip').forEach(c=>c.classList.toggle('on',(c.dataset.sort||'')===(SORTKEY||'')));
- bar.addEventListener('click',e=>{ const c=e.target.closest('.sortchip'); if(!c) return; SORTKEY=c.dataset.sort||''; localStorage.setItem('condoSort',SORTKEY); mark(); apply(); });
- mark();
-})();
+(function(){ const s=$('#csort'); if(s){ s.value=SORTKEY; s.addEventListener('change',()=>{ SORTKEY=s.value; localStorage.setItem('condoSort',SORTKEY); apply(); }); } })();
$('#cols').oninput=e=>{ document.documentElement.style.setProperty('--cols',e.target.value); localStorage.setItem('condoCols',e.target.value); };
const savedCols=localStorage.getItem('condoCols'); if(savedCols){ $('#cols').value=savedCols; document.documentElement.style.setProperty('--cols',savedCols); }
$$('#viewseg button').forEach(b=>b.classList.toggle('active',b.dataset.view===VIEW));
@@ -592,5 +581,6 @@ boot();
</script>
<script src="/col-resize.js" defer></script>
<script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body>
</html>
diff --git a/public/just-listed.html b/public/just-listed.html
index 32a5112..7dbe501 100644
--- a/public/just-listed.html
+++ b/public/just-listed.html
@@ -180,4 +180,5 @@ fetch('/data/listings.json').then(r=>r.json()).then(d=>{
buildRail();render();
}).catch(()=>{$('#count').textContent='failed to load /data/listings.json';});
</script> <script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body></html>
diff --git a/public/licensed.html b/public/licensed.html
index 78d2455..42c6284 100644
--- a/public/licensed.html
+++ b/public/licensed.html
@@ -208,4 +208,5 @@ document.addEventListener('click',async e=>{
});
boot().catch(e=>{$('#count').textContent='Failed to load: '+e.message;});
</script> <script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body></html>
diff --git a/public/mls.html b/public/mls.html
index 41327b1..b78493e 100644
--- a/public/mls.html
+++ b/public/mls.html
@@ -575,5 +575,6 @@ fetch('/data/ranked.json').then(r=>r.json()).then(async d=>{ DATA=(d.ranked||d)|
<script src="/column-manager.js" defer></script>
<script src="/col-resize.js" defer></script>
<script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body>
</html>
diff --git a/public/rent-rolls.html b/public/rent-rolls.html
index d857223..454d2d6 100644
--- a/public/rent-rolls.html
+++ b/public/rent-rolls.html
@@ -433,5 +433,6 @@ fetch('/api/rent-rolls').then(r=>r.json()).then(d=>{
</script>
<script src="/col-resize.js" defer></script>
<script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body>
</html>
diff --git a/public/sales.html b/public/sales.html
index 5f94b0a..a899e20 100644
--- a/public/sales.html
+++ b/public/sales.html
@@ -384,5 +384,6 @@ fetch('/api/closed-sales?limit=5000').then(r=>r.json()).then(d=>{
<script src="/column-manager.js" defer></script>
<script src="/col-resize.js" defer></script>
<script src="/global-search.js" defer></script>
+ <script src="/sort-bar.js" defer></script>
</body>
</html>
diff --git a/public/sort-bar.js b/public/sort-bar.js
new file mode 100644
index 0000000..fc2e86a
--- /dev/null
+++ b/public/sort-bar.js
@@ -0,0 +1,74 @@
+/* sort-bar.js — surface ALL sort options as a chip bar in the LEFT PANEL (Steve 2026-08-03:
+ * "provide all sort options in a bar … put on left panel"). Mirrors the page's existing sort
+ * <select> (#csort / #sortsel / #sort) into a .rail section of chips; clicking a chip drives the
+ * same select (so every page's existing sort logic is reused, including dynamic column lists).
+ * Falls back to an inline bar where a page has no left rail. Self-contained. */
+(function () {
+ 'use strict';
+ if (window.__sortBarInit) return; window.__sortBarInit = true;
+
+ var css = document.createElement('style');
+ css.textContent = [
+ '.sb-sec .sb-h{font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--mut,#8A93A2);margin:0 0 9px;font-weight:600;}',
+ '.sb-chips{display:flex;flex-wrap:wrap;gap:5px;}',
+ '.sbchip{background:var(--card,#141925);color:var(--mut,#8b949e);border:1px solid var(--line,#232a38);border-radius:7px;padding:4px 9px;font-size:12px;cursor:pointer;line-height:1.2;}',
+ '.sbchip:hover{color:var(--ink,#e6edf3);border-color:var(--mut,#8A93A2);}',
+ '.sbchip.on{border-color:var(--blue,#58a6ff);color:var(--blue,#58a6ff);background:rgba(88,166,255,.14);font-weight:600;}',
+ 'span.sb-sec{display:inline-flex;align-items:center;gap:5px;flex-wrap:wrap}'
+ ].join('');
+ document.head.appendChild(css);
+
+ function sel() { return document.querySelector('#csort, #sortsel, #sort'); }
+
+ function ensureChips(s) {
+ var host = document.getElementById('sb-host');
+ if (host) return host.querySelector('.sb-chips');
+ var rail = document.querySelector('.rail');
+ var chips = document.createElement('div'); chips.className = 'sb-chips';
+ if (rail) {
+ var sec = document.createElement('div'); sec.className = 'rsec sb-sec'; sec.id = 'sb-host';
+ sec.innerHTML = '<div class="sb-h">Sort</div>'; sec.appendChild(chips);
+ rail.insertBefore(sec, rail.firstChild); // top of the left panel
+ } else {
+ var span = document.createElement('span'); span.className = 'sb-sec'; span.id = 'sb-host';
+ var lbl = document.createElement('span'); lbl.className = 'sb-h'; lbl.textContent = 'Sort'; lbl.style.margin = '0 4px 0 0';
+ span.appendChild(lbl); span.appendChild(chips);
+ s.parentNode.insertBefore(span, s); // inline bar where there's no rail
+ }
+ s.style.display = 'none'; // the bar replaces the dropdown
+ return chips;
+ }
+
+ function build() {
+ var s = sel(); if (!s || s.tagName !== 'SELECT' || !s.options.length) return;
+ var chips = ensureChips(s);
+ chips.innerHTML = Array.prototype.map.call(s.options, function (o, i) {
+ var label = (o.textContent || '').replace(/^Sort:\s*/i, '').trim() || ('Option ' + (i + 1));
+ return '<button class="sbchip' + (i === s.selectedIndex ? ' on' : '') + '" data-i="' + i + '">' + label.replace(/</g, '<') + '</button>';
+ }).join('');
+ }
+
+ document.addEventListener('click', function (e) {
+ var c = e.target.closest && e.target.closest('#sb-host .sbchip'); if (!c) return;
+ var s = sel(); if (!s) return;
+ s.selectedIndex = +c.dataset.i;
+ s.dispatchEvent(new Event('change', { bubbles: true }));
+ c.parentNode.querySelectorAll('.sbchip').forEach(function (x) { x.classList.toggle('on', x === c); });
+ });
+
+ function syncActive() {
+ var s = sel(), chips = document.querySelector('#sb-host .sb-chips'); if (!s || !chips) return;
+ Array.prototype.forEach.call(chips.children, function (x) { x.classList.toggle('on', +x.dataset.i === s.selectedIndex); });
+ }
+
+ function boot() {
+ build();
+ var s = sel();
+ if (s) {
+ new MutationObserver(build).observe(s, { childList: true }); // dynamic/column-driven option lists
+ s.addEventListener('change', syncActive); // keep active chip in sync (e.g. header-click sort)
+ }
+ }
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { setTimeout(boot, 400); });
+ else setTimeout(boot, 400);
+})();
← 044dfa4 condos: sort control is now a visible BAR of all 8 sort-opti
·
back to Commercialrealestate
·
afternoon CRE update 2026-08-03 75aff2f →