Moves phases 08–11 execution artifacts from .planning/phases/ to .planning/milestones/v0.2-phases/ to keep .planning/phases/ clean for the next milestone. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.0 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 | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10-ux-interaction | 11 | frontend/interaction |
|
|
|
|
|
|
Phase 10 Plan 11: Drag-to-Move Click Guard + Teleported Dropdowns Summary
One-liner: Click-after-drag guard with nextTick reset in StorageBrowser; all three dropdown menus (StorageBrowser folder picker, DocumentCard folder picker, FolderRow three-dot menu) teleported to body with getBoundingClientRect positioning.
What Was Built
UX-11: Drag-to-Move Completion
The drag-to-move highlight and drop handlers were already wired from plan 10-06. This plan added the missing pieces:
-
Click-after-drag guard — file-row
@clicknow checksdraggingFile ? null : $emit('file-open', file). While a drag is in progress, clicks on file rows are suppressed. -
onFileDragEnd()handler — defersdraggingFile = nullvianextTick()instead of synchronously resetting. This ensures the click event fired by the browser immediately afterdragendstill seesdraggingFileas non-null and gets suppressed. -
onDropDocOnFolderupdated — also usesawait nextTick()before clearingdraggingFilefor the drop-on-folder case.
UX-13: Teleported Dropdowns
Three dropdowns replaced their position: absolute approach with <Teleport to="body"> + getBoundingClientRect fixed positioning:
- StorageBrowser folder picker — single shared teleport block; trigger mapped by
fileIdin aMapfor scroll repositioning.openFolderPicker(fileId, $event)captures the trigger and computes initial position. - DocumentCard folder picker —
pickerTriggerElref on the button;updatePickerPosition()called on toggle, scroll, and resize. - FolderRow three-dot menu —
menuTriggerElref on the button;updateMenuPosition()called on toggle, scroll, and resize. Menu anchored to right edge of trigger viarect.right - 160.
All three follow the scroll-reposition pattern from SearchableModelSelect.vue.
Tests
| File | Tests | Result |
|---|---|---|
| StorageBrowser.dragmove.test.js | 6 | All pass |
| dropdown.test.js | 4 | All pass |
| Full suite | 208 | All pass (3 pre-existing todos in skeleton test) |
Commits
| Hash | Type | Description |
|---|---|---|
f20420a |
test | Promote stub tests to real tests (RED phase) |
f9ddda8 |
feat | Click-after-drag guard + teleport folder picker in StorageBrowser |
e0606b4 |
feat | Teleport DocumentCard folder picker |
892abca |
feat | Teleport FolderRow three-dot menu |
Deviations from Plan
None — plan executed exactly as written.
D-18 Scope Decision
Per the plan's locked decision and confirmed during implementation: DocumentCard.vue does NOT have draggable="true" in Phase 10. It is not a drag source. The dragend-then-click guard (D-18's protection goal) is fully satisfied by the onFileDragEnd + click guard implemented on StorageBrowser file rows, which ARE the drag sources.
If DocumentCard.vue gains draggable="true" in a future phase, it will need its own drag handle at that time.
Known Stubs
None.
Threat Flags
None — no new network endpoints, auth paths, file access patterns, or schema changes introduced.
Self-Check: PASSED
Files exist:
frontend/src/components/storage/StorageBrowser.vue— FOUNDfrontend/src/components/documents/DocumentCard.vue— FOUNDfrontend/src/components/folders/FolderRow.vue— FOUNDfrontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js— FOUNDfrontend/src/components/ui/__tests__/dropdown.test.js— FOUND
Commits exist: f20420a, f9ddda8, e0606b4, 892abca — all verified in git log.
Test suite: 208 passed, 0 failed.