Files
initiative/specs/019-combatant-row-declutter/research.md

3.1 KiB

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.