diff --git a/packages/editor/src/editor/Canvas.tsx b/packages/editor/src/editor/Canvas.tsx index b728169..01cc151 100644 --- a/packages/editor/src/editor/Canvas.tsx +++ b/packages/editor/src/editor/Canvas.tsx @@ -436,6 +436,18 @@ export function Canvas(props: CanvasProps) { return; } + // If the node is already in the current selection, keep the selection as-is. + // This matches goView-ish multi-selection behavior (drag moves the whole selection). + if (props.selectionIds.includes(node.id)) { + if (node.locked) { + // Locked nodes are selectable, but should not start a move drag. + props.onSelectSingle(node.id); + return; + } + props.onBeginMove(e); + return; + } + // Locked nodes should still be selectable, but not movable. if (node.locked) { props.onSelectSingle(node.id); diff --git a/packages/editor/src/editor/store.ts b/packages/editor/src/editor/store.ts index 3c30ee2..93d95b9 100644 --- a/packages/editor/src/editor/store.ts +++ b/packages/editor/src/editor/store.ts @@ -584,12 +584,18 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS snapshot: new Map(), }; + // Only snapshot movable nodes. + // Locked/hidden nodes can remain selected (for context-menu ops), but they should not move. for (const id of state.selection.ids) { const node = state.doc.screen.nodes.find((n) => n.id === id); if (!node) continue; + if (node.locked || node.hidden) continue; drag.snapshot.set(id, { ...node.rect }); } + // Nothing movable selected → no-op (avoid entering a dragging state). + if (!drag.snapshot.size) return state; + return { ...state, canvas: { @@ -630,6 +636,8 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS const nodes = state.doc.screen.nodes.map((n) => { if (!state.selection.ids.includes(n.id)) return n; + // Locked/hidden nodes should not move, even if selected. + if (n.locked || n.hidden) return n; const snap0 = drag.snapshot.get(n.id); if (!snap0) return n;