Drag & Drop Experience Library

Standalone directives + services that power the modular canvas interactions.

Overview

@libs-ux/drag-drop encapsulates the reusable drag + drop UX that we now rely on inside the Modular Universe, widget palettes, and future editors. The library lives at libs/libs-ux/drag-drop and exposes:

The service + directives are completely standalone, so applications can import only what they need without dragging Angular CDK dependencies into every surface.

Library Exports

import {
  DragDropService,
  DragData,
  DraggableDirective,
  DropZoneDirective
} from '@libs-ux/drag-drop';
Export Description
DraggableDirective Adds dragstart, handle locking, custom drag image support, and active styling.
DropZoneDirective Detects top/inside/bottom regions, applies visual classes (drop-top, drop-bottom, drop-inside) and emits { data, position }.
DragDropService Holds dragItem, dragOverId, and dropPosition as Angular signals.
DragData Type-safe payload describing the entity being moved.

Usage Pattern

@Component({
  standalone: true,
  imports: [CommonModule, DraggableDirective, DropZoneDirective],
  template: `
    <div class="palette-item"
         libDraggable
         [dragData]="getDragData(widget)"
         [dragHandle]="'.drag-handle'">
      <span class="drag-handle">⋮⋮</span>
      {{ widget.name }}
    </div>

    <section class="canvas"
             libDropZone
             zoneId="hero-grid"
             (itemDrop)="onWidgetDrop($event)">
    </section>
  `
})
export class WidgetPaletteComponent {
  getDragData(widget: SpaceWidget): DragData {
    return { type: 'widget', data: widget, source: 'palette' };
  }

  onWidgetDrop(event: { data: DragData; position: DropPosition }): void {
    // 1. event.data contains the dragged module/widget.
    // 2. event.position tells you whether we hovered top / bottom / inside.
  }
}

Drop Zone Tuning

DropZoneDirective exposes an edgeThreshold input (default 0.32). Increase it if you want bigger “before / after” zones or decrease it if you need precise nesting.

<div libDropZone
     [zoneId]="section.id"
     [edgeThreshold]="0.4"
     [allowedTypes]="['section']"
     (itemDrop)="handleSectionDrop($event)">
</div>

Visual Feedback Enhancements

The directive now emits CSS classes (drop-top, drop-bottom, drop-inside) that the Modular Universe styles use to render:

The section-level SCSS lives in grid-section.component.scss. Other canvases can mirror the same classes for consistent visuals.

Integration Checklist

  1. Import from the library – never use relative paths into another feature package.
  2. Provide drag metadata – include type, data, and source in DragData so drop handlers can branch without introspection.
  3. Handle position – top/bottom should reorder arrays, inside should nest or assign content.
  4. Reset state – DragDropService.endDrag() is called automatically by the directives but can be invoked manually if a workflow cancels mid-flight.
  5. Customize thresholds – edgeThreshold and allowedTypes keep drop zones predictable between tools (canvas vs. palette).

Testing & Maintenance

Migration Notes