From 0f347e2f4278b1890a6638de6e3f06a6db5c7caa Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 26 Aug 2025 16:33:39 +0200 Subject: [PATCH] Fix connection design (#14070) - refactor edges related files into sub-module - remove top handle from workflow, workflowVersion and workflowRun - add a custom design for connection (edge being created) - connect when hovering a connectable node - fix marker z-index - refactor Handles ## Before https://github.com/user-attachments/assets/013dec36-5cc7-4561-938c-404511118578 image ## After https://github.com/user-attachments/assets/a5b32cbf-bfbd-48e6-9889-037234adf38f image --- .../components/CommandMenuOpenContainer.tsx | 2 +- .../components/WorkflowDiagramCanvasBase.tsx | 7 +- .../WorkflowDiagramCanvasEditable.tsx | 7 +- .../WorkflowDiagramCanvasEditableEffect.tsx | 2 +- .../WorkflowDiagramCanvasReadonly.tsx | 4 +- .../components/WorkflowRunDiagramCanvas.tsx | 4 +- .../constants/EdgeBranchArrowMarker.ts | 5 - .../workflow-diagram/hooks/useEdgeState.ts | 132 ------------------ .../generateWorkflowRunDiagram.test.ts | 14 ++ .../getWorkflowVersionDiagram.test.ts | 1 + .../utils/generateWorkflowDiagram.ts | 2 +- .../components/WorkflowDiagramBaseEdge.tsx | 2 +- .../components/WorkflowDiagramBlankEdge.tsx | 0 .../components/WorkflowDiagramConnection.tsx | 33 +++++ .../WorkflowDiagramCustomMarkers.tsx | 8 +- .../WorkflowDiagramDefaultEdgeEditable.tsx | 12 +- .../WorkflowDiagramDefaultEdgeReadonly.tsx | 0 .../WorkflowDiagramDefaultEdgeRun.tsx | 2 +- .../WorkflowDiagramEdgeButtonGroup.tsx | 0 .../WorkflowDiagramEdgeV2Container.tsx | 0 ...rkflowDiagramEdgeV2VisibilityContainer.tsx | 0 .../WorkflowDiagramFilterEdgeEditable.tsx | 12 +- .../WorkflowDiagramFilterEdgeReadonly.tsx | 6 +- .../WorkflowDiagramFilterEdgeRun.tsx | 8 +- .../components/WorkflowRunDiagramBaseEdge.tsx | 0 .../constants/EdgeBranchArrowMarker.ts | 23 +++ ...orkflowDiagramEdgeOptionsClickOutsideId.ts | 0 ...kflowVisualizerEdgeDefaultConfiguration.ts | 4 +- .../workflow-edges/hooks/useEdgeState.ts | 117 ++++++++++++++++ .../workflowHoveredEdgeComponentState.ts | 2 +- .../workflowSelectedEdgeComponentState.ts | 2 +- .../types/WorkflowDiagramEdge.ts | 0 .../WorkflowDiagramHandleReadonly.tsx | 53 ------- ...le.tsx => WorkflowDiagramHandleSource.tsx} | 52 +++---- .../WorkflowDiagramHandleTarget.tsx | 36 +++++ ...WorkflowDiagramStepNodeEditableContent.tsx | 51 +++---- .../WorkflowDiagramStepNodeReadonly.tsx | 19 +-- .../components/WorkflowNodeContainer.tsx | 1 + .../components/WorkflowRunDiagramStepNode.tsx | 19 +-- .../hooks/useConnectionState.ts | 36 +++++ .../workflow-steps/hooks/useCreateEdge.ts | 2 +- .../workflow-steps/hooks/useDeleteEdge.ts | 2 +- 42 files changed, 359 insertions(+), 323 deletions(-) delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/constants/EdgeBranchArrowMarker.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useEdgeState.ts rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramBaseEdge.tsx (91%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramBlankEdge.tsx (100%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramConnection.tsx rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramCustomMarkers.tsx (84%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramDefaultEdgeEditable.tsx (91%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramDefaultEdgeReadonly.tsx (100%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramDefaultEdgeRun.tsx (91%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramEdgeButtonGroup.tsx (100%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramEdgeV2Container.tsx (100%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramEdgeV2VisibilityContainer.tsx (100%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{workflow-nodes => workflow-edges}/components/WorkflowDiagramFilterEdgeEditable.tsx (95%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{workflow-nodes => workflow-edges}/components/WorkflowDiagramFilterEdgeReadonly.tsx (94%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowDiagramFilterEdgeRun.tsx (93%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/components/WorkflowRunDiagramBaseEdge.tsx (100%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker.ts rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/constants/WorkflowDiagramEdgeOptionsClickOutsideId.ts (100%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts (72%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState.ts rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/states/workflowHoveredEdgeComponentState.ts (81%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/states/workflowSelectedEdgeComponentState.ts (92%) rename packages/twenty-front/src/modules/workflow/workflow-diagram/{ => workflow-edges}/types/WorkflowDiagramEdge.ts (100%) delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly.tsx rename packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/{WorkflowDiagramHandleEditable.tsx => WorkflowDiagramHandleSource.tsx} (61%) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget.tsx create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState.ts diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx index d5e8852b96..f74423f3f0 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx @@ -10,7 +10,7 @@ import { PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/pa import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { WORKFLOW_DIAGRAM_CREATE_STEP_NODE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramCreateStepNodeClickOutsideId'; -import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramStepNodeClickOutsideId'; import { useTheme } from '@emotion/react'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx index 79fc8d17cd..07ed27be44 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx @@ -5,7 +5,7 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component- import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { WorkflowDiagramCustomMarkers } from '@/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers'; +import { WorkflowDiagramCustomMarkers } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramCustomMarkers'; import { WorkflowDiagramRightClickCommandMenu } from '@/workflow/workflow-diagram/components/WorkflowDiagramRightClickCommandMenu'; import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightDrawerState'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; @@ -54,7 +54,8 @@ import { isDefined } from 'twenty-shared/utils'; import { Tag, type TagColor } from 'twenty-ui/components'; import { THEME_COMMON } from 'twenty-ui/theme'; import { FeatureFlagKey } from '~/generated/graphql'; -import { useEdgeState } from '@/workflow/workflow-diagram/hooks/useEdgeState'; +import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; +import { WorkflowDiagramConnection } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramConnection'; const StyledResetReactflowStyles = styled.div` height: 100%; @@ -496,6 +497,8 @@ export const WorkflowDiagramCanvasBase = ({ nodesConnectable={isWorkflowBranchEnabled ? nodesConnectable : false} paneClickDistance={10} // Fix small unwanted user dragging does not select node preventScrolling={false} + connectionLineComponent={WorkflowDiagramConnection} + connectionRadius={0} > diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx index 184db42988..66e04b7ad9 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx @@ -2,11 +2,10 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; -import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBlankEdge'; +import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBlankEdge'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; import { WorkflowDiagramCanvasEditableEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect'; -import { WorkflowDiagramDefaultEdgeEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable'; - +import { WorkflowDiagramDefaultEdgeEditable } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowDiagramRightClickMenuPositionState } from '@/workflow/workflow-diagram/states/workflowDiagramRightClickMenuPositionState'; import { @@ -15,7 +14,7 @@ import { } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { getWorkflowVersionStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowVersionStatusTagProps'; import { WorkflowDiagramEmptyTriggerEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable'; -import { WorkflowDiagramFilterEdgeEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeEditable'; +import { WorkflowDiagramFilterEdgeEditable } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable'; import { WorkflowDiagramStepNodeEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable'; import { useCreateEdge } from '@/workflow/workflow-steps/hooks/useCreateEdge'; import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect.tsx index 25759528e6..47cc59f2d7 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect.tsx @@ -1,4 +1,4 @@ -import { useEdgeState } from '@/workflow/workflow-diagram/hooks/useEdgeState'; +import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { type OnSelectionChangeParams, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx index 0357dca8b5..682179c74b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx @@ -1,10 +1,10 @@ import { type WorkflowVersionStatus } from '@/workflow/types/Workflow'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; -import { WorkflowDiagramDefaultEdgeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeReadonly'; +import { WorkflowDiagramDefaultEdgeReadonly } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeReadonly'; import { getWorkflowVersionStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowVersionStatusTagProps'; import { WorkflowDiagramEmptyTriggerReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly'; -import { WorkflowDiagramFilterEdgeReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeReadonly'; +import { WorkflowDiagramFilterEdgeReadonly } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly'; import { WorkflowDiagramStepNodeReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly'; import { ReactFlowProvider } from '@xyflow/react'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx index d75ce012bd..86cd797800 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx @@ -1,7 +1,7 @@ import { type WorkflowRunStatus } from '@/workflow/types/Workflow'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; -import { WorkflowDiagramDefaultEdgeRun } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeRun'; -import { WorkflowDiagramFilterEdgeRun } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun'; +import { WorkflowDiagramDefaultEdgeRun } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeRun'; +import { WorkflowDiagramFilterEdgeRun } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun'; import { getWorkflowRunStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowRunStatusTagProps'; import { WorkflowRunDiagramStepNode } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/EdgeBranchArrowMarker.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/EdgeBranchArrowMarker.ts deleted file mode 100644 index e816638ea3..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/EdgeBranchArrowMarker.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum EdgeBranchArrowMarker { - Default = 'edge-branch-arrow-default', - Hover = 'edge-branch-arrow-hover', - Selected = 'edge-branch-arrow-selected', -} diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useEdgeState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useEdgeState.ts deleted file mode 100644 index 8eb326ea5c..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useEdgeState.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { workflowSelectedEdgeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedEdgeComponentState'; -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; -import { type WorkflowDiagramNodeHandles } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeHandles'; -import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; -import { workflowHoveredEdgeComponentState } from '@/workflow/workflow-diagram/states/workflowHoveredEdgeComponentState'; -import { useReactFlow } from '@xyflow/react'; -import { EdgeBranchArrowMarker } from '@/workflow/workflow-diagram/constants/EdgeBranchArrowMarker'; - -export const useEdgeState = () => { - const reactflow = useReactFlow(); - - const [workflowSelectedEdge, setWorkflowSelectedEdge] = - useRecoilComponentState(workflowSelectedEdgeComponentState); - - const [workflowHoveredEdge, setWorkflowHoveredEdge] = useRecoilComponentState( - workflowHoveredEdgeComponentState, - ); - - const isEdgeSelected = ({ source, target }: WorkflowDiagramEdge) => { - return ( - workflowSelectedEdge?.source === source && - workflowSelectedEdge?.target === target - ); - }; - - const isEdgeHovered = ({ source, target }: WorkflowDiagramEdge) => { - return ( - workflowHoveredEdge?.source === source && - workflowHoveredEdge?.target === target - ); - }; - - const getNodeHandlesSelectedState = ( - id: string, - ): WorkflowDiagramNodeHandles => { - return { - targetHandle: workflowSelectedEdge?.target === id, - sourceHandle: workflowSelectedEdge?.source === id, - }; - }; - - const getNodeHandlesHoveredState = ( - id: string, - ): WorkflowDiagramNodeHandles => { - return { - targetHandle: workflowHoveredEdge?.target === id, - sourceHandle: workflowHoveredEdge?.source === id, - }; - }; - - const setEdgeSelected = ({ source, target }: WorkflowDiagramEdge) => { - if ( - workflowSelectedEdge?.source === source && - workflowSelectedEdge?.target === target - ) { - return; - } - - setWorkflowSelectedEdge({ source, target }); - - reactflow.setEdges((edges) => - edges.map((edge) => ({ - ...edge, - markerEnd: - edge.source === source && edge.target === target - ? EdgeBranchArrowMarker.Selected - : EdgeBranchArrowMarker.Default, - })), - ); - }; - - const setEdgeHovered = ({ source, target }: WorkflowDiagramEdge) => { - if ( - workflowHoveredEdge?.source === source && - workflowHoveredEdge?.target === target - ) { - return; - } - - setWorkflowHoveredEdge({ source, target }); - - reactflow.setEdges((edges) => - edges.map((edge) => - edge.source === source && - edge.target === target && - edge.markerEnd !== EdgeBranchArrowMarker.Selected - ? { - ...edge, - markerEnd: EdgeBranchArrowMarker.Hover, - } - : edge, - ), - ); - }; - - const clearEdgeSelected = () => { - setWorkflowSelectedEdge(undefined); - - reactflow.setEdges((edges) => - edges.map((edge) => ({ - ...edge, - markerEnd: EdgeBranchArrowMarker.Default, - })), - ); - }; - - const clearEdgeHover = () => { - setWorkflowHoveredEdge(undefined); - - reactflow.setEdges((edges) => - edges.map((edge) => - edge.markerEnd === EdgeBranchArrowMarker.Hover - ? { - ...edge, - markerEnd: EdgeBranchArrowMarker.Default, - } - : edge, - ), - ); - }; - - return { - isEdgeSelected, - setEdgeSelected, - clearEdgeSelected, - getNodeHandlesSelectedState, - isEdgeHovered, - setEdgeHovered, - clearEdgeHover, - getNodeHandlesHoveredState, - }; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts index 84d841b121..6ab1ec3e46 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts @@ -125,6 +125,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "trigger", "target": "step1", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -139,6 +140,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step1", "target": "step2", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -153,6 +155,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step2", "target": "step3", "type": "empty-filter--run", + "zIndex": -2, }, ], "nodes": [ @@ -355,6 +358,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "trigger", "target": "step1", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -369,6 +373,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step1", "target": "step2", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -383,6 +388,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step2", "target": "step3", "type": "empty-filter--run", + "zIndex": -2, }, ], "nodes": [ @@ -585,6 +591,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "trigger", "target": "step1", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -599,6 +606,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step1", "target": "step2", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -613,6 +621,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step2", "target": "step3", "type": "empty-filter--run", + "zIndex": -2, }, ], "nodes": [ @@ -834,6 +843,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "trigger", "target": "step1", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -848,6 +858,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step1", "target": "step2", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -862,6 +873,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step2", "target": "step3", "type": "empty-filter--run", + "zIndex": -2, }, { "data": { @@ -876,6 +888,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "step3", "target": "step4", "type": "empty-filter--run", + "zIndex": -2, }, ], "nodes": [ @@ -1063,6 +1076,7 @@ describe('generateWorkflowRunDiagram', () => { "source": "trigger", "target": "step1", "type": "empty-filter--run", + "zIndex": -2, }, ], "nodes": [ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts index d31685087c..00377b368b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts @@ -163,6 +163,7 @@ describe('getWorkflowVersionDiagram', () => { "source": "trigger", "target": "step-1", "type": "empty-filter--editable", + "zIndex": -2, }, ], "nodes": [ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowDiagram.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowDiagram.ts index f430cd41b3..0604474f14 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowDiagram.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowDiagram.ts @@ -4,7 +4,7 @@ import { } from '@/workflow/types/Workflow'; import { FIRST_NODE_POSITION } from '@/workflow/workflow-diagram/constants/FirstNodePosition'; import { VERTICAL_DISTANCE_BETWEEN_TWO_NODES } from '@/workflow/workflow-diagram/constants/VerticalDistanceBetweenTwoNodes'; -import { WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION } from '@/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration'; +import { WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration'; import { type WorkflowDiagram, type WorkflowDiagramEdge, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBaseEdge.tsx similarity index 91% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBaseEdge.tsx index cfe4a51107..a88404e7bc 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBaseEdge.tsx @@ -1,7 +1,7 @@ import { useTheme } from '@emotion/react'; import { BaseEdge, type EdgeProps } from '@xyflow/react'; import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { useEdgeState } from '@/workflow/workflow-diagram/hooks/useEdgeState'; +import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; type WorkflowDiagramBaseEdgeProps = Pick< EdgeProps, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBlankEdge.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBlankEdge.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBlankEdge.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBlankEdge.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramConnection.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramConnection.tsx new file mode 100644 index 0000000000..9bca932bb6 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramConnection.tsx @@ -0,0 +1,33 @@ +import { + type ConnectionLineComponentProps, + getBezierPath, +} from '@xyflow/react'; +import { useTheme } from '@emotion/react'; +import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker'; + +type WorkflowDiagramConnectionProps = ConnectionLineComponentProps; + +export const WorkflowDiagramConnection = ({ + fromX, + fromY, + toX, + toY, +}: WorkflowDiagramConnectionProps) => { + const theme = useTheme(); + + const [path] = getBezierPath({ + sourceX: fromX, + sourceY: fromY + 4, + targetX: toX, + targetY: toY - 4, + }); + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramCustomMarkers.tsx similarity index 84% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramCustomMarkers.tsx index c9a3c0f1bf..31df069caf 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramCustomMarkers.tsx @@ -1,4 +1,4 @@ -import { EdgeBranchArrowMarker } from '@/workflow/workflow-diagram/constants/EdgeBranchArrowMarker'; +import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker'; import { useTheme } from '@emotion/react'; export const WorkflowDiagramCustomMarkers = () => { @@ -8,7 +8,7 @@ export const WorkflowDiagramCustomMarkers = () => { { { ; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeReadonly.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeReadonly.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeReadonly.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeRun.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeRun.tsx similarity index 91% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeRun.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeRun.tsx index 35c7db08db..0a65a8b56c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeRun.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeRun.tsx @@ -1,4 +1,4 @@ -import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge'; +import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowRunDiagramBaseEdge'; import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { type EdgeProps, getBezierPath } from '@xyflow/react'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx similarity index 95% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeEditable.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx index b73b2d3d1c..cbf66919f5 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx @@ -9,11 +9,12 @@ import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; -import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; -import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; -import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; -import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBaseEdge'; +import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup'; +import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container'; +import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer'; +import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; +import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState'; @@ -47,7 +48,6 @@ import { import { IconButtonGroup } from 'twenty-ui/input'; import { MenuItem } from 'twenty-ui/navigation'; import { FeatureFlagKey } from '~/generated/graphql'; -import { useEdgeState } from '@/workflow/workflow-diagram/hooks/useEdgeState'; type WorkflowDiagramFilterEdgeEditableProps = EdgeProps; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx similarity index 94% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeReadonly.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx index b89c3779f8..3d8c8c50c5 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramFilterEdgeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx @@ -1,9 +1,9 @@ import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; -import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; -import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container'; +import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer'; +import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { useOpenWorkflowViewFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx similarity index 93% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx index 55ed180d5c..717d86a3d4 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx @@ -1,8 +1,8 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; -import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; -import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge'; -import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container'; +import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer'; +import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowRunDiagramBaseEdge'; +import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { useOpenWorkflowRunFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowRunDiagramBaseEdge.tsx similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowRunDiagramBaseEdge.tsx diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker.ts new file mode 100644 index 0000000000..b13e03a898 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker.ts @@ -0,0 +1,23 @@ +enum EdgeBranchArrowState { + Default = 'Default', + Hover = 'Hover', + Selected = 'Selected', +} + +export const EDGE_BRANCH_ARROW_MARKER: Record< + EdgeBranchArrowState, + { markerEnd: string; zIndex: number } +> = { + [EdgeBranchArrowState.Default]: { + markerEnd: 'edge-branch-arrow-default', + zIndex: -2, + }, + [EdgeBranchArrowState.Hover]: { + markerEnd: 'edge-branch-arrow-hover', + zIndex: -1, + }, + [EdgeBranchArrowState.Selected]: { + markerEnd: 'edge-branch-arrow-selected', + zIndex: 0, + }, +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId.ts similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId.ts rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId.ts diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts similarity index 72% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts index c137477228..67b9ab2319 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts @@ -1,4 +1,4 @@ -import { EdgeBranchArrowMarker } from '@/workflow/workflow-diagram/constants/EdgeBranchArrowMarker'; +import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker'; import { type WorkflowDiagramEdge, type WorkflowDiagramEdgeType, @@ -7,7 +7,7 @@ import { export const WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION = { type: 'empty-filter--readonly' satisfies WorkflowDiagramEdgeType, markerStart: undefined, - markerEnd: EdgeBranchArrowMarker.Default, + ...EDGE_BRANCH_ARROW_MARKER.Default, deletable: false, selectable: false, data: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState.ts new file mode 100644 index 0000000000..15c61489d8 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState.ts @@ -0,0 +1,117 @@ +import { workflowSelectedEdgeComponentState } from '@/workflow/workflow-diagram/workflow-edges/states/workflowSelectedEdgeComponentState'; +import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge'; +import { workflowHoveredEdgeComponentState } from '@/workflow/workflow-diagram/workflow-edges/states/workflowHoveredEdgeComponentState'; +import { useReactFlow } from '@xyflow/react'; +import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker'; + +export const useEdgeState = () => { + const reactflow = useReactFlow(); + + const [workflowSelectedEdge, setWorkflowSelectedEdge] = + useRecoilComponentState(workflowSelectedEdgeComponentState); + + const [workflowHoveredEdge, setWorkflowHoveredEdge] = useRecoilComponentState( + workflowHoveredEdgeComponentState, + ); + + const isSourceSelected = (nodeId: string) => { + return workflowSelectedEdge?.source === nodeId; + }; + + const isSourceHovered = (nodeId: string) => { + return workflowHoveredEdge?.source === nodeId; + }; + + const isEdgeSelected = ({ source, target }: WorkflowDiagramEdge) => { + return isSourceSelected(source) && workflowSelectedEdge?.target === target; + }; + + const isEdgeHovered = ({ source, target }: WorkflowDiagramEdge) => { + return isSourceHovered(source) && workflowHoveredEdge?.target === target; + }; + + const setEdgeSelected = ({ source, target }: WorkflowDiagramEdge) => { + if (isEdgeSelected({ source, target })) { + return; + } + + setWorkflowSelectedEdge({ source, target }); + + reactflow.setEdges((edges) => + edges.map((edge) => { + if (edge.source === source && edge.target === target) { + return { + ...edge, + ...EDGE_BRANCH_ARROW_MARKER.Selected, + }; + } + + return { + ...edge, + ...EDGE_BRANCH_ARROW_MARKER.Default, + }; + }), + ); + }; + + const setEdgeHovered = ({ source, target }: WorkflowDiagramEdge) => { + setWorkflowHoveredEdge({ source, target }); + + reactflow.setEdges((edges) => + edges.map((edge) => { + if ( + edge.source === source && + edge.target === target && + edge.markerEnd !== EDGE_BRANCH_ARROW_MARKER.Selected.markerEnd + ) { + return { + ...edge, + ...EDGE_BRANCH_ARROW_MARKER.Hover, + }; + } + + return edge; + }), + ); + }; + + const clearEdgeSelected = () => { + setWorkflowSelectedEdge(undefined); + + reactflow.setEdges((edges) => + edges.map((edge) => ({ + ...edge, + ...EDGE_BRANCH_ARROW_MARKER.Default, + })), + ); + }; + + const clearEdgeHover = () => { + setWorkflowHoveredEdge(undefined); + + reactflow.setEdges((edges) => + edges.map((edge) => { + if (edge.markerEnd === EDGE_BRANCH_ARROW_MARKER.Hover.markerEnd) { + return { + ...edge, + ...EDGE_BRANCH_ARROW_MARKER.Default, + }; + } + + return edge; + }), + ); + }; + + return { + isEdgeSelected, + isSourceSelected, + setEdgeSelected, + clearEdgeSelected, + isEdgeHovered, + isSourceHovered, + setEdgeHovered, + clearEdgeHover, + }; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowHoveredEdgeComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowHoveredEdgeComponentState.ts similarity index 81% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowHoveredEdgeComponentState.ts rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowHoveredEdgeComponentState.ts index e9473b6444..36774acbe6 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowHoveredEdgeComponentState.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowHoveredEdgeComponentState.ts @@ -1,6 +1,6 @@ import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; -import type { WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge'; export const workflowHoveredEdgeComponentState = createComponentState< WorkflowDiagramEdge | undefined diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedEdgeComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowSelectedEdgeComponentState.ts similarity index 92% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedEdgeComponentState.ts rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowSelectedEdgeComponentState.ts index 711bb54927..a5940c5a65 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedEdgeComponentState.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/states/workflowSelectedEdgeComponentState.ts @@ -1,6 +1,6 @@ import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; -import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge'; export const workflowSelectedEdgeComponentState = createComponentState< WorkflowDiagramEdge | undefined diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramEdge.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge.ts similarity index 100% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramEdge.ts rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge.ts diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly.tsx deleted file mode 100644 index ded964f1a2..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { NODE_HANDLE_HEIGHT_PX } from '@/workflow/workflow-diagram/constants/NodeHandleHeightPx'; -import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/NodeHandleWidthPx'; -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; -import { Handle, type HandleProps } from '@xyflow/react'; - -type WorkflowDiagramHandleReadonlyProps = HandleProps & { - selected: boolean; -}; - -const StyledHandle = styled(Handle)<{ - selected: boolean; -}>` - &.react-flow__handle { - height: ${NODE_HANDLE_HEIGHT_PX}px; - width: ${NODE_HANDLE_WIDTH_PX}px; - border-color: ${({ theme, selected }) => - selected ? theme.color.blue : theme.border.color.strong}; - background: ${({ theme, selected }) => - selected ? theme.adaptiveColors.blue1 : theme.background.primary}; - transition: - transform 0.1s ease-out, - background 0.1s, - border-color 0.1s; - z-index: 1; - - ${({ position }) => { - if (position === 'top') { - return css` - transform: translate(-50%, -50%); - transform-origin: top left; - `; - } else if (position === 'bottom') { - return css` - transform: translate(-50%, 50%); - transform-origin: bottom left; - `; - } - }} - - &.connectionindicator { - cursor: default; - } - } -`; - -export const WorkflowDiagramHandleReadonly = ({ - type, - position, - selected, -}: WorkflowDiagramHandleReadonlyProps) => { - return ; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx similarity index 61% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleEditable.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx index 97dbf5735f..5222a41c9b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx @@ -3,12 +3,13 @@ import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/Node import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { css } from '@emotion/react'; import styled from '@emotion/styled'; -import { Handle, type HandleProps } from '@xyflow/react'; +import { Handle, Position, type HandleProps } from '@xyflow/react'; import { FeatureFlagKey } from '~/generated/graphql'; -type WorkflowDiagramHandleEditableProps = HandleProps & { +type WorkflowDiagramHandleSourceProps = { selected: boolean; hovered?: boolean; + readOnly?: boolean; }; const HANDLE_SCALE_ON_HOVER = 1.5; @@ -17,17 +18,19 @@ const StyledHandle = styled(Handle, { shouldForwardProp: (prop) => prop !== 'disableHoverEffect' && prop !== 'selected' && prop !== 'hovered', })<{ + type: HandleProps['type']; disableHoverEffect: boolean; selected: boolean; hovered?: boolean; }>` &.react-flow__handle { + opacity: ${({ type }) => (type === 'target' ? 0 : 1)}; height: ${NODE_HANDLE_HEIGHT_PX}px; width: ${NODE_HANDLE_WIDTH_PX}px; - border-color: ${({ theme, selected, hovered }) => + border-color: ${({ theme, selected, hovered, disableHoverEffect }) => selected ? theme.color.blue - : hovered + : hovered && !disableHoverEffect ? theme.font.color.light : theme.border.color.strong}; background: ${({ theme, selected }) => @@ -38,25 +41,14 @@ const StyledHandle = styled(Handle, { border-color 0.1s; z-index: 1; - ${({ position }) => { - if (position === 'top') { - return css` - transform: translate(-50%, -50%); - transform-origin: top left; - `; - } else if (position === 'bottom') { - return css` - transform: translate(-50%, 50%); - transform-origin: bottom left; - `; - } - }} + transform: translate(-50%, 50%); + transform-origin: bottom left; &.connectionindicator { cursor: pointer; } - ${({ disableHoverEffect, theme, position }) => { + ${({ disableHoverEffect, theme }) => { if (disableHoverEffect) { return undefined; } @@ -65,37 +57,27 @@ const StyledHandle = styled(Handle, { &:hover { background: ${theme.adaptiveColors.blue1} !important; border-color: ${theme.color.blue} !important; - - ${position === 'top' && - css` - transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(-50%, -50%); - `} - - ${position === 'bottom' && - css` - transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(-50%, 50%); - `} + transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(-50%, 50%); } `; }} } `; -export const WorkflowDiagramHandleEditable = ({ - type, - position, +export const WorkflowDiagramHandleSource = ({ selected, hovered = false, -}: WorkflowDiagramHandleEditableProps) => { + readOnly = false, +}: WorkflowDiagramHandleSourceProps) => { const isWorkflowBranchEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_WORKFLOW_BRANCH_ENABLED, ); return ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget.tsx new file mode 100644 index 0000000000..cdc88ba989 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget.tsx @@ -0,0 +1,36 @@ +import styled from '@emotion/styled'; +import { Handle, Position } from '@xyflow/react'; + +type WorkflowDiagramHandleTargetProps = { + isConnectable?: boolean; +}; + +const StyledHandle = styled(Handle)` + &.react-flow__handle { + opacity: 0; + z-index: 1; + border-radius: ${({ theme }) => theme.border.radius.md}; + width: 100%; + height: 100%; + left: 0; + top: 0; + transform: translate(-1px, -5px); + + &.connectionindicator { + cursor: pointer; + } + } +`; + +export const WorkflowDiagramHandleTarget = ({ + isConnectable = false, +}: WorkflowDiagramHandleTargetProps) => { + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx index 05b5b819eb..58ec629d9e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx @@ -1,10 +1,9 @@ import { WorkflowDiagramCreateStepElement } from '@/workflow/workflow-diagram/components/WorkflowDiagramCreateStepElement'; import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramStepNodeClickOutsideId'; -import { useEdgeState } from '@/workflow/workflow-diagram/hooks/useEdgeState'; +import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; -import { WorkflowDiagramHandleEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleEditable'; import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeIcon'; import { WorkflowNodeContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer'; import { WorkflowNodeIconContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeIconContainer'; @@ -13,11 +12,14 @@ import { WorkflowNodeLabelWithCounterPart } from '@/workflow/workflow-diagram/wo import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart'; import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle'; import styled from '@emotion/styled'; -import { Position } from '@xyflow/react'; import { useState } from 'react'; import { capitalize } from 'twenty-shared/utils'; import { IconTrash } from 'twenty-ui/display'; import { FloatingIconButton } from 'twenty-ui/input'; +import { useConnectionState } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState'; +import { css } from '@emotion/react'; +import { WorkflowDiagramHandleTarget } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget'; +import { WorkflowDiagramHandleSource } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource'; const StyledAddStepButtonContainer = styled.div<{ shouldDisplay: boolean; @@ -33,7 +35,9 @@ const StyledAddStepButtonContainer = styled.div<{ transform: translateX(-50%) translateY(100%); `; -const StyledNodeContainer = styled(WorkflowNodeContainer)` +const StyledNodeContainer = styled(WorkflowNodeContainer)<{ + isConnectable?: boolean; +}>` border-color: ${({ theme }) => theme.border.color.strong}; background: ${({ theme }) => theme.background.secondary}; @@ -44,6 +48,11 @@ const StyledNodeContainer = styled(WorkflowNodeContainer)` ${({ theme }) => theme.background.transparent.lighter} 100% ), ${({ theme }) => theme.background.secondary}; + ${({ theme, isConnectable }) => + isConnectable && + css` + border-color: ${theme.color.blue} !important; + `}; } .selected & { @@ -100,30 +109,21 @@ export const WorkflowDiagramStepNodeEditableContent = ({ const { isNodeCreationStarted } = useStartNodeCreation(); - const { getNodeHandlesSelectedState, getNodeHandlesHoveredState } = - useEdgeState(); + const { isConnectable, isSourceConnected, isInProgressConnection } = + useConnectionState(data.nodeType); - const handlesSelectedState = getNodeHandlesSelectedState(id); - - const handlesHoveredState = getNodeHandlesHoveredState(id); + const { isSourceSelected, isSourceHovered } = useEdgeState(); return ( <> - {data.nodeType !== 'trigger' && ( - - )} - + @@ -147,7 +147,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({ )} - {!data.hasNextStepIds && ( + {!data.hasNextStepIds && !isInProgressConnection && ( )} - ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly.tsx index 00d097ded4..689a0ccc46 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly.tsx @@ -9,7 +9,6 @@ import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/wor import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; -import { WorkflowDiagramHandleReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly'; import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeIcon'; import { WorkflowNodeContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer'; import { WorkflowNodeIconContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeIconContainer'; @@ -18,11 +17,12 @@ import { WorkflowNodeLabelWithCounterPart } from '@/workflow/workflow-diagram/wo import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart'; import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle'; import styled from '@emotion/styled'; -import { Position } from '@xyflow/react'; import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; import { capitalize, isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; +import { WorkflowDiagramHandleTarget } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget'; +import { WorkflowDiagramHandleSource } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource'; const StyledNodeContainer = styled(WorkflowNodeContainer)` border-color: ${({ theme }) => theme.border.color.strong}; @@ -109,18 +109,11 @@ export const WorkflowDiagramStepNodeReadonly = ({ return ( <> - {data.nodeType !== 'trigger' && ( - - )} - + @@ -134,11 +127,7 @@ export const WorkflowDiagramStepNodeReadonly = ({ - + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx index 259b2f02ee..cdde5ab0a0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx @@ -13,6 +13,7 @@ const StyledNodeContainer = styled.div` box-sizing: border-box; cursor: pointer; position: relative; + transition: border-color 0.1s; `; export { StyledNodeContainer as WorkflowNodeContainer }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx index ca6f2db2bd..2f21a4e0c4 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx @@ -9,7 +9,6 @@ import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/wor import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { type WorkflowRunDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; -import { WorkflowDiagramHandleReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleReadonly'; import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeIcon'; import { WorkflowNodeContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer'; import { WorkflowNodeIconContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeIconContainer'; @@ -19,13 +18,14 @@ import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-node import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { Position } from '@xyflow/react'; import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; import { capitalize, isDefined } from 'twenty-shared/utils'; import { StepStatus } from 'twenty-shared/workflow'; import { IconCheck, IconX, useIcons } from 'twenty-ui/display'; import { Loader } from 'twenty-ui/feedback'; +import { WorkflowDiagramHandleTarget } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget'; +import { WorkflowDiagramHandleSource } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource'; const StyledNodeContainer = styled(WorkflowNodeContainer)` border-color: ${({ theme }) => theme.border.color.strong}; @@ -177,19 +177,12 @@ export const WorkflowRunDiagramStepNode = ({ return ( <> - {data.nodeType !== 'trigger' && ( - - )} - + @@ -230,11 +223,7 @@ export const WorkflowRunDiagramStepNode = ({ - + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState.ts new file mode 100644 index 0000000000..1907b98e7a --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState.ts @@ -0,0 +1,36 @@ +import { useConnection, useEdges } from '@xyflow/react'; + +export const useConnectionState = (nodeType: 'action' | 'trigger') => { + const connection = useConnection(); + + const edges = useEdges(); + + const isInProgressConnection = connection.inProgress; + + const isConnectable = (nodeId: string) => { + if (nodeType === 'trigger') { + return false; + } + + if (!isInProgressConnection) { + return false; + } + + if (connection.fromNode.id === nodeId) { + return false; + } + + return !edges.some( + (edge) => + edge.target === nodeId && + connection.fromNode?.id && + edge.source === connection.fromNode.id, + ); + }; + + const isSourceConnected = (nodeId: string) => { + return connection.inProgress && connection.fromNode.id === nodeId; + }; + + return { isConnectable, isSourceConnected, isInProgressConnection }; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts index d7f44ab018..c03e5a1416 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts @@ -1,5 +1,5 @@ import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; -import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge'; import { useCreateWorkflowVersionEdge } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionEdge'; import { useState } from 'react'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts index 58cdf986a6..6f4a87cc99 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts @@ -1,5 +1,5 @@ import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; -import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdge'; import { useDeleteWorkflowVersionEdge } from '@/workflow/workflow-steps/hooks/useDeleteWorkflowVersionEdge'; import { useState } from 'react';