Diagram - Rotate
Rotate nodes with corner affordances or an exact angle, including supported selections and nested owners.
#Usage
import { Diagram } from '@primeui/vue-diagram';
import RotateSelection from './RotateSelection.vue';<Diagram.Root :store="store" page-id="page">
<RotateSelection />
<Diagram.Viewport label="Workflow editor" style="height: 24rem" />
</Diagram.Root>Rotation is a relative angle in degrees. The action runs after the root has initialized and returns a result that application controls can inspect.
Create RotateSelection.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: 'rotate', degrees: 15 });
feedback.value = result.status;
}
</script>
<template>
<button type="button" @click="run">Rotate selection</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 use the rotation affordance outside a corner. The editor previews the rotation and commits one change on release. Numeric controls are useful when you need a precise authored angle.
#Drag a corner or apply an exact angle
Select Review request, then move near a corner to expose its rotation affordance. Drag it to rotate. Enter 30 and choose Apply angle to set the node's local rotation to exactly 30 degrees. Watch the attached connector follow the geometry.
The public rotate command takes a relative angle, so an exact-angle control computes the difference from the current authored rotation:
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();
const result = session.value.edit({ kind: 'rotate', degrees: desiredAngle - node.rotation }, [node.id]);Angles are in degrees and must be finite. Positive angles turn clockwise in the
canvas's downward-positive Y coordinate system. For a nested node this exact
value is its local authored rotation, not its total inherited world orientation.
Inspect result.status and result.diagnostics; a rejected command leaves the
committed geometry unchanged.
The viewport provides four corner resize handles and corner rotation candidates. It shows one rotation affordance at a time: the hovered or active corner, with the southeast corner as its default. Rotation affordances sit 20 CSS pixels outward from their corner along the line from the selection center. Available handles follow the current edit policy and supported geometry.
Hold Shift during pointer rotation to snap the gesture's relative angle to 15-degree steps. Tab to an available rotation handle and press Enter or Space to rotate by 15 degrees. The numeric control supplies an exact angle and supports negative values without requiring pointer precision.
#Rotate several nodes around a shared center
Choose Select both cards, then rotate by a corner or Rotate selection 15°. Their positions turn around the selection center and their local rotations advance together. A connector follows its endpoints without an independent node transform.
import { useDiagramContext } from '@primeui/vue-diagram';
const { session } = useDiagramContext();
session.value.select(['first', 'second']);
const result = session.value.edit({ kind: 'rotate', degrees: 15 });With no ID argument the command uses the current selection. With no center,
the pivot is the center of the selected node roots' combined world bounds.
An application may supply a finite world-space center: { x, y } explicitly.
The default is a bounds center, not an average of node centers. Selected
connectors do not supply independently rotated bodies.
Multi-selection rotation affordances use the combined world-bounds frame; a single node's handles follow its transformed corners. The active rotation handle stays the gesture's owner as geometry changes. The copied selection-action row in the first example delegates its position and hiding to the runtime, so it disappears during pointer rotation and returns when the gesture finishes or cancels.
#Rotate a parent without rotating its child twice
Select the parent and child together, rotate, and compare their saved local angles. The parent advances; the child keeps its local angle and follows the parent in world space. Select the child alone to rotate it within that owner.
The editor deduplicates IDs and normalizes selected node roots. A selected ancestor absorbs selected descendants. Affected descendants still participate in policy and structure checks: a descendant lock can reject the whole operation. Layout-owned lane records and attached geometry cannot be rotated independently; use their owning workflows. This example uses ordinary containers and fixed-size nodes, not unsupported lane or notation transforms.
#Cancel, undo and permissions
Press Escape while dragging to discard the preview without adding a history entry. Pointer cancellation, lost capture and window blur also cancel unfinished gestures. An accepted release commits one Undo operation. Choose Undo to restore both positions and rotations; current permissions still apply to replay.
editingOptions defaults to {}. Root read-only, rotate: false, a locked
record, ancestor or layer, invalid geometry, a pending proposal or another preview
owner can make rotation unavailable. Ask session.availability(command, ids) for
the current reason instead of inferring it from selection count. Rotating several
records succeeds atomically or is rejected as a whole.
Reset restores the original model, angle field, history, selection and initial fitted view. Native numeric inputs keep their keyboard behavior. Mouse and keyboard are the authored paths; touch, pen and assistive-technology behavior still require device-specific verification.