feat(ui): open photo on tap for touch and fix narrow-screen panel
- Touch/pen tap opens the viewer; mouse click still selects, dblclick opens - Panel and rail emit a single transform utility so the panel actually slides in below 1280px (Tailwind v4 utility ordering made the base translate-x-full win over the conditional translate-x-0) - Add touch-action: manipulation to grid cells
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
См. proposal.md — Why. Текущее состояние:
|
||||
|
||||
- `AppShell.svelte`: у правого `<aside>` в списке классов одновременно постоянный `translate-x-full`
|
||||
и условный `translate-x-0` (при `panelOpen`). В Tailwind v4 скомпилированный порядок ставит
|
||||
`.translate-x-full` после `.translate-x-0`, поэтому постоянный класс побеждает и панель остаётся
|
||||
за экраном; `panelOpen` управляет только оверлеем. У рейла база `-translate-x-full` сортируется
|
||||
раньше `translate-x-0`, поэтому рейл открывается случайно корректно.
|
||||
- `PhotoGrid.svelte`: `onclick -> onSelect` (выделение), `ondblclick -> onOpen` (просмотр). Touch
|
||||
генерирует `click`, но не `dblclick`, поэтому касание только выделяет.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Панель на узком экране действительно выдвигается кнопкой «Панель».
|
||||
- Исключить класс трансформации, который может перебивать условный (панель и рейл).
|
||||
- Одиночное касание по фото открывает просмотр на сенсорных устройствах.
|
||||
- Мышь и клавиатура сохраняют прежнее поведение.
|
||||
|
||||
**Non-Goals:**
|
||||
- Новый UI выделения/мультивыбора на сенсорных экранах.
|
||||
- Изменения клавиатурной навигации, ровинга `tabindex` и режима просмотра.
|
||||
- Изменения бэкенда.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Ровно один класс трансформации для панели/рейла
|
||||
Эмитим единственное значение: `translate-x-0` при открытии, иначе `translate-x-full`. Постоянный
|
||||
класс из строки убирается (для рейла — `-translate-x-full`). На рабочем столе видимость задаётся
|
||||
вариантом `xl:translate-x-0` / `lg:translate-x-0`.
|
||||
|
||||
Альтернативы: инлайновый `style:translate`; arbitrary-value классы; `data-open` с собственным CSS.
|
||||
Выбрано исключение конфликта минимальной правкой — совпадает с уже существующим намерением кода и
|
||||
не тянет новый CSS-слой.
|
||||
|
||||
### 2. Детект сенсорного ввода — по `pointerType` события
|
||||
Тип ввода определяется на `pointerdown` (`touch`/`pen` → сенсорный, иначе указатель) и
|
||||
используется при обработке активации. Не используем ширину вьюпорта и `matchMedia('(pointer:coarse)')`.
|
||||
|
||||
Альтернатива: `matchMedia` по грубому указателю. Отклонено: на гибридных устройствах (тач + мышь)
|
||||
это статично и путает при вводе мышью. `pointerType` на конкретном событии точнее.
|
||||
|
||||
### 3. Тап открывает, не выделяет
|
||||
При активации сенсорным вводом вызывается открытие просмотра (`onOpen`); выделение, `cursorIndex` и
|
||||
сведения в панели не трогаются. Клик мышью — как прежде (`onSelect`), двойной — `onOpen`.
|
||||
На сенсорных ячейках имеет смысл `touch-action: manipulation`, чтобы исключить двойной тап-зум.
|
||||
|
||||
### 4. Выделение на touch — долгое нажатие
|
||||
Отдельный UI не добавляем: остаётся существующее контекстное меню ячейки (`oncontextmenu`), которое
|
||||
содержит «Открыть (просмотр)», «Переместить», «Копировать», «Удалить».
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [На части мобильных браузеров долгое нажатие не доходит до нашего `oncontextmenu` (нативное меню
|
||||
или выделение текста)] → зафиксировать как известное ограничение; при необходимости добавить
|
||||
`touch-action`/`user-select` в follow-up, не расширяя этот change.
|
||||
- [`pointerType` пуст для части синтетических кликов] → при не-сенсорном/неизвестном типе сохраняем
|
||||
прежнее поведение (выделение), чтобы мышь не ломалась.
|
||||
- [Регрессия порядка утилит Tailwind] → оба места (панель и рейл) используют единственный класс,
|
||||
поэтому конфликт невозможен независимо от порядка генерации.
|
||||
- [Двойной тап-зум на мобильном] → активация на первом касании делает его нерелевантным; при
|
||||
необходимости подавляется `touch-action: manipulation`.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Обратной совместимости нарушать не требуется. Откат — возврат правки классов/обработчиков.
|
||||
|
||||
## Open Questions
|
||||
|
||||
Нет.
|
||||
Reference in New Issue
Block a user