diff --git a/tasks/enduro-trails/prototype/static/app.css b/tasks/enduro-trails/prototype/static/app.css index 41b22d7..f0b0b4c 100644 --- a/tasks/enduro-trails/prototype/static/app.css +++ b/tasks/enduro-trails/prototype/static/app.css @@ -552,3 +552,79 @@ body.has-map-mode #sheet-backdrop.visible { pointer-events: none; } #btn-build-route:active { background: var(--accent-h); } .search-result-name { font-size: 14px; font-weight: 500; color: var(--text); } .search-result-detail { font-size: 12px; color: var(--text2); margin-top: 2px; } + +/* ── Mini route sheet ─────────────────────────────────────────── */ +#sheet-route-mini { + position: fixed; + bottom: 72px; /* above toolbar */ + left: 0; right: 0; + height: 80px; + background: var(--surface); + border-top: 1px solid var(--border); + border-radius: 16px 16px 0 0; + z-index: 350; + display: none; + flex-direction: column; + box-shadow: 0 -4px 20px var(--shadow); +} +#sheet-route-mini.visible { display: flex; } + +.mini-handle { + width: 36px; height: 4px; + background: var(--border); + border-radius: 2px; + margin: 8px auto 4px; + flex-shrink: 0; + cursor: pointer; +} + +.mini-cards-row { + display: flex; + gap: 8px; + overflow-x: auto; + padding: 0 12px 10px; + scrollbar-width: none; + flex: 1; + align-items: center; +} +.mini-cards-row::-webkit-scrollbar { display: none; } + +.mini-route-card { + display: flex; + align-items: center; + gap: 8px; + background: var(--surface2); + border: 1.5px solid var(--border); + border-radius: 10px; + padding: 6px 12px; + flex-shrink: 0; + cursor: pointer; + transition: border-color 0.15s; + white-space: nowrap; +} +.mini-route-card.active { + border-color: var(--accent); + background: var(--accent-bg); +} +.mini-route-dot { + width: 10px; height: 10px; + border-radius: 50%; + flex-shrink: 0; +} +.mini-route-label { + font-size: 13px; font-weight: 600; + color: var(--text); +} +.mini-route-stats { + font-size: 11px; + color: var(--text2); +} + +@media (min-width: 768px) { + #sheet-route-mini { + left: 72px; + width: 380px; + right: auto; + border-radius: 0 16px 0 0; + } +} diff --git a/tasks/enduro-trails/prototype/static/app.js b/tasks/enduro-trails/prototype/static/app.js index 1c02823..151ee8f 100644 --- a/tasks/enduro-trails/prototype/static/app.js +++ b/tasks/enduro-trails/prototype/static/app.js @@ -204,6 +204,9 @@ function closeSheet(id) { // Close sheet panel but keep the mode active (route stays on map) function minimizeSheet(id) { closeSheet(id); + if (id === 'sheet-route' && routeResults.length > 0) { + showMiniRouteSheet(); + } } function closeAllSheets() { @@ -399,9 +402,8 @@ function toggleRouteMode() { closeSheet('sheet-route'); window._map.getCanvas().style.cursor = ''; } else if (routeResults.length > 0) { - // Not in input mode, but route exists on map → clear it and start fresh - clearRoute(); - // Now enter input mode + // Route exists — show full panel (hide mini first) + hideMiniRouteSheet(); deactivateAllModes(); routeMode = true; btn.classList.add('active'); @@ -409,6 +411,7 @@ function toggleRouteMode() { window._map.getCanvas().style.cursor = 'crosshair'; } else { // No route, not in mode → enter route input mode + hideMiniRouteSheet(); deactivateAllModes(); routeMode = true; btn.classList.add('active'); @@ -420,6 +423,7 @@ function toggleRouteMode() { } function clearRoute() { + hideMiniRouteSheet(); waypointMarkers.forEach(m => m.remove()); waypointMarkers = []; routeWaypoints = []; @@ -618,6 +622,10 @@ function drawRouteResults(routes, activeIdx) { }); renderRouteCards(routes); + + // Update mini sheet if visible + const miniEl = document.getElementById('sheet-route-mini'); + if (miniEl && miniEl.classList.contains('visible')) showMiniRouteSheet(); } function selectRoute(idx) { @@ -1603,3 +1611,77 @@ document.addEventListener('DOMContentLoaded', () => { // Apply saved theme immediately (before map loads) applyTheme(); }); + +// ─── Mini route sheet ────────────────────────────────────────────── +function showMiniRouteSheet() { + if (routeResults.length === 0) return; + const mini = document.getElementById('sheet-route-mini'); + const row = document.getElementById('mini-route-cards'); + row.innerHTML = routeResults.map((r, i) => { + const km = (r.distance_m / 1000).toFixed(0); + const dirt = r.stats?.dirt_total_pct ?? '?'; + const color = ROUTE_COLORS[i] || '#888888'; + const active = i === activeRouteIdx ? 'active' : ''; + return `
+
+
+
Вариант ${i + 1}
+
${km} км · ${dirt}% грунт
+
+
`; + }).join(''); + mini.classList.add('visible'); + initMiniSheetSwipe(); +} + +function hideMiniRouteSheet() { + const mini = document.getElementById('sheet-route-mini'); + if (mini) mini.classList.remove('visible'); +} + +function selectRouteFromMini(idx) { + activeRouteIdx = idx; + const map = window._map; + for (let i = 0; i < routeResults.length; i++) { + const isActive = i === idx; + try { + if (map.getLayer('route-line-' + i)) { + map.setPaintProperty('route-line-' + i, 'line-width', isActive ? 5 : 3); + map.setPaintProperty('route-line-' + i, 'line-opacity', isActive ? 0.95 : 0.5); + } + if (map.getLayer('route-line-' + i + '-outline')) { + map.setPaintProperty('route-line-' + i + '-outline', 'line-width', isActive ? 7 : 4); + map.setPaintProperty('route-line-' + i + '-outline', 'line-opacity', isActive ? 0.6 : 0); + } + } catch(e) {} + } + // Update mini cards active state + document.querySelectorAll('.mini-route-card').forEach((c, i) => { + c.classList.toggle('active', i === idx); + }); +} + +function initMiniSheetSwipe() { + const handle = document.getElementById('mini-route-handle'); + if (!handle) return; + // Remove old listeners by cloning + const newHandle = handle.cloneNode(true); + handle.parentNode.replaceChild(newHandle, handle); + + let startY = 0; + newHandle.addEventListener('touchstart', e => { startY = e.touches[0].clientY; }, { passive: true }); + newHandle.addEventListener('touchend', e => { + const dy = e.changedTouches[0].clientY - startY; + if (dy < -40) { + // Swipe up → expand full sheet + hideMiniRouteSheet(); + openSheet('sheet-route'); + } + }); + + // Tap on handle = expand + newHandle.addEventListener('click', () => { + hideMiniRouteSheet(); + openSheet('sheet-route'); + }); +} diff --git a/tasks/enduro-trails/prototype/static/index.html b/tasks/enduro-trails/prototype/static/index.html index 491b5ee..f00a56a 100644 --- a/tasks/enduro-trails/prototype/static/index.html +++ b/tasks/enduro-trails/prototype/static/index.html @@ -229,6 +229,12 @@ + +
+
+
+
+