198 lines
12 KiB
Markdown
198 lines
12 KiB
Markdown
# BRD: Enduro Trails — Поиск точек маршрута
|
||
|
||
**Версия:** 1.1
|
||
**Дата:** 2026-05-05
|
||
**Автор:** Стрим 🌊
|
||
**Статус:** 🔄 В разработке
|
||
|
||
---
|
||
|
||
## 1. Контекст и проблема
|
||
|
||
Сейчас добавить точку маршрута можно только тапом на карту. Если нужно попасть в конкретное место (деревня, перекрёсток, POI) — нужно сначала найти его на карте, потом тапнуть. Это неудобно, особенно на мобиле.
|
||
|
||
Верхний search bar решал задачу навигации по карте, но не интегрирован с маршрутом — найденное место просто центрировало карту, не добавляя точку. Плюс bar занимал место и мешал карте.
|
||
|
||
---
|
||
|
||
## 2. Цель
|
||
|
||
Убрать верхний search bar. Добавить поиск прямо в список точек маршрута — inline, для каждой точки отдельно. При первом открытии режима маршрута — онбординг с полями ввода старта и финиша.
|
||
|
||
---
|
||
|
||
## 3. Что меняется
|
||
|
||
### 3.1 Убрать верхний search bar
|
||
|
||
- Удалить `#search-bar` и `#search-results` из HTML
|
||
- Удалить все CSS стили (`#search-bar`, `#search-input`, `#search-results`, `.search-result-item`, `.sri-*`)
|
||
- Удалить вызов `initSearch()` из JS
|
||
- Кнопку темы (`#btn-theme`) перенести в `#map-controls-r` (рядом с компасом и геолокацией)
|
||
|
||
### 3.2 Онбординг при активации режима маршрута
|
||
|
||
Когда пользователь тапает «Маршрут» в toolbar и точек ещё нет — показывать вместо пустого списка:
|
||
|
||
```
|
||
┌─────────────────────────────────┐
|
||
│ 🔍 Откуда? │ ← поле поиска старта
|
||
│ или тапни на карте │ ← подсказка
|
||
└─────────────────────────────────┘
|
||
```
|
||
|
||
После выбора старта автоматически появляется:
|
||
```
|
||
┌─────────────────────────────────┐
|
||
│ S Название старта │ ← добавленная точка
|
||
├─────────────────────────────────┤
|
||
│ 🔍 Куда? │ ← поле поиска финиша
|
||
│ или тапни на карте │
|
||
└─────────────────────────────────┘
|
||
```
|
||
|
||
После выбора финиша — поля исчезают, маршрут строится, показывается обычный `wl-list`.
|
||
|
||
**Детали:**
|
||
- Поля поиска используют тот же Nominatim что и inline поиск в wl-item
|
||
- Подсказка «или тапни на карте» — мелкий текст `var(--text3)` под полем
|
||
- Тап на карте тоже работает — добавляет точку и переходит к следующему полю
|
||
- Если точки уже есть — онбординг не показывается, сразу wl-list
|
||
|
||
### 3.3 Inline поиск в каждой точке маршрута
|
||
|
||
В каждом `wl-item` добавить кнопку-лупу. Тап → разворачивается inline панель поиска прямо под точкой.
|
||
|
||
**UX flow:**
|
||
1. Пользователь видит список точек маршрута (S → 1 → F)
|
||
2. Тапает иконку 🔍 рядом с нужной точкой
|
||
3. Под точкой появляется поле ввода с автофокусом
|
||
4. Вводит название места → через 400ms debounce → Nominatim поиск
|
||
5. Выбирает результат → точка обновляется, карта летит к ней, маршрут перестраивается
|
||
6. Панель закрывается
|
||
|
||
**Детали:**
|
||
- Одновременно открыта только одна панель поиска (остальные закрываются)
|
||
- Поиск через Nominatim (`countrycodes=ru`, `accept-language=ru`, limit=5)
|
||
- Результат: название + подзаголовок (область/район)
|
||
- После выбора: `routeWaypoints[idx]` обновляется, `rebuildWaypointMarkers()` + `renderWaypointsList()` + `debounceBuildRoute()`
|
||
- `map.flyTo()` к выбранному месту (zoom 13, duration 600ms)
|
||
|
||
---
|
||
|
||
## 4. UI компоненты
|
||
|
||
### Кнопка лупы в wl-item
|
||
- Иконка: Lucide `search` SVG (15×15)
|
||
- Цвет: `var(--text3)` → hover `var(--accent)`
|
||
- Позиция: между `.wl-info` и `.wl-drag-handle`
|
||
|
||
### Inline панель поиска
|
||
- Фон: `var(--surface)`, бордер сверху `var(--border)`
|
||
- Input: `var(--surface2)` фон, `var(--border)` бордер, focus → `var(--accent)` бордер
|
||
- Результаты: max-height 180px, scroll, hover `var(--surface2)`
|
||
- Каждый результат: название (bold) + подзаголовок (мелкий, `var(--text2)`)
|
||
|
||
### Кнопка темы (перенос)
|
||
- Переносится в `#map-controls-r` как `.map-btn`
|
||
- `updateThemeButtonIcon()` продолжает работать — находит `#btn-theme` по id
|
||
|
||
---
|
||
|
||
## 5. Технические детали
|
||
|
||
| Аспект | Решение |
|
||
|--------|---------|
|
||
| Поиск API | Nominatim `/search` (уже используется в проекте) |
|
||
| Debounce | 400ms |
|
||
| Состояние | `wpSearchTimeout` — глобальная переменная для debounce |
|
||
| Закрытие панели | Тап на лупу повторно — закрывает; открытие другой — закрывает текущую |
|
||
| После выбора | `routeWaypoints[idx]` = `{lat, lon}`, rebuild + route |
|
||
| Кнопка «Добавить точку» | `wl-add` остаётся без изменений |
|
||
|
||
---
|
||
|
||
## 6. Баги после редизайна (требуют исправления)
|
||
|
||
| Баг | Описание | Вероятная причина |
|
||
|-----|---------|------------------|
|
||
| Метки в верхнем левом углу | Попап метки отображается не на позиции маркера, а в углу экрана | CSS `position` маркера сбивает позиционирование MapLibre |
|
||
| Метки линейки в верхнем углу | Попап точки линейки отображается не на треке, а в углу | Та же причина — CSS позиционирование маркера |
|
||
| Подсказка «Тапни на карте» пропала | `#route-status` не виден когда точек нет | Скрыт или перекрыт после редизайна |
|
||
|
||
**Вероятная причина багов с маркерами:** в редизайне изменились глобальные CSS стили для `position`, `transform` или `z-index` — это сбивает позиционирование MapLibre маркеров. Маркер MapLibre должен иметь `position: absolute` без переопределения в глобальных стилях.
|
||
|
||
**Фикс:** проверить CSS для `.maplibregl-marker`, `.marker-flag`, `.ruler-marker` — убрать любой `position: fixed/relative/static` который может переопределять поведение маркера.
|
||
|
||
---
|
||
|
||
## 7. Definition of Done
|
||
|
||
| Критерий | Статус |
|
||
|----------|--------|
|
||
| Верхний search bar отсутствует | ⬜ |
|
||
| Кнопка темы работает в map-controls-r | ⬜ |
|
||
| При активации маршрута без точек — поле поиска старта | ⬜ |
|
||
| После старта — поле поиска финиша | ⬜ |
|
||
| Подсказка «или тапни на карте» видна | ⬜ |
|
||
| Иконка лупы в каждом wl-item | ⬜ |
|
||
| Тап на лупу → inline поиск открывается | ⬜ |
|
||
| Поиск находит места через Nominatim | ⬜ |
|
||
| Выбор результата → waypoint обновляется | ⬜ |
|
||
| Маршрут перестраивается после выбора | ⬜ |
|
||
| Карта летит к выбранному месту | ⬜ |
|
||
| Одновременно открыта только одна панель | ⬜ |
|
||
| Метки отображаются на позиции маркера (не в углу) | ⬜ |
|
||
| Метки линейки отображаются на треке | ⬜ |
|
||
| Деплой + health check OK | ⬜ |
|
||
|
||
---
|
||
|
||
## 8. Файлы
|
||
|
||
| Файл | Изменения |
|
||
|------|-----------|
|
||
| `prototype/static/index.html` | Удалить `#search-bar`, `#search-results`; перенести `#btn-theme` в `#map-controls-r` |
|
||
| `prototype/static/app.css` | Удалить стили search bar; добавить `.wl-search-*`; исправить позиционирование маркеров |
|
||
| `prototype/static/app.js` | Удалить `initSearch()`; добавить онбординг, `openWaypointSearch()`, `doWaypointSearch()`, `selectWaypointSearchResult()` |
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 9. Дополнительные замечания (05.05.2026 — пакет 2)
|
||
|
||
| # | Замечание | Детали |
|
||
|---|-----------|--------|
|
||
| 1 | Иконка колеса | Заменить на нормальное мотокросс колесо |
|
||
| 2 | Флажок финиша | Чёрно-белая шахматная раскраска как финишный флаг |
|
||
| 3 | Спиннер в основном листе | Вращающееся колесо пока строится маршрут (как в мини-баре) |
|
||
| 4 | Тёмная карта | При тёмной теме — тёмный стиль карты, при светлой — светлый |
|
||
| 5 | Автозум на маршрут | Плавный `fitBounds` после построения маршрута, маршрут по центру без выхода за границы |
|
||
| 6 | Мини-бар перекрывает кнопки справа | Исправить z-index/позиционирование `#sheet-route-mini` |
|
||
|
||
### Детали по пунктам
|
||
|
||
**П.1 Иконка колеса:**
|
||
Заменить текущую SVG на нормальное мотокросс колесо — спицы, кноблинг, центральная втулка. Использовать Lucide `bike` или нарисовать SVG колеса с спицами.
|
||
|
||
**П.2 Флажок финиша:**
|
||
В `waypointPinSvg('F', ...)` для финишной точки использовать шахматный узор (SVG `pattern` чёрные/белые клетки) вместо красного цвета.
|
||
|
||
**П.3 Спиннер в основном листе:**
|
||
В `#route-cards` показывать skeleton/спиннер пока идёт запрос к OSRM. Тот же спиннер что в мини-баре.
|
||
|
||
**П.4 Тёмная карта:**
|
||
`map.setStyle()` при смене темы. Тёмный стиль: `/style-dark.json` (или текущий тёмный), светлый: `/style-light.json` (или текущий светлый). После `setStyle` пересоздать слои маршрутов через `map.on('style.load', onMapStyleLoad)`.
|
||
|
||
**П.5 Автозум:**
|
||
После `drawRouteResults()` вызывать:
|
||
```js
|
||
const coords = routeResults[0].geometry.coordinates;
|
||
const bounds = coords.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(coords[0], coords[0]));
|
||
map.fitBounds(bounds, { padding: 60, duration: 1000, maxZoom: 14 });
|
||
```
|
||
|
||
**П.6 Мини-бар:**
|
||
`#sheet-route-mini` перекрывает `#map-controls-r`. Исправить `right` или `width` мини-бара чтобы не залезал на кнопки справа.
|