diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts
index a557de94af..f10f643519 100644
--- a/packages/editor-ui/src/Interface.ts
+++ b/packages/editor-ui/src/Interface.ts
@@ -21,11 +21,15 @@ import {
WorkflowExecuteMode,
} from 'n8n-workflow';
-import {
- PaintStyle,
-} from 'jsplumb';
-
declare module 'jsplumb' {
+ interface PaintStyle {
+ stroke?: string;
+ fill?: string;
+ strokeWidth?: number;
+ outlineStroke?: string;
+ outlineWidth?: number;
+ }
+
interface Anchor {
lastReturnValue: number[];
}
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index b9121761e8..72ced289df 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -109,6 +109,8 @@ import Vue from 'vue';
import {
Connection,
Overlay,
+ OverlaySpec,
+ PaintStyle,
} from 'jsplumb';
import { MessageBoxInputData } from 'element-ui/types/message-box';
import { jsPlumb, Endpoint, OnConnectionBindInfo } from 'jsplumb';
@@ -133,7 +135,7 @@ import NodeCreator from '@/components/NodeCreator/NodeCreator.vue';
import NodeSettings from '@/components/NodeSettings.vue';
import RunData from '@/components/RunData.vue';
-import { getLeftmostTopNode, getWorkflowCorners, scaleSmaller, scaleBigger, scaleReset, showOrHideMidpointArrow, addEndpointArrow, getDefaultOverlays, getIcon, getNewNodePosition, hideMidpointArrow, showOrHideItemsLabel } from './helpers';
+import { getLeftmostTopNode, getWorkflowCorners, scaleSmaller, scaleBigger, scaleReset, showOrHideMidpointArrow, addEndpointArrow, getIcon, getNewNodePosition, hideMidpointArrow, showOrHideItemsLabel } from './helpers';
import mixins from 'vue-typed-mixins';
import { v4 as uuidv4} from 'uuid';
@@ -218,6 +220,72 @@ if (!window.localStorage.getItem('PUSH_NODES_LENGTH')) {
// @ts-ignore
const _PUSH_NODES_LENGTH = parseInt(window.localStorage.getItem('PUSH_NODES_LENGTH'), 10);
+const CONNECTOR_PAINT_STYLE_DEFAULT: PaintStyle = {
+ stroke: getStyleTokenValue('--color-foreground-dark'),
+ strokeWidth: 2,
+ outlineWidth: 12,
+ outlineStroke: 'transparent',
+};
+
+const CONNECTOR_PAINT_STYLE_PRIMARY = {
+ ...CONNECTOR_PAINT_STYLE_DEFAULT,
+ stroke: getStyleTokenValue('--color-primary'),
+};
+
+const CONNECTOR_PAINT_STYLE_SUCCESS = {
+ ...CONNECTOR_PAINT_STYLE_DEFAULT,
+ stroke: getStyleTokenValue('--color-success'),
+};
+
+const CONNECTOR_TYPE_BEZIER = ['Bezier', { curviness: _CURVINESS }];
+const CONNECTOR_TYPE_FLOWCHART = ['Flowchart', { cornerRadius: 8, stub: JSPLUMB_FLOWCHART_STUB, gap: 5, alwaysRespectStubs: _ALWAYS_RESPECT_STUB}];
+
+const OVERLAY_DROP_NODE_ID = 'drop-add-node';
+
+const CONNECTOR_ARROW_OVERLAYS: OverlaySpec[] = [
+ [
+ 'Arrow',
+ {
+ id: 'endpoint-arrow',
+ location: 1,
+ width: 12,
+ foldback: 1,
+ length: 10,
+ visible: true,
+ },
+ ],
+ [
+ 'Arrow',
+ {
+ id: 'midpoint-arrow',
+ location: 0.5,
+ width: 12,
+ foldback: 1,
+ length: 10,
+ visible: false,
+ },
+ ],
+];
+
+const CONNECTOR_DROP_NODE_OVERLAY: OverlaySpec[] = [
+ [
+ 'Label',
+ {
+ id: OVERLAY_DROP_NODE_ID,
+ label: 'Drop connection
to create node',
+ cssClass: 'drop-add-node-label',
+ location: 0.5,
+ visible: true,
+ },
+ ],
+];
+
+const addOverlays = (connection: Connection, overlays: OverlaySpec[]) => {
+ overlays.forEach((overlay: OverlaySpec) => {
+ connection.addOverlay(overlay);
+ });
+};
+
export default mixins(
copyPaste,
externalHooks,
@@ -1326,14 +1394,14 @@ export default mixins(
},
initNodeView () {
this.instance.importDefaults({
- Connector: ['Bezier', { curviness: _CURVINESS }],
+ Connector: CONNECTOR_TYPE_BEZIER,
Endpoint: ['Dot', { radius: 5 }],
DragOptions: { cursor: 'pointer', zIndex: 5000 },
- PaintStyle: { strokeWidth: 2, stroke: getStyleTokenValue('--color-foreground-dark'), outlineStroke: _OUTLINE_STROKE_COLOR, outlineWidth: _OUTLINE_STROKE_WIDTH},
+ PaintStyle: { strokeWidth: 2, stroke: getStyleTokenValue('--color-foreground-dark')},
EndpointStyle: { radius: 9, fill: '#acd', stroke: 'red' },
HoverPaintStyle: { stroke: '#ff6d5a', lineWidth: 4 },
EndpointHoverStyle: { fill: '#ff6d5a', stroke: '#acd' },
- ConnectionOverlays: getDefaultOverlays(),
+ ConnectionOverlays: CONNECTOR_ARROW_OVERLAYS,
Container: '#node-view',
});
@@ -1357,9 +1425,9 @@ export default mixins(
}));
this.instance.bind('connection', (info: OnConnectionBindInfo) => {
- info.connection.setConnector(['Flowchart', { cornerRadius: 8, stub: JSPLUMB_FLOWCHART_STUB, gap: 5, alwaysRespectStubs: _ALWAYS_RESPECT_STUB}]);
- // @ts-ignore
- info.connection.setPaintStyle({stroke: getStyleTokenValue('--color-foreground-dark'), strokeWidth: 2, outlineStroke: _OUTLINE_STROKE_COLOR, outlineWidth: _OUTLINE_STROKE_WIDTH});
+ info.connection.setConnector(CONNECTOR_TYPE_FLOWCHART);
+ info.connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT);
+ addOverlays(info.connection, CONNECTOR_ARROW_OVERLAYS);
addEndpointArrow(info.connection);
showOrHideMidpointArrow(info.connection);
@@ -1375,8 +1443,7 @@ export default mixins(
const sourceNode = this.$store.getters.getNodeByName(sourceNodeName);
const targetNode = this.$store.getters.getNodeByName(targetNodeName);
- // @ts-ignore
- info.connection.removeOverlay('drop-add-node');
+ info.connection.removeOverlay(OVERLAY_DROP_NODE_ID);
if (this.isReadOnly === false) {
// Display the connection-delete button only on hover
@@ -1579,17 +1646,26 @@ export default mixins(
// @ts-ignore
this.instance.bind('connectionDrag', (connection: Connection) => {
+ addOverlays(connection, CONNECTOR_DROP_NODE_OVERLAY);
+
+ let droppable = false;
const onMouseMove = () => {
if (!connection) {
return;
}
const elements = document.querySelector('div.jtk-endpoint.dropHover');
- if (elements) {
- connection.setPaintStyle({stroke: getStyleTokenValue('--color-primary')});
+ if (elements && !droppable) {
+ droppable = true;
+ connection.setConnector(CONNECTOR_TYPE_FLOWCHART);
+ connection.setPaintStyle(CONNECTOR_PAINT_STYLE_PRIMARY);
+ addOverlays(connection, CONNECTOR_ARROW_OVERLAYS);
}
- else {
- connection.setPaintStyle({stroke: getStyleTokenValue('--color-foreground-dark')});
+ else if (!elements && droppable) {
+ droppable = false;
+ connection.setConnector(CONNECTOR_TYPE_BEZIER);
+ connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT);
+ addOverlays(connection, CONNECTOR_ARROW_OVERLAYS);
}
};
@@ -1804,7 +1880,7 @@ export default mixins(
outgoing.forEach((connection: Connection) => {
connection.removeOverlay('output-items-label');
- connection.setPaintStyle({stroke: getStyleTokenValue('--color-foreground-dark')});
+ connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT);
showOrHideMidpointArrow(connection);
});
@@ -1874,12 +1950,12 @@ export default mixins(
const output = outputMap[sourceEndpoint][targetId][targetEndpoint];
if (!output || !output.total) {
- conn.setPaintStyle({stroke: getStyleTokenValue('--color-foreground-dark')});
+ conn.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT);
conn.removeOverlay('output-items-label');
return;
}
- conn.setPaintStyle({stroke: getStyleTokenValue('--color-success')});
+ conn.setPaintStyle(CONNECTOR_PAINT_STYLE_SUCCESS);
if (conn.getOverlay('output-items-label')) {
conn.removeOverlay('output-items-label');
diff --git a/packages/editor-ui/src/views/helpers.ts b/packages/editor-ui/src/views/helpers.ts
index 5007d6c314..ed7702788c 100644
--- a/packages/editor-ui/src/views/helpers.ts
+++ b/packages/editor-ui/src/views/helpers.ts
@@ -85,40 +85,6 @@ export const scaleReset = (config: IZoomConfig): IZoomConfig => {
return config;
};
-export const getDefaultOverlays = (): OverlaySpec[] => ([
- [
- 'Arrow',
- {
- id: 'endpoint-arrow',
- location: 1,
- width: 12,
- foldback: 1,
- length: 10,
- visible: true,
- },
- ],
- [
- 'Label',
- {
- id: 'drop-add-node',
- label: 'Drop connection
to create node',
- cssClass: 'drop-add-node-label',
- location: 0.5,
- },
- ],
- [
- 'Arrow',
- {
- id: 'midpoint-arrow',
- location: 0.5,
- width: 12,
- foldback: 1,
- length: 10,
- visible: false,
- },
- ],
-]);
-
export const addEndpointArrow = (connection: Connection) => {
connection.addOverlay([
'Arrow',