Skip to content

Image Upload Components — Alternatives Considered


A-01: Avatar rewrite to compose ImageDisplay (deferred)

Section titled “A-01: Avatar rewrite to compose ImageDisplay (deferred)”

Context: Both Avatar and ImageDisplay render images with loaded/loading/error states and initials-based placeholders. Reimplementing Avatar as a thin wrapper over ImageDisplay would consolidate both paths.

Decision: Keep Avatar as-is. Only extract getInitials to a shared utility (minor modification).

Rationale:

  • Avatar is circular with size variants; ImageDisplay is rectangular. Making ImageDisplay shape-agnostic requires a displayShape parameter that adds complexity with no immediate consumer.
  • Risk of subtle visual regressions in a widely-used component mid-project.
  • ImageDisplay needs to be battle-tested in grid and form contexts first.

Revisit trigger: User profile image editing use case. See follow-up-work.md.


A-02: Controlled-only ImageInspectorOverlay (rejected)

Section titled “A-02: Controlled-only ImageInspectorOverlay (rejected)”

Context: The initial implementation only supported controlled mode (open + onClose as required props). Every consumer had to manage open/close state.

Alternative: Keep controlled-only, document the pattern as the standard consumer contract.

Decision: Add uncontrolled mode (omit open to get internal state + trigger button).

Rationale: Inconsistent with ImageDisplay which manages its own dialog state. The consumer burden (3 lines of state management per usage site) compounds across form fields, grid cells, and standalone inspectors. Uncontrolled mode reduces consumer code without breaking controlled usage.


A-03: ImageFormField with dedicated pencil icon for edit (rejected)

Section titled “A-03: ImageFormField with dedicated pencil icon for edit (rejected)”

Context: The initial design had three hover icons: eye (inspect), pencil (edit), and trash (remove).

Decision: Remove the pencil icon. Edit is handled by the underlying ImageDisplay double-click/Enter interaction.

Rationale: With ImageDisplay managing its own edit flow via onImageChange + config, a separate pencil button creates two competing edit affordances on the same element. Users would see a pencil icon but also discover that double-clicking the image opens the same dialog. The pencil icon adds confusion without adding capability.


A-04: Single worktree for all runs vs. parallel worktrees per run

Section titled “A-04: Single worktree for all runs vs. parallel worktrees per run”

Context: The team-split assessment recommended parallel agents within Runs 2 and 3 using separate worktrees per task.

Decision: Runs were executed sequentially with single worktrees per run. Parallel worktrees were available but not fully utilized.

Rationale: The sequential approach was simpler to coordinate and avoided merge conflicts between parallel agents within the same run. The 46 commits completed in a reasonable timeframe. For larger component sets (30+), parallel execution within runs would be worth the coordination overhead.


A-05: react-easy-crop vs. custom crop implementation

Section titled “A-05: react-easy-crop vs. custom crop implementation”

Context: ImagePreviewEditor needs crop/zoom/rotate/pan functionality.

Decision: Use react-easy-crop library.

Rationale: The external libraries assessment (external-libraries-assessment.md) evaluated this dependency and approved it. Building a custom crop implementation would have been a significant scope increase for functionality that react-easy-crop provides out of the box with good React integration and TypeScript support.


A-06: ImageCellEditor reusing ImageDisplay’s baked-in dialog vs. direct dialog rendering

Section titled “A-06: ImageCellEditor reusing ImageDisplay’s baked-in dialog vs. direct dialog rendering”

Context: After ImageDisplay gained baked-in edit flow, ImageCellEditor could theoretically reuse it instead of directly rendering ImageUploadDialog.

Decision: Keep direct dialog rendering in ImageCellEditor.

Rationale: AG Grid’s cell editor lifecycle requires imperative control (getValue, stopEditing, isPopup). The editor must open the dialog immediately on mount and capture the result for AG Grid’s value lifecycle. This is a fundamentally different interaction pattern than ImageDisplay’s user-initiated double-click flow. See inconsistencies-audit.md finding #5.


Copyright: (c) Arda Systems 2025-2026, All rights reserved