Improve workflow arrows' design (#9619)
Old design:  New design: 
This commit is contained in:
committed by
GitHub
parent
f2bee55e6c
commit
c543a930cd
@ -48,7 +48,7 @@ const StyledStepNodeInnerContainer = styled.div<{ variant?: Variant }>`
|
|||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: ${({ variant, theme }) =>
|
box-shadow: ${({ variant, theme }) =>
|
||||||
variant === 'placeholder' ? 'none' : theme.boxShadow.superHeavy};
|
variant === 'placeholder' ? 'none' : theme.boxShadow.strong};
|
||||||
|
|
||||||
.selectable.selected &,
|
.selectable.selected &,
|
||||||
.selectable:focus &,
|
.selectable:focus &,
|
||||||
@ -63,7 +63,7 @@ const StyledStepNodeLabel = styled.div<{ variant?: Variant }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-size: ${({ theme }) => theme.font.size.lg};
|
font-size: ${({ theme }) => theme.font.size.lg};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
column-gap: ${({ theme }) => theme.spacing(3)};
|
column-gap: ${({ theme }) => theme.spacing(2)};
|
||||||
color: ${({ variant, theme }) =>
|
color: ${({ variant, theme }) =>
|
||||||
variant === 'placeholder'
|
variant === 'placeholder'
|
||||||
? theme.font.color.extraLight
|
? theme.font.color.extraLight
|
||||||
@ -72,7 +72,10 @@ const StyledStepNodeLabel = styled.div<{ variant?: Variant }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledSourceHandle = styled(Handle)`
|
const StyledSourceHandle = styled(Handle)`
|
||||||
background-color: ${({ theme }) => theme.color.gray50};
|
background-color: ${({ theme }) => theme.grayScale.gray25};
|
||||||
|
border: none;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledTargetHandle = styled(Handle)`
|
export const StyledTargetHandle = styled(Handle)`
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose';
|
import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose';
|
||||||
import { WorkflowVersionStatus } from '@/workflow/types/Workflow';
|
import { WorkflowVersionStatus } from '@/workflow/types/Workflow';
|
||||||
|
import { WorkflowDiagramCustomMarkers } from '@/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers';
|
||||||
import { WorkflowVersionStatusTag } from '@/workflow/workflow-diagram/components/WorkflowVersionStatusTag';
|
import { WorkflowVersionStatusTag } from '@/workflow/workflow-diagram/components/WorkflowVersionStatusTag';
|
||||||
import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightDrawerState';
|
import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightDrawerState';
|
||||||
import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState';
|
import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState';
|
||||||
@ -46,6 +47,14 @@ const StyledResetReactflowStyles = styled.div`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react-flow__handle {
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.react-flow__handle.connectionindicator {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
--xy-node-border-radius: none;
|
--xy-node-border-radius: none;
|
||||||
--xy-node-border: none;
|
--xy-node-border: none;
|
||||||
--xy-node-background-color: none;
|
--xy-node-background-color: none;
|
||||||
@ -182,6 +191,8 @@ export const WorkflowDiagramCanvasBase = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledResetReactflowStyles ref={containerRef}>
|
<StyledResetReactflowStyles ref={containerRef}>
|
||||||
|
<WorkflowDiagramCustomMarkers />
|
||||||
|
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
ref={(node) => {
|
ref={(node) => {
|
||||||
if (isDefined(node)) {
|
if (isDefined(node)) {
|
||||||
@ -214,6 +225,7 @@ export const WorkflowDiagramCanvasBase = ({
|
|||||||
edgesFocusable={false}
|
edgesFocusable={false}
|
||||||
nodesDraggable={false}
|
nodesDraggable={false}
|
||||||
onPaneClick={closeCommandMenu}
|
onPaneClick={closeCommandMenu}
|
||||||
|
nodesConnectable={false}
|
||||||
paneClickDistance={10} // Fix small unwanted user dragging does not select node
|
paneClickDistance={10} // Fix small unwanted user dragging does not select node
|
||||||
>
|
>
|
||||||
<Background color={theme.border.color.medium} size={2} />
|
<Background color={theme.border.color.medium} size={2} />
|
||||||
|
|||||||
@ -2,16 +2,20 @@ import styled from '@emotion/styled';
|
|||||||
import { Handle, Position } from '@xyflow/react';
|
import { Handle, Position } from '@xyflow/react';
|
||||||
import { IconButton, IconPlus } from 'twenty-ui';
|
import { IconButton, IconPlus } from 'twenty-ui';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||||
|
`;
|
||||||
|
|
||||||
export const StyledTargetHandle = styled(Handle)`
|
export const StyledTargetHandle = styled(Handle)`
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const WorkflowDiagramCreateStepNode = () => {
|
export const WorkflowDiagramCreateStepNode = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<StyledContainer>
|
||||||
<StyledTargetHandle type="target" position={Position.Top} />
|
<StyledTargetHandle type="target" position={Position.Top} />
|
||||||
|
|
||||||
<IconButton Icon={IconPlus} size="medium" />
|
<IconButton Icon={IconPlus} size="medium" />
|
||||||
</>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { EDGE_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeRoundedArrowMarkerId';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
|
export const WorkflowDiagramCustomMarkers = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg style={{ position: 'absolute', top: 0, left: 0 }}>
|
||||||
|
<defs>
|
||||||
|
<marker
|
||||||
|
id={EDGE_ROUNDED_ARROW_MARKER_ID}
|
||||||
|
markerHeight={5}
|
||||||
|
markerWidth={6}
|
||||||
|
refX={3}
|
||||||
|
refY={2.5}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M0.31094 1.1168C0.178029 0.917434 0.320947 0.650391 0.560555 0.650391H5.43945C5.67905 0.650391 5.82197 0.917434 5.68906 1.1168L3.62404 4.21433C3.32717 4.65963 2.67283 4.65963 2.37596 4.21433L0.31094 1.1168Z"
|
||||||
|
fill={theme.grayScale.gray25}
|
||||||
|
/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const EDGE_ROUNDED_ARROW_MARKER_ID = 'arrow-rounded';
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { EDGE_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeRoundedArrowMarkerId';
|
||||||
|
import { WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||||
|
import { THEME_COMMON } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION = {
|
||||||
|
markerEnd: EDGE_ROUNDED_ARROW_MARKER_ID,
|
||||||
|
style: {
|
||||||
|
stroke: THEME_COMMON.grayScale.gray25,
|
||||||
|
},
|
||||||
|
deletable: false,
|
||||||
|
selectable: false,
|
||||||
|
} satisfies Partial<WorkflowDiagramEdge>;
|
||||||
@ -1,9 +1,9 @@
|
|||||||
|
import { WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION } from '@/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration';
|
||||||
import {
|
import {
|
||||||
WorkflowDiagram,
|
WorkflowDiagram,
|
||||||
WorkflowDiagramEdge,
|
WorkflowDiagramEdge,
|
||||||
WorkflowDiagramNode,
|
WorkflowDiagramNode,
|
||||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||||
import { MarkerType } from '@xyflow/react';
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
export const addCreateStepNodes = ({ nodes, edges }: WorkflowDiagram) => {
|
export const addCreateStepNodes = ({ nodes, edges }: WorkflowDiagram) => {
|
||||||
@ -30,13 +30,10 @@ export const addCreateStepNodes = ({ nodes, edges }: WorkflowDiagram) => {
|
|||||||
updatedNodes.push(newCreateStepNode);
|
updatedNodes.push(newCreateStepNode);
|
||||||
|
|
||||||
updatedEdges.push({
|
updatedEdges.push({
|
||||||
|
...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION,
|
||||||
id: v4(),
|
id: v4(),
|
||||||
source: node.id,
|
source: node.id,
|
||||||
target: newCreateStepNode.id,
|
target: newCreateStepNode.id,
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
deletable: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { WorkflowStep, WorkflowTrigger } from '@/workflow/types/Workflow';
|
import { WorkflowStep, WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
||||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||||
|
import { WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION } from '@/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration';
|
||||||
import {
|
import {
|
||||||
WorkflowDiagram,
|
WorkflowDiagram,
|
||||||
WorkflowDiagramEdge,
|
WorkflowDiagramEdge,
|
||||||
@ -8,7 +9,6 @@ import {
|
|||||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||||
|
|
||||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||||
import { MarkerType } from '@xyflow/react';
|
|
||||||
import { capitalize } from 'twenty-shared';
|
import { capitalize } from 'twenty-shared';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -23,7 +23,6 @@ export const generateWorkflowDiagram = ({
|
|||||||
const nodes: Array<WorkflowDiagramNode> = [];
|
const nodes: Array<WorkflowDiagramNode> = [];
|
||||||
const edges: Array<WorkflowDiagramEdge> = [];
|
const edges: Array<WorkflowDiagramEdge> = [];
|
||||||
|
|
||||||
// Helper function to generate nodes and edges recursively
|
|
||||||
const processNode = (
|
const processNode = (
|
||||||
step: WorkflowStep,
|
step: WorkflowStep,
|
||||||
parentNodeId: string,
|
parentNodeId: string,
|
||||||
@ -45,22 +44,16 @@ export const generateWorkflowDiagram = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create an edge from the parent node to this node
|
|
||||||
edges.push({
|
edges.push({
|
||||||
|
...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION,
|
||||||
id: v4(),
|
id: v4(),
|
||||||
source: parentNodeId,
|
source: parentNodeId,
|
||||||
target: nodeId,
|
target: nodeId,
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
deletable: false,
|
|
||||||
selectable: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return nodeId;
|
return nodeId;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start with the trigger node
|
|
||||||
const triggerNodeId = TRIGGER_STEP_ID;
|
const triggerNodeId = TRIGGER_STEP_ID;
|
||||||
|
|
||||||
if (isDefined(trigger)) {
|
if (isDefined(trigger)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user