965 flow control arrow menu 1/3 add insert step button (#12519)

Add insert step button to workflow edges



https://github.com/user-attachments/assets/7144f722-f1c7-450f-a8eb-c902071986a1



Also fixes `iconButtonGroup` UI component

## Before


https://github.com/user-attachments/assets/7b5f0245-d0e8-48af-9aa5-a29388a1caea


## After



https://github.com/user-attachments/assets/1820874f-aa99-41ae-8254-c76c275ee3ae
This commit is contained in:
martmull
2025-06-12 14:14:21 +02:00
committed by GitHub
parent a189f15313
commit cf01faf276
31 changed files with 755 additions and 291 deletions
@@ -68,7 +68,10 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
}
if (isCreateStepNode(selectedNode)) {
startNodeCreation(selectedNode.data.parentNodeId);
startNodeCreation({
parentStepId: selectedNode.data.parentNodeId,
nextStepId: undefined,
});
return;
}
@@ -1,18 +1,23 @@
import { useTheme } from '@emotion/react';
import { BaseEdge, EdgeProps, getStraightPath } from '@xyflow/react';
import { CREATE_STEP_NODE_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth';
import { WorkflowDiagramEdgeOptions } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeOptions';
import { WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
type WorkflowDiagramDefaultEdgeProps = EdgeProps;
type WorkflowDiagramDefaultEdgeProps = EdgeProps<WorkflowDiagramEdge>;
export const WorkflowDiagramDefaultEdge = ({
source,
target,
sourceY,
targetY,
markerStart,
markerEnd,
data,
}: WorkflowDiagramDefaultEdgeProps) => {
const theme = useTheme();
const [edgePath] = getStraightPath({
const [edgePath, labelX, labelY] = getStraightPath({
sourceX: CREATE_STEP_NODE_WIDTH,
sourceY,
targetX: CREATE_STEP_NODE_WIDTH,
@@ -20,11 +25,21 @@ export const WorkflowDiagramDefaultEdge = ({
});
return (
<BaseEdge
markerStart={markerStart}
markerEnd={markerEnd}
path={edgePath}
style={{ stroke: theme.border.color.strong }}
/>
<>
<BaseEdge
markerStart={markerStart}
markerEnd={markerEnd}
path={edgePath}
style={{ stroke: theme.border.color.strong }}
/>
{data?.shouldDisplayEdgeOptions && (
<WorkflowDiagramEdgeOptions
labelX={labelX}
labelY={labelY}
parentStepId={source}
nextStepId={target}
/>
)}
</>
);
};
@@ -0,0 +1,56 @@
import { EdgeLabelRenderer } from '@xyflow/react';
import { STEP_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth';
import styled from '@emotion/styled';
import { IconButtonGroup } from 'twenty-ui/input';
import { IconPlus } from 'twenty-ui/display';
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
import { isDefined } from 'twenty-shared/utils';
const EDGE_OPTION_BUTTON_LEFT_MARGIN = 8;
const StyledIconButtonGroup = styled(IconButtonGroup)`
pointer-events: all;
`;
const StyledContainer = styled.div<{
labelX?: number;
labelY?: number;
}>`
position: absolute;
transform: ${({ labelX, labelY }) =>
`translate(${labelX || 0}px, ${isDefined(labelY) ? labelY - STEP_ICON_WIDTH / 2 : 0}px) translateX(${EDGE_OPTION_BUTTON_LEFT_MARGIN}px)`};
`;
type WorkflowDiagramEdgeOptionsProps = {
labelX?: number;
labelY?: number;
parentStepId: string;
nextStepId: string;
};
export const WorkflowDiagramEdgeOptions = ({
labelX,
labelY,
parentStepId,
nextStepId,
}: WorkflowDiagramEdgeOptionsProps) => {
const { startNodeCreation } = useStartNodeCreation();
return (
<EdgeLabelRenderer>
<StyledContainer labelX={labelX} labelY={labelY}>
<StyledIconButtonGroup
className="nodrag nopan"
iconButtons={[
{
Icon: IconPlus,
onClick: () => {
startNodeCreation({ parentStepId, nextStepId });
},
},
]}
/>
</StyledContainer>
</EdgeLabelRenderer>
);
};
@@ -16,6 +16,7 @@ import { mergeWorkflowDiagrams } from '@/workflow/workflow-diagram/utils/mergeWo
import { useEffect } from 'react';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { addEdgeOptions } from '@/workflow/workflow-diagram/utils/addEdgeOptions';
export const WorkflowDiagramEffect = ({
workflowWithCurrentVersion,
@@ -44,10 +45,11 @@ export const WorkflowDiagramEffect = ({
);
const nextWorkflowDiagram = addCreateStepNodes(
getWorkflowVersionDiagram(currentVersion),
addEdgeOptions(getWorkflowVersionDiagram(currentVersion)),
);
let mergedWorkflowDiagram = nextWorkflowDiagram;
if (isDefined(previousWorkflowDiagram)) {
mergedWorkflowDiagram = mergeWorkflowDiagrams(
previousWorkflowDiagram,
@@ -59,6 +61,7 @@ export const WorkflowDiagramEffect = ({
snapshot,
workflowLastCreatedStepIdState,
);
if (isDefined(lastCreatedStepId)) {
mergedWorkflowDiagram.nodes = mergedWorkflowDiagram.nodes.map(
(node) => {
@@ -79,6 +82,7 @@ export const WorkflowDiagramEffect = ({
);
const currentVersion = workflowWithCurrentVersion?.currentVersion;
useEffect(() => {
if (!isDefined(currentVersion)) {
setFlow(undefined);