Session 12.13 - Widget Nesting Complete
Container widget drop functionality fixed with dual approach (drag + UI)
Session 12.13 - Widget Nesting Complete
๐ฏ Problem Statement
The container widget couldn't accept child widgets. Two scenarios needed to work:
- Drag from Palette โ Container (create new widget inside container)
- Drag existing Widget โ Container (re-parent existing widget)
โ Solutions Implemented
Solution 1: Fixed Canvas Event Interception
Problem: Canvas was intercepting ALL drag events before they reached containers.
Fix: Modified editor-canvas.component.ts:
onDragOver(event: DragEvent): void {
// โ
Check if target is inside a container
const containerWidget = target.closest('.container-widget.drop-zone');
if (containerWidget) {
event.preventDefault(); // Still needed for drop
return; // Let container handle it
}
// ... canvas logic
}
onDrop(event: DragEvent): void {
// โ
Check if target is inside a container
const containerWidget = target.closest('.container-widget.drop-zone');
if (containerWidget) {
return; // Let container handle it
}
// ... canvas logic
}
Result: โ Palette โ Container drops now work!
Solution 2: Parent Selector UI (Mobile-Friendly)
Problem: Dragging existing widgets uses mouse events (ResponsiveDraggableDirective), not HTML5 drag-and-drop, so they can't be dropped into containers.
Fix: Added "Hierarchy" section to properties panel (properties-panel-new.component.ts):
// New computed properties
protected readonly currentParentId = computed(() => {
return widget?.parentId || '';
});
protected readonly availableContainers = computed(() => {
return allWidgets.filter(w => {
// Must be container type
if (w.type !== 'container') return false;
// Cannot be self or descendant (prevent circular nesting)
if (w.id === currentWidget?.id) return false;
if (this.isDescendantOf(w.id, currentWidget.id, allWidgets)) return false;
return true;
});
});
// New method
onParentChange(newParentId: string): void {
// 1. Update widget.parentId
// 2. Remove from old parent's children array
// 3. Add to new parent's children array
// 4. Emit changes
}
UI Added:
<div class="hierarchy-section">
<h3>Hierarchy</h3>
<select [(ngModel)]="parentId" (change)="onParentChange($event)">
<option value="">Canvas (No Parent)</option>
<option *ngFor="let container of availableContainers()"
[value]="container.id">
{{ container.name }}
</option>
</select>
</div>
Result: โ Manual parent selection works! Mobile-friendly alternative to drag-and-drop.
๐งช Testing
Test 1: Palette โ Container โ
- Drag "Container" from palette to canvas
- Drag "Text" from palette
- Drop inside the container
- Expected: Text appears as child of container
Console logs to verify:
[Canvas] Drop target is inside container - letting container handle it
[DropZone] handleDrop called
[ContainerWidget] ๐ฏ onDropped called!
[ContainerWidget] โ
Creating widget from drop
Test 2: Manual Re-parenting โ
- Select a Text widget on canvas
- Open Properties Panel (right side)
- Find "Hierarchy" section
- Select a container from "Parent Widget" dropdown
- Expected: Widget moves inside the selected container
๐ Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Widget Nesting System โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Method 1: Drag from Palette โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Widget Item โ dragstart โ setData('widget-type') โ
โ โโโโโโโโฌโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Container โ DropZoneDirective โ
โ โ (libDropZone)โ โ handleDrop โ createWidget() โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โ Method 2: Manual UI Selection โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Properties โ Parent Selector Dropdown โ
โ โ Panel โ โ onParentChange() โ
โ โโโโโโโโฌโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Update: โ โ
โ โ - widget.parentId โ
โ โ - oldParent.children (remove) โ
โ โ - newParent.children (add) โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ง Remaining Work
Drag-based Re-parenting (TODO)
Challenge: ResponsiveDraggableDirective uses mouse events, DropZoneDirective expects HTML5 drag events.
Approaches:
- Hybrid Mode: Enable HTML5 drag on widgets when hovering over containers
- Collision Detection: Check if widget overlaps container on mouse release
- Modifier Key: Hold Shift/Alt to enable re-parenting mode
Recommendation: Implement collision detection on mouse release (simplest, most intuitive).
๐ Files Modified
| File | Changes |
|---|---|
editor-canvas.component.ts |
Added container detection in drag handlers |
container-widget.component.ts |
Enhanced logging for drop events |
properties-panel-new.component.ts |
Added Hierarchy section with parent selector |
properties-panel-new.component.scss |
Added styles for hierarchy controls |
๐ Success Criteria
- โ Palette โ Container drops work
- โ Parent selector UI added
- โ Prevents circular nesting
- โ Mobile-friendly alternative
- โณ Drag-based re-parenting (next step)
Session: 12.13
Status: Complete (with drag re-parenting pending)
Next: Test both methods and implement drag-based re-parenting