feat(Switch Node): Add support for infinite Switch outputs (#7499)
Github issue / Community forum post (link here to close automatically): https://community.n8n.io/t/add-more-outputs-to-switch-node/3864 --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -184,7 +184,14 @@ import { nodeHelpers } from '@/mixins/nodeHelpers';
|
||||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||
import { pinData } from '@/mixins/pinData';
|
||||
|
||||
import type { IExecutionsSummary, INodeTypeDescription, ITaskData } from 'n8n-workflow';
|
||||
import type {
|
||||
ConnectionTypes,
|
||||
IExecutionsSummary,
|
||||
INodeInputConfiguration,
|
||||
INodeOutputConfiguration,
|
||||
INodeTypeDescription,
|
||||
ITaskData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
|
||||
|
||||
import NodeIcon from '@/components/NodeIcon.vue';
|
||||
@@ -357,9 +364,15 @@ export default defineComponent({
|
||||
top: this.position[1] + 'px',
|
||||
};
|
||||
|
||||
const nonMainInputs = this.inputs.filter((input) => input !== NodeConnectionType.Main);
|
||||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const inputs =
|
||||
NodeHelpers.getNodeInputs(workflow, this.node, this.nodeType) ||
|
||||
([] as Array<ConnectionTypes | INodeInputConfiguration>);
|
||||
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
|
||||
|
||||
const nonMainInputs = inputTypes.filter((input) => input !== NodeConnectionType.Main);
|
||||
if (nonMainInputs.length) {
|
||||
const requiredNonMainInputs = this.inputs.filter(
|
||||
const requiredNonMainInputs = inputs.filter(
|
||||
(input) => typeof input !== 'string' && input.required,
|
||||
);
|
||||
|
||||
@@ -373,6 +386,15 @@ export default defineComponent({
|
||||
styles['--configurable-node-input-count'] = nonMainInputs.length + spacerCount;
|
||||
}
|
||||
|
||||
const outputs =
|
||||
NodeHelpers.getNodeOutputs(workflow, this.node, this.nodeType) ||
|
||||
([] as Array<ConnectionTypes | INodeOutputConfiguration>);
|
||||
|
||||
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
|
||||
|
||||
const mainOutputs = outputTypes.filter((output) => output === NodeConnectionType.Main);
|
||||
styles['--node-main-output-count'] = mainOutputs.length;
|
||||
|
||||
return styles;
|
||||
},
|
||||
nodeClass(): object {
|
||||
@@ -698,7 +720,12 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
.node-wrapper {
|
||||
--node-width: 100px;
|
||||
--node-height: 100px;
|
||||
/*
|
||||
Set the node height to 100px as a base.
|
||||
Increase height by 20px for each output beyond the 4th one.
|
||||
max(0, var(--node-main-output-count, 1) - 4) ensures that we only start counting after the 4th output.
|
||||
*/
|
||||
--node-height: calc(100px + max(0, var(--node-main-output-count, 1) - 4) * 20px);
|
||||
|
||||
--configurable-node-min-input-count: 4;
|
||||
--configurable-node-input-width: 65px;
|
||||
@@ -1158,6 +1185,7 @@ export default defineComponent({
|
||||
--endpoint-size-small: 14px;
|
||||
--endpoint-size-medium: 18px;
|
||||
--stalk-size: 40px;
|
||||
--stalk-switch-size: 60px;
|
||||
--stalk-success-size: 87px;
|
||||
--stalk-success-size-without-label: 40px;
|
||||
--stalk-long-size: 127px;
|
||||
@@ -1405,6 +1433,15 @@ export default defineComponent({
|
||||
margin-top: calc(var(--spacing-l) * -1);
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
// Switch node allows for dynamic connection labels
|
||||
// so we need to make sure the label does not overflow
|
||||
&[data-endpoint-node-type='n8n-nodes-base.switch'] {
|
||||
max-width: calc(var(--stalk-size) - (var(--endpoint-size-small)));
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: calc(var(--endpoint-size-small) + var(--spacing-2xs) + 10px);
|
||||
}
|
||||
}
|
||||
|
||||
.node-input-endpoint-label {
|
||||
@@ -1446,6 +1483,7 @@ export default defineComponent({
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.long-stalk {
|
||||
--stalk-size: var(--stalk-long-size);
|
||||
}
|
||||
@@ -1455,4 +1493,7 @@ export default defineComponent({
|
||||
.ep-success--without-label {
|
||||
--stalk-size: var(--stalk-success-size-without-label);
|
||||
}
|
||||
[data-endpoint-node-type='n8n-nodes-base.switch'] {
|
||||
--stalk-size: var(--stalk-switch-size);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -502,6 +502,7 @@ import type {
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeOutputConfiguration,
|
||||
INodeTypeDescription,
|
||||
IRunData,
|
||||
IRunExecutionData,
|
||||
@@ -543,6 +544,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useToast } from '@/composables';
|
||||
import { isObject } from 'lodash-es';
|
||||
|
||||
const RunDataTable = defineAsyncComponent(async () => import('@/components/RunDataTable.vue'));
|
||||
const RunDataJson = defineAsyncComponent(async () => import('@/components/RunDataJson.vue'));
|
||||
@@ -891,9 +893,8 @@ export default defineComponent({
|
||||
return this.outputIndex;
|
||||
},
|
||||
branches(): ITab[] {
|
||||
function capitalize(name: string) {
|
||||
return name.charAt(0).toLocaleUpperCase() + name.slice(1);
|
||||
}
|
||||
const capitalize = (name: string) => name.charAt(0).toLocaleUpperCase() + name.slice(1);
|
||||
|
||||
const branches: ITab[] = [];
|
||||
|
||||
for (let i = 0; i <= this.maxOutputIndex; i++) {
|
||||
@@ -903,6 +904,7 @@ export default defineComponent({
|
||||
const itemsCount = this.getDataCount(this.runIndex, i);
|
||||
const items = this.$locale.baseText('ndv.output.items', { adjustToNumber: itemsCount });
|
||||
let outputName = this.getOutputName(i);
|
||||
|
||||
if (`${outputName}` === `${i}`) {
|
||||
outputName = `${this.$locale.baseText('ndv.output')} ${outputName}`;
|
||||
} else {
|
||||
@@ -936,6 +938,18 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getResolvedNodeOutputs() {
|
||||
if (this.node && this.nodeType) {
|
||||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(this.node.name);
|
||||
|
||||
if (workflowNode) {
|
||||
const outputs = NodeHelpers.getNodeOutputs(workflow, workflowNode, this.nodeType);
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
},
|
||||
onItemHover(itemIndex: number | null) {
|
||||
if (itemIndex === null) {
|
||||
this.$emit('itemHover', null);
|
||||
@@ -1287,9 +1301,7 @@ export default defineComponent({
|
||||
this.closeBinaryDataDisplay();
|
||||
let outputTypes: ConnectionTypes[] = [];
|
||||
if (this.nodeType !== null && this.node !== null) {
|
||||
const workflow = this.workflowsStore.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(this.node.name);
|
||||
const outputs = NodeHelpers.getNodeOutputs(workflow, workflowNode, this.nodeType);
|
||||
const outputs = this.getResolvedNodeOutputs();
|
||||
outputTypes = NodeHelpers.getConnectionTypes(outputs);
|
||||
}
|
||||
this.connectionType = outputTypes.length === 0 ? NodeConnectionType.Main : outputTypes[0];
|
||||
@@ -1367,6 +1379,12 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const nodeType = this.nodeType;
|
||||
const outputs = this.getResolvedNodeOutputs();
|
||||
const outputConfiguration = outputs?.[outputIndex] as INodeOutputConfiguration;
|
||||
|
||||
if (outputConfiguration && isObject(outputConfiguration)) {
|
||||
return outputConfiguration?.displayName;
|
||||
}
|
||||
if (!nodeType?.outputNames || nodeType.outputNames.length <= outputIndex) {
|
||||
return outputIndex + 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user