Andrey Epifancev 3f75eb5909 Архив OpenSpec: sidebar (#69)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:01:08 +04:00

92 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
`main` 7911dc7 (после #66/#67). Оболочка: `web/src/shell/Layout.tsx` рисует `AppBar` (`shell/AppBar.tsx`: название,
`ProjectPicker`, `Segmented` разделов со ссылками, `DataStatus`, `ThemeSwitch`, `Menu` «Ещё» со «Сменить токен») и
`<main className="page …">`. Прокрутка страниц — у окна (`window.scrollY`): на этом держатся восстановление прокрутки
доски (`BoardCanvas`), липкая шапка доски (`.sticky`, `top: 0`, высота — в `--head-h`), липкая полоса дат, ящик
«Проблемы» и полоса горизонтальной прокрутки `.hscroll` (`bottom: 0`). Верхняя панель не липкая (решение 15 #65),
поэтому уезжает.
Доска: `.lane:nth-child(even of .lane){background:var(--lane-alt)}`; колонки дат `.dcol` (`.past` — 5% `--p-crit`,
`.today` — 7% действия) рисуются в DOM до дорожек, поэтому непрозрачный фон чётной дорожки закрывает их подсветку.
Карточка (`board/components/Card.tsx`) показывает «до {дата}» без признака просрочки; признак есть только в карточке
задачи (`issues/card.ts` `isOverdue`, бейдж «просрочено») и в списке (красная дата).
## Goals / Non-Goals
**Goals:** навигация и «Обновить из Jira» всегда на экране; доска шире не теряет (свёртка); просрочка видна на доске.
**Non-Goals:** см. proposal.
## Decisions
1. **Каркас.** `Layout` рисует `<div class="shell">` с `Sidebar` и `<main class="page …">`. `.shell` — grid
`grid-template-columns: var(--sidebar-w) minmax(0, 1fr)`; панель `position: sticky; top: 0; height: 100dvh;
overflow-y: auto`. Прокрутка остаётся у окна — восстановление прокрутки, липкие шапка доски, полоса дат, ящик и
`.hscroll` работают без изменений. Ширина: развёрнутая 240 px, свёрнутая 64 px (`--sidebar-w`).
2. **Состав развёрнутой панели** (сверху вниз): строка «Хаб Jira» + кнопка-иконка «Свернуть панель»
(`aria-expanded`); `ProjectPicker` на всю ширину; `<nav aria-label="Разделы">` — ссылки «Доска связей» и «Задачи»
со значками, высота 40 px, текущая — фон `--color-bg-selected`, текст действия, `aria-current="page"`; разделитель
с автоматическим отступом; блок данных `DataStatus` в колонку (агент, «данные на …», кнопка «Обновить из Jira» на
всю ширину, строка события под кнопкой с переносом — абсолютное позиционирование события из R-7 #65 больше не
нужно); строка: `ThemeSwitch` слева, `Menu` «Ещё» справа, меню открывается вверх.
3. **Свёрнутая панель.** Только значки: кнопка «Развернуть панель», метка текущего проекта мелким текстом (до 6
символов, полный текст в `title`; щелчок разворачивает панель), значки разделов (`aria-label` = название раздела,
`title`), «Обновить из Jira» — `IconButton` с крутящимся значком при пакете и точкой агента в углу, событие пакета —
в `title` и в скрытом `role="status"`, тема, меню. Ключ `localStorage` `jira-hub-sidebar`: `collapsed` — свёрнута,
иначе развёрнута; чтение и запись — в `try/catch`.
4. **Узкий экран (<640 px).** Панели в потоке нет: сверху липкая строка 56 px — кнопка «Меню» (`IconMenu`,
`aria-expanded`), «Хаб Jira», справа `IconButton` «Обновить из Jira». «Меню» открывает ту же развёрнутую панель
поверх страницы (фиксирована слева, ширина `min(300px, 85vw)`, подложка `--color-overlay`); закрывается выбором
раздела, Escape, щелчком по подложке; фокус при открытии — на первую ссылку раздела, при закрытии — на «Меню».
Свёртка на узком экране не действует. Режим определяется `matchMedia("(max-width: 639px)")` с подпиской на смену.
5. **Шапка доски** остаётся липкой с `top: 0` (теперь это верх окна); правило `@media (max-width:640px){.sticky{position:static}}`
не трогаем, только `top` у `.sticky`, `.dates-bar` и `.drawer` на узком экране сдвигается на высоту строки 56 px
через `--topbar-h` (0 на широких).
6. **Меню вверх.** `Menu` получает проп `up?: boolean` → класс `dd--up` (`.dd--up > .dd-menu { top: auto; bottom: calc(100% + 4px) }`).
7. **Иконки.** Новые в `ui/icons.tsx`: `IconBoard` (граф из трёх узлов), `IconList` (строки), `IconMenu` (три
линии), `IconSidebar` (прямоугольник с полосой слева).
8. **`AppBar` удаляется**, `Segmented` больше не нужен оболочке (остаётся в `ui` для списка задач). `.appbar …` в
`shell.css` и `projects.css` заменяются стилями `.sidebar`.
9. **Дорожки без чередования.** Правило `.lane:nth-child(even of .lane)` и токен `--lane-alt` удаляются в обеих темах;
развёрнутые дорожки прозрачны (фон — поверхность `.wrap`), разделитель — верхняя граница; свёрнутая дорожка
остаётся на `--color-bg-track`.
10. **Просрочка на карточке доски.** `Card` получает проп `today: string` (ISO, из `BoardPage`, где он уже есть для
`computeVisible`) и считает `late = !closed && !!n.due && n.due < today`; у `.due` класс `late` — цвет
`--color-status-error`, `title="просрочено"`, скрытый текст «, просрочено» (`.visually-hidden`). Карточка в слое
измерения тоже получает `today`, размер не меняется (скрытый текст вне потока). Сравнение в `cardPropsEqual`
дополняется `today`. Модель доски и эталоны не меняются.
11. **Тесты.** Vitest: `Sidebar` (разделы и `aria-current`, свёртка и ключ `jira-hub-sidebar`, «Сменить токен»,
узкий режим: «Меню» открывает и Escape закрывает, выбор раздела закрывает), `Card` (просрочка: незакрытая с
прошедшим сроком — `late`, закрытая и будущая — нет), тест токенов (нет `--lane-alt`). Playwright: существующие
сценарии с новыми селекторами; новый — панель видна после прокрутки доски вниз и свёртка переживает перезагрузку;
`screens.spec` — снимки свёрнутой панели и открытого меню на 390.
## Risks / Trade-offs
- [Панель 240 px сужает доску на ноутбуке] → свёртка в 64 px, выбор запоминается.
- [Две копии меню на узком экране (строка и панель)] → нет: на узком экране панель одна, она же выезжает; в строке
только «Меню» и «Обновить из Jira».
- [e2e и юнит-тесты, завязанные на `.appbar`] → правятся в той же задаче, что и разметка.
## Decisions made without discussion
Владелец 22.10.2026: «в графе связей идёт чередование строк по цвету. оно мешает. его или сделать менее заметным или
убрать вообще, тк не видно просроченные задачи, и закрепить хэдер, а лучше сделать его сайдбаром». Ранее (#65)
отдал UX на усмотрение агента. Всё ниже — решения агента.
| # | Дата | Решение | Обоснование | Цена ошибки |
|---|---|---|---|---|
| 1 | 22.10.2026 | Боковая панель, а не липкая верхняя (решения 1–2) | владелец предпочёл «лучше сайдбар»; вертикальный список разделов растёт без переноса строк | средняя: вернуть верхнюю панель — новая вёрстка оболочки |
| 2 | 22.10.2026 | Чередование убрано совсем, не ослаблено (решение 9) | любой непрозрачный фон дорожки снова закроет колонки дат; разделителя-линии достаточно | низкая: вернуть правило и токен |
| 3 | 22.10.2026 | Просрочка на карточке доски цветом ошибки + «просрочено» в подсказке (решение 10) | «не видно просроченные» — вне режима «по датам» просрочку на доске не было видно вовсе | низкая: класс и проп |
| 4 | 22.10.2026 | Свёртка панели в полосу 64 px с запоминанием (решение 3) | доске нужна ширина; раз в спринт граф — тогда удобно свернуть | низкая |
| 5 | 22.10.2026 | На узком экране — строка с «Меню» и выезжающая панель (решение 4) | закреплённая панель 4–5 строк (M-12 #65) съела бы экран | низкая: узкий экран — только «без поломок» |
| 6 | 22.10.2026 | Развёрнута по умолчанию, ширина 240 px | на 1920 место есть; название проекта видно целиком | низкая |
| 7 | 22.10.2026 | R-1: панель без `overflow-y:auto` (вопреки решению 1) | прокрутка панели обрезала бы меню «Ещё» свёрнутой панели, выходящее за 64 px | низкая: на окне ниже ~470 px низ панели обрезан (M-5) |
| 8 | 22.10.2026 | R-2: в выезжающей панели «Закрыть меню» вместо «Свернуть панель» | свёртка внутри выезжающей панели бессмысленна (решение 4) | нет |
| 9 | 22.10.2026 | R-3: e2e M-3 переходит по разделам обычным щелчком, не `dispatchEvent` | панель закреплена, щелчок больше не прокручивает доску; тест заодно проверяет закрепление | нет |
| 10 | 22.10.2026 | R-4 (C-1): `.shell>.page{width:100%}` и `.page` — контейнер (`container: page/inline-size`); пороги раскладки страниц (фильтры списка, прокрутка таблицы, колонка ящика «Проблемы») — запросами к контейнеру с порогами «прежний порог окна минус поля страницы 48 px» | рядом панель 240/64 px: ширина страницы больше не равна ширине окна, и пороги по окну давали таблицу шире страницы | низкая: пороги — числа в CSS |
| 11 | 22.10.2026 | R-5 (I-1): на узком экране событие пакета — строкой под закреплённой строкой (`aria-hidden`, озвучивает `role=status` кнопки) | на телефоне подсказок нет, результат «Обновить из Jira» был не виден | низкая: строка занимает 1–2 строки по высоте, пока есть событие |