diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue index cd44068116400860068cb36271c3b70b23864a92..bea8182b5eca5410ec985e2f3ed5e2143c94ddb3 100644 --- a/src/features/ePocFlow/ePocFlow.vue +++ b/src/features/ePocFlow/ePocFlow.vue @@ -15,6 +15,7 @@ const { vueFlowRef, project, updateEdge, edges, nodes, findNode } = useVueFlow( const editorStore = useEditorStore(); const graphStore = useGraphStore(); +const undoRedoStore = useUndoRedoStore(); const nodeTypes = { activity: markRaw(ActivityNode), @@ -173,6 +174,10 @@ function nodeDrag(event) { @edgeclick="onEdgeclick" @pane-click="editorStore.closeFormPanel()" @connect="connect" + @node-drag-start="nodeDragStart" + @node-drag-stop="nodeDragStop" + @keydown.meta.z="undo" + @keydown.meta.y="redo" > <template #node-custom="{ id, data }"> <PageNode :id="id" :data="data" /> diff --git a/src/shared/interfaces/index.ts b/src/shared/interfaces/index.ts index 0ae5d48d5c6ad87d56612c0163be9d930277ad39..3e9e3cb65cc1e6d4d3255108ab4dc483022ec1e7 100644 --- a/src/shared/interfaces/index.ts +++ b/src/shared/interfaces/index.ts @@ -5,4 +5,5 @@ export * from './form.interface'; export * from './nodeElement.interface'; export * from './formButton.interface'; export * from './field.interface'; -export * from './pageModel.interface'; \ No newline at end of file +export * from './pageModel.interface'; +export * from './undoRedo.interface'; \ No newline at end of file diff --git a/src/shared/interfaces/undoRedo.interface.ts b/src/shared/interfaces/undoRedo.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..b35f079f771dd41f8eaeca3da61bcc0d0f73e8c3 --- /dev/null +++ b/src/shared/interfaces/undoRedo.interface.ts @@ -0,0 +1,43 @@ +export interface UndoRedoAction { + type: string; +} + +export interface NodeMovedAction extends UndoRedoAction { + type: 'nodeMoved'; + nodeId: string; + deltaMovement: { x: number; y: number }; +} + +//? Is saving an entire node in this situation a good idea? +export interface NodeAddedAction extends UndoRedoAction { + type: 'nodeAdded'; + //TODO: use the type defined by vue flow + node: any; + position: { x: number; y: number }; +} + +export interface NodeRemovedAction extends UndoRedoAction { + type: 'nodeRemoved'; + node: any; + position: { x: number; y: number } +} + +export interface NodeUpdatedAction extends UndoRedoAction { + type: 'nodeUpdated'; + node: any; +} + +export interface EdgeConnectedAction extends UndoRedoAction { + type: 'edgeConnected'; + edge: any; +} + +export interface EdgeUpdatedAction extends UndoRedoAction { + type: 'edgeUpdated'; + edge: any; +} + +export interface EdgeRemovedAction extends UndoRedoAction { + type: 'edgeRemoved'; + edge: any; +} \ No newline at end of file diff --git a/src/shared/stores/undoRedoStore.ts b/src/shared/stores/undoRedoStore.ts new file mode 100644 index 0000000000000000000000000000000000000000..c364b87e8c657ced8ef67dedfbf117004a8d6472 --- /dev/null +++ b/src/shared/stores/undoRedoStore.ts @@ -0,0 +1,94 @@ +import { defineStore } from 'pinia'; +import { UndoRedoAction, NodeMovedAction } from '../interfaces'; +import { useVueFlow } from '@vue-flow/core'; + +const { findNode } = useVueFlow({ id: 'main' }); + +interface UndoRedoState { + undoStack: UndoRedoAction[]; + redoStack: UndoRedoAction[]; +} + +export const useUndoRedoStore = defineStore('epoc', { + state: (): UndoRedoState => ({ + undoStack: [], + redoStack: [], + }), + + actions: { + undo(): void { + if(this.undoStack.length === 0) return; + + const action = this.undoStack.pop(); + this.executeAction(action, this.redoStack); + }, + redo(): void { + if(this.redoStack.length === 0) return; + + const action = this.redoStack.pop(); + this.executeAction(action, this.undoStack); + }, + addAction(action: UndoRedoAction): void { + this.undoStack.push(action); + this.redoStack = []; + }, + executeAction(action: UndoRedoAction, reverseStack: UndoRedoAction[]): void { + switch(action.type) { + case 'nodeMoved': + this.moveNode(action, reverseStack); + break; + case 'nodeRemoved': + this.deleteNode(action); + break; + case 'nodeAdded': + this.addNode(action); + break; + case 'nodeUpdated': + this.updateNode(action); + break; + case 'edgeConnected': + this.connectEdge(action); + break; + case 'edgeUpdated': + this.updateEdge(action); + break; + case 'edgeRemoved': + this.deleteEdge(action); + break; + } + }, + moveNode(action: NodeMovedAction, reverseStack: UndoRedoAction[]): void { + const node = findNode(action.nodeId); + node.position.x -= action.deltaMovement.x; + node.position.y -= action.deltaMovement.y; + + const reverseAction: NodeMovedAction = { + type: 'nodeMoved', + nodeId: action.nodeId, + deltaMovement: { + x: -action.deltaMovement.x, + y: -action.deltaMovement.y + } + }; + reverseStack.push(reverseAction); + }, + deleteNode() { + return; + }, + addNode() { + return; + }, + updateNode() { + return; + }, + connectEdge() { + return; + }, + updateEdge() { + return; + }, + deleteEdge() { + return; + } + } +}); \ No newline at end of file