refactor helpers

This commit is contained in:
Mutasem 2021-11-01 15:38:24 +01:00
parent 71b50aeaf6
commit 0015cb4f34
5 changed files with 190 additions and 191 deletions

View file

@ -681,3 +681,11 @@ export interface IZoomConfig {
scale: number; scale: number;
offset: XYPosition; offset: XYPosition;
} }
export interface IBounds {
minX: number;
minY: number;
maxX: number;
maxY: number;
}

View file

@ -6,7 +6,7 @@ import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
import { nodeIndex } from '@/components/mixins/nodeIndex'; import { nodeIndex } from '@/components/mixins/nodeIndex';
import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE } from '@/constants'; import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE } from '@/constants';
import { getStyleTokenValue } from '../helpers'; import { getStyleTokenValue } from '../helpers';
import { OVERLAY_INPUT_NAME_LABEL } from '@/views/canvasHelpers'; import * as CanvasHelpers from '@/views/canvasHelpers';
export const nodeBase = mixins( export const nodeBase = mixins(
deviceSupportHelpers, deviceSupportHelpers,
@ -200,7 +200,7 @@ export const nodeBase = mixins(
newEndpointData.overlays = [ newEndpointData.overlays = [
['Label', ['Label',
{ {
id: OVERLAY_INPUT_NAME_LABEL, id: CanvasHelpers.OVERLAY_INPUT_NAME_LABEL,
location: [-3, 0.5], location: [-3, 0.5],
label: nodeTypeData.inputNames[index], label: nodeTypeData.inputNames[index],
cssClass: 'node-input-endpoint-label', cssClass: 'node-input-endpoint-label',

View file

@ -110,7 +110,8 @@ export const showMessage = mixins(externalHooks).extend({
return errorMessage; return errorMessage;
}, },
$showError(error: Error, title: string, message?: string) { $showError(e: Error | unknown, title: string, message?: string) {
const error = e as Error;
const messageLine = message ? `${message}<br/>` : ''; const messageLine = message ? `${message}<br/>` : '';
this.$showMessage({ this.$showMessage({
title, title,

View file

@ -135,7 +135,7 @@ import NodeCreator from '@/components/NodeCreator/NodeCreator.vue';
import NodeSettings from '@/components/NodeSettings.vue'; import NodeSettings from '@/components/NodeSettings.vue';
import RunData from '@/components/RunData.vue'; import RunData from '@/components/RunData.vue';
import { OVERLAY_INPUT_NAME_LABEL, JSPLUMB_FLOWCHART_STUB, getLeftmostTopNode, getWorkflowCorners, scaleSmaller, scaleBigger, scaleReset, showOrHideMidpointArrow, getIcon, getNewNodePosition, hideOverlay, showOrHideItemsLabel, showOverlay, OVERLAY_ENDPOINT_ARROW_ID, OVERLAY_MIDPOINT_ARROW_ID, OVERLAY_DROP_NODE_ID, OVERLAY_RUN_ITEMS_ID, OVERLAY_CONNECTION_ACTIONS_ID, getConnectorLengths, getRelativePosition, getMousePosition } from './canvasHelpers'; import * as CanvasHelpers from './canvasHelpers';
import mixins from 'vue-typed-mixins'; import mixins from 'vue-typed-mixins';
import { v4 as uuidv4} from 'uuid'; import { v4 as uuidv4} from 'uuid';
@ -158,7 +158,6 @@ import {
import { import {
ICredentialsResponse, ICredentialsResponse,
IExecutionResponse, IExecutionResponse,
IN8nUISettings,
IWorkflowDb, IWorkflowDb,
IWorkflowData, IWorkflowData,
INodeUi, INodeUi,
@ -174,113 +173,6 @@ import { mapGetters } from 'vuex';
import { getStyleTokenValue } from '@/components/helpers'; import { getStyleTokenValue } from '@/components/helpers';
import '../plugins/N8nFlowchartType'; import '../plugins/N8nFlowchartType';
const NODE_SIZE = 100;
const DEFAULT_START_POSITION_X = 240;
const DEFAULT_START_POSITION_Y = 300;
const HEADER_HEIGHT = 65;
const SIDEBAR_WIDTH = 65;
const MAX_X_TO_PUSH_DOWNSTREAM_NODES = 300;
const DEFAULT_START_NODE = {
name: 'Start',
type: START_NODE_TYPE,
typeVersion: 1,
position: [
DEFAULT_START_POSITION_X,
DEFAULT_START_POSITION_Y,
] as XYPosition,
parameters: {},
};
const _OUTLINE_STROKE_COLOR = 'transparent';
const _OUTLINE_STROKE_WIDTH = 12;
const _ALWAYS_RESPECT_STUB = true;
const _PUSH_NODES_LENGTH = 200;
const CONNECTOR_PAINT_STYLE_DEFAULT: PaintStyle = {
stroke: getStyleTokenValue('--color-foreground-dark'),
strokeWidth: 2,
outlineWidth: _OUTLINE_STROKE_WIDTH,
outlineStroke: _OUTLINE_STROKE_COLOR,
};
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_STRIGHT = ['Straight'];
const getFlowChartType = (connection: Connection) => {
const inputIndex = connection.__meta ? connection.__meta.targetOutputIndex : 0;
const outputIndex = connection.__meta ? connection.__meta.sourceOutputIndex : 0;
const outputEndpoint = connection.endpoints[0];
const outputOverlay = outputEndpoint.getOverlay('output-name-label');
let labelOffset = 0;
if (outputOverlay && outputOverlay.label && outputOverlay.label.length > 1) {
labelOffset = 16;
}
return ['N8nFlowchart', {
cornerRadius: 4,
stub: JSPLUMB_FLOWCHART_STUB + 10 * outputIndex + 10 * inputIndex + labelOffset,
gap: 5,
alwaysRespectStubs: _ALWAYS_RESPECT_STUB,
yOffset: NODE_SIZE,
loopbackMinimum: 140,
}];
};
const CONNECTOR_ARROW_OVERLAYS: OverlaySpec[] = [
[
'Arrow',
{
id: OVERLAY_ENDPOINT_ARROW_ID,
location: 1,
width: 12,
foldback: 1,
length: 10,
visible: true,
},
],
[
'Arrow',
{
id: OVERLAY_MIDPOINT_ARROW_ID,
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<br />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( export default mixins(
copyPaste, copyPaste,
externalHooks, externalHooks,
@ -557,18 +449,18 @@ export default mixins(
const nodes = data.workflow.nodes; const nodes = data.workflow.nodes;
const hasStartNode = !!nodes.find(node => node.type === START_NODE_TYPE); const hasStartNode = !!nodes.find(node => node.type === START_NODE_TYPE);
const leftmostTop = getLeftmostTopNode(nodes); const leftmostTop = CanvasHelpers.getLeftmostTopNode(nodes);
const diffX = DEFAULT_START_POSITION_X - leftmostTop.position[0]; const diffX = CanvasHelpers.DEFAULT_START_POSITION_X - leftmostTop.position[0];
const diffY = DEFAULT_START_POSITION_Y - leftmostTop.position[1]; const diffY = CanvasHelpers.DEFAULT_START_POSITION_Y - leftmostTop.position[1];
data.workflow.nodes.map((node) => { data.workflow.nodes.map((node) => {
node.position[0] += diffX + (hasStartNode? 0 : NODE_SIZE * 2); node.position[0] += diffX + (hasStartNode? 0 : CanvasHelpers.NODE_SIZE * 2);
node.position[1] += diffY; node.position[1] += diffY;
}); });
if (!hasStartNode) { if (!hasStartNode) {
data.workflow.nodes.push(DEFAULT_START_NODE); data.workflow.nodes.push(CanvasHelpers.DEFAULT_START_NODE);
} }
this.blankRedirect = true; this.blankRedirect = true;
@ -976,21 +868,21 @@ export default mixins(
}, },
resetZoom () { resetZoom () {
const { scale, offset } = scaleReset({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition}); const { scale, offset } = CanvasHelpers.scaleReset({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition});
this.setZoomLevel(scale); this.setZoomLevel(scale);
this.$store.commit('setNodeViewOffsetPosition', {newOffset: offset}); this.$store.commit('setNodeViewOffsetPosition', {newOffset: offset});
}, },
zoomIn() { zoomIn() {
const { scale, offset: [xOffset, yOffset] } = scaleBigger({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition}); const { scale, offset: [xOffset, yOffset] } = CanvasHelpers.scaleBigger({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition});
this.setZoomLevel(scale); this.setZoomLevel(scale);
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]});
}, },
zoomOut() { zoomOut() {
const { scale, offset: [xOffset, yOffset] } = scaleSmaller({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition}); const { scale, offset: [xOffset, yOffset] } = CanvasHelpers.scaleSmaller({scale: this.nodeViewScale, offset: this.$store.getters.getNodeViewOffsetPosition});
this.setZoomLevel(scale); this.setZoomLevel(scale);
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]});
@ -1021,24 +913,24 @@ export default mixins(
return; return;
} }
const {minX, minY, maxX, maxY} = getWorkflowCorners(nodes); const {minX, minY, maxX, maxY} = CanvasHelpers.getWorkflowCorners(nodes);
const PADDING = NODE_SIZE * 4; const PADDING = CanvasHelpers.NODE_SIZE * 4;
const editorWidth = window.innerWidth; const editorWidth = window.innerWidth;
const diffX = maxX - minX + SIDEBAR_WIDTH + PADDING; const diffX = maxX - minX + CanvasHelpers.SIDEBAR_WIDTH + PADDING;
const scaleX = editorWidth / diffX; const scaleX = editorWidth / diffX;
const editorHeight = window.innerHeight; const editorHeight = window.innerHeight;
const diffY = maxY - minY + HEADER_HEIGHT + PADDING; const diffY = maxY - minY + CanvasHelpers.HEADER_HEIGHT + PADDING;
const scaleY = editorHeight / diffY; const scaleY = editorHeight / diffY;
const zoomLevel = Math.min(scaleX, scaleY, 1); const zoomLevel = Math.min(scaleX, scaleY, 1);
let xOffset = (minX * -1) * zoomLevel + SIDEBAR_WIDTH; // find top right corner let xOffset = (minX * -1) * zoomLevel + CanvasHelpers.SIDEBAR_WIDTH; // find top right corner
xOffset += (editorWidth - SIDEBAR_WIDTH - (maxX - minX + NODE_SIZE) * zoomLevel) / 2; // add padding to center workflow xOffset += (editorWidth - CanvasHelpers.SIDEBAR_WIDTH - (maxX - minX + CanvasHelpers.NODE_SIZE) * zoomLevel) / 2; // add padding to center workflow
let yOffset = (minY * -1) * zoomLevel + HEADER_HEIGHT; // find top right corner let yOffset = (minY * -1) * zoomLevel + CanvasHelpers.HEADER_HEIGHT; // find top right corner
yOffset += (editorHeight - HEADER_HEIGHT - (maxY - minY + NODE_SIZE * 2) * zoomLevel) / 2; // add padding to center workflow yOffset += (editorHeight - CanvasHelpers.HEADER_HEIGHT - (maxY - minY + CanvasHelpers.NODE_SIZE * 2) * zoomLevel) / 2; // add padding to center workflow
this.setZoomLevel(zoomLevel); this.setZoomLevel(zoomLevel);
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]});
@ -1190,7 +1082,7 @@ export default mixins(
// Fix the node position as it could be totally offscreen // Fix the node position as it could be totally offscreen
// and the pasted nodes would so not be directly visible to // and the pasted nodes would so not be directly visible to
// the user // the user
this.updateNodePositions(workflowData, getNewNodePosition(this.nodes, this.lastClickPosition)); this.updateNodePositions(workflowData, CanvasHelpers.getNewNodePosition(this.nodes, this.lastClickPosition));
const data = await this.addNodesToWorkflow(workflowData); const data = await this.addNodesToWorkflow(workflowData);
@ -1319,14 +1211,14 @@ export default mixins(
if (lastSelectedNode) { if (lastSelectedNode) {
const lastSelectedConnection = this.lastSelectedConnection; const lastSelectedConnection = this.lastSelectedConnection;
if (lastSelectedConnection) { if (lastSelectedConnection) {
const [diffX] = getConnectorLengths(lastSelectedConnection); const [diffX] = CanvasHelpers.getConnectorLengths(lastSelectedConnection);
if (diffX <= MAX_X_TO_PUSH_DOWNSTREAM_NODES) { if (diffX <= CanvasHelpers.MAX_X_TO_PUSH_DOWNSTREAM_NODES) {
this.pushDownstreamNodes(lastSelectedNode.name, _PUSH_NODES_LENGTH); this.pushDownstreamNodes(lastSelectedNode.name, CanvasHelpers.PUSH_NODES_OFFSET);
} }
} }
if (this.newNodeInsertPosition) { if (this.newNodeInsertPosition) {
newNodeData.position = getNewNodePosition(this.nodes, [this.newNodeInsertPosition[0], this.newNodeInsertPosition[1] - NODE_SIZE / 2]); newNodeData.position = CanvasHelpers.getNewNodePosition(this.nodes, [this.newNodeInsertPosition[0], this.newNodeInsertPosition[1] - CanvasHelpers.NODE_SIZE / 2]);
this.newNodeInsertPosition = null; this.newNodeInsertPosition = null;
} }
else { else {
@ -1343,7 +1235,7 @@ export default mixins(
// If a node is active then add the new node directly after the current one // If a node is active then add the new node directly after the current one
// newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60]; // newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60];
newNodeData.position = getNewNodePosition( newNodeData.position = CanvasHelpers.getNewNodePosition(
this.nodes, this.nodes,
[lastSelectedNode.position[0] + 200, lastSelectedNode.position[1] + yOffset], [lastSelectedNode.position[0] + 200, lastSelectedNode.position[1] + yOffset],
[100, 0], [100, 0],
@ -1351,7 +1243,7 @@ export default mixins(
} }
} else { } else {
// If no node is active find a free spot // If no node is active find a free spot
newNodeData.position = getNewNodePosition(this.nodes, this.lastClickPosition); newNodeData.position = CanvasHelpers.getNewNodePosition(this.nodes, this.lastClickPosition);
} }
// Check if node-name is unique else find one that is // Check if node-name is unique else find one that is
@ -1427,12 +1319,12 @@ export default mixins(
}, },
initNodeView () { initNodeView () {
this.instance.importDefaults({ this.instance.importDefaults({
Connector: CONNECTOR_TYPE_STRIGHT , Connector: CanvasHelpers.CONNECTOR_TYPE_STRIGHT,
Endpoint: ['Dot', { radius: 5 }], Endpoint: ['Dot', { radius: 5 }],
DragOptions: { cursor: 'pointer', zIndex: 5000 }, DragOptions: { cursor: 'pointer', zIndex: 5000 },
PaintStyle: { strokeWidth: 2, stroke: getStyleTokenValue('--color-foreground-dark')}, PaintStyle: { strokeWidth: 2, stroke: getStyleTokenValue('--color-foreground-dark')},
HoverPaintStyle: { stroke: getStyleTokenValue('--color-primary'), lineWidth: 4 }, HoverPaintStyle: { stroke: getStyleTokenValue('--color-primary'), lineWidth: 4 },
ConnectionOverlays: CONNECTOR_ARROW_OVERLAYS, ConnectionOverlays: CanvasHelpers.CONNECTOR_ARROW_OVERLAYS,
Container: '#node-view', Container: '#node-view',
}); });
@ -1489,18 +1381,18 @@ export default mixins(
const hideActions = (connection: Connection | null) => { const hideActions = (connection: Connection | null) => {
if (connection) { if (connection) {
hideOverlay(connection, OVERLAY_CONNECTION_ACTIONS_ID); CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID);
showOrHideItemsLabel(connection); CanvasHelpers.showOrHideItemsLabel(connection);
showOrHideMidpointArrow(connection); CanvasHelpers.showOrHideMidpointArrow(connection);
} }
}; };
const showActions = (connection: Connection | null) => { const showActions = (connection: Connection | null) => {
if (connection) { if (connection) {
showOverlay(connection, OVERLAY_CONNECTION_ACTIONS_ID); CanvasHelpers.showOverlay(connection, CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID);
hideOverlay(connection, OVERLAY_RUN_ITEMS_ID); CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_RUN_ITEMS_ID);
if (!connection.getOverlay(OVERLAY_RUN_ITEMS_ID)) { if (!connection.getOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID)) {
hideOverlay(connection, OVERLAY_MIDPOINT_ARROW_ID); CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_MIDPOINT_ARROW_ID);
} }
} }
}; };
@ -1519,15 +1411,15 @@ export default mixins(
targetOutputIndex: targetInfo.index, targetOutputIndex: targetInfo.index,
}; };
const connectorType = getFlowChartType(info.connection); const connectorType = CanvasHelpers.getFlowChartType(info.connection);
info.connection.setConnector(connectorType); info.connection.setConnector(connectorType);
info.connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); info.connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT);
addOverlays(info.connection, CONNECTOR_ARROW_OVERLAYS); CanvasHelpers.addOverlays(info.connection, CanvasHelpers.CONNECTOR_ARROW_OVERLAYS);
showOrHideMidpointArrow(info.connection); CanvasHelpers.showOrHideMidpointArrow(info.connection);
info.connection.removeOverlay(OVERLAY_DROP_NODE_ID); info.connection.removeOverlay(CanvasHelpers.OVERLAY_DROP_NODE_ID);
if (this.isReadOnly === false) { if (this.isReadOnly === false) {
let exitTimer: NodeJS.Timeout | undefined; let exitTimer: NodeJS.Timeout | undefined;
@ -1583,9 +1475,9 @@ export default mixins(
info.connection.addOverlay([ info.connection.addOverlay([
'Label', 'Label',
{ {
id: OVERLAY_CONNECTION_ACTIONS_ID, id: CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID,
label: `<div class="add">${getIcon('plus')}</div> <div class="delete">${getIcon('trash')}</div>`, label: `<div class="add">${CanvasHelpers.getIcon('plus')}</div> <div class="delete">${CanvasHelpers.getIcon('trash')}</div>`,
cssClass: OVERLAY_CONNECTION_ACTIONS_ID, cssClass: CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID,
visible: false, visible: false,
events: { events: {
mousedown: (overlay: Overlay, event: MouseEvent) => { mousedown: (overlay: Overlay, event: MouseEvent) => {
@ -1610,7 +1502,7 @@ export default mixins(
]); ]);
} }
const inputNameOverlay = info.targetEndpoint.getOverlay(OVERLAY_INPUT_NAME_LABEL); const inputNameOverlay = info.targetEndpoint.getOverlay(CanvasHelpers.OVERLAY_INPUT_NAME_LABEL);
if (inputNameOverlay) { if (inputNameOverlay) {
inputNameOverlay.setLocation([-4.5, .5]); inputNameOverlay.setLocation([-4.5, .5]);
} }
@ -1642,7 +1534,7 @@ export default mixins(
} }
} }
if (targetEndpoint !== undefined && targetEndpoint.connections!.length === maxConnections) { if (targetEndpoint !== undefined && targetEndpoint.connections!.length === maxConnections) {
const inputNameOverlay = targetEndpoint.getOverlay(OVERLAY_INPUT_NAME_LABEL); const inputNameOverlay = targetEndpoint.getOverlay(CanvasHelpers.OVERLAY_INPUT_NAME_LABEL);
if (![null, undefined].includes(inputNameOverlay)) { if (![null, undefined].includes(inputNameOverlay)) {
inputNameOverlay.setVisible(true); inputNameOverlay.setVisible(true);
} }
@ -1682,7 +1574,7 @@ export default mixins(
}); });
this.instance.bind('connectionDetached', (info) => { this.instance.bind('connectionDetached', (info) => {
const inputNameOverlay = info.targetEndpoint.getOverlay(OVERLAY_INPUT_NAME_LABEL); const inputNameOverlay = info.targetEndpoint.getOverlay(CanvasHelpers.OVERLAY_INPUT_NAME_LABEL);
if (inputNameOverlay) { if (inputNameOverlay) {
// todo // todo
inputNameOverlay.setLocation([-3, .5]); inputNameOverlay.setLocation([-3, .5]);
@ -1696,7 +1588,7 @@ export default mixins(
// @ts-ignore // @ts-ignore
this.instance.bind('connectionDrag', (connection: Connection) => { this.instance.bind('connectionDrag', (connection: Connection) => {
this.newNodeInsertPosition = null; this.newNodeInsertPosition = null;
addOverlays(connection, CONNECTOR_DROP_NODE_OVERLAY); CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_DROP_NODE_OVERLAY);
let droppable = false; let droppable = false;
const onMouseMove = () => { const onMouseMove = () => {
@ -1707,17 +1599,17 @@ export default mixins(
const elements = document.querySelector('div.jtk-endpoint.dropHover'); const elements = document.querySelector('div.jtk-endpoint.dropHover');
if (elements && !droppable) { if (elements && !droppable) {
droppable = true; droppable = true;
connection.setConnector(getFlowChartType(connection)); connection.setConnector(CanvasHelpers.getFlowChartType(connection));
connection.setPaintStyle(CONNECTOR_PAINT_STYLE_PRIMARY); connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_PRIMARY);
addOverlays(connection, CONNECTOR_ARROW_OVERLAYS); CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_ARROW_OVERLAYS);
hideOverlay(connection, OVERLAY_DROP_NODE_ID); CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_DROP_NODE_ID);
} }
else if (!elements && droppable) { else if (!elements && droppable) {
droppable = false; droppable = false;
connection.setConnector(CONNECTOR_TYPE_STRIGHT ); connection.setConnector(CanvasHelpers.CONNECTOR_TYPE_STRIGHT);
connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT);
addOverlays(connection, CONNECTOR_ARROW_OVERLAYS); CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_ARROW_OVERLAYS);
showOverlay(connection, OVERLAY_DROP_NODE_ID); CanvasHelpers.showOverlay(connection, CanvasHelpers.OVERLAY_DROP_NODE_ID);
} }
}; };
@ -1736,9 +1628,9 @@ export default mixins(
await this.$store.dispatch('workflows/setNewWorkflowName'); await this.$store.dispatch('workflows/setNewWorkflowName');
this.$store.commit('setStateDirty', false); this.$store.commit('setStateDirty', false);
await this.addNodes([DEFAULT_START_NODE]); await this.addNodes([CanvasHelpers.DEFAULT_START_NODE]);
this.nodeSelectedByName(DEFAULT_START_NODE.name, false); this.nodeSelectedByName(CanvasHelpers.DEFAULT_START_NODE.name, false);
this.$store.commit('setStateDirty', false); this.$store.commit('setStateDirty', false);
@ -1889,7 +1781,7 @@ export default mixins(
// Check if node-name is unique else find one that is // Check if node-name is unique else find one that is
newNodeData.name = this.getUniqueNodeName(newNodeData.name); newNodeData.name = this.getUniqueNodeName(newNodeData.name);
newNodeData.position = getNewNodePosition( newNodeData.position = CanvasHelpers.getNewNodePosition(
this.nodes, this.nodes,
[node.position[0], node.position[1] + 140], [node.position[0], node.position[1] + 140],
[0, 140], [0, 140],
@ -1947,8 +1839,8 @@ export default mixins(
}) as Connection[]; }) as Connection[];
[...incoming, ...outgoing].forEach((connection: Connection) => { [...incoming, ...outgoing].forEach((connection: Connection) => {
showOrHideMidpointArrow(connection); CanvasHelpers.showOrHideMidpointArrow(connection);
showOrHideItemsLabel(connection); CanvasHelpers.showOrHideItemsLabel(connection);
}); });
}, },
onNodeRun ({name, data}: {name: string, data: ITaskData[] | null}) { onNodeRun ({name, data}: {name: string, data: ITaskData[] | null}) {
@ -1957,9 +1849,9 @@ export default mixins(
const sourceId = `${NODE_NAME_PREFIX}${sourceIndex}`; const sourceId = `${NODE_NAME_PREFIX}${sourceIndex}`;
const resetConnection = (connection: Connection) => { const resetConnection = (connection: Connection) => {
connection.removeOverlay(OVERLAY_RUN_ITEMS_ID); connection.removeOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID);
connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT);
showOrHideMidpointArrow(connection); CanvasHelpers.showOrHideMidpointArrow(connection);
// @ts-ignore // @ts-ignore
if (connection.canvas) { if (connection.canvas) {
// @ts-ignore // @ts-ignore
@ -2039,15 +1931,15 @@ export default mixins(
return; return;
} }
conn.setPaintStyle(CONNECTOR_PAINT_STYLE_SUCCESS); conn.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_SUCCESS);
// @ts-ignore // @ts-ignore
if (conn.canvas) { if (conn.canvas) {
// @ts-ignore // @ts-ignore
(conn.canvas as Element).classList.add('success'); (conn.canvas as Element).classList.add('success');
} }
if (conn.getOverlay(OVERLAY_RUN_ITEMS_ID)) { if (conn.getOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID)) {
conn.removeOverlay(OVERLAY_RUN_ITEMS_ID); conn.removeOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID);
} }
let label = `${output.total}`; let label = `${output.total}`;
@ -2057,15 +1949,15 @@ export default mixins(
conn.addOverlay([ conn.addOverlay([
'Label', 'Label',
{ {
id: OVERLAY_RUN_ITEMS_ID, id: CanvasHelpers.OVERLAY_RUN_ITEMS_ID,
label, label,
cssClass: 'connection-output-items-label', cssClass: 'connection-output-items-label',
location: .5, location: .5,
}, },
]); ]);
showOrHideItemsLabel(conn); CanvasHelpers.showOrHideItemsLabel(conn);
showOrHideMidpointArrow(conn); CanvasHelpers.showOrHideMidpointArrow(conn);
}); });
}); });
}); });

View file

@ -1,5 +1,7 @@
import { INodeUi, IZoomConfig, XYPosition } from "@/Interface"; import { getStyleTokenValue } from "@/components/helpers";
import { Connection } from "jsplumb"; import { START_NODE_TYPE } from "@/constants";
import { IBounds, INodeUi, IZoomConfig, XYPosition } from "@/Interface";
import { Connection, OverlaySpec, PaintStyle } from "jsplumb";
export const OVERLAY_DROP_NODE_ID = 'drop-add-node'; export const OVERLAY_DROP_NODE_ID = 'drop-add-node';
export const OVERLAY_MIDPOINT_ARROW_ID = 'midpoint-arrow'; export const OVERLAY_MIDPOINT_ARROW_ID = 'midpoint-arrow';
@ -9,15 +11,111 @@ export const OVERLAY_CONNECTION_ACTIONS_ID = 'connection-actions';
export const JSPLUMB_FLOWCHART_STUB = 26; export const JSPLUMB_FLOWCHART_STUB = 26;
export const OVERLAY_INPUT_NAME_LABEL = 'input-name-label'; export const OVERLAY_INPUT_NAME_LABEL = 'input-name-label';
const _MIN_X_TO_SHOW_OUTPUT_LABEL = 90; const MIN_X_TO_SHOW_OUTPUT_LABEL = 90;
const _MIN_Y_TO_SHOW_OUTPUT_LABEL = 100; const MIN_Y_TO_SHOW_OUTPUT_LABEL = 100;
interface ICorners { export const NODE_SIZE = 100;
minX: number; export const DEFAULT_START_POSITION_X = 240;
minY: number; export const DEFAULT_START_POSITION_Y = 300;
maxX: number; export const HEADER_HEIGHT = 65;
maxY: number; export const SIDEBAR_WIDTH = 65;
} export const MAX_X_TO_PUSH_DOWNSTREAM_NODES = 300;
export const PUSH_NODES_OFFSET = 200;
export const DEFAULT_START_NODE = {
name: 'Start',
type: START_NODE_TYPE,
typeVersion: 1,
position: [
DEFAULT_START_POSITION_X,
DEFAULT_START_POSITION_Y,
] as XYPosition,
parameters: {},
};
export const CONNECTOR_PAINT_STYLE_DEFAULT: PaintStyle = {
stroke: getStyleTokenValue('--color-foreground-dark'),
strokeWidth: 2,
outlineWidth: 12,
outlineStroke: 'transparent',
};
export const CONNECTOR_PAINT_STYLE_PRIMARY = {
...CONNECTOR_PAINT_STYLE_DEFAULT,
stroke: getStyleTokenValue('--color-primary'),
};
export const CONNECTOR_PAINT_STYLE_SUCCESS = {
...CONNECTOR_PAINT_STYLE_DEFAULT,
stroke: getStyleTokenValue('--color-success'),
};
export const CONNECTOR_TYPE_STRIGHT = ['Straight'];
export const getFlowChartType = (connection: Connection) => {
const inputIndex = connection.__meta ? connection.__meta.targetOutputIndex : 0;
const outputIndex = connection.__meta ? connection.__meta.sourceOutputIndex : 0;
const outputEndpoint = connection.endpoints[0];
const outputOverlay = outputEndpoint.getOverlay('output-name-label');
let labelOffset = 0;
if (outputOverlay && outputOverlay.label && outputOverlay.label.length > 1) {
labelOffset = 16;
}
return ['N8nFlowchart', {
cornerRadius: 4,
stub: JSPLUMB_FLOWCHART_STUB + 10 * outputIndex + 10 * inputIndex + labelOffset,
gap: 5,
alwaysRespectStubs: true,
yOffset: NODE_SIZE,
loopbackMinimum: 140,
}];
};
export const CONNECTOR_ARROW_OVERLAYS: OverlaySpec[] = [
[
'Arrow',
{
id: OVERLAY_ENDPOINT_ARROW_ID,
location: 1,
width: 12,
foldback: 1,
length: 10,
visible: true,
},
],
[
'Arrow',
{
id: OVERLAY_MIDPOINT_ARROW_ID,
location: 0.5,
width: 12,
foldback: 1,
length: 10,
visible: false,
},
],
];
export const CONNECTOR_DROP_NODE_OVERLAY: OverlaySpec[] = [
[
'Label',
{
id: OVERLAY_DROP_NODE_ID,
label: 'Drop connection<br />to create node',
cssClass: 'drop-add-node-label',
location: 0.5,
visible: true,
},
],
];
export const addOverlays = (connection: Connection, overlays: OverlaySpec[]) => {
overlays.forEach((overlay: OverlaySpec) => {
connection.addOverlay(overlay);
});
};
export const getLeftmostTopNode = (nodes: INodeUi[]): INodeUi => { export const getLeftmostTopNode = (nodes: INodeUi[]): INodeUi => {
return nodes.reduce((leftmostTop, node) => { return nodes.reduce((leftmostTop, node) => {
@ -29,8 +127,8 @@ export const getLeftmostTopNode = (nodes: INodeUi[]): INodeUi => {
}); });
}; };
export const getWorkflowCorners = (nodes: INodeUi[]): ICorners => { export const getWorkflowCorners = (nodes: INodeUi[]): IBounds => {
return nodes.reduce((accu: ICorners, node: INodeUi) => { return nodes.reduce((accu: IBounds, node: INodeUi) => {
if (node.position[0] < accu.minX) { if (node.position[0] < accu.minX) {
accu.minX = node.position[0]; accu.minX = node.position[0];
} }
@ -147,7 +245,7 @@ export const showOrHideItemsLabel = (connection: Connection) => {
const [diffX, diffY] = getConnectorLengths(connection); const [diffX, diffY] = getConnectorLengths(connection);
if (diffX < _MIN_X_TO_SHOW_OUTPUT_LABEL && diffY < _MIN_Y_TO_SHOW_OUTPUT_LABEL) { if (diffX < MIN_X_TO_SHOW_OUTPUT_LABEL && diffY < MIN_Y_TO_SHOW_OUTPUT_LABEL) {
overlay.setVisible(false); overlay.setVisible(false);
} }
else { else {