Diagram - Drag and Drop

Move one or several nodes, respect containment and editing policies, and cancel or undo a move.

#Usage

import { Diagram } from '@primeui/vue-diagram';
import MoveSelection from './MoveSelection.vue';
<Diagram.Root :store="store" page-id="page">
    <MoveSelection />
    <Diagram.Viewport label="Workflow editor" style="height: 24rem" />
</Diagram.Root>

Drag a selected node in the viewport or offer a non-drag action. Movement uses world units and current ownership constraints.

Create MoveSelection.vue and mount it under the root as above. The handler reads the current session when activated, including after a page or source change.

<script setup lang="ts">
import { shallowRef } from 'vue';
import { useDiagramContext } from '@primeui/vue-diagram';

const { session } = useDiagramContext();
const feedback = shallowRef('');

function run() {
    const result = session.value.edit({ kind: 'move', delta: { x: 40, y: 0 } });
    feedback.value = result.status;
}
</script>

<template>
    <button type="button" @click="run">Move selection right</button>
    <output aria-live="polite">{{ feedback }}</output>
</template>

This skeleton assumes an initialized store with page page. Use First Diagram for model creation, style imports and store disposal.

Select a node and drag its body to move it. The session previews geometry while dragging and commits the accepted move on release as one history operation. Connectors attached to the moved nodes follow their endpoints.

#Move one or several nodes

Select the first card or both cards, then drag a selected card. Release the selection modifier before dragging. Numeric X and Y values provide an application control for the same public editing gateway.

Loading Demo...

The following action reads the current session in a child component rendered inside Diagram.Root. In setup, context refs require .value.

import { useDiagramContext } from '@primeui/vue-diagram';

const { session } = useDiagramContext();

session.value.select(['first', 'second']);
const result = session.value.edit({ kind: 'move', delta: { x: 40, y: 0 } });

The default target is the current selection; pass an explicit ID list as the second argument when an application action has its own target. An explicit list does not itself select those records. Movement is a relative delta in world units. X increases to the right and Y downward. Both values must be finite. Numeric zero movement can be a no-op; it does not need a new Undo entry.

The editor normalizes selected node roots. If a parent and its child are selected, the child travels with the parent rather than receiving the delta twice. Selected connectors are excluded from node transforms. Attached or layout-owned geometry may reject independent movement and require its owner's API.

ActionDefault behavior
Drag an unselected nodeSelect it, then move after the pointer threshold
Drag a selected nodeMove the selected node roots together
Shift, Command or Ctrl-clickToggle selection; release the modifier before moving
Alt+Arrow / Alt+Shift+ArrowMove selected nodes by 1 / 10 world units from the canvas
Plain arrow keysNavigate keyboard focus
Undo / Command or Ctrl+ZReplay a committed move if current policy permits

The pointer threshold is 4 viewport CSS pixels. The world delta accounts for viewport zoom and each node's parent transform. A completed move changes authored geometry; selection and viewport navigation do not become model history.

#Stay within a container or reject a move

Try Move inside 30, Try outside 300, and Movement block. The container has containment: 'inside'; an escaping child body rejects the numeric command. A move: false policy rejects it independently of containment.

Loading Demo...

The gateway checks read-only state, record constraints, owning locks, affected descendants and structure rules. Check result.status and result.diagnostics; use session.availability(command, ids) to explain a currently unavailable action. During a pointer drag an invalid frame does not replace the last valid preview. A rejected finish cancels the gesture; it does not silently commit an invalid drop.

#Move between owners

An ordinary node-body drag changes position within its current owner. It does not infer a new parent from the node under the pointer. Use a structural command for an application-owned container change:

import { useDiagramContext } from '@primeui/vue-diagram';

const { session } = useDiagramContext();

const result = session.value.structure.execute({
    kind: 'reparent',
    ids: ['child'],
    parentId: 'destination'
});

Reparenting preserves world placement through parent transforms and checks the prospective ownership rules. Use parentId: null to return a node to page space. See containers and ownership for grouping, clipping, collapse, pages, layers and destination constraints.

Swimlane transfer has its own selected-content handle and public transfer API. Move within a lane with ordinary node movement; move into or out of a lane with the transfer workflow. Invalid headers or a destination that cannot fit the content cancel the transfer. Lane records themselves are layout-owned and cannot be moved independently. The Swimlanes guide retains the dedicated transfer examples and exact ownership contract.

#Cancel or undo

Press Escape during a pointer drag to discard its preview. Pointer cancellation, lost capture and window blur also cancel an unfinished gesture. Cancellation adds no model history entry. Release an accepted gesture, then choose Undo to restore its previous geometry. Current policy can block history replay.

Reset restores the example's model, numeric values, history, selection and initial fitted view. Native numeric inputs retain their own keyboard behavior. Mouse and keyboard are the authored paths here; touch, pen and assistive-technology behavior remain subject to device-specific verification.