Implement the 019-combatant-row-declutter feature that replaces always-visible HP controls and AC/MaxHP inputs with compact click-to-edit and click-to-adjust patterns in the encounter tracker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-06 15:07:04 +01:00
parent e59fd83292
commit 0c0da9b90e
11 changed files with 723 additions and 207 deletions

View File

@@ -0,0 +1,42 @@
# Research: Combatant Row Declutter
**Feature**: 019-combatant-row-declutter
**Date**: 2026-03-06
## R1: Popover Dismissal Pattern
**Decision**: Use a click-outside listener with ref-based boundary detection, consistent with the existing ConditionPicker component pattern.
**Rationale**: The codebase already uses this pattern in `condition-picker.tsx` / `condition-tags.tsx` where clicking outside the picker closes it. Reusing the same approach maintains consistency and avoids introducing new dependencies (e.g., a popover library). The popover will use `useEffect` with a document-level click handler that checks if the click target is outside the popover ref.
**Alternatives considered**:
- Headless UI library (Radix Popover, Floating UI): Adds a dependency for a simple use case. Rejected — the project has no headless UI library and introducing one for a single popover is over-engineering.
- HTML `<dialog>` / `popover` attribute: Native browser support is good, but the `popover` attribute doesn't provide the positioning control needed (anchored to the HP value). Rejected for insufficient positioning semantics.
## R2: Click-to-Edit Pattern for AC
**Decision**: Reuse the exact same pattern as the existing `EditableName` component — toggle between static display and input on click, commit on Enter/blur, cancel on Escape.
**Rationale**: `EditableName` in `combatant-row.tsx` already implements this exact interaction pattern. The AC click-to-edit can follow the same state machine (editing boolean, draft state, commit/cancel callbacks). This ensures behavioral consistency across the row.
**Alternatives considered**:
- Popover (same as HP): AC editing is simpler (just set a value, no damage/heal distinction), so a popover adds unnecessary complexity. Rejected.
- Double-click to edit: Less discoverable than single-click. Rejected — the existing name edit uses single-click.
## R3: HP Popover Positioning
**Decision**: Position the popover directly below (or above if near viewport bottom) the current HP value using simple CSS absolute/relative positioning.
**Rationale**: The popover only needs to appear near the trigger element. The combatant row is a simple list layout — no complex scrolling containers or overflow clipping that would require a positioning library. Simple CSS positioning (relative parent + absolute child) is sufficient.
**Alternatives considered**:
- Floating UI / Popper.js: Provides advanced positioning with flip/shift. Overkill for this use case since the encounter list is a straightforward vertical layout. Rejected.
## R4: Keyboard Shortcuts in Popover
**Decision**: Handle Enter (damage) and Shift+Enter (heal) via `onKeyDown` on the popover's input element. Escape handled at the same level to close the popover.
**Rationale**: This matches the existing QuickHpInput pattern where Enter applies damage. Adding Shift+Enter for healing is a natural modifier key extension. The input captures all keyboard events, so no global listeners are needed.
**Alternatives considered**:
- Separate keyboard shortcut system: Unnecessary complexity for two shortcuts scoped to a single input. Rejected.