diff --git a/web/static/app.js b/web/static/app.js index 72f645a..9bd6ada 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -30,6 +30,23 @@ let activeSlot = 'from'; // 'from' | 'to' | null let selectedFrom = null; // stop name string or null let selectedTo = null; // stop name string or null +// ── Mobile panel ─────────────────────────────────────────────────────────── + +function isMobile() { return window.innerWidth < 768; } + +function openPanel() { + document.getElementById('sidebar').classList.add('panel-open'); +} + +function updateHandleLabel(text) { + const el = document.getElementById('sidebar-handle-label'); + if (el) el.textContent = text; +} + +document.getElementById('sidebar-handle').addEventListener('click', () => { + document.getElementById('sidebar').classList.toggle('panel-open'); +}); + // ── Helpers ──────────────────────────────────────────────────────────────── /** @@ -351,6 +368,8 @@ async function loadRoute(relationId) { document.getElementById('route-panel').classList.remove('d-none'); document.getElementById('route-name').textContent = data.name; document.title = `${data.name} – ${DEFAULT_TITLE}`; + updateHandleLabel(data.name); + if (isMobile()) openPanel(); document.getElementById('route-osm-link').href = `https://www.openstreetmap.org/relation/${relationId}`; @@ -440,6 +459,8 @@ async function loadRouteMaster(relationId) { document.getElementById('route-master-panel').classList.remove('d-none'); document.getElementById('route-master-name').textContent = data.name; document.title = `${data.name} – ${DEFAULT_TITLE}`; + updateHandleLabel(data.name); + if (isMobile()) openPanel(); document.getElementById('route-master-osm-link').href = `https://www.openstreetmap.org/relation/${relationId}`; diff --git a/web/static/style.css b/web/static/style.css index 6bef7ef..cb7abf1 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -10,10 +10,69 @@ html, body { #sidebar { height: 100%; min-height: 0; /* flex items default to min-height:auto, preventing overflow scrolling */ + overflow: hidden; + display: flex; + flex-direction: column; +} + +#sidebar-inner { overflow-y: auto; + flex: 1; padding: 1rem 1rem 0; /* no bottom padding — WebKit ignores it on overflow containers */ } +/* ── Mobile bottom sheet ───────────────────────────────── */ + +#sidebar-handle { + height: 48px; + flex-shrink: 0; + cursor: pointer; + user-select: none; + border-bottom: 1px solid #f0f0f0; + background: #fff; + border-radius: 16px 16px 0 0; +} + +#sidebar-handle-pill { + width: 36px; + height: 4px; + border-radius: 2px; + background: #ced4da; + flex-shrink: 0; +} + +@media (max-width: 767.98px) { + #sidebar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + width: 100% !important; + max-width: 100% !important; + flex: none !important; + height: 65vh; + max-height: calc(100vh - 56px); + min-height: unset; + transform: translateY(calc(100% - 48px)); + transition: transform 0.3s ease; + z-index: 1000; + border-radius: 16px 16px 0 0; + border-right: none !important; + box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.15); + background: #fff; + } + + #sidebar.panel-open { + transform: translateY(0); + } + + #map { + width: 100% !important; + max-width: 100% !important; + flex: 0 0 100% !important; + } +} + #map { height: 100%; } diff --git a/web/templates/index.html b/web/templates/index.html index 87f228f..ec3cc16 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -23,6 +23,15 @@