mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 11:22:15 +00:00
refactor(editor): Move editor-ui and design-system to frontend dir (no-changelog) (#13564)
This commit is contained in:
208
packages/frontend/editor-ui/src/components/InputNodeSelect.vue
Normal file
208
packages/frontend/editor-ui/src/components/InputNodeSelect.vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { isPresent } from '@/utils/typesUtils';
|
||||
import type { IConnectedNode, Workflow } from 'n8n-workflow';
|
||||
import { computed } from 'vue';
|
||||
import NodeIcon from './NodeIcon.vue';
|
||||
import { truncate } from '@n8n/utils/string/truncate';
|
||||
|
||||
type Props = {
|
||||
nodes: IConnectedNode[];
|
||||
workflow: Workflow;
|
||||
modelValue: string | null;
|
||||
};
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:model-value': [value: string];
|
||||
}>();
|
||||
|
||||
const i18n = useI18n();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const ndvStore = useNDVStore();
|
||||
|
||||
const selectedInputNode = computed(() => workflowsStore.getNodeByName(props.modelValue ?? ''));
|
||||
|
||||
const selectedInputNodeType = computed(() => {
|
||||
const node = selectedInputNode.value;
|
||||
if (!node) return null;
|
||||
|
||||
return nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||
});
|
||||
|
||||
const inputNodes = computed(() =>
|
||||
props.nodes
|
||||
.map((node) => {
|
||||
const fullNode = workflowsStore.getNodeByName(node.name);
|
||||
if (!fullNode) return null;
|
||||
|
||||
return {
|
||||
node: fullNode,
|
||||
type: nodeTypesStore.getNodeType(fullNode.type, fullNode.typeVersion),
|
||||
depth: node.depth,
|
||||
};
|
||||
})
|
||||
.filter(isPresent),
|
||||
);
|
||||
|
||||
const activeNode = computed(() => ndvStore.activeNode);
|
||||
|
||||
const activeNodeType = computed(() => {
|
||||
const node = activeNode.value;
|
||||
if (!node) return null;
|
||||
|
||||
return nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||
});
|
||||
|
||||
const isMultiInputNode = computed(() => {
|
||||
const nodeType = activeNodeType.value;
|
||||
return nodeType !== null && nodeType.inputs.length > 1;
|
||||
});
|
||||
|
||||
const connectedTo = (nodeName: string) => {
|
||||
const connections = ndvStore.ndvNodeInputNumber[nodeName];
|
||||
if (!connections) return '';
|
||||
if (connections.length === 1) {
|
||||
return `Input ${ndvStore.ndvNodeInputNumber[nodeName]}`;
|
||||
}
|
||||
return `Inputs ${ndvStore.ndvNodeInputNumber[nodeName].join(', ')}`;
|
||||
};
|
||||
|
||||
function getMultipleNodesText(nodeName: string): string {
|
||||
if (
|
||||
!nodeName ||
|
||||
!isMultiInputNode.value ||
|
||||
!activeNode.value ||
|
||||
!activeNodeType.value?.inputNames
|
||||
)
|
||||
return '';
|
||||
|
||||
const activeNodeConnections =
|
||||
props.workflow.connectionsByDestinationNode[activeNode.value.name].main || [];
|
||||
// Collect indexes of connected nodes
|
||||
const connectedInputIndexes = activeNodeConnections.reduce((acc: number[], node, index) => {
|
||||
if (node?.[0] && node[0].node === nodeName) return [...acc, index];
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// Match connected input indexes to their names specified by active node
|
||||
const connectedInputs = connectedInputIndexes.map(
|
||||
(inputIndex) => activeNodeType.value?.inputNames?.[inputIndex],
|
||||
);
|
||||
|
||||
if (connectedInputs.length === 0) return '';
|
||||
|
||||
return `(${connectedInputs.join(' & ')})`;
|
||||
}
|
||||
|
||||
function title(nodeName: string, length = 30) {
|
||||
return truncate(nodeName, length);
|
||||
}
|
||||
|
||||
function subtitle(nodeName: string, depth: number) {
|
||||
const multipleNodesText = getMultipleNodesText(nodeName);
|
||||
if (multipleNodesText) return multipleNodesText;
|
||||
return i18n.baseText('ndv.input.nodeDistance', { adjustToNumber: depth });
|
||||
}
|
||||
|
||||
function onInputNodeChange(value: string) {
|
||||
emit('update:model-value', value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n8n-select
|
||||
:model-value="modelValue"
|
||||
:no-data-text="i18n.baseText('ndv.input.noNodesFound')"
|
||||
:placeholder="i18n.baseText('ndv.input.parentNodes')"
|
||||
:class="$style.select"
|
||||
teleported
|
||||
size="small"
|
||||
filterable
|
||||
data-test-id="ndv-input-select"
|
||||
@update:model-value="onInputNodeChange"
|
||||
>
|
||||
<template #prefix>
|
||||
<NodeIcon
|
||||
:disabled="selectedInputNode?.disabled"
|
||||
:node-type="selectedInputNodeType"
|
||||
:size="14"
|
||||
:shrink="false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<n8n-option
|
||||
v-for="{ node, type, depth } of inputNodes"
|
||||
:key="node.name"
|
||||
:value="node.name"
|
||||
:class="[$style.node, { [$style.disabled]: node.disabled }]"
|
||||
:label="`${title(node.name)} ${getMultipleNodesText(node.name)}`"
|
||||
data-test-id="ndv-input-option"
|
||||
>
|
||||
<NodeIcon
|
||||
:disabled="node.disabled"
|
||||
:node-type="type"
|
||||
:size="14"
|
||||
:shrink="false"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
<span :class="$style.title">
|
||||
{{ title(node.name) }}
|
||||
<span v-if="node.disabled">({{ i18n.baseText('node.disabled') }})</span>
|
||||
</span>
|
||||
|
||||
<span :class="$style.subtitle">{{
|
||||
connectedTo(node.name) ? connectedTo(node.name) : subtitle(node.name, depth)
|
||||
}}</span>
|
||||
</n8n-option>
|
||||
</n8n-select>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.select {
|
||||
--max-select-width: 224px;
|
||||
max-width: var(--max-select-width);
|
||||
|
||||
:global(.el-input--suffix .el-input__inner) {
|
||||
padding-left: calc(var(--spacing-l) + var(--spacing-4xs));
|
||||
padding-right: var(--spacing-l);
|
||||
}
|
||||
}
|
||||
|
||||
.node {
|
||||
--select-option-padding: 0 var(--spacing-s);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--font-size-2xs);
|
||||
gap: var(--spacing-4xs);
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-right: var(--spacing-4xs);
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--color-text-dark);
|
||||
font-weight: var(--font-weight-regular);
|
||||
max-width: var(--max-select-width);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.disabled .title {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-left: auto;
|
||||
padding-left: var(--spacing-2xs);
|
||||
color: var(--color-text-light);
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user