Skip to content

Draggable Zone ​

An element can be both a draggable and a droppable at the same time. Register both makeDraggable and makeDroppable on the same ref. Use placementMargins to split the element into zones: pointer near the edges sorts, pointer in the center nests.

Demo ​

Project tree
Drag via ⠿ handle · hover edges to sort · hover center to nest inside a folder
⠿📂Frontend
⠿📂components
⠿📄Button.vue
⠿📄Input.vue
⠿📄App.vue
⠿📄main.ts
⠿📂Backend
⠿📄routes.ts
⠿📄db.ts
⠿📄package.json
⠿📄README.md

Each node is a dual-role element. The handle (⠿) makes it draggable. The same element is also a droppable:

  • Top / bottom edge — insert line → sort node among its siblings
  • Center — colored glow → nest the dragged item inside this node (works even on leaf nodes)

How it works ​

ts
const rowRef = useTemplateRef<HTMLElement>('rowRef');

// Dual-role: edge → sort siblings; center → nest inside this node
const { isDragging, isDragOver: placement } = makeDraggable(
  rowRef,
  {
    dragHandle: '.drag-handle',
    placementMargins: { top: 12, bottom: 12 },
  },
  () => [props.index, props.siblings]
);

// Center drop fires when pointer is NOT in the edge margins
const { isDragOver: isOver } = makeDroppable(
  rowRef,
  { events: { onDrop: (e) => emit('drop', e) } },
  () => props.node.children
);

Internal mechanism ​

hover.ts detects the dual-role situation (isDualRole branch) and activates only one side depending on placement:

Pointer positionActive sidehoveredDraggabledropZone
Top / bottom edgeDraggableset to nodenull
CenterDroppablenullset to node

Drop handler (root) ​

ts
function applyToTree(oldArr: TreeNode[], newArr: TreeNode[]) {
  if (oldArr === tree.value) {
    tree.value = newArr;
  } else {
    findAndReplace(tree.value, oldArr, newArr);
  }
}

function handleDrop(e: IDragEvent) {
  const r = e.helpers.suggestSort('vertical');
  if (!r) return;

  const srcArr = e.draggedItems[0]?.items as TreeNode[];
  const tgtArr = (e.hoveredDraggable?.items ?? e.dropZone?.items) as TreeNode[];

  applyToTree(srcArr, r.sourceItems as TreeNode[]);
  if (!r.sameList) applyToTree(tgtArr, r.targetItems as TreeNode[]);
}

Source ​

See also ​

Released under the MIT License.