mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Make adjustments to status icon and connector port position in canvas (#16469)
This commit is contained in:
@@ -94,6 +94,7 @@ describe('CanvasNode', () => {
|
|||||||
inputs: [
|
inputs: [
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
{ type: NodeConnectionTypes.Main, index: 0 },
|
||||||
{ type: NodeConnectionTypes.AiAgent, index: 0, required: true },
|
{ type: NodeConnectionTypes.AiAgent, index: 0, required: true },
|
||||||
|
{ type: NodeConnectionTypes.AiMemory, index: 0 },
|
||||||
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
||||||
],
|
],
|
||||||
outputs: [],
|
outputs: [],
|
||||||
@@ -109,8 +110,9 @@ describe('CanvasNode', () => {
|
|||||||
|
|
||||||
const inputHandles = getAllByTestId('canvas-node-input-handle');
|
const inputHandles = getAllByTestId('canvas-node-input-handle');
|
||||||
|
|
||||||
expect(inputHandles[1]).toHaveStyle('left: 20%');
|
expect(inputHandles[1]).toHaveStyle('left: 40px');
|
||||||
expect(inputHandles[2]).toHaveStyle('left: 80%');
|
expect(inputHandles[2]).toHaveStyle('left: 160px');
|
||||||
|
expect(inputHandles[3]).toHaveStyle('left: 200px');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import type { EventBus } from '@n8n/utils/event-bus';
|
|||||||
import { createEventBus } from '@n8n/utils/event-bus';
|
import { createEventBus } from '@n8n/utils/event-bus';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
import CanvasNodeTrigger from '@/components/canvas/elements/nodes/render-types/parts/CanvasNodeTrigger.vue';
|
import CanvasNodeTrigger from '@/components/canvas/elements/nodes/render-types/parts/CanvasNodeTrigger.vue';
|
||||||
|
import { GRID_SIZE } from '@/utils/nodeViewUtils';
|
||||||
|
|
||||||
type Props = NodeProps<CanvasNodeData> & {
|
type Props = NodeProps<CanvasNodeData> & {
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
@@ -183,6 +184,10 @@ const createEndpointMappingFn =
|
|||||||
connectingHandle.value?.nodeId === props.id &&
|
connectingHandle.value?.nodeId === props.id &&
|
||||||
connectingHandle.value?.handleType === handleType &&
|
connectingHandle.value?.handleType === handleType &&
|
||||||
connectingHandle.value?.handleId === handleId;
|
connectingHandle.value?.handleId === handleId;
|
||||||
|
const offsetValue =
|
||||||
|
position === Position.Bottom
|
||||||
|
? `${GRID_SIZE * (2 + index * 2)}px`
|
||||||
|
: `${(100 / (endpoints.length + 1)) * (index + 1)}%`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...endpoint,
|
...endpoint,
|
||||||
@@ -191,7 +196,7 @@ const createEndpointMappingFn =
|
|||||||
isConnecting,
|
isConnecting,
|
||||||
position,
|
position,
|
||||||
offset: {
|
offset: {
|
||||||
[offsetAxis]: `${(100 / (endpoints.length + 1)) * (index + 1)}%`,
|
[offsetAxis]: offsetValue,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,84 +33,41 @@ describe('CanvasNodeDefault', () => {
|
|||||||
expect(getByTestId('canvas-default-node')).toMatchSnapshot();
|
expect(getByTestId('canvas-default-node')).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inputs', () => {
|
describe('inputs and outputs', () => {
|
||||||
it('should adjust height css variable based on the number of inputs (1 input)', () => {
|
it.each([
|
||||||
const { getByText } = renderComponent({
|
[1, 1, '100px'],
|
||||||
global: {
|
[3, 1, '100px'],
|
||||||
provide: {
|
[4, 1, '140px'],
|
||||||
...createCanvasNodeProvide({
|
[1, 1, '100px'],
|
||||||
data: {
|
[1, 3, '100px'],
|
||||||
inputs: [{ type: NodeConnectionTypes.Main, index: 0 }],
|
[1, 4, '140px'],
|
||||||
},
|
[4, 4, '140px'],
|
||||||
}),
|
])(
|
||||||
|
'should adjust height css variable based on the number of inputs and outputs (%i inputs, %i outputs)',
|
||||||
|
(inputCount, outputCount, expected) => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
global: {
|
||||||
|
provide: {
|
||||||
|
...createCanvasNodeProvide({
|
||||||
|
data: {
|
||||||
|
inputs: Array.from({ length: inputCount }).map(() => ({
|
||||||
|
type: NodeConnectionTypes.Main,
|
||||||
|
index: 0,
|
||||||
|
})),
|
||||||
|
outputs: Array.from({ length: outputCount }).map(() => ({
|
||||||
|
type: NodeConnectionTypes.Main,
|
||||||
|
index: 0,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const nodeElement = getByText('Test Node').closest('.node');
|
const nodeElement = getByText('Test Node').closest('.node');
|
||||||
expect(nodeElement).toHaveStyle({ '--canvas-node--main-input-count': '1' }); // height calculation based on the number of inputs
|
expect(nodeElement).toHaveStyle({ '--canvas-node--height': expected });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
it('should adjust height css variable based on the number of inputs (multiple inputs)', () => {
|
|
||||||
const { getByText } = renderComponent({
|
|
||||||
global: {
|
|
||||||
provide: {
|
|
||||||
...createCanvasNodeProvide({
|
|
||||||
data: {
|
|
||||||
inputs: [
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeElement = getByText('Test Node').closest('.node');
|
|
||||||
expect(nodeElement).toHaveStyle({ '--canvas-node--main-input-count': '3' }); // height calculation based on the number of inputs
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('outputs', () => {
|
|
||||||
it('should adjust height css variable based on the number of outputs (1 output)', () => {
|
|
||||||
const { getByText } = renderComponent({
|
|
||||||
global: {
|
|
||||||
provide: {
|
|
||||||
...createCanvasNodeProvide({
|
|
||||||
data: {
|
|
||||||
outputs: [{ type: NodeConnectionTypes.Main, index: 0 }],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeElement = getByText('Test Node').closest('.node');
|
|
||||||
expect(nodeElement).toHaveStyle({ '--canvas-node--main-output-count': '1' }); // height calculation based on the number of outputs
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should adjust height css variable based on the number of outputs (multiple outputs)', () => {
|
|
||||||
const { getByText } = renderComponent({
|
|
||||||
global: {
|
|
||||||
provide: {
|
|
||||||
...createCanvasNodeProvide({
|
|
||||||
data: {
|
|
||||||
outputs: [
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeElement = getByText('Test Node').closest('.node');
|
|
||||||
expect(nodeElement).toHaveStyle({ '--canvas-node--main-output-count': '3' }); // height calculation based on the number of outputs
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('selected', () => {
|
describe('selected', () => {
|
||||||
@@ -244,33 +201,67 @@ describe('CanvasNodeDefault', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('inputs', () => {
|
describe('inputs', () => {
|
||||||
it('should adjust width css variable based on the number of non-main inputs', () => {
|
it.each([
|
||||||
const { getByText } = renderComponent({
|
[
|
||||||
global: {
|
'1 required',
|
||||||
provide: {
|
[{ type: NodeConnectionTypes.AiLanguageModel, index: 0, required: true }],
|
||||||
...createCanvasNodeProvide({
|
'240px',
|
||||||
data: {
|
],
|
||||||
inputs: [
|
[
|
||||||
{ type: NodeConnectionTypes.Main, index: 0 },
|
'2 required, 1 optional',
|
||||||
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
[
|
||||||
{ type: NodeConnectionTypes.AiDocument, index: 0, required: true },
|
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
||||||
{ type: NodeConnectionTypes.AiMemory, index: 0, required: true },
|
{ type: NodeConnectionTypes.AiDocument, index: 0, required: true },
|
||||||
],
|
{ type: NodeConnectionTypes.AiMemory, index: 0, required: true },
|
||||||
render: {
|
],
|
||||||
type: CanvasNodeRenderType.Default,
|
'240px',
|
||||||
options: {
|
],
|
||||||
configurable: true,
|
[
|
||||||
|
'2 required, 2 optional',
|
||||||
|
[
|
||||||
|
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
||||||
|
{ type: NodeConnectionTypes.AiLanguageModel, index: 0 },
|
||||||
|
{ type: NodeConnectionTypes.AiDocument, index: 0, required: true },
|
||||||
|
{ type: NodeConnectionTypes.AiMemory, index: 0, required: true },
|
||||||
|
],
|
||||||
|
'240px',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'1 required, 4 optional',
|
||||||
|
[
|
||||||
|
{ type: NodeConnectionTypes.AiLanguageModel, index: 0, required: true },
|
||||||
|
{ type: NodeConnectionTypes.AiTool, index: 0 },
|
||||||
|
{ type: NodeConnectionTypes.AiDocument, index: 0 },
|
||||||
|
{ type: NodeConnectionTypes.AiMemory, index: 0 },
|
||||||
|
{ type: NodeConnectionTypes.AiMemory, index: 0 },
|
||||||
|
],
|
||||||
|
'280px',
|
||||||
|
],
|
||||||
|
])(
|
||||||
|
'should adjust width css variable based on the number of non-main inputs (%s)',
|
||||||
|
(_, nonMainInputs, expected) => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
global: {
|
||||||
|
provide: {
|
||||||
|
...createCanvasNodeProvide({
|
||||||
|
data: {
|
||||||
|
inputs: [{ type: NodeConnectionTypes.Main, index: 0 }, ...nonMainInputs],
|
||||||
|
render: {
|
||||||
|
type: CanvasNodeRenderType.Default,
|
||||||
|
options: {
|
||||||
|
configurable: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const nodeElement = getByText('Test Node').closest('.node');
|
const nodeElement = getByText('Test Node').closest('.node');
|
||||||
expect(nodeElement).toHaveStyle({ '--configurable-node--input-count': '3' });
|
expect(nodeElement).toHaveStyle({ '--canvas-node--width': expected });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { computed, ref, useCssModule, watch } from 'vue';
|
|||||||
import { useNodeConnections } from '@/composables/useNodeConnections';
|
import { useNodeConnections } from '@/composables/useNodeConnections';
|
||||||
import { useI18n } from '@n8n/i18n';
|
import { useI18n } from '@n8n/i18n';
|
||||||
import { useCanvasNode } from '@/composables/useCanvasNode';
|
import { useCanvasNode } from '@/composables/useCanvasNode';
|
||||||
import { NODE_INSERT_SPACER_BETWEEN_INPUT_GROUPS } from '@/constants';
|
|
||||||
import type { CanvasNodeDefaultRender } from '@/types';
|
import type { CanvasNodeDefaultRender } from '@/types';
|
||||||
import { useCanvas } from '@/composables/useCanvas';
|
import { useCanvas } from '@/composables/useCanvas';
|
||||||
import { useNodeSettingsInCanvas } from '@/components/canvas/composables/useNodeSettingsInCanvas';
|
import { useNodeSettingsInCanvas } from '@/components/canvas/composables/useNodeSettingsInCanvas';
|
||||||
|
import { calculateNodeSize } from '@/utils/nodeViewUtils';
|
||||||
import ExperimentalCanvasNodeSettings from '../../../components/ExperimentalCanvasNodeSettings.vue';
|
import ExperimentalCanvasNodeSettings from '../../../components/ExperimentalCanvasNodeSettings.vue';
|
||||||
|
|
||||||
const $style = useCssModule();
|
const $style = useCssModule();
|
||||||
@@ -36,18 +36,12 @@ const {
|
|||||||
hasIssues,
|
hasIssues,
|
||||||
render,
|
render,
|
||||||
} = useCanvasNode();
|
} = useCanvasNode();
|
||||||
const {
|
const { mainOutputs, mainOutputConnections, mainInputs, mainInputConnections, nonMainInputs } =
|
||||||
mainOutputs,
|
useNodeConnections({
|
||||||
mainOutputConnections,
|
inputs,
|
||||||
mainInputs,
|
outputs,
|
||||||
mainInputConnections,
|
connections,
|
||||||
nonMainInputs,
|
});
|
||||||
requiredNonMainInputs,
|
|
||||||
} = useNodeConnections({
|
|
||||||
inputs,
|
|
||||||
outputs,
|
|
||||||
connections,
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderOptions = computed(() => render.value.options as CanvasNodeDefaultRender['options']);
|
const renderOptions = computed(() => render.value.options as CanvasNodeDefaultRender['options']);
|
||||||
|
|
||||||
@@ -71,29 +65,24 @@ const classes = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const styles = computed(() => {
|
const iconSize = computed(() => (renderOptions.value.configuration ? 30 : 40));
|
||||||
const stylesObject: Record<string, string | number> = {};
|
|
||||||
|
|
||||||
if (renderOptions.value.configurable) {
|
const nodeSize = computed(() =>
|
||||||
let spacerCount = 0;
|
calculateNodeSize(
|
||||||
if (NODE_INSERT_SPACER_BETWEEN_INPUT_GROUPS && requiredNonMainInputs.value.length > 0) {
|
renderOptions.value.configuration ?? false,
|
||||||
const requiredNonMainInputsCount = requiredNonMainInputs.value.length;
|
renderOptions.value.configurable ?? false,
|
||||||
const optionalNonMainInputsCount = nonMainInputs.value.length - requiredNonMainInputsCount;
|
mainInputs.value.length,
|
||||||
spacerCount = requiredNonMainInputsCount > 0 && optionalNonMainInputsCount > 0 ? 1 : 0;
|
mainOutputs.value.length,
|
||||||
}
|
nonMainInputs.value.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
stylesObject['--configurable-node--input-count'] = nonMainInputs.value.length + spacerCount;
|
const styles = computed(() => ({
|
||||||
}
|
'--canvas-node--width': `${nodeSize.value.width}px`,
|
||||||
|
'--canvas-node--height': `${nodeSize.value.height}px`,
|
||||||
if (nodeSettingsZoom.value !== undefined) {
|
'--node-icon-size': `${iconSize.value}px`,
|
||||||
stylesObject['--zoom'] = nodeSettingsZoom.value;
|
...(nodeSettingsZoom.value === undefined ? {} : { '--zoom': nodeSettingsZoom.value }),
|
||||||
}
|
}));
|
||||||
|
|
||||||
stylesObject['--canvas-node--main-input-count'] = mainInputs.value.length;
|
|
||||||
stylesObject['--canvas-node--main-output-count'] = mainOutputs.value.length;
|
|
||||||
|
|
||||||
return stylesObject;
|
|
||||||
});
|
|
||||||
|
|
||||||
const dataTestId = computed(() => {
|
const dataTestId = computed(() => {
|
||||||
let type = 'default';
|
let type = 'default';
|
||||||
@@ -117,8 +106,6 @@ const isStrikethroughVisible = computed(() => {
|
|||||||
return isDisabled.value && isSingleMainInputNode && isSingleMainOutputNode;
|
return isDisabled.value && isSingleMainInputNode && isSingleMainOutputNode;
|
||||||
});
|
});
|
||||||
|
|
||||||
const iconSize = computed(() => (renderOptions.value.configuration ? 30 : 40));
|
|
||||||
|
|
||||||
const iconSource = computed(() => renderOptions.value.icon);
|
const iconSource = computed(() => renderOptions.value.icon);
|
||||||
|
|
||||||
const showTooltip = ref(false);
|
const showTooltip = ref(false);
|
||||||
@@ -156,8 +143,13 @@ function onActivate(event: MouseEvent) {
|
|||||||
<ExperimentalCanvasNodeSettings v-if="nodeSettingsZoom !== undefined" :node-id="id" />
|
<ExperimentalCanvasNodeSettings v-if="nodeSettingsZoom !== undefined" :node-id="id" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<CanvasNodeTooltip v-if="renderOptions.tooltip" :visible="showTooltip" />
|
<CanvasNodeTooltip v-if="renderOptions.tooltip" :visible="showTooltip" />
|
||||||
<NodeIcon :icon-source="iconSource" :size="iconSize" :shrink="false" :disabled="isDisabled" />
|
<NodeIcon
|
||||||
<CanvasNodeStatusIcons v-if="!isDisabled" :class="$style.statusIcons" />
|
:icon-source="iconSource"
|
||||||
|
:size="iconSize"
|
||||||
|
:shrink="false"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:class="$style.icon"
|
||||||
|
/>
|
||||||
<CanvasNodeDisabledStrikeThrough v-if="isStrikethroughVisible" />
|
<CanvasNodeDisabledStrikeThrough v-if="isStrikethroughVisible" />
|
||||||
<div :class="$style.description">
|
<div :class="$style.description">
|
||||||
<div v-if="label" :class="$style.label">
|
<div v-if="label" :class="$style.label">
|
||||||
@@ -168,24 +160,14 @@ function onActivate(event: MouseEvent) {
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="subtitle" :class="$style.subtitle">{{ subtitle }}</div>
|
<div v-if="subtitle" :class="$style.subtitle">{{ subtitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<CanvasNodeStatusIcons v-if="!isDisabled" :class="$style.statusIcons" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.node {
|
.node {
|
||||||
--canvas-node--max-vertical-handles: max(
|
|
||||||
var(--canvas-node--main-input-count),
|
|
||||||
var(--canvas-node--main-output-count),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
--canvas-node--height: calc(100px + max(0, var(--canvas-node--max-vertical-handles) - 3) * 42px);
|
|
||||||
--canvas-node--width: 100px;
|
|
||||||
--canvas-node-border-width: 2px;
|
--canvas-node-border-width: 2px;
|
||||||
--configurable-node--min-input-count: 4;
|
|
||||||
--configurable-node--input-width: 64px;
|
|
||||||
--configurable-node--icon-offset: 30px;
|
|
||||||
--configurable-node--icon-size: 30px;
|
|
||||||
--trigger-node--border-radius: 36px;
|
--trigger-node--border-radius: 36px;
|
||||||
--canvas-node--status-icons-offset: var(--spacing-3xs);
|
--canvas-node--status-icons-offset: var(--spacing-3xs);
|
||||||
--node-icon-color: var(--color-foreground-dark);
|
--node-icon-color: var(--color-foreground-dark);
|
||||||
@@ -207,7 +189,6 @@ function onActivate(event: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.settingsView {
|
&.settingsView {
|
||||||
/*margin-top: calc(var(--canvas-node--width) * 0.8);*/
|
|
||||||
height: calc(var(--canvas-node--height) * 2.4) !important;
|
height: calc(var(--canvas-node--height) * 2.4) !important;
|
||||||
width: calc(var(--canvas-node--width) * 1.6) !important;
|
width: calc(var(--canvas-node--width) * 1.6) !important;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -226,13 +207,10 @@ function onActivate(event: MouseEvent) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
&.configuration {
|
&.configuration {
|
||||||
--canvas-node--width: 80px;
|
|
||||||
--canvas-node--height: 80px;
|
|
||||||
|
|
||||||
background: var(--canvas-node--background, var(--node-type-supplemental-background));
|
background: var(--canvas-node--background, var(--node-type-supplemental-background));
|
||||||
border: var(--canvas-node-border-width) solid
|
border: var(--canvas-node-border-width) solid
|
||||||
var(--canvas-node--border-color, var(--color-foreground-dark));
|
var(--canvas-node--border-color, var(--color-foreground-dark));
|
||||||
border-radius: 50px;
|
border-radius: calc(var(--canvas-node--height) / 2);
|
||||||
|
|
||||||
.statusIcons {
|
.statusIcons {
|
||||||
right: unset;
|
right: unset;
|
||||||
@@ -240,16 +218,8 @@ function onActivate(event: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.configurable {
|
&.configurable {
|
||||||
--canvas-node--height: 100px;
|
.icon {
|
||||||
--canvas-node--width: calc(
|
margin-left: calc(40px - (var(--node-icon-size)) / 2 - var(--canvas-node-border-width));
|
||||||
max(var(--configurable-node--input-count, 4), var(--configurable-node--min-input-count)) *
|
|
||||||
var(--configurable-node--input-width)
|
|
||||||
);
|
|
||||||
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
:global(.n8n-node-icon) {
|
|
||||||
margin-left: var(--configurable-node--icon-offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
@@ -260,11 +230,10 @@ function onActivate(event: MouseEvent) {
|
|||||||
margin-right: var(--spacing-s);
|
margin-right: var(--spacing-s);
|
||||||
width: auto;
|
width: auto;
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
max-width: calc(
|
overflow: hidden;
|
||||||
var(--canvas-node--width) - var(--configurable-node--icon-offset) - var(
|
text-overflow: ellipsis;
|
||||||
--configurable-node--icon-size
|
flex-grow: 1;
|
||||||
) - 2 * var(--spacing-s)
|
flex-shrink: 1;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
@@ -276,11 +245,19 @@ function onActivate(event: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.configuration {
|
&.configuration {
|
||||||
--canvas-node--height: 75px;
|
.icon {
|
||||||
|
margin-left: calc((var(--canvas-node--height) - var(--node-icon-size)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
.statusIcons {
|
&:not(.running) {
|
||||||
right: calc(-1 * var(--spacing-2xs));
|
.statusIcons {
|
||||||
bottom: 0;
|
position: static;
|
||||||
|
margin-right: var(--spacing-2xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-right: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,7 +310,6 @@ function onActivate(event: MouseEvent) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-4xs);
|
gap: var(--spacing-4xs);
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.label,
|
.label,
|
||||||
@@ -367,4 +343,9 @@ function onActivate(event: MouseEvent) {
|
|||||||
bottom: var(--canvas-node--status-icons-offset);
|
bottom: var(--canvas-node--status-icons-offset);
|
||||||
right: var(--canvas-node--status-icons-offset);
|
right: var(--canvas-node--status-icons-offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ exports[`CanvasNodeDefault > configurable > should render configurable node corr
|
|||||||
<div
|
<div
|
||||||
class="node configurable"
|
class="node configurable"
|
||||||
data-test-id="canvas-configurable-node"
|
data-test-id="canvas-configurable-node"
|
||||||
style="--configurable-node--input-count: 0; --canvas-node--main-input-count: 0; --canvas-node--main-output-count: 0;"
|
style="--canvas-node--width: 240px; --canvas-node--height: 100px; --node-icon-size: 40px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<div
|
<div
|
||||||
class="n8n-node-icon"
|
class="n8n-node-icon icon icon"
|
||||||
shrink="false"
|
shrink="false"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -27,7 +27,6 @@ exports[`CanvasNodeDefault > configurable > should render configurable node corr
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
@@ -43,6 +42,7 @@ exports[`CanvasNodeDefault > configurable > should render configurable node corr
|
|||||||
Test Node Subtitle
|
Test Node Subtitle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -51,12 +51,12 @@ exports[`CanvasNodeDefault > configuration > should render configurable configur
|
|||||||
<div
|
<div
|
||||||
class="node configurable configuration"
|
class="node configurable configuration"
|
||||||
data-test-id="canvas-configurable-node"
|
data-test-id="canvas-configurable-node"
|
||||||
style="--configurable-node--input-count: 0; --canvas-node--main-input-count: 0; --canvas-node--main-output-count: 0;"
|
style="--canvas-node--width: 240px; --canvas-node--height: 75px; --node-icon-size: 30px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<div
|
<div
|
||||||
class="n8n-node-icon"
|
class="n8n-node-icon icon icon"
|
||||||
shrink="false"
|
shrink="false"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -74,7 +74,6 @@ exports[`CanvasNodeDefault > configuration > should render configurable configur
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
@@ -90,6 +89,7 @@ exports[`CanvasNodeDefault > configuration > should render configurable configur
|
|||||||
Test Node Subtitle
|
Test Node Subtitle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -98,12 +98,12 @@ exports[`CanvasNodeDefault > configuration > should render configuration node co
|
|||||||
<div
|
<div
|
||||||
class="node configuration"
|
class="node configuration"
|
||||||
data-test-id="canvas-configuration-node"
|
data-test-id="canvas-configuration-node"
|
||||||
style="--canvas-node--main-input-count: 0; --canvas-node--main-output-count: 0;"
|
style="--canvas-node--width: 80px; --canvas-node--height: 80px; --node-icon-size: 30px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<div
|
<div
|
||||||
class="n8n-node-icon"
|
class="n8n-node-icon icon icon"
|
||||||
shrink="false"
|
shrink="false"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -121,7 +121,6 @@ exports[`CanvasNodeDefault > configuration > should render configuration node co
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
@@ -137,6 +136,7 @@ exports[`CanvasNodeDefault > configuration > should render configuration node co
|
|||||||
Test Node Subtitle
|
Test Node Subtitle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -145,12 +145,12 @@ exports[`CanvasNodeDefault > should render node correctly 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="node"
|
class="node"
|
||||||
data-test-id="canvas-default-node"
|
data-test-id="canvas-default-node"
|
||||||
style="--canvas-node--main-input-count: 0; --canvas-node--main-output-count: 0;"
|
style="--canvas-node--width: 100px; --canvas-node--height: 100px; --node-icon-size: 40px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<div
|
<div
|
||||||
class="n8n-node-icon"
|
class="n8n-node-icon icon icon"
|
||||||
shrink="false"
|
shrink="false"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -168,7 +168,6 @@ exports[`CanvasNodeDefault > should render node correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
@@ -184,6 +183,7 @@ exports[`CanvasNodeDefault > should render node correctly 1`] = `
|
|||||||
Test Node Subtitle
|
Test Node Subtitle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -192,12 +192,12 @@ exports[`CanvasNodeDefault > trigger > should render trigger node correctly 1`]
|
|||||||
<div
|
<div
|
||||||
class="node trigger"
|
class="node trigger"
|
||||||
data-test-id="canvas-trigger-node"
|
data-test-id="canvas-trigger-node"
|
||||||
style="--canvas-node--main-input-count: 0; --canvas-node--main-output-count: 0;"
|
style="--canvas-node--width: 100px; --canvas-node--height: 100px; --node-icon-size: 40px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<div
|
<div
|
||||||
class="n8n-node-icon"
|
class="n8n-node-icon icon icon"
|
||||||
shrink="false"
|
shrink="false"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -215,7 +215,6 @@ exports[`CanvasNodeDefault > trigger > should render trigger node correctly 1`]
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
<!--v-if-->
|
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
>
|
>
|
||||||
@@ -231,6 +230,7 @@ exports[`CanvasNodeDefault > trigger > should render trigger node correctly 1`]
|
|||||||
Test Node Subtitle
|
Test Node Subtitle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ export const EXPRESSIONS_DOCS_URL = `https://${DOCS_DOMAIN}/code-examples/expres
|
|||||||
export const N8N_PRICING_PAGE_URL = 'https://n8n.io/pricing';
|
export const N8N_PRICING_PAGE_URL = 'https://n8n.io/pricing';
|
||||||
export const N8N_MAIN_GITHUB_REPO_URL = 'https://github.com/n8n-io/n8n';
|
export const N8N_MAIN_GITHUB_REPO_URL = 'https://github.com/n8n-io/n8n';
|
||||||
|
|
||||||
export const NODE_INSERT_SPACER_BETWEEN_INPUT_GROUPS = false;
|
export const NODE_MIN_INPUT_ITEMS_COUNT = 5;
|
||||||
export const NODE_MIN_INPUT_ITEMS_COUNT = 4;
|
|
||||||
|
|
||||||
// node types
|
// node types
|
||||||
export const BAMBOO_HR_NODE_TYPE = 'n8n-nodes-base.bambooHr';
|
export const BAMBOO_HR_NODE_TYPE = 'n8n-nodes-base.bambooHr';
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { CanvasConnectionMode } from '@/types';
|
|||||||
import type { INodeUi } from '@/Interface';
|
import type { INodeUi } from '@/Interface';
|
||||||
import type { Connection } from '@vue-flow/core';
|
import type { Connection } from '@vue-flow/core';
|
||||||
import { createTestNode } from '@/__tests__/mocks';
|
import { createTestNode } from '@/__tests__/mocks';
|
||||||
|
import { NODE_MIN_INPUT_ITEMS_COUNT } from '@/constants';
|
||||||
|
|
||||||
vi.mock('uuid', () => ({
|
vi.mock('uuid', () => ({
|
||||||
v4: vi.fn(() => 'mock-uuid'),
|
v4: vi.fn(() => 'mock-uuid'),
|
||||||
@@ -990,52 +991,56 @@ describe('insertSpacersBetweenEndpoints', () => {
|
|||||||
it('should insert spacers when there are less than min endpoints count', () => {
|
it('should insert spacers when there are less than min endpoints count', () => {
|
||||||
const endpoints = [{ index: 0, required: true }];
|
const endpoints = [{ index: 0, required: true }];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual([{ index: 0, required: true }, null, null, null]);
|
expect(result).toEqual([{ index: 0, required: true }, null, null, null, null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not insert spacers when there are at least min endpoints count', () => {
|
it('should not insert spacers when there are at least min endpoints count', () => {
|
||||||
const endpoints = [{ index: 0, required: true }, { index: 1 }, { index: 2 }, { index: 3 }];
|
const endpoints = [
|
||||||
|
{ index: 0, required: true },
|
||||||
|
{ index: 1 },
|
||||||
|
{ index: 2 },
|
||||||
|
{ index: 3 },
|
||||||
|
{ index: 4 },
|
||||||
|
];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual(endpoints);
|
expect(result).toEqual(endpoints);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle zero required endpoints', () => {
|
it('should handle zero required endpoints', () => {
|
||||||
const endpoints = [{ index: 0, required: false }];
|
const endpoints = [{ index: 0, required: false }];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual([null, null, null, { index: 0, required: false }]);
|
expect(result).toEqual([null, null, null, null, { index: 0, required: false }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle no endpoints', () => {
|
it('should handle no endpoints', () => {
|
||||||
const endpoints: Array<{ index: number; required: boolean }> = [];
|
const endpoints: Array<{ index: number; required: boolean }> = [];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual([null, null, null, null]);
|
expect(result).toEqual([null, null, null, null, null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle required endpoints greater than min endpoints count', () => {
|
it('should handle required endpoints greater than NODE_MIN_INPUT_ITEMS_COUNT', () => {
|
||||||
const endpoints = [
|
const endpoints = Array.from({ length: NODE_MIN_INPUT_ITEMS_COUNT + 1 }).map((_, index) => ({
|
||||||
{ index: 0, required: true },
|
index,
|
||||||
{ index: 1, required: true },
|
required: true,
|
||||||
{ index: 2, required: true },
|
}));
|
||||||
{ index: 3, required: true },
|
|
||||||
{ index: 4, required: true },
|
|
||||||
];
|
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual(endpoints);
|
expect(result).toEqual(endpoints);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should insert spacers between required and optional endpoints', () => {
|
it('should insert spacers between required and optional endpoints', () => {
|
||||||
const endpoints = [{ index: 0, required: true }, { index: 1, required: true }, { index: 2 }];
|
const endpoints = [{ index: 0, required: true }, { index: 1, required: true }, { index: 2 }];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{ index: 0, required: true },
|
{ index: 0, required: true },
|
||||||
{ index: 1, required: true },
|
{ index: 1, required: true },
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
{ index: 2 },
|
{ index: 2 },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -1043,14 +1048,7 @@ describe('insertSpacersBetweenEndpoints', () => {
|
|||||||
it('should handle required endpoints count greater than endpoints length', () => {
|
it('should handle required endpoints count greater than endpoints length', () => {
|
||||||
const endpoints = [{ index: 0, required: true }];
|
const endpoints = [{ index: 0, required: true }];
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 4);
|
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount);
|
||||||
expect(result).toEqual([{ index: 0, required: true }, null, null, null]);
|
expect(result).toEqual([{ index: 0, required: true }, null, null, null, null]);
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle min endpoints count less than required endpoints count', () => {
|
|
||||||
const endpoints = [{ index: 0, required: false }];
|
|
||||||
const requiredEndpointsCount = endpoints.filter((endpoint) => endpoint.required).length;
|
|
||||||
const result = insertSpacersBetweenEndpoints(endpoints, requiredEndpointsCount, 0);
|
|
||||||
expect(result).toEqual([{ index: 0, required: false }]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { CanvasConnectionMode } from '@/types';
|
|||||||
import type { Connection } from '@vue-flow/core';
|
import type { Connection } from '@vue-flow/core';
|
||||||
import { isValidCanvasConnectionMode, isValidNodeConnectionType } from '@/utils/typeGuards';
|
import { isValidCanvasConnectionMode, isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||||
|
import { NODE_MIN_INPUT_ITEMS_COUNT } from '@/constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps multiple legacy n8n connections to VueFlow connections
|
* Maps multiple legacy n8n connections to VueFlow connections
|
||||||
@@ -246,18 +247,15 @@ export function checkOverlap(node1: BoundingBox, node2: BoundingBox) {
|
|||||||
/**
|
/**
|
||||||
* Inserts spacers between endpoints to visually separate them
|
* Inserts spacers between endpoints to visually separate them
|
||||||
*/
|
*/
|
||||||
export function insertSpacersBetweenEndpoints<T>(
|
export function insertSpacersBetweenEndpoints<T>(endpoints: T[], requiredEndpointsCount = 0) {
|
||||||
endpoints: T[],
|
|
||||||
requiredEndpointsCount = 0,
|
|
||||||
minEndpointsCount = 4,
|
|
||||||
) {
|
|
||||||
const endpointsWithSpacers: Array<T | null> = [...endpoints];
|
const endpointsWithSpacers: Array<T | null> = [...endpoints];
|
||||||
const optionalNonMainInputsCount = endpointsWithSpacers.length - requiredEndpointsCount;
|
const optionalNonMainInputsCount = endpointsWithSpacers.length - requiredEndpointsCount;
|
||||||
const spacerCount = minEndpointsCount - requiredEndpointsCount - optionalNonMainInputsCount;
|
const spacerCount =
|
||||||
|
NODE_MIN_INPUT_ITEMS_COUNT - requiredEndpointsCount - optionalNonMainInputsCount;
|
||||||
|
|
||||||
// Insert `null` in between required non-main inputs and non-required non-main inputs
|
// Insert `null` in between required non-main inputs and non-required non-main inputs
|
||||||
// to separate them visually if there are less than 4 inputs in total
|
// to separate them visually if there are less than `minEndpointsCount` inputs in total
|
||||||
if (endpointsWithSpacers.length < minEndpointsCount) {
|
if (endpointsWithSpacers.length < NODE_MIN_INPUT_ITEMS_COUNT) {
|
||||||
for (let i = 0; i < spacerCount; i++) {
|
for (let i = 0; i < spacerCount; i++) {
|
||||||
endpointsWithSpacers.splice(requiredEndpointsCount + i, 0, null);
|
endpointsWithSpacers.splice(requiredEndpointsCount + i, 0, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
AI_MCP_TOOL_NODE_TYPE,
|
AI_MCP_TOOL_NODE_TYPE,
|
||||||
LIST_LIKE_NODE_OPERATIONS,
|
LIST_LIKE_NODE_OPERATIONS,
|
||||||
MAIN_HEADER_TABS,
|
MAIN_HEADER_TABS,
|
||||||
|
NODE_MIN_INPUT_ITEMS_COUNT,
|
||||||
NODE_POSITION_CONFLICT_ALLOWLIST,
|
NODE_POSITION_CONFLICT_ALLOWLIST,
|
||||||
SET_NODE_TYPE,
|
SET_NODE_TYPE,
|
||||||
SPLIT_IN_BATCHES_NODE_TYPE,
|
SPLIT_IN_BATCHES_NODE_TYPE,
|
||||||
@@ -33,14 +34,14 @@ import {
|
|||||||
|
|
||||||
export const GRID_SIZE = 20;
|
export const GRID_SIZE = 20;
|
||||||
|
|
||||||
export const NODE_SIZE = 100;
|
export const NODE_SIZE = GRID_SIZE * 5;
|
||||||
export const DEFAULT_NODE_SIZE: [number, number] = [100, 100];
|
export const DEFAULT_NODE_SIZE: [number, number] = [GRID_SIZE * 5, GRID_SIZE * 5];
|
||||||
export const CONFIGURATION_NODE_SIZE: [number, number] = [80, 80];
|
export const CONFIGURATION_NODE_SIZE: [number, number] = [GRID_SIZE * 4, GRID_SIZE * 4];
|
||||||
export const CONFIGURABLE_NODE_SIZE: [number, number] = [256, 100];
|
export const CONFIGURABLE_NODE_SIZE: [number, number] = [GRID_SIZE * 12, GRID_SIZE * 5];
|
||||||
export const DEFAULT_START_POSITION_X = 180;
|
export const DEFAULT_START_POSITION_X = GRID_SIZE * 9;
|
||||||
export const DEFAULT_START_POSITION_Y = 240;
|
export const DEFAULT_START_POSITION_Y = GRID_SIZE * 12;
|
||||||
export const HEADER_HEIGHT = 65;
|
export const HEADER_HEIGHT = 65;
|
||||||
export const MAX_X_TO_PUSH_DOWNSTREAM_NODES = 300;
|
export const MAX_X_TO_PUSH_DOWNSTREAM_NODES = GRID_SIZE * 15;
|
||||||
export const PUSH_NODES_OFFSET = NODE_SIZE * 2 + GRID_SIZE;
|
export const PUSH_NODES_OFFSET = NODE_SIZE * 2 + GRID_SIZE;
|
||||||
export const DEFAULT_VIEWPORT_BOUNDARIES: ViewportBoundaries = {
|
export const DEFAULT_VIEWPORT_BOUNDARIES: ViewportBoundaries = {
|
||||||
xMin: -Infinity,
|
xMin: -Infinity,
|
||||||
@@ -600,3 +601,27 @@ export function updateViewportToContainNodes(
|
|||||||
zoom,
|
zoom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calculateNodeSize(
|
||||||
|
isConfiguration: boolean,
|
||||||
|
isConfigurable: boolean,
|
||||||
|
mainInputCount: number,
|
||||||
|
mainOutputCount: number,
|
||||||
|
nonMainInputCount: number,
|
||||||
|
): { width: number; height: number } {
|
||||||
|
const maxVerticalHandles = Math.max(mainInputCount, mainOutputCount, 1);
|
||||||
|
const height = 100 + Math.max(0, maxVerticalHandles - 3) * GRID_SIZE * 2;
|
||||||
|
|
||||||
|
if (isConfigurable) {
|
||||||
|
return {
|
||||||
|
width: (Math.max(NODE_MIN_INPUT_ITEMS_COUNT - 1, nonMainInputCount) * 2 + 4) * GRID_SIZE,
|
||||||
|
height: isConfiguration ? 75 : height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConfiguration) {
|
||||||
|
return { width: GRID_SIZE * 4, height: GRID_SIZE * 4 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { width: 100, height };
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user