auto-sync: 2026-05-05 19:30:01

This commit is contained in:
Stream
2026-05-05 19:30:01 +03:00
parent 1052ce0d4a
commit 01b2b94716
2 changed files with 75 additions and 32 deletions

View File

@@ -284,25 +284,26 @@ body.has-map-mode #sheet-backdrop.visible { pointer-events: none; }
.wl-remove:active { background: var(--red-bg); color: var(--red); }
.wl-remove svg { width: 14px; height: 14px; }
/* Route actions */
.route-actions {
display: flex; gap: 8px; margin: 8px 0;
}
.btn-action {
height: 36px; padding: 0 14px;
display: flex; align-items: center; justify-content: center; gap: 6px;
background: var(--surface2); border: 1px solid var(--border);
border-radius: 12px; color: var(--text2);
font-size: 13px; font-weight: 600; cursor: pointer;
/* Sheet icon buttons (header) */
.sheet-icon-btn {
width: 32px; height: 32px;
display: flex; align-items: center; justify-content: center;
background: none; border: none; color: var(--text3);
border-radius: 8px; cursor: pointer; padding: 0;
flex-shrink: 0;
transition: background 0.15s, color 0.15s;
-webkit-tap-highlight-color: transparent;
transition: background 0.15s, transform 0.1s, color 0.15s;
}
.btn-action svg { width: 16px; height: 16px; flex-shrink: 0; transition: transform 0.1s; }
.btn-action:active { transform: scale(0.94); background: var(--surface3, var(--border)); }
.btn-action.primary { border-color: var(--accent); color: var(--accent); }
.btn-action.primary:active { background: var(--accent); color: #fff; border-color: var(--accent); }
.btn-action.danger { color: var(--red); }
.btn-action.danger:active { background: var(--red); color: #fff; border-color: var(--red); }
.sheet-icon-btn svg { width: 18px; height: 18px; }
.sheet-icon-btn:active { background: var(--surface2); }
.sheet-icon-btn.danger { color: var(--red); }
.sheet-icon-btn.danger:active { background: var(--red); color: #fff; }
/* Add waypoint row */
.wl-add { cursor: pointer; }
.wl-add:active { background: var(--surface); }
.wl-add .wl-pin svg path { fill: var(--text3) !important; }
.wl-add .wl-label { color: var(--text3); }
/* ── Route Status ─────────────────────────────── */
#route-status { font-size: 13px; color: var(--text2); padding: 8px 0; display: flex; align-items: center; gap: 6px; }

View File

@@ -443,7 +443,6 @@ function clearRoute() {
}
}
document.getElementById('route-status').textContent = 'Тапни точку старта на карте';
document.getElementById('route-actions').style.display = 'none';
document.getElementById('route-cards').innerHTML = '';
document.getElementById('waypoints-list').innerHTML = '';
if (routeMode && map) map.getCanvas().style.cursor = 'crosshair';
@@ -533,7 +532,7 @@ async function renderWaypointsList() {
const list = document.getElementById('waypoints-list');
if (!routeWaypoints.length) { list.innerHTML = ''; return; }
list.innerHTML = routeWaypoints.map((wp, i) => {
let html = routeWaypoints.map((wp, i) => {
const isStart = i === 0;
const isEnd = i === routeWaypoints.length - 1;
const label = isStart ? 'S' : isEnd ? 'F' : String(i);
@@ -548,6 +547,16 @@ async function renderWaypointsList() {
</div>`;
}).join('');
// Кнопка «Добавить точку» в стиле wl-item
if (routeWaypoints.length < 10) {
html += `<div class="wl-item wl-add" onclick="addWaypointMode()">
<div class="wl-pin">${waypointPinSvg('+', 'var(--text3)')}</div>
<span class="wl-label">Добавить точку</span>
</div>`;
}
list.innerHTML = html;
// Async geocode
routeWaypoints.forEach(async (wp, i) => {
const name = await reverseGeocode(wp.lat, wp.lon);
@@ -571,8 +580,6 @@ function removeWaypoint(idx) {
}
routeResults = [];
document.getElementById('route-cards').innerHTML = '';
document.getElementById('route-actions').style.display =
routeWaypoints.length >= 2 ? 'block' : 'none';
document.getElementById('route-status').textContent =
routeWaypoints.length === 0 ? 'Тапни точку старта на карте' :
routeWaypoints.length === 1 ? 'Тапни точку финиша' : '';
@@ -607,7 +614,6 @@ async function buildRoute() {
drawRouteResults(routeResults, 0);
document.getElementById('route-status').textContent = `${routeResults.length} маршрут(ов)`;
document.getElementById('route-actions').style.display = 'block';
} catch(e) {
document.getElementById('route-status').textContent = '❌ ' + e.message;
document.getElementById('route-cards').innerHTML = '';
@@ -737,16 +743,14 @@ function escapeXml(str) {
return (str || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function downloadGPX() {
function generateGPX() {
const route = routeResults[activeRouteIdx];
if (!route) return;
if (!route) return '';
const now = new Date();
const dateStr = now.toISOString().slice(0, 10);
const timeStr = now.toISOString().replace(/[-:]/g, '').slice(0, 15);
const filename = `enduro-${timeStr}.gpx`;
const distKm = (route.distance_m / 1000).toFixed(1);
const dirtPct = route.stats ? route.stats.dirt_total_pct : '?';
const wpts = routeWaypoints.map((wp, i) => {
const name = i === 0 ? 'Старт' : i === routeWaypoints.length - 1 ? 'Финиш' : `Точка ${i}`;
return ` <wpt lat="${wp.lat}" lon="${wp.lon}"><name>${escapeXml(name)}</name></wpt>`;
@@ -755,12 +759,12 @@ function downloadGPX() {
markers.forEach(m => {
wpts.push(` <wpt lat="${m.lat}" lon="${m.lon}"><name>${escapeXml(m.name)}</name><sym>${escapeXml(m.icon)}</sym></wpt>`);
});
const trkpts = route.geometry.coordinates.map(([lon, lat]) =>
` <trkpt lat="${lat}" lon="${lon}"/>`
).join('\n');
const gpx = `<?xml version="1.0" encoding="UTF-8"?>
return `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Enduro Trails" xmlns="http://www.topografix.com/GPX/1/1">
<metadata>
<name>Enduro route ${dateStr}</name>
@@ -775,7 +779,14 @@ ${trkpts}
</trkseg>
</trk>
</gpx>`;
}
function downloadGPX() {
const gpx = generateGPX();
if (!gpx) return;
const now = new Date();
const timeStr = now.toISOString().replace(/[-:]/g, '').slice(0, 15);
const filename = `enduro-${timeStr}.gpx`;
const blob = new Blob([gpx], { type: 'application/gpx+xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -785,6 +796,38 @@ ${trkpts}
URL.revokeObjectURL(url);
}
async function shareRoute() {
const route = routeResults[activeRouteIdx];
if (!route) { alert('Сначала построй маршрут'); return; }
const distKm = (route.distance_m / 1000).toFixed(0);
const dirtPct = route.stats ? route.stats.dirt_total_pct : 0;
const shareText = `Маршрут: ${distKm} км, ${dirtPct}% грунт`;
const gpx = generateGPX();
const blob = new Blob([gpx], { type: 'application/gpx+xml' });
const file = new File([blob], 'enduro-route.gpx', { type: 'application/gpx+xml' });
// 1. File share (HTTPS only)
if (navigator.share && location.protocol === 'https:') {
try {
const canShare = navigator.canShare && navigator.canShare({ files: [file] });
if (canShare) {
await navigator.share({ title: 'Enduro Route', text: shareText, files: [file] });
return;
}
// canShare rejected files → share text only
await navigator.share({ title: 'Enduro Route', text: shareText });
return;
} catch(e) {
if (e.name === 'AbortError') return;
console.warn('Share failed, falling back to download:', e);
}
}
// 2. HTTPS unavailable → download GPX directly
downloadGPX();
}
// ─── Флажки / именованные метки ────────────────────────────────────
const MARKER_ICONS = ['🚩', '⛺', '🔧', '⛽', '💧', '📍'];
const MARKERS_KEY = 'enduro_markers';
@@ -1089,7 +1132,6 @@ function initRouteClicks(map) {
} else if (routeWaypoints.length === 1) {
routeWaypoints.push({ lon: lng, lat: lat });
rebuildWaypointMarkers(); renderWaypointsList();
document.getElementById('route-actions').style.display = 'block';
buildRoute();
}
});