feat: Add ability to drag arrow tip to another node (#14657)
Allow users to drag the target of edges when editing workflows. --------- Co-authored-by: Devessier <baptiste@devessier.fr>
This commit is contained in:
+10
@@ -41,6 +41,7 @@ import {
|
||||
type OnBeforeDelete,
|
||||
type OnDelete,
|
||||
type OnNodeDrag,
|
||||
type OnReconnect,
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import React, {
|
||||
@@ -102,6 +103,9 @@ export const WorkflowDiagramCanvasBase = ({
|
||||
onConnect,
|
||||
onDeleteEdge,
|
||||
onNodeDragStop,
|
||||
onReconnect,
|
||||
onReconnectStart,
|
||||
onReconnectEnd,
|
||||
handlePaneContextMenu,
|
||||
nodesConnectable = false,
|
||||
nodesDraggable = false,
|
||||
@@ -136,6 +140,9 @@ export const WorkflowDiagramCanvasBase = ({
|
||||
onConnect?: (params: WorkflowConnection) => void;
|
||||
onDeleteEdge?: (edge: WorkflowDiagramEdge) => void;
|
||||
onNodeDragStop?: OnNodeDrag<WorkflowDiagramNode>;
|
||||
onReconnect?: OnReconnect;
|
||||
onReconnectStart?: () => void;
|
||||
onReconnectEnd?: () => void;
|
||||
nodesConnectable?: boolean;
|
||||
nodesDraggable?: boolean;
|
||||
handlePaneContextMenu?: ({
|
||||
@@ -476,6 +483,9 @@ export const WorkflowDiagramCanvasBase = ({
|
||||
onNodesChange={handleNodesChanges}
|
||||
onEdgesChange={handleEdgesChange}
|
||||
onConnect={handleConnect}
|
||||
onReconnect={onReconnect}
|
||||
onReconnectStart={onReconnectStart}
|
||||
onReconnectEnd={onReconnectEnd}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onBeforeDelete={onBeforeDelete}
|
||||
onDelete={onDelete}
|
||||
|
||||
+34
-2
@@ -11,6 +11,8 @@ import {
|
||||
type WorkflowDiagramEdge,
|
||||
type WorkflowDiagramNode,
|
||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { assertEdgeHasDefinedHandlesOrThrow } from '@/workflow/workflow-diagram/utils/assertEdgeHasDefinedHandlesOrThrow';
|
||||
import { assertWorkflowConnectionOrThrow } from '@/workflow/workflow-diagram/utils/assertWorkflowConnectionOrThrow';
|
||||
import { getWorkflowVersionStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowVersionStatusTagProps';
|
||||
import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBlankEdge';
|
||||
import { WorkflowDiagramDefaultEdgeEditable } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable';
|
||||
@@ -21,8 +23,13 @@ import { useCreateEdge } from '@/workflow/workflow-steps/hooks/useCreateEdge';
|
||||
import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge';
|
||||
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
|
||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger';
|
||||
import { addEdge, ReactFlowProvider } from '@xyflow/react';
|
||||
import React from 'react';
|
||||
import {
|
||||
addEdge,
|
||||
ReactFlowProvider,
|
||||
type Connection,
|
||||
type Edge,
|
||||
} from '@xyflow/react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const WorkflowDiagramCanvasEditable = () => {
|
||||
@@ -73,6 +80,30 @@ export const WorkflowDiagramCanvasEditable = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleReconnect = useCallback(
|
||||
async (oldEdge: Edge, connection: Connection) => {
|
||||
assertEdgeHasDefinedHandlesOrThrow(oldEdge);
|
||||
assertWorkflowConnectionOrThrow(connection);
|
||||
|
||||
await deleteEdge({
|
||||
source: oldEdge.source,
|
||||
target: oldEdge.target,
|
||||
sourceConnectionOptions: getConnectionOptionsForSourceHandle({
|
||||
sourceHandleId: oldEdge.sourceHandle,
|
||||
}),
|
||||
});
|
||||
|
||||
await createEdge({
|
||||
source: connection.source,
|
||||
target: connection.target,
|
||||
connectionOptions: getConnectionOptionsForSourceHandle({
|
||||
sourceHandleId: connection.sourceHandle,
|
||||
}),
|
||||
});
|
||||
},
|
||||
[deleteEdge, createEdge],
|
||||
);
|
||||
|
||||
const onDeleteEdge = async (edge: WorkflowDiagramEdge) => {
|
||||
await deleteEdge({
|
||||
source: edge.source,
|
||||
@@ -140,6 +171,7 @@ export const WorkflowDiagramCanvasEditable = () => {
|
||||
tagColor={tagProps.color}
|
||||
tagText={tagProps.text}
|
||||
onConnect={onConnect}
|
||||
onReconnect={handleReconnect}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
handlePaneContextMenu={handlePaneContextMenu}
|
||||
nodesConnectable
|
||||
|
||||
+1
@@ -159,6 +159,7 @@ describe('getWorkflowVersionDiagram', () => {
|
||||
"id": "8f3b2121-f194-4ba4-9fbf-0",
|
||||
"markerEnd": "edge-branch-arrow-default",
|
||||
"markerStart": undefined,
|
||||
"reconnectable": "target",
|
||||
"selectable": true,
|
||||
"source": "trigger",
|
||||
"sourceHandle": "default",
|
||||
|
||||
+6
-2
@@ -79,8 +79,12 @@ export const generateNodesAndEdgesForDefaultNode = ({
|
||||
nextStepId,
|
||||
}),
|
||||
},
|
||||
...(edgeTypeBetweenTwoNodes.includes('editable')
|
||||
? { deletable: true, selectable: true }
|
||||
...(workflowContext === 'workflow'
|
||||
? {
|
||||
deletable: true,
|
||||
selectable: true,
|
||||
reconnectable: 'target',
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
|
||||
+12
-4
@@ -91,8 +91,12 @@ export const generateNodesAndEdgesForIteratorNode = ({
|
||||
source: step.id,
|
||||
sourceHandle: WORKFLOW_DIAGRAM_ITERATOR_NODE_LOOP_HANDLE_ID,
|
||||
target: initialLoopStepId,
|
||||
...(edgeTypeBetweenTwoNodes.includes('editable')
|
||||
? { deletable: true, selectable: true }
|
||||
...(workflowContext === 'workflow'
|
||||
? {
|
||||
deletable: true,
|
||||
selectable: true,
|
||||
reconnectable: 'target',
|
||||
}
|
||||
: {}),
|
||||
targetHandle: WORKFLOW_DIAGRAM_NODE_DEFAULT_TARGET_HANDLE_ID,
|
||||
data: {
|
||||
@@ -115,8 +119,12 @@ export const generateNodesAndEdgesForIteratorNode = ({
|
||||
source: step.id,
|
||||
sourceHandle: WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID,
|
||||
target: nextStepId,
|
||||
...(edgeTypeBetweenTwoNodes.includes('editable')
|
||||
? { deletable: true, selectable: true }
|
||||
...(workflowContext === 'workflow'
|
||||
? {
|
||||
deletable: true,
|
||||
selectable: true,
|
||||
reconnectable: 'target',
|
||||
}
|
||||
: {}),
|
||||
targetHandle: WORKFLOW_DIAGRAM_NODE_DEFAULT_TARGET_HANDLE_ID,
|
||||
data: {
|
||||
|
||||
+6
-2
@@ -52,8 +52,12 @@ export const generateWorkflowDiagram = ({
|
||||
source: TRIGGER_STEP_ID,
|
||||
sourceHandle: WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID,
|
||||
target: stepLinkToTriggerId,
|
||||
...(edgeTypeBetweenTwoNodes.includes('editable')
|
||||
? { deletable: true, selectable: true }
|
||||
...(workflowContext === 'workflow'
|
||||
? {
|
||||
deletable: true,
|
||||
selectable: true,
|
||||
reconnectable: 'target',
|
||||
}
|
||||
: {}),
|
||||
targetHandle: WORKFLOW_DIAGRAM_NODE_DEFAULT_TARGET_HANDLE_ID,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user