11 KiB
TASK-008C: ComponentsPanel Drag-Drop System
Overview
This subtask addresses the systematic implementation and debugging of the drag-drop system for the React-based ComponentsPanel. Previous attempts have been piecemeal, leading to circular debugging. This document provides a complete scope and test matrix.
Expected Behaviors (Full Requirements)
A. DRAG INITIATION
| Requirement | Description |
|---|---|
| A1 | Click + hold on any component → initiates drag |
| A2 | Click + hold on any folder → initiates drag |
| A3 | Click + hold on any component-folder (component with nested children) → initiates drag |
| A4 | Visual feedback: dragged item follows cursor with ghost/label |
| A5 | Drag threshold: 5px movement before drag activates (prevents accidental drags) |
B. DROP TARGETS
| ID | Source | Target | Result | Example |
|---|---|---|---|---|
| B1 | Component | Component | Creates nesting | /PageA → /PageB = /PageB/PageA |
| B2 | Component | Folder | Moves into folder | /MyComp → /Folder/ = /Folder/MyComp |
| B3 | Component | Empty Space (Root) | Moves to root level | /Folder/MyComp → root = /MyComp |
| B4 | Folder | Folder | Moves folder + all contents | /FolderA/ → /FolderB/ = /FolderB/FolderA/... |
| B5 | Folder | Component | Nests folder inside component | /FolderA/ → /PageB = /PageB/FolderA/... |
| B6 | Folder | Empty Space (Root) | Moves folder to root | /Parent/FolderA/ → root = /FolderA/... |
| B7 | Component-Folder | Any target | Same as folder (moves component + all children) | Same as B4/B5/B6 |
C. VALIDATION
| Requirement | Description |
|---|---|
| C1 | Cannot drop item onto itself |
| C2 | Cannot drop parent into its own descendant (circular reference) |
| C3 | Cannot create naming conflicts (same name at same level) |
| C4 | Show "forbidden" cursor when drop not allowed |
D. VISUAL FEEDBACK
| Requirement | Description |
|---|---|
| D1 | Hover over valid target → highlight with border/background |
| D2 | Hover over invalid target → show forbidden indicator |
| D3 | Hover over empty space → show root drop zone indicator (blue tint) |
| D4 | Cursor changes based on drop validity (move vs none) |
E. COMPLETION
| Requirement | Description |
|---|---|
| E1 | Successful drop → item moves, tree re-renders at new location |
| E2 | Failed/cancelled drop → item returns to origin (no change) |
| E3 | All operations support Undo (Cmd+Z) |
| E4 | All operations support Redo (Cmd+Shift+Z) |
Current Implementation Status
Code Inventory
| File | Purpose |
|---|---|
useDragDrop.ts |
React hook managing drag state, uses PopupLayer.startDragging |
useComponentActions.ts |
Drop handlers: handleDropOn(), handleDropOnRoot() |
ComponentItem.tsx |
Drag initiation + drop target handlers for components |
FolderItem.tsx |
Drag initiation + drop target handlers for folders |
ComponentsPanelReact.tsx |
Background drop zone handlers |
popuplayer.js |
Legacy jQuery drag system (startDragging, indicateDropType, endDrag) |
Feature Status Matrix
| Feature | Handler Exists | Wired Up | Tested | Works? |
|---|---|---|---|---|
| B1: Component → Component | ✅ handleDropOn |
✅ | ⏳ | ❓ |
| B2: Component → Folder | ✅ handleDropOn |
✅ | ⏳ | ❓ |
| B3: Component → Root | ✅ handleDropOnRoot |
✅ | ⏳ | ❌ |
| B4: Folder → Folder | ✅ handleDropOn |
✅ | ⏳ | ❓ |
| B5: Folder → Component | ✅ handleDropOn |
✅ | ⏳ | ❌ |
| B6: Folder → Root | ✅ handleDropOnRoot |
✅ | ⏳ | ❌ |
| B7: Component-Folder → any | ✅ (handled as folder) | ✅ | ⏳ | ❌ |
Known Issues
Issue 1: Background Drop Zone Not Triggering
- Symptom: Dragging to empty space doesn't trigger root move
- Likely cause:
e.target === e.currentTargetcheck may be wrong, or handlers not attached properly - Debug approach: Add console.log to
handleBackgroundMouseEnter
Issue 2: Nested Component → Other Component Not Working
- Symptom: Can't drag a nested component to another component to create new nesting
- Likely cause:
canDropvalidation or drop handler not triggering - Debug approach: Add console.log to
handleDropin ComponentItem
Issue 3: Parent Folder → Component Not Working
- Symptom: Can't drag a folder with children onto a component
- Likely cause: Folder→Component case may not be recognized
- Debug approach: Check
handleDropOnfor folder→component case
Issue 4: Component-Folder Drag Returns to Origin
- Symptom: Dragging component-folders snaps back instead of completing drop
- Likely cause: Missing
PopupLayer.endDrag()call or wrong case branch - Debug approach: Add logging to each case in
handleDropOn
Implementation Plan
Phase 1: Diagnostic Logging (30 min)
Add comprehensive logging to understand current behavior:
// In ComponentItem.tsx handleMouseEnter
console.log('🎯 Component hover:', { node, isDragging: PopupLayer.instance.isDragging() });
// In FolderItem.tsx handleMouseEnter
console.log('📁 Folder hover:', { folder, isDragging: PopupLayer.instance.isDragging() });
// In ComponentsPanelReact.tsx handleBackgroundMouseEnter
console.log('🏠 Background hover:', { target: e.target, currentTarget: e.currentTarget });
// In useComponentActions.ts handleDropOn
console.log('💾 handleDropOn called:', { draggedItem, targetItem });
// In useComponentActions.ts handleDropOnRoot
console.log('🏠 handleDropOnRoot called:', { draggedItem });
Phase 2: Test Each Combination (1 hour)
Create test scenario for each combination and verify:
- B1: Create
/CompA,/CompB. Drag/CompAonto/CompB. - B2: Create
/CompA,/Folder. Drag/CompAonto/Folder. - B3: Create
/Folder/CompA. Drag/CompAto empty space. - B4: Create
/FolderA,/FolderB. Drag/FolderAonto/FolderB. - B5: Create
/FolderA,/CompB. Drag/FolderAonto/CompB. - B6: Create
/Parent/FolderA. Drag/FolderAto empty space. - B7: Create
/CompParentwith nested/CompParent/Child. Drag/CompParentonto another component.
Phase 3: Fix Issues (2-3 hours)
Address each failing combination based on diagnostic output.
Phase 4: Remove Logging & Test (30 min)
Clean up debug code and verify all combinations work.
Acceptance Criteria
All items must pass:
- B1: Component → Component creates proper nesting
- B2: Component → Folder moves component into folder
- B3: Component → Root moves component to top level
- B4: Folder → Folder moves entire folder hierarchy
- B5: Folder → Component nests folder inside component
- B6: Folder → Root moves folder to top level
- B7: Component-Folder → any target works as folder
- C1-C4: All validations prevent invalid operations
- D1-D4: Visual feedback works for all scenarios
- E1-E4: Completion and undo/redo work
Technical Notes
PopupLayer Drag System Integration
The legacy PopupLayer uses a jQuery-based drag system:
// Start drag
PopupLayer.instance.startDragging({
label: 'Item Name',
type: 'component' | 'folder',
dragTarget: HTMLElement,
onDragEnd: () => {
/* cleanup */
}
});
// During drag, from drop targets:
PopupLayer.instance.isDragging(); // Check if dragging
PopupLayer.instance.indicateDropType('move' | 'none'); // Visual feedback
// Complete drag
PopupLayer.instance.endDrag(); // Must be called for drop to complete!
Critical: If endDrag() is not called, the dragged element returns to origin.
Component-Folder Pattern
When a component has nested children (e.g., /Parent with /Parent/Child), it's rendered as a FolderItem with attached component data:
// In tree building:
{
type: 'folder',
data: {
path: '/Parent',
name: 'Parent',
isComponentFolder: true,
component: ComponentModel // The component at /Parent
}
}
Drop handlers must check node.data.component to handle these properly.
Background Drop Zone
The background drop zone should trigger when:
- User is dragging (PopupLayer.isDragging() === true)
- Mouse enters the tree container
- Mouse is NOT over any tree item (target === currentTarget)
The current implementation uses e.target === e.currentTarget which may be too restrictive.
Files to Modify
- ComponentItem.tsx - Add diagnostic logging, verify drop handlers
- FolderItem.tsx - Add diagnostic logging, verify drop handlers
- ComponentsPanelReact.tsx - Fix background drop zone
- useDragDrop.ts - Verify canDrop logic
- useComponentActions.ts - Verify all drop handler cases
References
- TASK-008 CHANGELOG - Previous fix attempts documented
- popuplayer.js - Legacy drag system (don't modify, just understand)
- UNDO-QUEUE-PATTERNS.md - Correct undo patterns for operations
Created: December 27, 2025 Last Updated: December 27, 2025