From 3fdb38d3a0ea7f95ea7b800c3439db90ce67d2fe Mon Sep 17 00:00:00 2001 From: Polen Date: Tue, 7 Apr 2026 22:41:03 -0400 Subject: [PATCH] better mobile UX --- static/map.js | 26 ++++++++++++- static/style.css | 92 ++++++++++++++++++++++++++++++++++++++++------ templates/map.html | 26 +++++++------ 3 files changed, 120 insertions(+), 24 deletions(-) diff --git a/static/map.js b/static/map.js index 573b453..7bf63e0 100644 --- a/static/map.js +++ b/static/map.js @@ -131,7 +131,7 @@ document.getElementById('slider-label').textContent = Math.ceil(maxPrice) + '¢/L'; const fuelLabel = { regular: 'Régulier', super: 'Super', diesel: 'Diesel' }[currentFuel]; - document.getElementById('legend-title').textContent = `Prix ${fuelLabel.toLowerCase()} (¢/L)`; + document.getElementById('legend-title').textContent = `${fuelLabel} (¢/L)`; document.getElementById('min-price').textContent = minPrice.toFixed(1) + '¢'; document.getElementById('max-price').textContent = maxPrice.toFixed(1) + '¢'; @@ -232,6 +232,29 @@ showCostco = this.checked; rebuildMarkers(); }); + + // ── Mobile filter panel toggle ───────────────────────── + var filterToggle = document.getElementById('filter-toggle'); + var sliderPanel = document.getElementById('slider-panel'); + var filterBackdrop = document.getElementById('filter-backdrop'); + + function openFilterPanel() { + sliderPanel.classList.add('open'); + filterToggle.classList.add('hidden'); + if (filterBackdrop) filterBackdrop.classList.add('visible'); + } + function closeFilterPanel() { + sliderPanel.classList.remove('open'); + filterToggle.classList.remove('hidden'); + if (filterBackdrop) filterBackdrop.classList.remove('visible'); + } + + if (filterToggle) { + filterToggle.addEventListener('click', openFilterPanel); + } + if (filterBackdrop) { + filterBackdrop.addEventListener('click', closeFilterPanel); + } } // ── Initialise ───────────────────────────────────────────── @@ -279,7 +302,6 @@ }); loadingEl.style.display = 'none'; - document.getElementById('map-stats').textContent = allStations.length + ' stations'; rebuildMarkers(); map.addLayer(clusterGroup); diff --git a/static/style.css b/static/style.css index 1556751..26b713a 100644 --- a/static/style.css +++ b/static/style.css @@ -94,26 +94,31 @@ html, body { height: 100%; margin: 0; padding: 0; } box-shadow: 0 2px 8px rgba(0,0,0,0.15); } -/* Legend */ +/* Legend (inline inside slider-panel) */ #legend { - position: absolute; bottom: 20px; right: 20px; - padding: 12px 16px; - z-index: 1000; font-size: 13px; min-width: 180px; + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid var(--border); + font-size: 11px; + color: var(--muted-foreground); } -#legend h3 { margin-bottom: 8px; font-size: 14px; } -.legend-gradient { - height: 16px; border-radius: 4px; - background: linear-gradient(to right, #16a34a, #65a30d, #ca8a04, #ea580c, #dc2626); +.legend-header { margin-bottom: 4px; + font-weight: 500; +} +.legend-gradient { + height: 8px; border-radius: 3px; + background: linear-gradient(to right, #16a34a, #65a30d, #ca8a04, #ea580c, #dc2626); + margin-bottom: 2px; + opacity: 0.85; } .legend-labels { display: flex; justify-content: space-between; - font-size: 11px; color: var(--muted-foreground); + font-size: 10px; color: var(--muted-foreground); } -#map-stats { margin-top: 8px; font-size: 11px; color: var(--muted-foreground); } #last-updated { - position: absolute; bottom: 8px; left: 12px; + position: absolute; bottom: 24px; right: 8px; padding: 5px 10px; z-index: 1000; font-size: 11px; color: var(--muted-foreground); @@ -233,3 +238,68 @@ html, body { height: 100%; margin: 0; padding: 0; } /* ── htmx loading indicator ──────────────────────────── */ .htmx-request #stats-content { opacity: 0.5; transition: opacity 0.2s; } + +/* ── Filter toggle button (mobile only) ─────────────── */ +#filter-toggle { + display: none; /* hidden on desktop */ + position: absolute; + bottom: 16px; left: 12px; + z-index: 1001; + padding: 8px 14px; + font-size: 13px; font-weight: 600; + cursor: pointer; + align-items: center; gap: 6px; + border: none; + color: var(--card-foreground); +} +#filter-toggle svg { flex-shrink: 0; } + +/* ── Mobile layout ──────────────────────────────────── */ +@media (max-width: 768px) { + #filter-toggle { display: flex; } + + #slider-panel { + position: fixed; + top: auto; left: 0; right: 0; bottom: 0; + min-width: 0; width: 100%; + border-radius: 16px 16px 0 0; + max-height: 70vh; + overflow-y: auto; + padding: 16px 16px 20px; + transform: translateY(100%); + transition: transform 0.3s ease; + z-index: 1100; + } + #slider-panel.open { transform: translateY(0); } + + /* Drag handle hint at top of the panel */ + #slider-panel::before { + content: ''; + display: block; + width: 36px; height: 4px; + background: var(--muted-foreground); + border-radius: 2px; + margin: 0 auto 12px; + opacity: 0.4; + } + + /* Semi-transparent backdrop when panel is open */ + #filter-backdrop { + display: none; + position: fixed; inset: 0; + background: rgba(0,0,0,0.3); + z-index: 1099; + } + #filter-backdrop.visible { display: block; } + + /* Hide the toggle when panel is open */ + #filter-toggle.hidden { display: none; } + + /* Cluster tooltip: constrain on small screens */ + .cluster-info-tooltip { + white-space: normal; + max-width: 70vw; + left: auto; right: 0; + transform: translateY(-50%); + } +} diff --git a/templates/map.html b/templates/map.html index f708e20..28a00ee 100644 --- a/templates/map.html +++ b/templates/map.html @@ -9,6 +9,11 @@
+ +