mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Show input panel for mapping in embedded NDV (no-changelog) (#17227)
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { createTestNode, createTestWorkflow, createTestWorkflowObject } from '@/__tests__/mocks';
|
import { createTestNode, createTestWorkflow, createTestWorkflowObject } from '@/__tests__/mocks';
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
import InputPanel, { type Props } from '@/components/InputPanel.vue';
|
import InputPanel, { type Props } from '@/components/InputPanel.vue';
|
||||||
import { STORES } from '@n8n/stores';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import { waitFor } from '@testing-library/vue';
|
import { waitFor } from '@testing-library/vue';
|
||||||
@@ -50,7 +49,6 @@ const render = (props: Partial<Props> = {}, pinData?: INodeExecutionData[], runD
|
|||||||
|
|
||||||
const pinia = createTestingPinia({
|
const pinia = createTestingPinia({
|
||||||
stubActions: false,
|
stubActions: false,
|
||||||
initialState: { [STORES.NDV]: { activeNodeName: props.currentNodeName ?? nodes[1].name } },
|
|
||||||
});
|
});
|
||||||
setActivePinia(pinia);
|
setActivePinia(pinia);
|
||||||
|
|
||||||
@@ -97,9 +95,12 @@ const render = (props: Partial<Props> = {}, pinData?: INodeExecutionData[], runD
|
|||||||
props: {
|
props: {
|
||||||
pushRef: 'pushRef',
|
pushRef: 'pushRef',
|
||||||
runIndex: 0,
|
runIndex: 0,
|
||||||
currentNodeName: nodes[1].name,
|
currentNodeName: nodes[0].name,
|
||||||
|
activeNodeName: nodes[1].name,
|
||||||
workflow: workflowObject,
|
workflow: workflowObject,
|
||||||
displayMode: 'schema',
|
displayMode: 'schema',
|
||||||
|
focusedMappableInput: '',
|
||||||
|
isMappingOnboarded: false,
|
||||||
},
|
},
|
||||||
global: {
|
global: {
|
||||||
stubs: {
|
stubs: {
|
||||||
@@ -118,14 +119,14 @@ describe('InputPanel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("opens mapping tab by default if the node hasn't run yet", async () => {
|
it("opens mapping tab by default if the node hasn't run yet", async () => {
|
||||||
const { findByTestId } = render({ currentNodeName: 'Tool' });
|
const { findByTestId } = render({ activeNodeName: 'Tool' });
|
||||||
|
|
||||||
expect((await findByTestId('radio-button-mapping')).parentNode).toBeChecked();
|
expect((await findByTestId('radio-button-mapping')).parentNode).toBeChecked();
|
||||||
expect((await findByTestId('radio-button-debugging')).parentNode).not.toBeChecked();
|
expect((await findByTestId('radio-button-debugging')).parentNode).not.toBeChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens debugging tab by default if the node has already run', async () => {
|
it('opens debugging tab by default if the node has already run', async () => {
|
||||||
const { findByTestId } = render({ currentNodeName: 'Tool' }, undefined, {
|
const { findByTestId } = render({ activeNodeName: 'Tool' }, undefined, {
|
||||||
Tool: [
|
Tool: [
|
||||||
{
|
{
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
|
||||||
import { waitingNodeTooltip } from '@/utils/executionUtils';
|
import { waitingNodeTooltip } from '@/utils/executionUtils';
|
||||||
import uniqBy from 'lodash/uniqBy';
|
import uniqBy from 'lodash/uniqBy';
|
||||||
import { N8nIcon, N8nRadioButtons, N8nText, N8nTooltip } from '@n8n/design-system';
|
import { N8nIcon, N8nRadioButtons, N8nText, N8nTooltip } from '@n8n/design-system';
|
||||||
@@ -22,7 +21,6 @@ import {
|
|||||||
NodeConnectionTypes,
|
NodeConnectionTypes,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import InputNodeSelect from './InputNodeSelect.vue';
|
import InputNodeSelect from './InputNodeSelect.vue';
|
||||||
import NodeExecuteButton from './NodeExecuteButton.vue';
|
import NodeExecuteButton from './NodeExecuteButton.vue';
|
||||||
@@ -38,6 +36,7 @@ export type Props = {
|
|||||||
runIndex: number;
|
runIndex: number;
|
||||||
workflow: Workflow;
|
workflow: Workflow;
|
||||||
pushRef: string;
|
pushRef: string;
|
||||||
|
activeNodeName: string;
|
||||||
currentNodeName?: string;
|
currentNodeName?: string;
|
||||||
canLinkRuns?: boolean;
|
canLinkRuns?: boolean;
|
||||||
linkedRuns?: boolean;
|
linkedRuns?: boolean;
|
||||||
@@ -45,6 +44,10 @@ export type Props = {
|
|||||||
isProductionExecutionPreview?: boolean;
|
isProductionExecutionPreview?: boolean;
|
||||||
isPaneActive?: boolean;
|
isPaneActive?: boolean;
|
||||||
displayMode: IRunDataDisplayMode;
|
displayMode: IRunDataDisplayMode;
|
||||||
|
compact?: boolean;
|
||||||
|
disableDisplayModeSelection?: boolean;
|
||||||
|
focusedMappableInput: string;
|
||||||
|
isMappingOnboarded: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -91,15 +94,10 @@ const inputModes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
const ndvStore = useNDVStore();
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
const posthogStore = usePostHog();
|
const posthogStore = usePostHog();
|
||||||
|
|
||||||
const {
|
const activeNode = computed(() => workflowsStore.getNodeByName(props.activeNodeName));
|
||||||
activeNode,
|
|
||||||
focusedMappableInput,
|
|
||||||
isMappingOnboarded: isUserOnboarded,
|
|
||||||
} = storeToRefs(ndvStore);
|
|
||||||
|
|
||||||
const rootNode = computed(() => {
|
const rootNode = computed(() => {
|
||||||
if (!activeNode.value) return null;
|
if (!activeNode.value) return null;
|
||||||
@@ -128,7 +126,7 @@ const showDraggableHint = computed(() => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!focusedMappableInput.value && !isUserOnboarded.value;
|
return !!props.focusedMappableInput && !props.isMappingOnboarded;
|
||||||
});
|
});
|
||||||
|
|
||||||
const isActiveNodeConfig = computed(() => {
|
const isActiveNodeConfig = computed(() => {
|
||||||
@@ -396,6 +394,8 @@ function handleChangeCollapsingColumn(columnName: string | null) {
|
|||||||
data-test-id="ndv-input-panel"
|
data-test-id="ndv-input-panel"
|
||||||
:disable-ai-content="true"
|
:disable-ai-content="true"
|
||||||
:collapsing-table-column-name="collapsingColumnName"
|
:collapsing-table-column-name="collapsingColumnName"
|
||||||
|
:compact="compact"
|
||||||
|
:disable-display-mode-selection="disableDisplayModeSelection"
|
||||||
@activate-pane="activatePane"
|
@activate-pane="activatePane"
|
||||||
@item-hover="onItemHover"
|
@item-hover="onItemHover"
|
||||||
@link-run="onLinkRun"
|
@link-run="onLinkRun"
|
||||||
@@ -408,9 +408,14 @@ function handleChangeCollapsingColumn(columnName: string | null) {
|
|||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div :class="[$style.titleSection, { [$style.titleSectionV2]: isNDVV2 }]">
|
<div :class="[$style.titleSection, { [$style.titleSectionV2]: isNDVV2 }]">
|
||||||
<span :class="[$style.title, { [$style.titleV2]: isNDVV2 }]">{{
|
<N8nText
|
||||||
i18n.baseText('ndv.input')
|
:bold="true"
|
||||||
}}</span>
|
color="text-light"
|
||||||
|
:size="compact ? 'small' : 'medium'"
|
||||||
|
:class="[$style.title, { [$style.titleV2]: isNDVV2 }]"
|
||||||
|
>
|
||||||
|
{{ i18n.baseText('ndv.input') }}
|
||||||
|
</N8nText>
|
||||||
<N8nRadioButtons
|
<N8nRadioButtons
|
||||||
v-if="isActiveNodeConfig && !readOnly"
|
v-if="isActiveNodeConfig && !readOnly"
|
||||||
data-test-id="input-panel-mode"
|
data-test-id="input-panel-mode"
|
||||||
@@ -686,10 +691,7 @@ function handleChangeCollapsingColumn(columnName: string | null) {
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--color-text-light);
|
|
||||||
letter-spacing: 3px;
|
letter-spacing: 3px;
|
||||||
font-size: var(--font-size-s);
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleV2 {
|
.titleV2 {
|
||||||
|
|||||||
@@ -783,12 +783,15 @@ onBeforeUnmount(() => {
|
|||||||
:can-link-runs="canLinkRuns"
|
:can-link-runs="canLinkRuns"
|
||||||
:run-index="inputRun"
|
:run-index="inputRun"
|
||||||
:linked-runs="linked"
|
:linked-runs="linked"
|
||||||
|
:active-node-name="activeNode.name"
|
||||||
:current-node-name="inputNodeName"
|
:current-node-name="inputNodeName"
|
||||||
:push-ref="pushRef"
|
:push-ref="pushRef"
|
||||||
:read-only="readOnly || hasForeignCredential"
|
:read-only="readOnly || hasForeignCredential"
|
||||||
:is-production-execution-preview="isProductionExecutionPreview"
|
:is-production-execution-preview="isProductionExecutionPreview"
|
||||||
:is-pane-active="isInputPaneActive"
|
:is-pane-active="isInputPaneActive"
|
||||||
:display-mode="inputPanelDisplayMode"
|
:display-mode="inputPanelDisplayMode"
|
||||||
|
:is-mapping-onboarded="ndvStore.isMappingOnboarded"
|
||||||
|
:focused-mappable-input="ndvStore.focusedMappableInput"
|
||||||
@activate-pane="activateInputPane"
|
@activate-pane="activateInputPane"
|
||||||
@link-run="onLinkRunToInput"
|
@link-run="onLinkRunToInput"
|
||||||
@unlink-run="() => onUnlinkRun('input')"
|
@unlink-run="() => onUnlinkRun('input')"
|
||||||
|
|||||||
@@ -759,6 +759,7 @@ onBeforeUnmount(() => {
|
|||||||
:can-link-runs="canLinkRuns"
|
:can-link-runs="canLinkRuns"
|
||||||
:run-index="inputRun"
|
:run-index="inputRun"
|
||||||
:linked-runs="linked"
|
:linked-runs="linked"
|
||||||
|
:active-node-name="activeNode.name"
|
||||||
:current-node-name="inputNodeName"
|
:current-node-name="inputNodeName"
|
||||||
:push-ref="pushRef"
|
:push-ref="pushRef"
|
||||||
:read-only="readOnly || hasForeignCredential"
|
:read-only="readOnly || hasForeignCredential"
|
||||||
@@ -766,6 +767,8 @@ onBeforeUnmount(() => {
|
|||||||
:is-pane-active="isInputPaneActive"
|
:is-pane-active="isInputPaneActive"
|
||||||
:display-mode="inputPanelDisplayMode"
|
:display-mode="inputPanelDisplayMode"
|
||||||
:class="$style.input"
|
:class="$style.input"
|
||||||
|
:is-mapping-onboarded="ndvStore.isMappingOnboarded"
|
||||||
|
:focused-mappable-input="ndvStore.focusedMappableInput"
|
||||||
@activate-pane="activateInputPane"
|
@activate-pane="activateInputPane"
|
||||||
@link-run="onLinkRunToInput"
|
@link-run="onLinkRunToInput"
|
||||||
@unlink-run="() => onUnlinkRun('input')"
|
@unlink-run="() => onUnlinkRun('input')"
|
||||||
|
|||||||
@@ -1126,7 +1126,7 @@ function handleWheelEvent(event: WheelEvent) {
|
|||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
border-radius: var(--spacing-2xs);
|
border-radius: var(--spacing-2xs);
|
||||||
background: var(--color-foreground-dark);
|
background: var(--color-foreground-dark);
|
||||||
border: var(--spacing-5xs) solid white;
|
border: var(--spacing-5xs) solid var(--color-background-xlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ type Props = {
|
|||||||
hidePagination?: boolean;
|
hidePagination?: boolean;
|
||||||
calloutMessage?: string;
|
calloutMessage?: string;
|
||||||
disableRunIndexSelection?: boolean;
|
disableRunIndexSelection?: boolean;
|
||||||
|
disableDisplayModeSelection?: boolean;
|
||||||
disableEdit?: boolean;
|
disableEdit?: boolean;
|
||||||
disablePin?: boolean;
|
disablePin?: boolean;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
@@ -168,6 +169,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
hidePagination: false,
|
hidePagination: false,
|
||||||
calloutMessage: undefined,
|
calloutMessage: undefined,
|
||||||
disableRunIndexSelection: false,
|
disableRunIndexSelection: false,
|
||||||
|
disableDisplayModeSelection: false,
|
||||||
disableEdit: false,
|
disableEdit: false,
|
||||||
disablePin: false,
|
disablePin: false,
|
||||||
disableHoverHighlight: false,
|
disableHoverHighlight: false,
|
||||||
@@ -1447,6 +1449,7 @@ defineExpose({ enterEditMode });
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<RunDataDisplayModeSelect
|
<RunDataDisplayModeSelect
|
||||||
|
v-if="!disableDisplayModeSelection"
|
||||||
v-show="
|
v-show="
|
||||||
hasPreviewSchema ||
|
hasPreviewSchema ||
|
||||||
(hasNodeRun &&
|
(hasNodeRun &&
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ exports[`InputPanel > should render 1`] = `
|
|||||||
class="titleSection"
|
class="titleSection"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="title"
|
class="n8n-text text-light size-medium bold title title"
|
||||||
>
|
>
|
||||||
|
|
||||||
Input
|
Input
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="n8n-radio-buttons radioGroup"
|
class="n8n-radio-buttons radioGroup"
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ExperimentalCanvasNodeSettings from './ExperimentalCanvasNodeSettings.vue';
|
import InputPanel from '@/components/InputPanel.vue';
|
||||||
import { onBeforeUnmount, ref, computed, provide } from 'vue';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import { useExperimentalNdvStore } from '../experimentalNdv.store';
|
|
||||||
import NodeTitle from '@/components/NodeTitle.vue';
|
import NodeTitle from '@/components/NodeTitle.vue';
|
||||||
import { N8nIcon, N8nIconButton } from '@n8n/design-system';
|
|
||||||
import { useVueFlow } from '@vue-flow/core';
|
|
||||||
import { watchOnce } from '@vueuse/core';
|
|
||||||
import { ExpressionLocalResolveContextSymbol } from '@/constants';
|
import { ExpressionLocalResolveContextSymbol } from '@/constants';
|
||||||
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
||||||
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import type { ExpressionLocalResolveContext } from '@/types/expressions';
|
import type { ExpressionLocalResolveContext } from '@/types/expressions';
|
||||||
|
import { N8nIcon, N8nIconButton } from '@n8n/design-system';
|
||||||
|
import { useVueFlow } from '@vue-flow/core';
|
||||||
|
import { useActiveElement, watchOnce } from '@vueuse/core';
|
||||||
|
import { computed, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
|
||||||
|
import { useExperimentalNdvStore } from '../experimentalNdv.store';
|
||||||
|
import ExperimentalCanvasNodeSettings from './ExperimentalCanvasNodeSettings.vue';
|
||||||
|
|
||||||
const { nodeId, isReadOnly, isConfigurable } = defineProps<{
|
const { nodeId, isReadOnly, isConfigurable } = defineProps<{
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
@@ -18,6 +20,7 @@ const { nodeId, isReadOnly, isConfigurable } = defineProps<{
|
|||||||
isConfigurable: boolean;
|
isConfigurable: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const ndvStore = useNDVStore();
|
||||||
const experimentalNdvStore = useExperimentalNdvStore();
|
const experimentalNdvStore = useExperimentalNdvStore();
|
||||||
const isExpanded = computed(() => !experimentalNdvStore.collapsedNodes[nodeId]);
|
const isExpanded = computed(() => !experimentalNdvStore.collapsedNodes[nodeId]);
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
@@ -58,15 +61,17 @@ const isVisible = computed(() =>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
const isOnceVisible = ref(isVisible.value);
|
const isOnceVisible = ref(isVisible.value);
|
||||||
|
const shouldShowInputPanel = ref(false);
|
||||||
|
|
||||||
provide(
|
const containerRef = useTemplateRef('container');
|
||||||
ExpressionLocalResolveContextSymbol,
|
const inputPanelContainerRef = useTemplateRef('inputPanelContainer');
|
||||||
computed<ExpressionLocalResolveContext | undefined>(() => {
|
const activeElement = useActiveElement();
|
||||||
|
|
||||||
|
const expressionResolveCtx = computed<ExpressionLocalResolveContext | undefined>(() => {
|
||||||
if (!node.value) {
|
if (!node.value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflow = workflowsStore.getCurrentWorkflow();
|
|
||||||
const runIndex = 0; // not changeable for now
|
const runIndex = 0; // not changeable for now
|
||||||
const execution = workflowsStore.workflowExecutionData;
|
const execution = workflowsStore.workflowExecutionData;
|
||||||
const nodeName = node.value.name;
|
const nodeName = node.value.name;
|
||||||
@@ -83,7 +88,7 @@ provide(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputs = workflow.getParentNodesByDepth(nodeName, 1);
|
const inputs = workflow.value.getParentNodesByDepth(nodeName, 1);
|
||||||
|
|
||||||
if (inputs.length > 0) {
|
if (inputs.length > 0) {
|
||||||
return {
|
return {
|
||||||
@@ -99,22 +104,38 @@ provide(
|
|||||||
return {
|
return {
|
||||||
localResolve: true,
|
localResolve: true,
|
||||||
envVars: useEnvironmentsStore().variablesAsObject,
|
envVars: useEnvironmentsStore().variablesAsObject,
|
||||||
workflow,
|
workflow: workflow.value,
|
||||||
execution,
|
execution,
|
||||||
nodeName,
|
nodeName,
|
||||||
additionalKeys: {},
|
additionalKeys: {},
|
||||||
inputNode: findInputNode(),
|
inputNode: findInputNode(),
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
const workflow = computed(() => workflowsStore.getCurrentWorkflow());
|
||||||
|
|
||||||
|
function handleToggleExpand() {
|
||||||
|
experimentalNdvStore.setNodeExpanded(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
provide(ExpressionLocalResolveContextSymbol, expressionResolveCtx);
|
||||||
|
|
||||||
watchOnce(isVisible, (visible) => {
|
watchOnce(isVisible, (visible) => {
|
||||||
isOnceVisible.value = isOnceVisible.value || visible;
|
isOnceVisible.value = isOnceVisible.value || visible;
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleToggleExpand() {
|
watch([activeElement, vf.getSelectedNodes], ([active, selected]) => {
|
||||||
experimentalNdvStore.setNodeExpanded(nodeId);
|
if (active && containerRef.value?.contains(active)) {
|
||||||
}
|
// TODO: find a way to implement this without depending on test ID
|
||||||
|
shouldShowInputPanel.value =
|
||||||
|
!!active.closest('[data-test-id=inline-expression-editor-input]') ||
|
||||||
|
!!inputPanelContainerRef.value?.contains(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.every((sel) => sel.id !== node.value?.id)) {
|
||||||
|
shouldShowInputPanel.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -129,6 +150,7 @@ function handleToggleExpand() {
|
|||||||
<template v-if="isOnceVisible">
|
<template v-if="isOnceVisible">
|
||||||
<ExperimentalCanvasNodeSettings
|
<ExperimentalCanvasNodeSettings
|
||||||
v-if="isExpanded"
|
v-if="isExpanded"
|
||||||
|
tabindex="-1"
|
||||||
:node-id="nodeId"
|
:node-id="nodeId"
|
||||||
:class="$style.settingsView"
|
:class="$style.settingsView"
|
||||||
:no-wheel="
|
:no-wheel="
|
||||||
@@ -158,6 +180,34 @@ function handleToggleExpand() {
|
|||||||
/>
|
/>
|
||||||
<N8nIcon icon="maximize-2" size="large" />
|
<N8nIcon icon="maximize-2" size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
<Transition name="input">
|
||||||
|
<div
|
||||||
|
v-if="shouldShowInputPanel && node"
|
||||||
|
ref="inputPanelContainer"
|
||||||
|
:class="$style.inputPanelContainer"
|
||||||
|
:tabindex="-1"
|
||||||
|
>
|
||||||
|
<InputPanel
|
||||||
|
:class="$style.inputPanel"
|
||||||
|
:workflow="workflow"
|
||||||
|
:run-index="0"
|
||||||
|
compact
|
||||||
|
push-ref=""
|
||||||
|
display-mode="schema"
|
||||||
|
disable-display-mode-selection
|
||||||
|
:active-node-name="node.name"
|
||||||
|
:current-node-name="expressionResolveCtx?.inputNode?.name"
|
||||||
|
:is-mapping-onboarded="ndvStore.isMappingOnboarded"
|
||||||
|
:focused-mappable-input="ndvStore.focusedMappableInput"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<N8nText :class="$style.inputPanelTitle" :bold="true" color="text-light" size="small">
|
||||||
|
Input
|
||||||
|
</N8nText>
|
||||||
|
</template>
|
||||||
|
</InputPanel>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -167,7 +217,6 @@ function handleToggleExpand() {
|
|||||||
position: relative;
|
position: relative;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
overflow: hidden;
|
|
||||||
border-width: 1px !important;
|
border-width: 1px !important;
|
||||||
border-radius: var(--border-radius-base) !important;
|
border-radius: var(--border-radius-base) !important;
|
||||||
width: calc(var(--canvas-node--width) * var(--node-width-scaler));
|
width: calc(var(--canvas-node--width) * var(--node-width-scaler));
|
||||||
@@ -175,9 +224,12 @@ function handleToggleExpand() {
|
|||||||
&.expanded {
|
&.expanded {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
max-height: min(calc(var(--canvas-node--height) * 2), 300px);
|
||||||
|
min-height: var(--spacing-3xl);
|
||||||
}
|
}
|
||||||
&.collapsed {
|
&.collapsed {
|
||||||
height: calc(16px * 4);
|
overflow: hidden;
|
||||||
|
height: var(--spacing-3xl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,9 +245,10 @@ function handleToggleExpand() {
|
|||||||
:root .settingsView {
|
:root .settingsView {
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: min(calc(var(--canvas-node--height) * 2), 300px);
|
max-height: calc(min(calc(var(--canvas-node--height) * 2), 300px) - var(--border-width-base) * 2);
|
||||||
min-height: var(--spacing-3xl); // should be multiple of GRID_SIZE
|
min-height: var(--spacing-3xl); // should be multiple of GRID_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,4 +274,43 @@ function handleToggleExpand() {
|
|||||||
zoom: var(--zoom);
|
zoom: var(--zoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inputPanelContainer {
|
||||||
|
position: absolute;
|
||||||
|
right: 100%;
|
||||||
|
top: 0;
|
||||||
|
padding-right: var(--spacing-4xs);
|
||||||
|
margin-top: calc(-1 * var(--border-width-base));
|
||||||
|
width: 180px;
|
||||||
|
z-index: 2000;
|
||||||
|
max-height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputPanel {
|
||||||
|
border: var(--border-base);
|
||||||
|
border-width: 1px;
|
||||||
|
background-color: var(--color-background-light);
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
zoom: var(--zoom);
|
||||||
|
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.05);
|
||||||
|
padding: var(--spacing-2xs);
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputPanelTitle {
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.input-enter-active,
|
||||||
|
.input-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-enter-from,
|
||||||
|
.input-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user