← back to Watches
Add sort + density slider with localStorage persistence to watch search
08f02d605ac70fc4a02ff60a1aa054208bd62ffb · 2026-05-19 21:37:15 -0700 · Steve Abrams
Sort hydrates BEFORE first /api/advanced-search call. Default sort flipped
from "relevance" to "Newest (Year)". Density slider rewrites a CSS var so
the auto-fill min card width updates without re-rendering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M public/search-interface.html
Diff
commit 08f02d605ac70fc4a02ff60a1aa054208bd62ffb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 19 21:37:15 2026 -0700
Add sort + density slider with localStorage persistence to watch search
Sort hydrates BEFORE first /api/advanced-search call. Default sort flipped
from "relevance" to "Newest (Year)". Density slider rewrites a CSS var so
the auto-fill min card width updates without re-rendering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
public/search-interface.html | 71 +++++++++++++++++++++++++++++++++++---------
1 file changed, 57 insertions(+), 14 deletions(-)
diff --git a/public/search-interface.html b/public/search-interface.html
index 3cf8ad6..f734d91 100755
--- a/public/search-interface.html
+++ b/public/search-interface.html
@@ -274,9 +274,10 @@
}
/* Watch Grid */
+ :root { --watch-card-min: 300px; }
.watch-grid {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(var(--watch-card-min), 1fr));
gap: 20px;
}
@@ -423,19 +424,26 @@
<button class="clear-btn" onclick="clearAll()">Clear All</button>
</div>
- <!-- Sort Dropdown -->
- <div class="sort-section">
- <label class="sort-label">Sort by:</label>
- <select id="sortSelect" class="sort-select" onchange="performSearch()">
- <option value="relevance">Relevance</option>
- <option value="price_asc">Price: Low to High</option>
- <option value="price_desc">Price: High to Low</option>
- <option value="year_asc">Year: Oldest First</option>
- <option value="year_desc">Year: Newest First</option>
- <option value="appreciation_desc">Highest Appreciation</option>
- <option value="name_asc">Name: A to Z</option>
- <option value="name_desc">Name: Z to A</option>
- </select>
+ <!-- Sort + Density Controls -->
+ <div class="sort-section" style="display:flex; gap:24px; align-items:center; flex-wrap:wrap;">
+ <div style="display:flex; gap:8px; align-items:center;">
+ <label class="sort-label" for="sortSelect">Sort by:</label>
+ <select id="sortSelect" class="sort-select" onchange="onSortChange()">
+ <option value="year_desc">Newest (Year)</option>
+ <option value="year_asc">Oldest (Year)</option>
+ <option value="relevance">Relevance</option>
+ <option value="name_asc">Model A→Z</option>
+ <option value="name_desc">Model Z→A</option>
+ <option value="price_asc">Price: Low to High</option>
+ <option value="price_desc">Price: High to Low</option>
+ <option value="appreciation_desc">Highest Appreciation</option>
+ </select>
+ </div>
+ <div style="display:flex; gap:8px; align-items:center;">
+ <label class="sort-label" for="densityRange">Density:</label>
+ <input type="range" id="densityRange" min="200" max="480" step="20" value="300" oninput="onDensityChange(this.value)" style="width:160px;">
+ <span id="densityVal" style="font-size:12px; color:#6c757d; min-width:48px;">300px</span>
+ </div>
</div>
<!-- Collection Filter Buttons -->
@@ -530,8 +538,43 @@
let currentView = 'grid';
let chart = null;
+ // ---- Sort + density persistence (run BEFORE first performSearch) ----
+ const LS_SORT = 'watches.search.sort';
+ const LS_DENSITY = 'watches.search.density';
+ function hydrateControls() {
+ try {
+ const savedSort = localStorage.getItem(LS_SORT);
+ if (savedSort) {
+ const sel = document.getElementById('sortSelect');
+ if ([...sel.options].some(o => o.value === savedSort)) sel.value = savedSort;
+ }
+ const savedDensity = parseInt(localStorage.getItem(LS_DENSITY), 10);
+ if (savedDensity >= 200 && savedDensity <= 480) {
+ document.getElementById('densityRange').value = String(savedDensity);
+ applyDensity(savedDensity);
+ } else {
+ applyDensity(parseInt(document.getElementById('densityRange').value, 10));
+ }
+ } catch (e) { /* localStorage may be unavailable */ }
+ }
+ function applyDensity(px) {
+ document.documentElement.style.setProperty('--watch-card-min', px + 'px');
+ const lbl = document.getElementById('densityVal');
+ if (lbl) lbl.textContent = px + 'px';
+ }
+ function onSortChange() {
+ try { localStorage.setItem(LS_SORT, document.getElementById('sortSelect').value); } catch (e) {}
+ performSearch();
+ }
+ function onDensityChange(v) {
+ const px = parseInt(v, 10);
+ applyDensity(px);
+ try { localStorage.setItem(LS_DENSITY, String(px)); } catch (e) {}
+ }
+
// Initialize on page load
document.addEventListener('DOMContentLoaded', () => {
+ hydrateControls();
performSearch();
setupAutocomplete();
});
← 96a9a73 snapshot: 1 file(s) changed, ~1 modified
·
back to Watches
·
Broaden .gitignore: snapshot leftovers + SQLite WAL/SHM 253afbe →