Files
Chandler Copeland 37f8691cac docs(05-02): complete pdf-fill-and-field-mapping plan 02
- FieldPlacer.tsx: dnd-kit drag-and-drop field placer with Y-flip coordinate conversion
- PdfViewer.tsx: extended with pageInfo state and FieldPlacer integration
- @dnd-kit/core and @dnd-kit/utilities installed
- Fields persist via PUT /api/documents/[id]/fields on every add/remove
- 05-02-SUMMARY.md created, STATE.md and ROADMAP.md updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:01:59 -06:00

4.6 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
05-pdf-fill-and-field-mapping 02 frontend/documents
dnd-kit
pdf-fields
field-placer
coordinate-conversion
drag-and-drop
requires provides affects
05-01
field-placement-ui
signature-field-overlay
documents-detail-page
pdf-viewer
added patterns
@dnd-kit/core@^6.3.1
@dnd-kit/utilities@^3.2.2
dnd-kit-drag-overlay
y-flip-coordinate-conversion
droppable-pdf-zone
created modified
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx
DragOverlay used for ghost rendering during drag — avoids transform-based dragging which breaks coordinate math relative to the droppable container
containerRef.getBoundingClientRect() called at drop time (not stale pageInfo.width) — captures current rendered size after zoom changes
activatorEvent + delta pattern for final drop coordinates — activatorEvent gives the client position where drag started, delta gives displacement
DroppableZone assigns both setNodeRef (dnd-kit) and containerRef (coordinate math) to same DOM node via combined ref callback
top: top - heightPx applied to overlay divs — pdfToScreenCoords returns y of bottom-left corner, must shift up by field height for DOM top-left origin
duration completed_date tasks_completed files_created files_modified
1 min 2026-03-20 2 1 1

Phase 5 Plan 02: Drag-and-Drop Signature Field Placer Summary

One-liner: dnd-kit DndContext with draggable palette token + droppable PDF zone; Y-flip coordinate conversion persists fields to DB via PUT /api/documents/[id]/fields.

What Was Built

A complete drag-and-drop field placement system integrated into the document detail page:

  1. FieldPlacer.tsx (321 lines) — New client component providing:

    • A DndContext wrapping both palette and PDF canvas
    • DraggableToken sub-component using useDraggable for the "Signature Field" palette item
    • DroppableZone sub-component using useDroppable to make the PDF canvas a valid drop target
    • DragOverlay showing a blue ghost rectangle during drag
    • screenToPdfCoords() — Y-flip formula converting DOM coordinates to PDF user space
    • pdfToScreenCoords() — inverse formula for rendering stored fields as overlays
    • Server sync: GET on mount (load existing), PUT on every add/remove (persist)
    • Per-page filtering (field.page === currentPage) so page 1 fields don't show on page 2
  2. PdfViewer.tsx (updated) — Extended with:

    • pageInfo state (PageInfo | null) populated via onLoadSuccess callback
    • Math.max(page.view[0], page.view[2]) / Math.max(page.view[1], page.view[3]) for robust originalWidth/originalHeight (handles non-standard mediaBox ordering)
    • FieldPlacer wraps the Document/Page tree, receiving docId, pageInfo, currentPage
    • Only scale prop used on <Page> (not both width + scale)

Verification Results

  • npm run build — compiled successfully (both tasks)
  • FieldPlacer.tsx: 321 lines (minimum 80 required)
  • @dnd-kit/core and @dnd-kit/utilities present in package.json
  • PdfViewerWrapper.tsx unchanged (dynamic import wrapper still passes docId through)

Coordinate Conversion Details

The Y-flip formula is critical for correct field placement:

  • DOM: Y=0 at top, increases downward
  • PDF user space: Y=0 at bottom, increases upward
  • pdfY = ((renderedH - screenY) / renderedH) * originalHeight
  • This means dragging to the visual top of a page stores a high pdfY value (near originalHeight)
  • containerRect.width/height used at drop time (not stale pageInfo) to handle zoom changes correctly

Commits

Task Commit Description
Task 1 6069ae5 feat(05-02): install dnd-kit and create FieldPlacer component
Task 2 7a36736 feat(05-02): extend PdfViewer with pageInfo state and FieldPlacer integration

Deviations from Plan

None — plan executed exactly as written.

The DroppableZone component uses a combined ref callback to assign both the dnd-kit setNodeRef and the containerRef simultaneously to the same DOM node — this was a minor implementation detail not explicitly specified in the plan but required for correct operation.

Self-Check: PASSED

  • FOUND: FieldPlacer.tsx (321 lines)
  • FOUND: PdfViewer.tsx (updated)
  • FOUND commit: 6069ae5
  • FOUND commit: 7a36736
  • @dnd-kit/core and @dnd-kit/utilities in package.json
  • Build: compiled successfully