- Cursor separate from selection; arrow keys move it, Space/Shift+Arrow/Enter/Esc act - Click on empty grid space clears selection back to album properties - Focus returns to the last viewed photo after closing the viewer
7.0 KiB
7.0 KiB
Tasks
1. PhotoGrid: API курсора и визуализация
- 1.1 Добавить в
lib/components/PhotoGrid.svelteпропcursorIndexи колбэкиonCursor,onToggle,onExtend,onOpen,onClear; прокинуть их изroutes/(app)/albums/[id]/+page.svelte. Проверка:cd frontend && npm run checkпроходит, сетка рендерится без изменений поведения мыши. - 1.2 Реализовать roving tabindex: у ячейки под курсором
tabindex="0", у остальных-1; при пустом курсоре0у первой ячейки. Проверка: Tab вводит фокус в сетку один раз, стрелки не покидают сетку. - 1.3 Добавить визуальный контур курсора (
data-cursor, внешнийoutline-2 outline-offset-2в токенеtext) с показом только при:focus-within. Проверка: в светлой, тёмной и нейтральной темах контур отличим отring-primaryи читается, когда ячейка одновременно выделена и под курсором.
2. Клавиши сетки
- 2.1 Реализовать геометрию перемещения по плоскому индексу:
←/→—index ± 1(естественный переход на соседнюю строку),↑/↓—index ± density.colsс зажимом в[0, n-1]. Проверка: ручной прогон —→в конце строки уходит на следующую строку,↑/↓не переносят через верх/низ. - 2.2 Повесить
keydownна контейнер сетки (не наwindow); игнорировать события вне фокуса сетки;preventDefaultтолько для обрабатываемых клавиш. Проверка: при фокусе в поле ввода панели или в диалоге стрелки иSpaceне двигают курсор. - 2.3 Обработать клавиши-действия:
Space→onToggle,Shift+стрелка →onExtend,Enter→onOpen,Esc→onClear. Проверка:Spaceпереключает выделение и обновляет правую панель,Shift+стрелка расширяет диапазон от якоря,Enterоткрывает просмотр,Escснимает выделение и оставляет курсор.
3. Снятие выделения кликом
- 3.1 Добавить обработчик клика на контейнер сетки вместе с отступами: пропускать цель внутри
[data-photo-cell], иначеselection.clear(); контейнеру датьtabindex="-1"и фокусировать его после снятия. Проверка: клик по пустому месту возвращает в правой панели свойства альбома, клик по фото/кнопке тулбара/элементу панели выделение не снимает, после снятия стрелки сразу двигают курсор.
4. Интеграция страницы альбома
- 4.1 Ввести
cursorIndexв+page.svelte; синхронизировать с мышью (клик,ctrl/cmd-клик,shift-клик, контекстное меню ставят курсор); сбрасывать вnullпри смене альбома в том же$effect, гдеselection.clear(). - 4.2 При перемещении курсора вызывать
scrollIntoView({ block: 'nearest' })для ячейки. Проверка: движение к краю прокручивает сетку, ячейка под курсором видна. - 4.3 При движении вперёд за последнее загруженное фото и наличии
nextCursorвызыватьloadMore(), затем ставить курсор на новое фото; безnextCursor— зажим. Проверка:→на последнем загруженном фото догружает следующую порцию и перемещает курсор. - 4.4 Реализовать
onExtend: если якорь не задан, взять текущий курсор; выставлять выделение диапазоном от якоря до курсора черезselection.set. Проверка:Shift+стрелка добавляет соседние фото в выделение, правая панель показывает количество.
5. Интеграционная проверка
- 5.1 Прогнать
cd frontend && npm run checkбез ошибок типов и предупреждений. - 5.2 Вручную пройти сценарии дельты
ui-workspaceв светлой, тёмной и нейтральной темах: движение курсора без смены панели,Space+панель,Shift-диапазон,Enter,Esc, клик по пустому месту, догрузка; убедиться, что клик/дабл-клик/контекстное меню/drag работают как прежде.
6. Возврат фокуса из просмотра
- 6.1 Добавить модульный стор
lib/stores/viewer-return.ts(однослотовая память{albumId, photoId}сremember/consume) и вызыватьremember(albumId, photoId)вroutes/(app)/albums/[id]/photos/[photoId]/+page.svelteчерез$effectнаphotoId. Проверка:npm run checkпроходит; при открытии просмотра и листании слот хранит текущийphotoId. - 6.2 В
routes/(app)/albums/[id]/+page.svelteв$effectнаalbumIdвызыватьviewerReturn.consume(albumId), а послеloading = falseиtick()— восстанавливать фокус:selection.set(albumId, [photo]),anchorId,cursorIndex,.focus({preventScroll:true})иscrollIntoView({block:'center'}); если фото нет среди загруженных — догружатьloadMore()в цикле. Проверка: возврат из просмотра выделяет и фокусирует просмотренное фото, стрелки работают сразу; фото со второй страницы догружается. - 6.3 Расширить QA-скрипт сценариями возврата (закрытие просмотра, листание и возврат, фото вне первой страницы, возврат в другой альбом) и прогнать его. Проверка: все проверки PASS,
npm run checkбез ошибок и предупреждений.