auto-sync: 2026-05-06 09:30:01
This commit is contained in:
@@ -100,6 +100,8 @@ docker restart prototype-enduro-trails-1
|
||||
| F-17 | PWA + офлайн | Service Worker, MBTiles, GPS-трекинг | ⏳ Бэклог | 7 |
|
||||
| F-18 | Светлая карта | Создать `style-light.json` — светлый стиль карты для светлой темы. Сейчас при светлой теме карта остаётся тёмной (`style-light.json` отсутствует) | ⏳ Бэклог | 5.1 |
|
||||
| F-19 | Иконка колеса + спиннер | Заменить иконку колеса в мини-баре на нормальную мотокросс (спицы, кноблинг). Спиннер в основном листе маршрута пока строится | ⏳ Бэклог | 5.1 |
|
||||
| F-20 | Линейка: перетаскивание точек | Возможность перетащить уже поставленную точку линейки на новое место | ⏳ Бэклог | 5.x |
|
||||
| F-21 | Линейка: сохранение между сессиями | Сохранять точки линейки в localStorage, восстанавливать при перезагрузке | ⏳ Бэклог | 5.x |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -441,7 +441,9 @@ body.has-map-mode #sheet-backdrop.visible { pointer-events: none; }
|
||||
#ruler-info {
|
||||
position: fixed;
|
||||
top: calc(max(env(safe-area-inset-top,0px),12px) + 58px);
|
||||
left: 12px; right: 12px;
|
||||
left: 12px;
|
||||
width: fit-content;
|
||||
max-width: 320px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
@@ -454,22 +456,49 @@ body.has-map-mode #sheet-backdrop.visible { pointer-events: none; }
|
||||
#ruler-info #ruler-dist { flex: 1; }
|
||||
.ruler-action-btn {
|
||||
flex-shrink: 0;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
height: 32px;
|
||||
min-width: 32px;
|
||||
padding: 4px 10px;
|
||||
background: var(--surface2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.ruler-action-btn--danger {
|
||||
color: var(--danger, #e05252);
|
||||
border-color: var(--danger, #e05252);
|
||||
font-size: 16px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* ── Ruler toast hint ────────────────────────── */
|
||||
#ruler-toast {
|
||||
position: fixed;
|
||||
top: calc(max(env(safe-area-inset-top,0px),12px) + 100px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(20,20,20,0.82);
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
z-index: 210;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#ruler-toast.visible { opacity: 1; }
|
||||
|
||||
/* ── Fix: MapLibre markers must stay absolute ────── */
|
||||
.maplibregl-marker {
|
||||
position: absolute !important;
|
||||
@@ -510,7 +539,7 @@ body.has-map-mode #sheet-backdrop.visible { pointer-events: none; }
|
||||
.bottom-sheet.swiping { transition: none; }
|
||||
#map-controls-r { right: 12px; bottom: 12px; }
|
||||
#sheet-backdrop { display: none; }
|
||||
#ruler-info { left: 84px; }
|
||||
#ruler-info { left: 84px; max-width: 320px; }
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
|
||||
52
tasks/enduro-trails/prototype/static/app.js
vendored
52
tasks/enduro-trails/prototype/static/app.js
vendored
@@ -1631,6 +1631,8 @@ function toggleRuler() {
|
||||
window._map.getCanvas().style.cursor = 'crosshair';
|
||||
clearRuler();
|
||||
document.getElementById('ruler-info').classList.add('visible');
|
||||
// Fix 4: Show toast hint on activation
|
||||
showRulerToast();
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
window._map.getCanvas().style.cursor = '';
|
||||
@@ -1640,6 +1642,14 @@ function toggleRuler() {
|
||||
updateMapModeClass();
|
||||
}
|
||||
|
||||
// Fix 4: Toast hint helper
|
||||
function showRulerToast() {
|
||||
const toast = document.getElementById('ruler-toast');
|
||||
if (!toast) return;
|
||||
toast.classList.add('visible');
|
||||
setTimeout(() => toast.classList.remove('visible'), 3000);
|
||||
}
|
||||
|
||||
// Exit ruler mode without clearing points/markers ("Завершить")
|
||||
function exitRulerMode() {
|
||||
if (!rulerMode) return;
|
||||
@@ -1702,11 +1712,19 @@ function updateRulerLabels() {
|
||||
rulerTotal = 0;
|
||||
for (let i = 0; i < rulerMarkers.length; i++) {
|
||||
const markerEl = rulerMarkers[i].getElement();
|
||||
const dot = markerEl.querySelector('.ruler-dot');
|
||||
const label = markerEl.querySelector('.ruler-label');
|
||||
const btn = markerEl.querySelector('.ruler-remove-btn');
|
||||
const labelText = label ? label.querySelector('span') : null;
|
||||
|
||||
// Fix 5: Update dot color for first point
|
||||
if (dot) {
|
||||
const dotColor = i === 0 ? '#2EA043' : '#0088ff';
|
||||
dot.style.background = dotColor;
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
if (labelText) labelText.textContent = '';
|
||||
if (labelText) labelText.textContent = 'Старт';
|
||||
} else {
|
||||
const segDist = haversineKm(rulerPoints[i - 1], rulerPoints[i]);
|
||||
rulerTotal += segDist;
|
||||
@@ -1738,34 +1756,41 @@ function addRulerPoint(lngLat) {
|
||||
rulerTotal += segDist;
|
||||
}
|
||||
|
||||
// Hide toast on first tap
|
||||
if (idx === 0) {
|
||||
const toast = document.getElementById('ruler-toast');
|
||||
if (toast) toast.classList.remove('visible');
|
||||
}
|
||||
|
||||
// Wrapper element for dot + label row
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.cssText = 'position:relative;display:flex;flex-direction:column;align-items:center;';
|
||||
|
||||
// Dot
|
||||
// Dot - Fix 5: first point green
|
||||
const dot = document.createElement('div');
|
||||
dot.className = 'ruler-dot';
|
||||
dot.style.cssText = 'width:10px;height:10px;background:#0088ff;border:2px solid #fff;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,0.3);display:block;flex-shrink:0;';
|
||||
const dotColor = idx === 0 ? '#2EA043' : '#0088ff';
|
||||
dot.style.cssText = `width:10px;height:10px;background:${dotColor};border:2px solid #fff;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,0.3);display:block;flex-shrink:0;`;
|
||||
|
||||
// Label row: [distance text][× button] in one line
|
||||
const label = document.createElement('div');
|
||||
label.className = 'ruler-label';
|
||||
label.style.cssText = 'margin-top:3px;display:flex;align-items:center;gap:2px;background:rgba(20,20,20,0.75);color:#fff;font-size:10px;padding:1px 4px;border-radius:3px;white-space:nowrap;';
|
||||
label.style.cssText = 'margin-top:3px;display:flex;align-items:center;gap:4px;background:rgba(20,20,20,0.75);color:#fff;font-size:10px;padding:2px 6px;border-radius:3px;white-space:nowrap;';
|
||||
|
||||
const labelText = document.createElement('span');
|
||||
if (idx === 0) {
|
||||
labelText.textContent = '';
|
||||
labelText.textContent = 'Старт';
|
||||
} else {
|
||||
labelText.textContent = segDist >= 1
|
||||
? segDist.toFixed(1) + ' км'
|
||||
: Math.round(segDist * 1000) + ' м';
|
||||
}
|
||||
|
||||
// Remove button × inline with label
|
||||
// Remove button × inline with label - Fix 1: bigger tap target
|
||||
const btn = document.createElement('span');
|
||||
btn.className = 'ruler-remove-btn';
|
||||
btn.textContent = '×';
|
||||
btn.style.cssText = 'cursor:pointer;font-size:11px;line-height:1;opacity:0.85;padding-left:2px;';
|
||||
btn.style.cssText = 'cursor:pointer;font-size:16px;line-height:1;opacity:0.85;padding:4px 8px;min-width:32px;min-height:32px;display:flex;align-items:center;justify-content:center;margin:-2px -6px -2px 0;';
|
||||
btn.onclick = (e) => { e.stopPropagation(); removeRulerPoint(idx); };
|
||||
|
||||
label.appendChild(labelText);
|
||||
@@ -1791,12 +1816,19 @@ function initRulerClicks(map) {
|
||||
e.preventDefault();
|
||||
exitRulerMode();
|
||||
});
|
||||
// Fix 3: tap on ruler line restores panel
|
||||
// Fix 2 & 6: tap on ruler line shows panel AND resumes ruler mode
|
||||
map.on('click', 'ruler-line', (e) => {
|
||||
if (rulerMode) return; // already active
|
||||
e.originalEvent.stopPropagation();
|
||||
if (rulerPoints.length > 0) {
|
||||
// Fix 6: Resume ruler mode
|
||||
if (!rulerMode) {
|
||||
rulerMode = true;
|
||||
const btn = document.getElementById('tb-ruler');
|
||||
btn.classList.add('active');
|
||||
window._map.getCanvas().style.cursor = 'crosshair';
|
||||
updateMapModeClass();
|
||||
}
|
||||
document.getElementById('ruler-info').classList.add('visible');
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
<button class="ruler-action-btn" onclick="exitRulerMode()" title="Завершить">✓ Завершить</button>
|
||||
<button class="ruler-action-btn ruler-action-btn--danger" onclick="toggleRuler()" title="Удалить линейку">✕</button>
|
||||
</div>
|
||||
<!-- ── Ruler toast hint ───────────────────── -->
|
||||
<div id="ruler-toast">Тапни на карту чтобы добавить точку</div>
|
||||
|
||||
<!-- ── No data warning ───────────────────── -->
|
||||
<div id="no-data-warning">⚠️ База данных недоступна</div>
|
||||
|
||||
Reference in New Issue
Block a user