feat(frontend): select photo in album grid and show info in panel

- single click selects a photo (ring + aria-pressed) and shows its info in the right panel without navigating; double click opens the viewer
- shared 'selection' store; panel uses the route photo in viewer mode and the selected photo in album mode; selection resets on album change
- openspec: sync lightroom-workspace-ui (grid-selection requirement, viewer chrome wording without timers, nested f fullscreen, tasks 9.9/9.10)
This commit is contained in:
iu
2026-10-10 21:33:59 +03:00
parent 642e3bcf8a
commit 78996bc053
8 changed files with 114 additions and 33 deletions
@@ -104,10 +104,13 @@ filmstrip, панель метаданных/комментариев, тулб
### 13. Просмотр: полный экран и зум
Полный экран — `requestFullscreen` на корне `Viewer` (кнопка + `f`), состояние — по
`fullscreenchange`. В полноэкранном режиме верхняя/нижняя панели и стрелки соседних фото
скрыты и всплывают у соответствующих кромок (панели — по касанию края, скрытие ~1 с после
ухода; стрелки — при курсоре в 1/6 левой/правой области, скрытие ~300 мс; в зуме стрелки
скрыты полностью). Зум — состояние `zoomed`/`scale` с масштабом от истинных размеров фото:
`fullscreenchange`. `f`/кнопка переключают именно полноэкранный режим вьюера: если активен
полноэкранный режим приложения, `f` входит в просмотр фото вложенным режимом и не выключает
режим приложения. В полноэкранном режиме верхняя/нижняя панели и стрелки соседних фото
всплывают у соответствующих кромок без таймеров — показываются и скрываются сразу по позиции
курсора (панели — по касанию края; стрелки — при курсоре в 1/6 левой/правой области, только
внутри границ вьюера, поэтому при уходе в боковые панели они скрыты; в зуме стрелки скрыты
полностью). Зум — состояние `zoomed`/`scale` с масштабом от истинных размеров фото:
колесо зумит с привязкой к указателю (без прокрутки, `overscroll-contain`), предел — 200%;
`Space` возвращает из любого зума во вписанный вид, а из вписанного — в 100%; вход в зум
догружает оригинал и подменяет его по факту загрузки (до этого — превью); панорамирование —
@@ -162,6 +165,15 @@ EXIF извлекается при загрузке и хранится в `phot
`exif`/`taken_at`/координаты. Альтернатива — фиксированный набор полей — отклонена:
пользователю нужны все атрибуты.
### 19. Выбор фото в сетке через общий стор
Выбор фото в сетке и контекстная панель живут в разных местах дерева (страница маршрута и слот
`AppShell`), поэтому выбранное фото хранится в общем сторе `selection` (`albumId` + `photo`).
Одиночный клик в `PhotoGrid` записывает выбор (рамка `ring` + `aria-pressed`), и панель
показывает сведения о выбранном фото; двойной клик открывает маршрут просмотра. Панель
использует `panelPhoto = photoId ? routePhoto : selectedPhoto`, поэтому в просмотре приоритет у
маршрута, а в альбоме — у выбора; выбор сбрасывается при смене альбома. Альтернатива — хранить
выбор в URL (query) — избыточно для локального выделения.
## Risks / Trade-offs
- [Много альбомов → много presign в списке] → presign это локальная крипто-операция без сети;
@@ -41,6 +41,7 @@
удаление текущего фото с модальным подтверждением; возврат к обзору.
- **Разделы и панели.** Правая панель — сворачиваемые разделы (состояние сохраняется), с
разделом «Информация» (EXIF/метаданные); левая и правая панели ресайзятся перетаскиванием.
Выбор фото в сетке показывается в панели (одиночный клик), двойной клик открывает просмотр.
- **Настройки информации.** Раздел «Информация» получает кнопку-шестерёнку, открывающую
модалку со списком EXIF-атрибутов: галочки видимости (с действиями «отметить все»/«снять
все»), пользовательские подписи и сброс. Скрытые атрибуты сворачиваются под элементом «…»
@@ -161,12 +161,16 @@ SHALL отображаться вне каркаса, но в общей тем
#### Scenario: Панель по кромке
- **WHEN** курсор касается верхней или нижней кромки экрана
- **THEN** соответствующая панель показывается и скрывается примерно через секунду после ухода курсора
- **THEN** соответствующая панель показывается, а при уходе курсора скрывается сразу
#### Scenario: Выход из полного экрана
- **WHEN** пользователь повторно нажимает кнопку или клавишу `f`
- **THEN** полноэкранный режим закрывается
#### Scenario: Полноэкранный режим приложения
- **WHEN** включён полноэкранный режим приложения, и пользователь нажимает `f`
- **THEN** открывается полноэкранный просмотр фото (вложенно), а полноэкранный режим приложения не выключается
### Requirement: Зум в просмотре
Просмотр SHALL поддерживать зум колесом мыши без прокрутки, с привязкой к указателю и
верхним пределом 200% размеров фото. `Space` SHALL возвращать изображение из любого зума во
@@ -188,7 +192,7 @@ SHALL отображаться вне каркаса, но в общей тем
#### Scenario: Стрелки соседних фото
- **WHEN** курсор находится у левой или правой кромки области просмотра
- **THEN** показывается соответствующая стрелка, скрываясь примерно через 300 мс после ухода; в зуме стрелки скрыты
- **THEN** показывается соответствующая стрелка, скрываясь сразу после ухода; в зуме стрелки скрыты
#### Scenario: Панорамирование
- **WHEN** фото увеличено
@@ -257,3 +261,19 @@ SHALL отображаться вне каркаса, но в общей тем
#### Scenario: Нет скрытых атрибутов
- **WHEN** ни один атрибут не скрыт
- **THEN** элемент «…» не отображается
### Requirement: Выбор фото в сетке
В режиме альбома одиночный выбор фото SHALL отмечать его рамкой и показывать его сведения в
контекстной панели, не открывая просмотр; двойной выбор SHALL открывать режим просмотра фото.
#### Scenario: Одиночный выбор
- **WHEN** пользователь кликает по фото в сетке
- **THEN** фото отмечено рамкой, правая панель показывает сведения об этом фото, просмотр не открывается
#### Scenario: Двойной выбор
- **WHEN** пользователь дважды кликает по фото в сетке
- **THEN** открывается режим просмотра этого фото
#### Scenario: Смена альбома
- **WHEN** пользователь переходит в другой альбом
- **THEN** отметка выбора сбрасывается
@@ -47,7 +47,7 @@
- [x] 8.1 Переименование фото: `PATCH /photos/:id` (права автора/owner/admin, пустое имя 400) и инлайн-редактор в правой панели; событие `photo.updated`. Проверка: переименование сохраняется, пустое имя отклоняется, чужое фото — отказ, событие приходит.
- [x] 8.2 Удаление фото из просмотра с модальным подтверждением; переход к соседнему и обновление ленты. Проверка: после удаления лента не содержит удалённое фото.
- [x] 8.3 Полноэкранный просмотр (кнопка и `f`) с автоскрытием верхней/нижней панелей у кромок и стрелок (1/6 края, скрытие ~300 мс, в зуме скрыты); зум колесом к указателю без прокрутки (макс 200%), `Space` возвращает из любого зума во вписанный вид, в зуме подгружается оригинал. Проверка: поведение подтверждено в браузере (автоскрытие, зум к указателю, предел 200%, подмена превью→оригинал).
- [x] 8.3 Полноэкранный просмотр (кнопка и `f`) с автоскрытием верхней/нижней панелей у кромок и стрелок (1/6 края, в зуме скрыты); поведение без таймеров — панели и стрелки показываются/скрываются сразу, стрелки только в границах вьюера; зум колесом к указателю без прокрутки (макс 200%), `Space` возвращает из любого зума во вписанный вид, в зуме подгружается оригинал. Проверка: поведение подтверждено в браузере (автоскрытие, зум к указателю, предел 200%, подмена превью→оригинал).
- [x] 8.4 Сворачиваемые разделы правой панели с сохранением состояния и раздел «Информация» (EXIF). Проверка: состояние сохраняется между сессиями, метаданные отображаются.
- [x] 8.5 Ресайз левой и правой панелей перетаскиванием с сохранением ширины. Проверка: ширина восстанавливается после перезагрузки.
- [x] 8.6 Лимит загрузки 100 МБ и понятные ошибки (клиентская проверка и `413 file_too_large`). Проверка: 60 МБ проходит разбор, превышение лимита даёт явную ошибку размера.
@@ -67,3 +67,5 @@
- [x] 9.6 `AlbumPanel`: фильтрация атрибутов по оверрайдам, элемент «…» для показа скрытых (при отсутствии скрытых — не показывается). Проверка: скрытые сворачиваются, «…» раскрывает все, дефолт — все видимы.
- [x] 9.7 Проверка: `cd frontend && npm run check`, `cd backend && go test ./...`, `make build`, `openspec validate "lightroom-workspace-ui"`.
- [x] 9.8 Базовый набор атрибутов по умолчанию при пустых настройках (модель `visible` вместо `hidden`); в диалоге — иконки «отметить все»/«снять все» вместо «Показать всё»; «Сбросить» удаляет настройки из профиля (`PATCH {}`). Проверка: Playwright — дефолт даёт базовый набор и «…», снятие/отметка всех, сброс очищает профиль (12/12).
- [x] 9.9 Выбор фото в сетке: одиночный клик выделяет фото рамкой и показывает его сведения в правой панели, двойной — открывает просмотр; стор `selection`, сброс при смене альбома. Проверка: Playwright — выделение без навигации, панель показывает инфо, двойной клик открывает просмотр (5/5).
- [x] 9.10 Вьюер и модалки: убраны таймеры автоскрытия (панели/стрелки мгновенно), стрелки появляются только в границах вьюера, `f`/кнопка переключают вложенный fullscreen вьюера (не выключают режим приложения); модалки рендерятся порталом в `document.body` (центрирование во вьюпорте). Проверка: Playwright — стрелки 4/4, fullscreen+f 6/6, центрирование 2/2.