mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(editor): Add support for Simulate nodes to new canvas
This commit is contained in:
parent
2c03f8640e
commit
f667c3ec48
|
@ -45,6 +45,7 @@ import { onKeyDown, onKeyUp, useThrottleFn } from '@vueuse/core';
|
|||
import CanvasArrowHeadMarker from './elements/edges/CanvasArrowHeadMarker.vue';
|
||||
import CanvasBackground from './elements/background/CanvasBackground.vue';
|
||||
import { useCanvasTraversal } from '@/composables/useCanvasTraversal';
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { useCanvasNodeHover } from '@/composables/useCanvasNodeHover';
|
||||
import { useCanvasLayout } from '@/composables/useCanvasLayout';
|
||||
|
@ -96,6 +97,7 @@ const props = withDefaults(
|
|||
defineProps<{
|
||||
id?: string;
|
||||
nodes: CanvasNode[];
|
||||
nodeTypeDescriptions: Record<string, INodeTypeDescription>;
|
||||
connections: CanvasConnection[];
|
||||
controlsPosition?: PanelPosition;
|
||||
eventBus?: EventBus<CanvasEventBusEvents>;
|
||||
|
@ -804,6 +806,10 @@ provide(CanvasKey, {
|
|||
:event-bus="eventBus"
|
||||
:hovered="nodesHoveredById[nodeProps.id]"
|
||||
:nearby-hovered="nodeProps.id === hoveredTriggerNode.id.value"
|
||||
:node-type-description="
|
||||
nodeTypeDescriptions[`${nodeProps.data.type}@${nodeProps.data.typeVersion}`]
|
||||
"
|
||||
:simulated-node-type-description="nodeTypeDescriptions[nodeProps.data.simulatedType]"
|
||||
@delete="onDeleteNode"
|
||||
@run="onRunNode"
|
||||
@select="onSelectNode"
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import Canvas from '@/components/canvas/Canvas.vue';
|
||||
import { computed, ref, toRef, useCssModule } from 'vue';
|
||||
import type { Workflow } from 'n8n-workflow';
|
||||
import type { INodeTypeDescription, Workflow } from 'n8n-workflow';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
import { useCanvasMapping } from '@/composables/useCanvasMapping';
|
||||
import type { EventBus } from '@n8n/utils/event-bus';
|
||||
import { createEventBus } from '@n8n/utils/event-bus';
|
||||
import type { CanvasEventBusEvents } from '@/types';
|
||||
import { useVueFlow } from '@vue-flow/core';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { SIMULATE_NODE_TYPE, SIMULATE_TRIGGER_NODE_TYPE } from '@/constants';
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
|
@ -34,6 +36,8 @@ const props = withDefaults(
|
|||
|
||||
const $style = useCssModule();
|
||||
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
|
||||
const { onNodesInitialized } = useVueFlow({ id: props.id });
|
||||
|
||||
const workflow = toRef(props, 'workflow');
|
||||
|
@ -52,6 +56,33 @@ const { nodes: mappedNodes, connections: mappedConnections } = useCanvasMapping(
|
|||
workflowObject,
|
||||
});
|
||||
|
||||
const nodeTypeDescriptions = computed(() => {
|
||||
return mappedNodes.value.reduce<Record<string, INodeTypeDescription>>((acc, node) => {
|
||||
if (!node.data) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (node.data.simulatedType) {
|
||||
const simulatedNodeType = nodeTypesStore.getNodeType(node.data.simulatedType);
|
||||
if (simulatedNodeType) {
|
||||
acc[simulatedNodeType.name] = simulatedNodeType;
|
||||
}
|
||||
}
|
||||
|
||||
const key = `${node.data.type}@${node.data.typeVersion}`;
|
||||
if (acc[key]) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const nodeTypeDescription = nodeTypesStore.getNodeType(node.data.type, node.data.typeVersion);
|
||||
if (nodeTypeDescription) {
|
||||
acc[key] = nodeTypeDescription;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
|
||||
const initialFitViewDone = ref(false); // Workaround for https://github.com/bcakmakoglu/vue-flow/issues/1636
|
||||
onNodesInitialized(() => {
|
||||
if (!initialFitViewDone.value || props.showFallbackNodes) {
|
||||
|
@ -68,6 +99,7 @@ onNodesInitialized(() => {
|
|||
v-if="workflow"
|
||||
:id="id"
|
||||
:nodes="mappedNodes"
|
||||
:node-type-descriptions="nodeTypeDescriptions"
|
||||
:connections="mappedConnections"
|
||||
:event-bus="eventBus"
|
||||
:read-only="readOnly"
|
||||
|
|
|
@ -18,7 +18,6 @@ import type {
|
|||
} from '@/types';
|
||||
import { CanvasConnectionMode, CanvasNodeRenderType } from '@/types';
|
||||
import NodeIcon from '@/components/NodeIcon.vue';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import CanvasNodeToolbar from '@/components/canvas/elements/nodes/CanvasNodeToolbar.vue';
|
||||
import CanvasNodeRenderer from '@/components/canvas/elements/nodes/CanvasNodeRenderer.vue';
|
||||
import CanvasHandleRenderer from '@/components/canvas/elements/handles/CanvasHandleRenderer.vue';
|
||||
|
@ -36,12 +35,15 @@ import type { EventBus } from '@n8n/utils/event-bus';
|
|||
import { createEventBus } from '@n8n/utils/event-bus';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import CanvasNodeTrigger from '@/components/canvas/elements/nodes/render-types/parts/CanvasNodeTrigger.vue';
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
type Props = NodeProps<CanvasNodeData> & {
|
||||
readOnly?: boolean;
|
||||
eventBus?: EventBus<CanvasEventBusEvents>;
|
||||
hovered?: boolean;
|
||||
nearbyHovered?: boolean;
|
||||
nodeTypeDescription: INodeTypeDescription;
|
||||
simulatedNodeTypeDescription?: INodeTypeDescription;
|
||||
};
|
||||
|
||||
const slots = defineSlots<{
|
||||
|
@ -71,7 +73,6 @@ const style = useCssModule();
|
|||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const contextMenu = useContextMenu();
|
||||
|
||||
const { connectingHandle } = useCanvas();
|
||||
|
@ -98,10 +99,6 @@ const {
|
|||
|
||||
const isDisabled = computed(() => props.data.disabled);
|
||||
|
||||
const nodeTypeDescription = computed(() => {
|
||||
return nodeTypesStore.getNodeType(props.data.type, props.data.typeVersion);
|
||||
});
|
||||
|
||||
const classes = computed(() => ({
|
||||
[style.canvasNode]: true,
|
||||
[style.showToolbar]: showToolbar.value,
|
||||
|
@ -407,7 +404,7 @@ onBeforeUnmount(() => {
|
|||
@open:contextmenu="onOpenContextMenuFromNode"
|
||||
>
|
||||
<NodeIcon
|
||||
:node-type="nodeTypeDescription"
|
||||
:node-type="simulatedNodeTypeDescription ?? nodeTypeDescription"
|
||||
:size="nodeIconSize"
|
||||
:shrink="false"
|
||||
:disabled="isDisabled"
|
||||
|
|
|
@ -44,7 +44,14 @@ import {
|
|||
WAIT_INDEFINITELY,
|
||||
} from 'n8n-workflow';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import { CUSTOM_API_CALL_KEY, FORM_NODE_TYPE, STICKY_NODE_TYPE, WAIT_NODE_TYPE } from '@/constants';
|
||||
import {
|
||||
CUSTOM_API_CALL_KEY,
|
||||
FORM_NODE_TYPE,
|
||||
SIMULATE_NODE_TYPE,
|
||||
SIMULATE_TRIGGER_NODE_TYPE,
|
||||
STICKY_NODE_TYPE,
|
||||
WAIT_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { sanitizeHtml } from '@/utils/htmlUtils';
|
||||
import { MarkerType } from '@vue-flow/core';
|
||||
import { useNodeHelpers } from './useNodeHelpers';
|
||||
|
@ -510,6 +517,26 @@ export function useCanvasMapping({
|
|||
);
|
||||
});
|
||||
|
||||
const simulatedNodeTypesById = computed(() => {
|
||||
return nodes.value.reduce<Record<string, string | undefined>>((acc, node) => {
|
||||
if ([SIMULATE_NODE_TYPE, SIMULATE_TRIGGER_NODE_TYPE].includes(node.type)) {
|
||||
const icon = node.parameters?.icon as string;
|
||||
const iconValue = workflowObject.value.expression.getSimpleParameterValue(
|
||||
node,
|
||||
icon,
|
||||
'internal',
|
||||
{},
|
||||
);
|
||||
|
||||
if (iconValue && typeof iconValue === 'string') {
|
||||
acc[node.id] = iconValue;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
|
||||
const mappedNodes = computed<CanvasNode[]>(() => [
|
||||
...nodes.value.map<CanvasNode>((node) => {
|
||||
const inputConnections = workflowObject.value.connectionsByDestinationNode[node.name] ?? {};
|
||||
|
@ -521,6 +548,7 @@ export function useCanvasMapping({
|
|||
subtitle: nodeSubtitleById.value[node.id] ?? '',
|
||||
type: node.type,
|
||||
typeVersion: node.typeVersion,
|
||||
simulatedType: simulatedNodeTypesById.value[node.id],
|
||||
disabled: node.disabled,
|
||||
inputs: nodeInputsById.value[node.id] ?? [],
|
||||
outputs: nodeOutputsById.value[node.id] ?? [],
|
||||
|
|
|
@ -95,6 +95,7 @@ export interface CanvasNodeData {
|
|||
subtitle: string;
|
||||
type: INodeUi['type'];
|
||||
typeVersion: INodeUi['typeVersion'];
|
||||
simulatedType?: INodeUi['type'];
|
||||
disabled: INodeUi['disabled'];
|
||||
inputs: CanvasConnectionPort[];
|
||||
outputs: CanvasConnectionPort[];
|
||||
|
|
Loading…
Reference in a new issue