2021-11-19 01:17:13 -08:00
|
|
|
import { IEndpointOptions, INodeUi, XYPosition } from '@/Interface';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
import mixins from 'vue-typed-mixins';
|
|
|
|
|
2020-10-23 04:44:34 -07:00
|
|
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
2019-06-23 03:35:23 -07:00
|
|
|
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
2021-10-18 20:57:49 -07:00
|
|
|
import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE } from '@/constants';
|
2021-11-19 01:17:13 -08:00
|
|
|
import * as CanvasHelpers from '@/views/canvasHelpers';
|
|
|
|
import { Endpoint } from 'jsplumb';
|
|
|
|
|
|
|
|
import {
|
|
|
|
INodeTypeDescription,
|
|
|
|
} from 'n8n-workflow';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2020-10-23 04:44:34 -07:00
|
|
|
export const nodeBase = mixins(
|
|
|
|
deviceSupportHelpers,
|
|
|
|
nodeIndex,
|
|
|
|
).extend({
|
2019-06-23 03:35:23 -07:00
|
|
|
mounted () {
|
|
|
|
// Initialize the node
|
|
|
|
if (this.data !== null) {
|
|
|
|
this.__addNode(this.data);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
data (): INodeUi {
|
2021-11-19 01:17:13 -08:00
|
|
|
return this.$store.getters.getNodeByName(this.name);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2021-11-19 01:17:13 -08:00
|
|
|
nodeId (): string {
|
2019-06-23 03:35:23 -07:00
|
|
|
return NODE_NAME_PREFIX + this.nodeIndex;
|
|
|
|
},
|
|
|
|
nodeIndex (): string {
|
|
|
|
return this.$store.getters.getNodeIndex(this.data.name).toString();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
props: [
|
|
|
|
'name',
|
|
|
|
'instance',
|
2020-05-24 05:06:22 -07:00
|
|
|
'isReadOnly',
|
2021-08-07 00:35:59 -07:00
|
|
|
'isActive',
|
2021-11-19 01:17:13 -08:00
|
|
|
'hideActions',
|
2019-06-23 03:35:23 -07:00
|
|
|
],
|
|
|
|
methods: {
|
2021-11-19 01:17:13 -08:00
|
|
|
__addInputEndpoints (node: INodeUi, nodeTypeData: INodeTypeDescription) {
|
2019-06-23 03:35:23 -07:00
|
|
|
// Add Inputs
|
2021-11-19 01:17:13 -08:00
|
|
|
let index;
|
|
|
|
const indexData: {
|
2019-06-23 03:35:23 -07:00
|
|
|
[key: string]: number;
|
|
|
|
} = {};
|
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
nodeTypeData.inputs.forEach((inputName: string, i: number) => {
|
2019-06-23 03:35:23 -07:00
|
|
|
// Increment the index for inputs with current name
|
|
|
|
if (indexData.hasOwnProperty(inputName)) {
|
|
|
|
indexData[inputName]++;
|
|
|
|
} else {
|
|
|
|
indexData[inputName] = 0;
|
|
|
|
}
|
|
|
|
index = indexData[inputName];
|
|
|
|
|
|
|
|
// Get the position of the anchor depending on how many it has
|
2021-11-19 01:17:13 -08:00
|
|
|
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.input[nodeTypeData.inputs.length][index];
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
const newEndpointData: IEndpointOptions = {
|
|
|
|
uuid: CanvasHelpers.getInputEndpointUUID(this.nodeIndex, index),
|
2019-06-23 03:35:23 -07:00
|
|
|
anchor: anchorPosition,
|
2021-11-19 01:17:13 -08:00
|
|
|
maxConnections: -1,
|
|
|
|
endpoint: 'Rectangle',
|
|
|
|
endpointStyle: CanvasHelpers.getInputEndpointStyle(nodeTypeData, '--color-foreground-xdark'),
|
|
|
|
endpointHoverStyle: CanvasHelpers.getInputEndpointStyle(nodeTypeData, '--color-primary'),
|
2019-06-23 03:35:23 -07:00
|
|
|
isSource: false,
|
2020-05-24 05:06:22 -07:00
|
|
|
isTarget: !this.isReadOnly,
|
2019-06-23 03:35:23 -07:00
|
|
|
parameters: {
|
|
|
|
nodeIndex: this.nodeIndex,
|
|
|
|
type: inputName,
|
|
|
|
index,
|
|
|
|
},
|
2021-11-19 01:17:13 -08:00
|
|
|
enabled: !this.isReadOnly,
|
|
|
|
dragAllowedWhenFull: true,
|
2019-06-23 03:35:23 -07:00
|
|
|
dropOptions: {
|
|
|
|
tolerance: 'touch',
|
|
|
|
hoverClass: 'dropHover',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-08-02 06:56:05 -07:00
|
|
|
if (nodeTypeData.inputNames) {
|
|
|
|
// Apply input names if they got set
|
|
|
|
newEndpointData.overlays = [
|
2021-11-19 01:17:13 -08:00
|
|
|
CanvasHelpers.getInputNameOverlay(nodeTypeData.inputNames[index]),
|
2019-08-02 06:56:05 -07:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
const endpoint: Endpoint = this.instance.addEndpoint(this.nodeId, newEndpointData);
|
|
|
|
endpoint.__meta = {
|
|
|
|
nodeName: node.name,
|
|
|
|
index: i,
|
|
|
|
};
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2019-08-02 06:56:05 -07:00
|
|
|
// TODO: Activate again if it makes sense. Currently makes problems when removing
|
|
|
|
// connection on which the input has a name. It does not get hidden because
|
|
|
|
// the endpoint to which it connects when letting it go over the node is
|
|
|
|
// different to the regular one (have different ids). So that seems to make
|
|
|
|
// problems when hiding the input-name.
|
|
|
|
|
|
|
|
// if (index === 0 && inputName === 'main') {
|
|
|
|
// // Make the first main-input the default one to connect to when connection gets dropped on node
|
2021-11-19 01:17:13 -08:00
|
|
|
// this.instance.makeTarget(this.nodeId, newEndpointData);
|
2019-08-02 06:56:05 -07:00
|
|
|
// }
|
2019-06-23 03:35:23 -07:00
|
|
|
});
|
2021-11-19 01:17:13 -08:00
|
|
|
},
|
|
|
|
__addOutputEndpoints(node: INodeUi, nodeTypeData: INodeTypeDescription) {
|
|
|
|
let index;
|
|
|
|
const indexData: {
|
|
|
|
[key: string]: number;
|
|
|
|
} = {};
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
nodeTypeData.outputs.forEach((inputName: string, i: number) => {
|
2019-06-23 03:35:23 -07:00
|
|
|
// Increment the index for outputs with current name
|
|
|
|
if (indexData.hasOwnProperty(inputName)) {
|
|
|
|
indexData[inputName]++;
|
|
|
|
} else {
|
|
|
|
indexData[inputName] = 0;
|
|
|
|
}
|
|
|
|
index = indexData[inputName];
|
|
|
|
|
|
|
|
// Get the position of the anchor depending on how many it has
|
2021-11-19 01:17:13 -08:00
|
|
|
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.output[nodeTypeData.outputs.length][index];
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
const newEndpointData: IEndpointOptions = {
|
|
|
|
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeIndex, index),
|
2019-06-23 03:35:23 -07:00
|
|
|
anchor: anchorPosition,
|
2021-11-19 01:17:13 -08:00
|
|
|
maxConnections: -1,
|
|
|
|
endpoint: 'Dot',
|
|
|
|
endpointStyle: CanvasHelpers.getOutputEndpointStyle(nodeTypeData, '--color-foreground-xdark'),
|
|
|
|
endpointHoverStyle: CanvasHelpers.getOutputEndpointStyle(nodeTypeData, '--color-primary'),
|
|
|
|
isSource: true,
|
2019-06-23 03:35:23 -07:00
|
|
|
isTarget: false,
|
2021-11-19 01:17:13 -08:00
|
|
|
enabled: !this.isReadOnly,
|
2019-06-23 03:35:23 -07:00
|
|
|
parameters: {
|
|
|
|
nodeIndex: this.nodeIndex,
|
|
|
|
type: inputName,
|
|
|
|
index,
|
|
|
|
},
|
2021-11-19 01:17:13 -08:00
|
|
|
dragAllowedWhenFull: false,
|
2019-06-23 03:35:23 -07:00
|
|
|
dragProxy: ['Rectangle', { width: 1, height: 1, strokeWidth: 0 }],
|
|
|
|
};
|
|
|
|
|
|
|
|
if (nodeTypeData.outputNames) {
|
|
|
|
// Apply output names if they got set
|
|
|
|
newEndpointData.overlays = [
|
2021-11-19 01:17:13 -08:00
|
|
|
CanvasHelpers.getOutputNameOverlay(nodeTypeData.outputNames[index]),
|
2019-06-23 03:35:23 -07:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
const endpoint: Endpoint = this.instance.addEndpoint(this.nodeId, newEndpointData);
|
|
|
|
endpoint.__meta = {
|
|
|
|
nodeName: node.name,
|
|
|
|
index: i,
|
|
|
|
};
|
2019-06-23 03:35:23 -07:00
|
|
|
});
|
2021-11-19 01:17:13 -08:00
|
|
|
},
|
|
|
|
__makeInstanceDraggable(node: INodeUi) {
|
2020-06-07 15:34:15 -07:00
|
|
|
// TODO: This caused problems with displaying old information
|
|
|
|
// https://github.com/jsplumb/katavorio/wiki
|
|
|
|
// https://jsplumb.github.io/jsplumb/home.html
|
|
|
|
// Make nodes draggable
|
2021-11-19 01:17:13 -08:00
|
|
|
this.instance.draggable(this.nodeId, {
|
|
|
|
grid: [CanvasHelpers.GRID_SIZE, CanvasHelpers.GRID_SIZE],
|
2020-06-07 15:34:15 -07:00
|
|
|
start: (params: { e: MouseEvent }) => {
|
|
|
|
if (this.isReadOnly === true) {
|
|
|
|
// Do not allow to move nodes in readOnly mode
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.e && !this.$store.getters.isNodeSelected(this.data.name)) {
|
|
|
|
// Only the node which gets dragged directly gets an event, for all others it is
|
|
|
|
// undefined. So check if the currently dragged node is selected and if not clear
|
|
|
|
// the drag-selection.
|
|
|
|
this.instance.clearDragSelection();
|
|
|
|
this.$store.commit('resetSelectedNodes');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$store.commit('addActiveAction', 'dragActive');
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
stop: (params: { e: MouseEvent }) => {
|
|
|
|
if (this.$store.getters.isActionActive('dragActive')) {
|
|
|
|
const moveNodes = this.$store.getters.getSelectedNodes.slice();
|
|
|
|
const selectedNodeNames = moveNodes.map((node: INodeUi) => node.name);
|
|
|
|
if (!selectedNodeNames.includes(this.data.name)) {
|
|
|
|
// If the current node is not in selected add it to the nodes which
|
|
|
|
// got moved manually
|
|
|
|
moveNodes.push(this.data);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2020-06-07 15:34:15 -07:00
|
|
|
// This does for some reason just get called once for the node that got clicked
|
|
|
|
// even though "start" and "drag" gets called for all. So lets do for now
|
|
|
|
// some dirty DOM query to get the new positions till I have more time to
|
|
|
|
// create a proper solution
|
2021-11-19 01:17:13 -08:00
|
|
|
let newNodePositon: XYPosition;
|
2020-06-07 15:34:15 -07:00
|
|
|
moveNodes.forEach((node: INodeUi) => {
|
|
|
|
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
|
|
|
|
const element = document.getElementById(nodeElement);
|
|
|
|
if (element === null) {
|
|
|
|
return;
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2020-06-07 15:34:15 -07:00
|
|
|
newNodePositon = [
|
|
|
|
parseInt(element.style.left!.slice(0, -2), 10),
|
|
|
|
parseInt(element.style.top!.slice(0, -2), 10),
|
|
|
|
];
|
|
|
|
|
|
|
|
const updateInformation = {
|
|
|
|
name: node.name,
|
|
|
|
properties: {
|
|
|
|
// @ts-ignore, draggable does not have definitions
|
|
|
|
position: newNodePositon,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
this.$store.commit('updateNodeProperties', updateInformation);
|
|
|
|
});
|
2021-11-19 01:17:13 -08:00
|
|
|
|
|
|
|
this.$emit('moved', node);
|
2020-06-07 15:34:15 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
filter: '.node-description, .node-description .node-name, .node-description .node-subtitle',
|
|
|
|
});
|
2021-11-19 01:17:13 -08:00
|
|
|
},
|
|
|
|
__addNode (node: INodeUi) {
|
|
|
|
let nodeTypeData = this.$store.getters.nodeType(node.type) as INodeTypeDescription | null;
|
|
|
|
if (!nodeTypeData) {
|
|
|
|
// If node type is not know use by default the base.noOp data to display it
|
|
|
|
nodeTypeData = this.$store.getters.nodeType(NO_OP_NODE_TYPE) as INodeTypeDescription;
|
|
|
|
}
|
2020-06-07 15:34:15 -07:00
|
|
|
|
2021-11-19 01:17:13 -08:00
|
|
|
this.__addInputEndpoints(node, nodeTypeData);
|
|
|
|
this.__addOutputEndpoints(node, nodeTypeData);
|
|
|
|
this.__makeInstanceDraggable(node);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2021-08-08 02:14:09 -07:00
|
|
|
touchEnd(e: MouseEvent) {
|
|
|
|
if (this.isTouchDevice) {
|
|
|
|
if (this.$store.getters.isActionActive('dragActive')) {
|
|
|
|
this.$store.commit('removeActiveAction', 'dragActive');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-10-23 09:15:52 -07:00
|
|
|
mouseLeftClick (e: MouseEvent) {
|
2020-12-23 23:37:13 -08:00
|
|
|
// @ts-ignore
|
|
|
|
const path = e.path || (e.composedPath && e.composedPath());
|
|
|
|
for (let index = 0; index < path.length; index++) {
|
|
|
|
if (path[index].className && typeof path[index].className === 'string' && path[index].className.includes('no-select-on-click')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 09:15:52 -07:00
|
|
|
if (!this.isTouchDevice) {
|
|
|
|
if (this.$store.getters.isActionActive('dragActive')) {
|
|
|
|
this.$store.commit('removeActiveAction', 'dragActive');
|
2019-07-17 10:05:03 -07:00
|
|
|
} else {
|
2020-10-23 09:15:52 -07:00
|
|
|
if (this.isCtrlKeyPressed(e) === false) {
|
|
|
|
this.$emit('deselectAllNodes');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.$store.getters.isNodeSelected(this.data.name)) {
|
|
|
|
this.$emit('deselectNode', this.name);
|
|
|
|
} else {
|
|
|
|
this.$emit('nodeSelected', this.name);
|
|
|
|
}
|
2019-07-17 10:05:03 -07:00
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|