fix(editor): Tweak configurable node width (#17512)

This commit is contained in:
Suguru Inoue
2025-07-23 17:08:52 +02:00
committed by GitHub
parent d520059ec3
commit 3825f8a806
6 changed files with 26 additions and 23 deletions

View File

@@ -111,8 +111,8 @@ describe('CanvasNode', () => {
const inputHandles = getAllByTestId('canvas-node-input-handle');
expect(inputHandles[1]).toHaveStyle('left: 40px');
expect(inputHandles[2]).toHaveStyle('left: 168px');
expect(inputHandles[3]).toHaveStyle('left: 232px');
expect(inputHandles[2]).toHaveStyle('left: 136px');
expect(inputHandles[3]).toHaveStyle('left: 184px');
});
});

View File

@@ -34,7 +34,7 @@ import type { EventBus } from '@n8n/utils/event-bus';
import { createEventBus } from '@n8n/utils/event-bus';
import isEqual from 'lodash/isEqual';
import CanvasNodeTrigger from '@/components/canvas/elements/nodes/render-types/parts/CanvasNodeTrigger.vue';
import { CONFIGURATION_NODE_OFFSET, GRID_SIZE } from '@/utils/nodeViewUtils';
import { CONFIGURATION_NODE_RADIUS, GRID_SIZE } from '@/utils/nodeViewUtils';
type Props = NodeProps<CanvasNodeData> & {
readOnly?: boolean;
@@ -187,7 +187,7 @@ const createEndpointMappingFn =
connectingHandle.value?.handleId === handleId;
const offsetValue =
position === Position.Bottom
? `${GRID_SIZE * 2 * (1 + index * 2) + CONFIGURATION_NODE_OFFSET}px`
? `${CONFIGURATION_NODE_RADIUS + GRID_SIZE * (3 * index)}px`
: isExperimentalNdvActive.value && endpoints.length === 1
? `${(1 + index) * (GRID_SIZE * 1.5)}px`
: `${(100 / (endpoints.length + 1)) * (index + 1)}%`;

View File

@@ -209,7 +209,7 @@ describe('CanvasNodeDefault', () => {
[
'1 required',
[{ type: NodeConnectionTypes.AiLanguageModel, index: 0, required: true }],
'272px',
'224px',
],
[
'2 required, 1 optional',
@@ -218,7 +218,7 @@ describe('CanvasNodeDefault', () => {
{ type: NodeConnectionTypes.AiDocument, index: 0, required: true },
{ type: NodeConnectionTypes.AiMemory, index: 0, required: true },
],
'272px',
'224px',
],
[
'2 required, 2 optional',
@@ -228,7 +228,7 @@ describe('CanvasNodeDefault', () => {
{ type: NodeConnectionTypes.AiDocument, index: 0, required: true },
{ type: NodeConnectionTypes.AiMemory, index: 0, required: true },
],
'272px',
'224px',
],
[
'1 required, 4 optional',
@@ -239,7 +239,7 @@ describe('CanvasNodeDefault', () => {
{ type: NodeConnectionTypes.AiMemory, index: 0 },
{ type: NodeConnectionTypes.AiMemory, index: 0 },
],
'336px',
'272px',
],
])(
'should adjust width css variable based on the number of non-main inputs (%s)',

View File

@@ -4,7 +4,7 @@ exports[`CanvasNodeDefault > configurable > should render configurable node corr
<div
class="node configurable"
data-test-id="canvas-configurable-node"
style="--canvas-node--width: 272px; --canvas-node--height: 96px; --node-icon-size: 40px;"
style="--canvas-node--width: 224px; --canvas-node--height: 96px; --node-icon-size: 40px;"
>
<!--v-if-->
<div
@@ -57,7 +57,7 @@ exports[`CanvasNodeDefault > configuration > should render configurable configur
<div
class="node configurable configuration"
data-test-id="canvas-configurable-node"
style="--canvas-node--width: 272px; --canvas-node--height: 80px; --node-icon-size: 30px;"
style="--canvas-node--width: 240px; --canvas-node--height: 80px; --node-icon-size: 30px;"
>
<!--v-if-->
<div

View File

@@ -483,21 +483,21 @@ describe('calculateNodeSize', () => {
const nonMainInputCount = 5;
const mainInputCount = 3;
const mainOutputCount = 2;
// width = max(4, 5) * 2 * 16 * 2 = 5 * 2 * 16 * 2 + offset = 336
// width = 80 + 0 + (max(4, 5) - 1) * 16 * 3 = 272
// height = DEFAULT_NODE_SIZE[1] + max(0, max(3,2,1) - 2) * 16 * 2
// maxVerticalHandles = 3
// height = 96 + (3 - 2) * 32 = 96 + 32 = 128
expect(
calculateNodeSize(false, true, mainInputCount, mainOutputCount, nonMainInputCount),
).toEqual({ width: 336, height: 128 });
).toEqual({ width: 272, height: 128 });
});
it('should return configurable configuration node size when both isConfigurable and isConfiguration are true', () => {
const nonMainInputCount = 2;
// width = max(4, 2) * 2 * 16 * 2 = 4 * 2 * 16 * 2 + offset = 272
// width = 80 + 16 + (max(4, 2) - 1) * 16 * 3 = 240
// height = CONFIGURATION_NODE_SIZE[1] = 16 * 5 = 80
expect(calculateNodeSize(true, true, 1, 1, nonMainInputCount)).toEqual({
width: 272,
width: 240,
height: 80,
});
});
@@ -524,12 +524,12 @@ describe('calculateNodeSize', () => {
it('should respect the minimum width for configurable nodes', () => {
const nonMainInputCount = 2; // less than NODE_MIN_INPUT_ITEMS_COUNT
// width = 4 * 2 * 16 * 2 + offset = 272
// width = 80 + 0 + (max(2, 4) - 1) * 16 * 3 = 224
// height = default path, mainInputCount = 1, mainOutputCount = 1
// maxVerticalHandles = 1
// height = 96 + (1 - 2) * 32 = 96 + 0 = 96
expect(calculateNodeSize(false, true, 1, 1, nonMainInputCount)).toEqual({
width: 272,
width: 224,
height: 96,
});
});

View File

@@ -35,7 +35,11 @@ import {
export const GRID_SIZE = 16;
export const DEFAULT_NODE_SIZE: [number, number] = [GRID_SIZE * 6, GRID_SIZE * 6];
export const CONFIGURATION_NODE_SIZE: [number, number] = [GRID_SIZE * 5, GRID_SIZE * 5];
export const CONFIGURATION_NODE_RADIUS = (GRID_SIZE * 5) / 2;
export const CONFIGURATION_NODE_SIZE: [number, number] = [
CONFIGURATION_NODE_RADIUS * 2,
CONFIGURATION_NODE_RADIUS * 2,
]; // the node has circle shape
export const CONFIGURABLE_NODE_SIZE: [number, number] = [GRID_SIZE * 16, GRID_SIZE * 6];
export const DEFAULT_START_POSITION_X = GRID_SIZE * 11;
export const DEFAULT_START_POSITION_Y = GRID_SIZE * 15;
@@ -48,10 +52,6 @@ export const DEFAULT_VIEWPORT_BOUNDARIES: ViewportBoundaries = {
yMax: Infinity,
};
// The top-center of the configuration node is not a multiple of GRID_SIZE,
// therefore we need to offset non-main inputs to align with the nodes top-center
export const CONFIGURATION_NODE_OFFSET = (CONFIGURATION_NODE_SIZE[0] / 2) % GRID_SIZE;
/**
* Utility functions for returning nodes found at the edges of a group
*/
@@ -619,10 +619,13 @@ export function calculateNodeSize(
const height = DEFAULT_NODE_SIZE[1] + Math.max(0, maxVerticalHandles - 2) * GRID_SIZE * 2;
if (isConfigurable) {
const portCount = Math.max(NODE_MIN_INPUT_ITEMS_COUNT, nonMainInputCount);
return {
// Configuration node has extra width so that its centered port aligns to the grid
width:
Math.max(NODE_MIN_INPUT_ITEMS_COUNT, nonMainInputCount) * GRID_SIZE * 4 +
CONFIGURATION_NODE_OFFSET * 2,
CONFIGURATION_NODE_RADIUS * 2 +
GRID_SIZE * ((isConfiguration ? 1 : 0) + (portCount - 1) * 3),
height: isConfiguration ? CONFIGURATION_NODE_SIZE[1] : height,
};
}