Files

74 lines
5.6 KiB
Markdown
Raw Permalink 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.
# 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
Нет.