auto-sync: 2026-05-05 15:40:01
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
88
tasks/enduro-trails/prototype/static/app.js
vendored
88
tasks/enduro-trails/prototype/static/app.js
vendored
@@ -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 `<div class="mini-route-card ${active}" onclick="selectRouteFromMini(${i})">
|
||||
<div class="mini-route-dot" style="background:${color}"></div>
|
||||
<div>
|
||||
<div class="mini-route-label">Вариант ${i + 1}</div>
|
||||
<div class="mini-route-stats">${km} км · ${dirt}% грунт</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).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');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -229,6 +229,12 @@
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Mini route sheet -->
|
||||
<div id="sheet-route-mini">
|
||||
<div class="mini-handle" id="mini-route-handle"></div>
|
||||
<div class="mini-cards-row" id="mini-route-cards"></div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.js"></script>
|
||||
<script src="https://unpkg.com/suncalc@1.9.0/suncalc.min.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user