# ТРЗ — ET-014: Z-index конфликт terrain-popup vs sheet-gps-filters **Work Item:** ET-014 **Связан с BRD:** 01-brd.md **Тип задачи:** Bug-fix (UI / стили / DOM-stacking) --- ## 1. Анализ текущего состояния ### 1.1 DOM-структура (как есть) - `#terrain-popup` (`src/web/index.html:43`) — `position: fixed`, `z-index: 500` (`src/web/app.css:785-795`). Открывается по клику на кнопку «Рельеф» (`#terrain-toggle` в `#map-controls-r`). Содержит чекбоксы слоёв, переключатели подложки и единиц, а также кнопку-ссылку `#public-tracks-filters-btn` с текстом «Фильтры…». - `#sheet-gps-filters` (`src/web/index.html:478`) — класс `.bottom-sheet`, `position: fixed`, `z-index: 400` (`src/web/app.css:183-196`). Открывается через `togglePublicTracksFiltersSheet()` в `src/web/gps_tracks.js:737`, который вызывает `openSheet('sheet-gps-filters')`. - `#sheet-backdrop` (`src/web/index.html:19`) — `z-index: 390` (`src/web/app.css:222-228`). На mobile перекрывает экран при открытом sheet'е; на desktop скрыт (`#sheet-backdrop { display: none; }` в media-query, `src/web/app.css:543`). ### 1.2 Стек z-index в проекте (для ориентира) | Элемент | z-index | Файл/строка | |-------------------|---------|-------------------------| | `#map` | 0 | app.css:68 | | `#no-data-warning`| 200 | app.css:410 | | `#sheet-backdrop` | 390 | app.css:225 | | `.bottom-sheet` | 400 | app.css:188 | | `#map-controls-r` | 400 | app.css:129 | | `.terrain-popup` | **500** | app.css:787 | | `#marker-dialog` | 500 | app.css:399 | | `#search-panel` | 600 | app.css:1101 | | `#ruler-info` | 600 | app.css:1122 | ### 1.3 Корень проблемы 1. `togglePublicTracksFiltersSheet()` открывает sheet (z=400), но **не закрывает** `#terrain-popup` (z=500). Popup остаётся на экране и визуально/event-but перекрывает sheet. 2. Клик по ссылке «Фильтры…» внутри popup не триггерит `closeTerrainOnOutside` (popup.contains(target) === true), поэтому popup не закрывается сам. 3. Backdrop sheet'а (z=390) тоже ниже popup'а (z=500), поэтому даже на mobile нет визуальной индикации, что popup стал «фоном». ## 2. Требования к решению ### 2.1 Функциональные (REQ-F) | ID | Требование | |------------|------------| | REQ-F-01 | При открытии `#sheet-gps-filters` из «Фильтры…» панель `#terrain-popup` НЕ должна перекрывать sheet ни визуально, ни для событий ввода. | | REQ-F-02 | Когда `#sheet-gps-filters` открыт, состояние кнопки `#terrain-toggle` (класс `.active`) должно быть консистентно с состоянием popup: если popup скрывается / закрывается на время открытия фильтров — кнопка не должна оставаться визуально «прижатой». | | REQ-F-03 | После закрытия `#sheet-gps-filters` (через `✕`, свайп вниз, клик по backdrop на mobile, либо `closeAllSheets()`) пользователь возвращается к карте. Возврат панели слоёв — на усмотрение архитектора (см. §3 «Варианты решения»). В любом случае не должно оставаться «фантомных» оверлеев / неактивных DOM в видимой области. | | REQ-F-04 | Решение должно работать единообразно при инициации фильтров повторно (открыли → закрыли → открыли снова). | | REQ-F-05 | Поведение `#terrain-popup` для всех других сценариев (открыть/закрыть кнопкой, кликнуть вне popup'а, переключить чекбокс/подложку/единицы) **не должно регрессировать**. | | REQ-F-06 | Поведение остальных bottom-sheets (`#sheet-route`, `#sheet-recon`, `#sheet-scenic`, `#sheet-link`, `#sheet-gpx`) **не должно регрессировать**. | | REQ-F-07 | Решение должно одинаково корректно работать в светлой и тёмной теме. | ### 2.2 Нефункциональные (REQ-NF) | ID | Требование | |-------------|------------| | REQ-NF-01 | Изменения локализованы во фронте (`src/web/`). Backend (`src/api/`) не затрагивается. | | REQ-NF-02 | Нет регрессий по производительности (никаких новых тяжёлых обработчиков resize/scroll). | | REQ-NF-03 | Если решение меняет z-index — оно не должно ломать стекинг `#marker-dialog` (z=500), `#search-panel` (z=600), `#ruler-info` (z=600). | | REQ-NF-04 | Решение совместимо с PWA-режимом (PH-9, в работе): в standalone display и при наличии safe-area-inset. | | REQ-NF-05 | Решение работает на mobile viewport 360–414 px (Chrome Android), desktop ≥1024 px (Chrome desktop). | ## 3. Варианты решения (на усмотрение архитектора) > Аналитик не выбирает архитектуру. Перечисляю опции, которые могут быть > рассмотрены реализатором/архитектором: - **Вариант A — закрывать `#terrain-popup` при открытии sheet-gps-filters.** В `togglePublicTracksFiltersSheet()` перед `openSheet(...)` явно скрыть popup (как делает `closeTerrainOnOutside`) и снять `.active` с `#terrain-toggle`. Backdrop sheet'а корректно затемнит фон на mobile. - **Вариант B — поднять z-index sheet'ов выше terrain-popup.** Например, `.bottom-sheet { z-index: 510; }` и `#sheet-backdrop { z-index: 505; }`. Тогда sheet физически окажется поверх popup'а. Требует проверки на не- конфликт с marker-dialog (z=500) и не-перекрытие toolbar / search-panel. - **Вариант C — точечно поднять z-index только `#sheet-gps-filters` и его backdrop.** Узкий хак: `#sheet-gps-filters { z-index: 510; }`. Менее системно, но минимальные риски регрессии для других sheet'ов. Решение фиксируется архитектором в ADR работы (`06-adr/`). ## 4. Acceptance hooks См. полные критерии в `03-acceptance-criteria.md`. Краткая выжимка: - Открытие фильтров → панель полностью видна, кликабельна (mobile и desktop). - Панель слоёв не перекрывает фильтры (визуально и для событий). - Закрытие фильтров → возврат к карте без артефактов. - Остальные оверлеи (marker-dialog, search-panel, ruler-info, остальные sheets) — без регрессий. ## 5. Тесты См. `04-test-plan.yaml` (функциональные тесты) и `04b-ui-test-cases.md` (Playwright UI тест-кейсы). ## 6. Артефакты для модификации (ожидание аналитика) - `src/web/app.css` — стили stacking-context (если выбран вариант B/C). - `src/web/gps_tracks.js` — логика `togglePublicTracksFiltersSheet()` (если выбран вариант A). - Возможно `src/web/app.js` — если в `openSheet` / `closeAllSheets` требуется хук «при открытии sheet закрыть popup» как универсальное решение для будущих кейсов. Это рекомендация, конкретный набор файлов определит архитектор.