From 0015cb4f344647a714f7a5a7f48ed12b7cb31c94 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Mon, 1 Nov 2021 15:38:24 +0100 Subject: [PATCH] refactor helpers --- packages/editor-ui/src/Interface.ts | 8 + .../src/components/mixins/nodeBase.ts | 4 +- .../src/components/mixins/showMessage.ts | 3 +- packages/editor-ui/src/views/NodeView.vue | 242 +++++------------- packages/editor-ui/src/views/canvasHelpers.ts | 124 ++++++++- 5 files changed, 190 insertions(+), 191 deletions(-) diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 820419c19e..864cae1aaa 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -681,3 +681,11 @@ export interface IZoomConfig { scale: number; offset: XYPosition; } + +export interface IBounds { + minX: number; + minY: number; + maxX: number; + maxY: number; +} + diff --git a/packages/editor-ui/src/components/mixins/nodeBase.ts b/packages/editor-ui/src/components/mixins/nodeBase.ts index 7aa0eb6c27..a15651e459 100644 --- a/packages/editor-ui/src/components/mixins/nodeBase.ts +++ b/packages/editor-ui/src/components/mixins/nodeBase.ts @@ -6,7 +6,7 @@ import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers'; import { nodeIndex } from '@/components/mixins/nodeIndex'; import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE } from '@/constants'; import { getStyleTokenValue } from '../helpers'; -import { OVERLAY_INPUT_NAME_LABEL } from '@/views/canvasHelpers'; +import * as CanvasHelpers from '@/views/canvasHelpers'; export const nodeBase = mixins( deviceSupportHelpers, @@ -200,7 +200,7 @@ export const nodeBase = mixins( newEndpointData.overlays = [ ['Label', { - id: OVERLAY_INPUT_NAME_LABEL, + id: CanvasHelpers.OVERLAY_INPUT_NAME_LABEL, location: [-3, 0.5], label: nodeTypeData.inputNames[index], cssClass: 'node-input-endpoint-label', diff --git a/packages/editor-ui/src/components/mixins/showMessage.ts b/packages/editor-ui/src/components/mixins/showMessage.ts index 950f35b3ab..da7452efe5 100644 --- a/packages/editor-ui/src/components/mixins/showMessage.ts +++ b/packages/editor-ui/src/components/mixins/showMessage.ts @@ -110,7 +110,8 @@ export const showMessage = mixins(externalHooks).extend({ 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}
` : ''; this.$showMessage({ title, diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index da01d72d0b..7b9cd93551 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -135,7 +135,7 @@ import NodeCreator from '@/components/NodeCreator/NodeCreator.vue'; import NodeSettings from '@/components/NodeSettings.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 { v4 as uuidv4} from 'uuid'; @@ -158,7 +158,6 @@ import { import { ICredentialsResponse, IExecutionResponse, - IN8nUISettings, IWorkflowDb, IWorkflowData, INodeUi, @@ -174,113 +173,6 @@ import { mapGetters } from 'vuex'; import { getStyleTokenValue } from '@/components/helpers'; 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
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, @@ -557,18 +449,18 @@ export default mixins( const nodes = data.workflow.nodes; 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 diffY = DEFAULT_START_POSITION_Y - leftmostTop.position[1]; + const diffX = CanvasHelpers.DEFAULT_START_POSITION_X - leftmostTop.position[0]; + const diffY = CanvasHelpers.DEFAULT_START_POSITION_Y - leftmostTop.position[1]; 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; }); if (!hasStartNode) { - data.workflow.nodes.push(DEFAULT_START_NODE); + data.workflow.nodes.push(CanvasHelpers.DEFAULT_START_NODE); } this.blankRedirect = true; @@ -976,21 +868,21 @@ export default mixins( }, 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.$store.commit('setNodeViewOffsetPosition', {newOffset: offset}); }, 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.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); }, 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.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); @@ -1021,24 +913,24 @@ export default mixins( 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 diffX = maxX - minX + SIDEBAR_WIDTH + PADDING; + const diffX = maxX - minX + CanvasHelpers.SIDEBAR_WIDTH + PADDING; const scaleX = editorWidth / diffX; const editorHeight = window.innerHeight; - const diffY = maxY - minY + HEADER_HEIGHT + PADDING; + const diffY = maxY - minY + CanvasHelpers.HEADER_HEIGHT + PADDING; const scaleY = editorHeight / diffY; const zoomLevel = Math.min(scaleX, scaleY, 1); - let xOffset = (minX * -1) * zoomLevel + SIDEBAR_WIDTH; // find top right corner - xOffset += (editorWidth - SIDEBAR_WIDTH - (maxX - minX + NODE_SIZE) * zoomLevel) / 2; // add padding to center workflow + let xOffset = (minX * -1) * zoomLevel + CanvasHelpers.SIDEBAR_WIDTH; // find top right corner + 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 - yOffset += (editorHeight - HEADER_HEIGHT - (maxY - minY + NODE_SIZE * 2) * zoomLevel) / 2; // add padding to center workflow + let yOffset = (minY * -1) * zoomLevel + CanvasHelpers.HEADER_HEIGHT; // find top right corner + yOffset += (editorHeight - CanvasHelpers.HEADER_HEIGHT - (maxY - minY + CanvasHelpers.NODE_SIZE * 2) * zoomLevel) / 2; // add padding to center workflow this.setZoomLevel(zoomLevel); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [xOffset, yOffset]}); @@ -1190,7 +1082,7 @@ export default mixins( // Fix the node position as it could be totally offscreen // and the pasted nodes would so not be directly visible to // 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); @@ -1319,14 +1211,14 @@ export default mixins( if (lastSelectedNode) { const lastSelectedConnection = this.lastSelectedConnection; if (lastSelectedConnection) { - const [diffX] = getConnectorLengths(lastSelectedConnection); - if (diffX <= MAX_X_TO_PUSH_DOWNSTREAM_NODES) { - this.pushDownstreamNodes(lastSelectedNode.name, _PUSH_NODES_LENGTH); + const [diffX] = CanvasHelpers.getConnectorLengths(lastSelectedConnection); + if (diffX <= CanvasHelpers.MAX_X_TO_PUSH_DOWNSTREAM_NODES) { + this.pushDownstreamNodes(lastSelectedNode.name, CanvasHelpers.PUSH_NODES_OFFSET); } } 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; } else { @@ -1343,7 +1235,7 @@ export default mixins( // 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 = getNewNodePosition( + newNodeData.position = CanvasHelpers.getNewNodePosition( this.nodes, [lastSelectedNode.position[0] + 200, lastSelectedNode.position[1] + yOffset], [100, 0], @@ -1351,7 +1243,7 @@ export default mixins( } } else { // 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 @@ -1427,12 +1319,12 @@ export default mixins( }, initNodeView () { this.instance.importDefaults({ - Connector: CONNECTOR_TYPE_STRIGHT , + Connector: CanvasHelpers.CONNECTOR_TYPE_STRIGHT, Endpoint: ['Dot', { radius: 5 }], DragOptions: { cursor: 'pointer', zIndex: 5000 }, PaintStyle: { strokeWidth: 2, stroke: getStyleTokenValue('--color-foreground-dark')}, HoverPaintStyle: { stroke: getStyleTokenValue('--color-primary'), lineWidth: 4 }, - ConnectionOverlays: CONNECTOR_ARROW_OVERLAYS, + ConnectionOverlays: CanvasHelpers.CONNECTOR_ARROW_OVERLAYS, Container: '#node-view', }); @@ -1489,18 +1381,18 @@ export default mixins( const hideActions = (connection: Connection | null) => { if (connection) { - hideOverlay(connection, OVERLAY_CONNECTION_ACTIONS_ID); - showOrHideItemsLabel(connection); - showOrHideMidpointArrow(connection); + CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID); + CanvasHelpers.showOrHideItemsLabel(connection); + CanvasHelpers.showOrHideMidpointArrow(connection); } }; const showActions = (connection: Connection | null) => { if (connection) { - showOverlay(connection, OVERLAY_CONNECTION_ACTIONS_ID); - hideOverlay(connection, OVERLAY_RUN_ITEMS_ID); - if (!connection.getOverlay(OVERLAY_RUN_ITEMS_ID)) { - hideOverlay(connection, OVERLAY_MIDPOINT_ARROW_ID); + CanvasHelpers.showOverlay(connection, CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID); + CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_RUN_ITEMS_ID); + if (!connection.getOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID)) { + CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_MIDPOINT_ARROW_ID); } } }; @@ -1519,15 +1411,15 @@ export default mixins( targetOutputIndex: targetInfo.index, }; - const connectorType = getFlowChartType(info.connection); + const connectorType = CanvasHelpers.getFlowChartType(info.connection); info.connection.setConnector(connectorType); - info.connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); - addOverlays(info.connection, CONNECTOR_ARROW_OVERLAYS); + info.connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT); + 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) { let exitTimer: NodeJS.Timeout | undefined; @@ -1583,9 +1475,9 @@ export default mixins( info.connection.addOverlay([ 'Label', { - id: OVERLAY_CONNECTION_ACTIONS_ID, - label: `
${getIcon('plus')}
${getIcon('trash')}
`, - cssClass: OVERLAY_CONNECTION_ACTIONS_ID, + id: CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID, + label: `
${CanvasHelpers.getIcon('plus')}
${CanvasHelpers.getIcon('trash')}
`, + cssClass: CanvasHelpers.OVERLAY_CONNECTION_ACTIONS_ID, visible: false, events: { 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) { inputNameOverlay.setLocation([-4.5, .5]); } @@ -1642,7 +1534,7 @@ export default mixins( } } 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)) { inputNameOverlay.setVisible(true); } @@ -1682,7 +1574,7 @@ export default mixins( }); 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) { // todo inputNameOverlay.setLocation([-3, .5]); @@ -1696,7 +1588,7 @@ export default mixins( // @ts-ignore this.instance.bind('connectionDrag', (connection: Connection) => { this.newNodeInsertPosition = null; - addOverlays(connection, CONNECTOR_DROP_NODE_OVERLAY); + CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_DROP_NODE_OVERLAY); let droppable = false; const onMouseMove = () => { @@ -1707,17 +1599,17 @@ export default mixins( const elements = document.querySelector('div.jtk-endpoint.dropHover'); if (elements && !droppable) { droppable = true; - connection.setConnector(getFlowChartType(connection)); - connection.setPaintStyle(CONNECTOR_PAINT_STYLE_PRIMARY); - addOverlays(connection, CONNECTOR_ARROW_OVERLAYS); - hideOverlay(connection, OVERLAY_DROP_NODE_ID); + connection.setConnector(CanvasHelpers.getFlowChartType(connection)); + connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_PRIMARY); + CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_ARROW_OVERLAYS); + CanvasHelpers.hideOverlay(connection, CanvasHelpers.OVERLAY_DROP_NODE_ID); } else if (!elements && droppable) { droppable = false; - connection.setConnector(CONNECTOR_TYPE_STRIGHT ); - connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); - addOverlays(connection, CONNECTOR_ARROW_OVERLAYS); - showOverlay(connection, OVERLAY_DROP_NODE_ID); + connection.setConnector(CanvasHelpers.CONNECTOR_TYPE_STRIGHT); + connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT); + CanvasHelpers.addOverlays(connection, CanvasHelpers.CONNECTOR_ARROW_OVERLAYS); + CanvasHelpers.showOverlay(connection, CanvasHelpers.OVERLAY_DROP_NODE_ID); } }; @@ -1736,9 +1628,9 @@ export default mixins( await this.$store.dispatch('workflows/setNewWorkflowName'); 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); @@ -1889,7 +1781,7 @@ export default mixins( // Check if node-name is unique else find one that is newNodeData.name = this.getUniqueNodeName(newNodeData.name); - newNodeData.position = getNewNodePosition( + newNodeData.position = CanvasHelpers.getNewNodePosition( this.nodes, [node.position[0], node.position[1] + 140], [0, 140], @@ -1947,8 +1839,8 @@ export default mixins( }) as Connection[]; [...incoming, ...outgoing].forEach((connection: Connection) => { - showOrHideMidpointArrow(connection); - showOrHideItemsLabel(connection); + CanvasHelpers.showOrHideMidpointArrow(connection); + CanvasHelpers.showOrHideItemsLabel(connection); }); }, onNodeRun ({name, data}: {name: string, data: ITaskData[] | null}) { @@ -1957,9 +1849,9 @@ export default mixins( const sourceId = `${NODE_NAME_PREFIX}${sourceIndex}`; const resetConnection = (connection: Connection) => { - connection.removeOverlay(OVERLAY_RUN_ITEMS_ID); - connection.setPaintStyle(CONNECTOR_PAINT_STYLE_DEFAULT); - showOrHideMidpointArrow(connection); + connection.removeOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID); + connection.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_DEFAULT); + CanvasHelpers.showOrHideMidpointArrow(connection); // @ts-ignore if (connection.canvas) { // @ts-ignore @@ -2039,15 +1931,15 @@ export default mixins( return; } - conn.setPaintStyle(CONNECTOR_PAINT_STYLE_SUCCESS); + conn.setPaintStyle(CanvasHelpers.CONNECTOR_PAINT_STYLE_SUCCESS); // @ts-ignore if (conn.canvas) { // @ts-ignore (conn.canvas as Element).classList.add('success'); } - if (conn.getOverlay(OVERLAY_RUN_ITEMS_ID)) { - conn.removeOverlay(OVERLAY_RUN_ITEMS_ID); + if (conn.getOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID)) { + conn.removeOverlay(CanvasHelpers.OVERLAY_RUN_ITEMS_ID); } let label = `${output.total}`; @@ -2057,15 +1949,15 @@ export default mixins( conn.addOverlay([ 'Label', { - id: OVERLAY_RUN_ITEMS_ID, + id: CanvasHelpers.OVERLAY_RUN_ITEMS_ID, label, cssClass: 'connection-output-items-label', location: .5, }, ]); - showOrHideItemsLabel(conn); - showOrHideMidpointArrow(conn); + CanvasHelpers.showOrHideItemsLabel(conn); + CanvasHelpers.showOrHideMidpointArrow(conn); }); }); }); diff --git a/packages/editor-ui/src/views/canvasHelpers.ts b/packages/editor-ui/src/views/canvasHelpers.ts index 9581896c9e..760e9d5c62 100644 --- a/packages/editor-ui/src/views/canvasHelpers.ts +++ b/packages/editor-ui/src/views/canvasHelpers.ts @@ -1,5 +1,7 @@ -import { INodeUi, IZoomConfig, XYPosition } from "@/Interface"; -import { Connection } from "jsplumb"; +import { getStyleTokenValue } from "@/components/helpers"; +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_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 OVERLAY_INPUT_NAME_LABEL = 'input-name-label'; -const _MIN_X_TO_SHOW_OUTPUT_LABEL = 90; -const _MIN_Y_TO_SHOW_OUTPUT_LABEL = 100; +const MIN_X_TO_SHOW_OUTPUT_LABEL = 90; +const MIN_Y_TO_SHOW_OUTPUT_LABEL = 100; -interface ICorners { - minX: number; - minY: number; - maxX: number; - maxY: number; -} +export const NODE_SIZE = 100; +export const DEFAULT_START_POSITION_X = 240; +export const DEFAULT_START_POSITION_Y = 300; +export const HEADER_HEIGHT = 65; +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
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 => { return nodes.reduce((leftmostTop, node) => { @@ -29,8 +127,8 @@ export const getLeftmostTopNode = (nodes: INodeUi[]): INodeUi => { }); }; -export const getWorkflowCorners = (nodes: INodeUi[]): ICorners => { - return nodes.reduce((accu: ICorners, node: INodeUi) => { +export const getWorkflowCorners = (nodes: INodeUi[]): IBounds => { + return nodes.reduce((accu: IBounds, node: INodeUi) => { if (node.position[0] < accu.minX) { accu.minX = node.position[0]; } @@ -147,7 +245,7 @@ export const showOrHideItemsLabel = (connection: 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); } else {