mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00

824 lines
23 KiB
Vue
824 lines
23 KiB
Vue
<template>
|
|
<el-dialog
|
|
:modelValue="(!!activeNode || renaming) && !isActiveStickyNode"
|
|
:before-close="close"
|
|
:show-close="false"
|
|
class="data-display-wrapper ndv-wrapper"
|
|
overlay-class="data-display-overlay"
|
|
width="auto"
|
|
append-to-body
|
|
data-test-id="ndv"
|
|
>
|
|
<n8n-tooltip
|
|
placement="bottom-start"
|
|
:visible="showTriggerWaitingWarning"
|
|
:disabled="!showTriggerWaitingWarning"
|
|
>
|
|
<template #content>
|
|
<div :class="$style.triggerWarning">
|
|
{{ $locale.baseText('ndv.backToCanvas.waitingForTriggerWarning') }}
|
|
</div>
|
|
</template>
|
|
<div :class="$style.backToCanvas" @click="close" data-test-id="back-to-canvas">
|
|
<n8n-icon icon="arrow-left" color="text-xlight" size="medium" />
|
|
<n8n-text color="text-xlight" size="medium" :bold="true">
|
|
{{ $locale.baseText('ndv.backToCanvas') }}
|
|
</n8n-text>
|
|
</div>
|
|
</n8n-tooltip>
|
|
|
|
<div
|
|
v-if="activeNode"
|
|
class="data-display"
|
|
ref="container"
|
|
@keydown.capture="onKeyDown"
|
|
tabindex="0"
|
|
>
|
|
<div @click="close" :class="$style.modalBackground"></div>
|
|
<NDVDraggablePanels
|
|
:isTriggerNode="isTriggerNode"
|
|
:hideInputAndOutput="activeNodeType === null"
|
|
:position="isTriggerNode && !showTriggerPanel ? 0 : undefined"
|
|
:isDraggable="!isTriggerNode"
|
|
:hasDoubleWidth="activeNodeType?.parameterPane === 'wide'"
|
|
:nodeType="activeNodeType"
|
|
@close="close"
|
|
@init="onPanelsInit"
|
|
@dragstart="onDragStart"
|
|
@dragend="onDragEnd"
|
|
>
|
|
<template #input v-if="showTriggerPanel || !isTriggerNode">
|
|
<TriggerPanel
|
|
v-if="showTriggerPanel"
|
|
:nodeName="activeNode.name"
|
|
:sessionId="sessionId"
|
|
@execute="onNodeExecute"
|
|
@activate="onWorkflowActivate"
|
|
/>
|
|
<InputPanel
|
|
v-else-if="!isTriggerNode"
|
|
:workflow="workflow"
|
|
:canLinkRuns="canLinkRuns"
|
|
:runIndex="inputRun"
|
|
:linkedRuns="linked"
|
|
:currentNodeName="inputNodeName"
|
|
:sessionId="sessionId"
|
|
:readOnly="readOnly || hasForeignCredential"
|
|
:isProductionExecutionPreview="isProductionExecutionPreview"
|
|
:isPaneActive="isInputPaneActive"
|
|
@activatePane="activateInputPane"
|
|
@linkRun="onLinkRunToInput"
|
|
@unlinkRun="() => onUnlinkRun('input')"
|
|
@runChange="onRunInputIndexChange"
|
|
@openSettings="openSettings"
|
|
@changeInputNode="onInputNodeChange"
|
|
@execute="onNodeExecute"
|
|
@tableMounted="onInputTableMounted"
|
|
@itemHover="onInputItemHover"
|
|
@search="onSearch"
|
|
/>
|
|
</template>
|
|
<template #output>
|
|
<OutputPanel
|
|
data-test-id="output-panel"
|
|
:canLinkRuns="canLinkRuns"
|
|
:runIndex="outputRun"
|
|
:linkedRuns="linked"
|
|
:sessionId="sessionId"
|
|
:isReadOnly="readOnly || hasForeignCredential"
|
|
:blockUI="blockUi && isTriggerNode && !isExecutableTriggerNode"
|
|
:isProductionExecutionPreview="isProductionExecutionPreview"
|
|
:isPaneActive="isOutputPaneActive"
|
|
@activatePane="activateOutputPane"
|
|
@linkRun="onLinkRunToOutput"
|
|
@unlinkRun="() => onUnlinkRun('output')"
|
|
@runChange="onRunOutputIndexChange"
|
|
@openSettings="openSettings"
|
|
@tableMounted="onOutputTableMounted"
|
|
@itemHover="onOutputItemHover"
|
|
@search="onSearch"
|
|
/>
|
|
</template>
|
|
<template #main>
|
|
<NodeSettings
|
|
:eventBus="settingsEventBus"
|
|
:dragging="isDragging"
|
|
:sessionId="sessionId"
|
|
:nodeType="activeNodeType"
|
|
:foreignCredentials="foreignCredentials"
|
|
:readOnly="readOnly"
|
|
:blockUI="blockUi && showTriggerPanel"
|
|
:executable="!readOnly"
|
|
@valueChanged="valueChanged"
|
|
@execute="onNodeExecute"
|
|
@stopExecution="onStopExecution"
|
|
@redrawRequired="redrawRequired = true"
|
|
@activate="onWorkflowActivate"
|
|
/>
|
|
<a
|
|
v-if="featureRequestUrl"
|
|
@click="onFeatureRequestClick"
|
|
:class="$style.featureRequest"
|
|
target="_blank"
|
|
>
|
|
<font-awesome-icon icon="lightbulb" />
|
|
{{ $locale.baseText('ndv.featureRequest') }}
|
|
</a>
|
|
</template>
|
|
</NDVDraggablePanels>
|
|
</div>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent } from 'vue';
|
|
import { mapStores } from 'pinia';
|
|
import { createEventBus } from 'n8n-design-system/utils';
|
|
import type {
|
|
INodeConnections,
|
|
INodeTypeDescription,
|
|
IRunData,
|
|
IRunExecutionData,
|
|
Workflow,
|
|
} from 'n8n-workflow';
|
|
import { jsonParse, NodeHelpers, NodeConnectionType } from 'n8n-workflow';
|
|
import type { IExecutionResponse, INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
|
|
|
|
import { externalHooks } from '@/mixins/externalHooks';
|
|
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
|
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
|
|
|
import NodeSettings from '@/components/NodeSettings.vue';
|
|
import NDVDraggablePanels from './NDVDraggablePanels.vue';
|
|
|
|
import OutputPanel from './OutputPanel.vue';
|
|
import InputPanel from './InputPanel.vue';
|
|
import TriggerPanel from './TriggerPanel.vue';
|
|
import {
|
|
BASE_NODE_SURVEY_URL,
|
|
EnterpriseEditionFeature,
|
|
EXECUTABLE_TRIGGER_NODE_TYPES,
|
|
MODAL_CONFIRM,
|
|
START_NODE_TYPE,
|
|
STICKY_NODE_TYPE,
|
|
} from '@/constants';
|
|
import { workflowActivate } from '@/mixins/workflowActivate';
|
|
import { pinData } from '@/mixins/pinData';
|
|
import { dataPinningEventBus } from '@/event-bus';
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
import { useUIStore } from '@/stores/ui.store';
|
|
import { useSettingsStore } from '@/stores/settings.store';
|
|
import { useDeviceSupport } from 'n8n-design-system';
|
|
import { useMessage } from '@/composables';
|
|
|
|
export default defineComponent({
|
|
name: 'NodeDetailsView',
|
|
mixins: [externalHooks, nodeHelpers, workflowHelpers, workflowActivate, pinData],
|
|
components: {
|
|
NodeSettings,
|
|
InputPanel,
|
|
OutputPanel,
|
|
NDVDraggablePanels,
|
|
TriggerPanel,
|
|
},
|
|
props: {
|
|
readOnly: {
|
|
type: Boolean,
|
|
},
|
|
renaming: {
|
|
type: Boolean,
|
|
},
|
|
isProductionExecutionPreview: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
setup(props) {
|
|
return {
|
|
...useDeviceSupport(),
|
|
...useMessage(),
|
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
...workflowActivate.setup?.(props),
|
|
};
|
|
},
|
|
data() {
|
|
return {
|
|
settingsEventBus: createEventBus(),
|
|
redrawRequired: false,
|
|
runInputIndex: -1,
|
|
runOutputIndex: -1,
|
|
isLinkingEnabled: true,
|
|
selectedInput: undefined as string | undefined,
|
|
triggerWaitingWarningEnabled: false,
|
|
isDragging: false,
|
|
mainPanelPosition: 0,
|
|
pinDataDiscoveryTooltipVisible: false,
|
|
avgInputRowHeight: 0,
|
|
avgOutputRowHeight: 0,
|
|
isInputPaneActive: false,
|
|
isOutputPaneActive: false,
|
|
isPairedItemHoveringEnabled: true,
|
|
};
|
|
},
|
|
mounted() {
|
|
dataPinningEventBus.on('data-pinning-discovery', this.setIsTooltipVisible);
|
|
},
|
|
beforeUnmount() {
|
|
dataPinningEventBus.off('data-pinning-discovery', this.setIsTooltipVisible);
|
|
},
|
|
computed: {
|
|
...mapStores(useNodeTypesStore, useNDVStore, useUIStore, useWorkflowsStore, useSettingsStore),
|
|
sessionId(): string {
|
|
return this.ndvStore.sessionId;
|
|
},
|
|
workflowRunning(): boolean {
|
|
return this.uiStore.isActionActive('workflowRunning');
|
|
},
|
|
showTriggerWaitingWarning(): boolean {
|
|
return (
|
|
this.triggerWaitingWarningEnabled &&
|
|
!!this.activeNodeType &&
|
|
!this.activeNodeType.group.includes('trigger') &&
|
|
this.workflowRunning &&
|
|
this.workflowsStore.executionWaitingForWebhook
|
|
);
|
|
},
|
|
activeNode(): INodeUi | null {
|
|
return this.ndvStore.activeNode;
|
|
},
|
|
inputNodeName(): string | undefined {
|
|
return this.selectedInput || this.parentNode;
|
|
},
|
|
inputNode(): INodeUi | null {
|
|
if (this.inputNodeName) {
|
|
return this.workflowsStore.getNodeByName(this.inputNodeName);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
activeNodeType(): INodeTypeDescription | null {
|
|
if (this.activeNode) {
|
|
return this.nodeTypesStore.getNodeType(this.activeNode.type, this.activeNode.typeVersion);
|
|
}
|
|
return null;
|
|
},
|
|
showTriggerPanel(): boolean {
|
|
const isWebhookBasedNode = !!this.activeNodeType?.webhooks?.length;
|
|
const isPollingNode = this.activeNodeType?.polling;
|
|
const override = !!this.activeNodeType?.triggerPanel;
|
|
return (
|
|
!this.readOnly && this.isTriggerNode && (isWebhookBasedNode || isPollingNode || override)
|
|
);
|
|
},
|
|
workflow(): Workflow {
|
|
return this.getCurrentWorkflow();
|
|
},
|
|
parentNodes(): string[] {
|
|
if (this.activeNode) {
|
|
return (
|
|
this.workflow.getParentNodesByDepth(this.activeNode.name, 1).map(({ name }) => name) || []
|
|
);
|
|
}
|
|
|
|
return [];
|
|
},
|
|
parentNode(): string | undefined {
|
|
const pinData = this.workflowsStore.getPinData;
|
|
|
|
// Return the first parent node that contains data
|
|
for (const parentNodeName of this.parentNodes) {
|
|
// Check first for pinned data
|
|
if (pinData[parentNodeName]) {
|
|
return parentNodeName;
|
|
}
|
|
|
|
// Check then the data of the current execution
|
|
if (this.workflowRunData?.[parentNodeName]) {
|
|
return parentNodeName;
|
|
}
|
|
}
|
|
|
|
return this.parentNodes[0];
|
|
},
|
|
isExecutableTriggerNode(): boolean {
|
|
if (!this.activeNodeType) return false;
|
|
|
|
return EXECUTABLE_TRIGGER_NODE_TYPES.includes(this.activeNodeType.name);
|
|
},
|
|
isTriggerNode(): boolean {
|
|
return (
|
|
!!this.activeNodeType &&
|
|
(this.activeNodeType.group.includes('trigger') ||
|
|
this.activeNodeType.name === START_NODE_TYPE)
|
|
);
|
|
},
|
|
isActiveStickyNode(): boolean {
|
|
return !!this.ndvStore.activeNode && this.ndvStore.activeNode.type === STICKY_NODE_TYPE;
|
|
},
|
|
workflowExecution(): IExecutionResponse | null {
|
|
return this.workflowsStore.getWorkflowExecution;
|
|
},
|
|
workflowRunData(): IRunData | null {
|
|
if (this.workflowExecution === null) {
|
|
return null;
|
|
}
|
|
const executionData: IRunExecutionData | undefined = this.workflowExecution.data;
|
|
if (executionData?.resultData) {
|
|
return executionData.resultData.runData;
|
|
}
|
|
return null;
|
|
},
|
|
maxOutputRun(): number {
|
|
if (this.activeNode === null) {
|
|
return 0;
|
|
}
|
|
|
|
const runData: IRunData | null = this.workflowRunData;
|
|
|
|
if (runData === null || !runData.hasOwnProperty(this.activeNode.name)) {
|
|
return 0;
|
|
}
|
|
|
|
if (runData[this.activeNode.name].length) {
|
|
return runData[this.activeNode.name].length - 1;
|
|
}
|
|
|
|
return 0;
|
|
},
|
|
outputRun(): number {
|
|
if (this.runOutputIndex === -1) {
|
|
return this.maxOutputRun;
|
|
}
|
|
|
|
return Math.min(this.runOutputIndex, this.maxOutputRun);
|
|
},
|
|
maxInputRun(): number {
|
|
if (this.inputNode === null || this.activeNode === null) {
|
|
return 0;
|
|
}
|
|
|
|
const workflowNode = this.workflow.getNode(this.activeNode.name);
|
|
const outputs = NodeHelpers.getNodeOutputs(this.workflow, workflowNode, this.activeNodeType);
|
|
|
|
let node = this.inputNode;
|
|
|
|
const runData: IRunData | null = this.workflowRunData;
|
|
|
|
if (outputs.some((output) => output !== NodeConnectionType.Main)) {
|
|
node = this.activeNode;
|
|
}
|
|
|
|
if (!node || !runData || !runData.hasOwnProperty(node.name)) {
|
|
return 0;
|
|
}
|
|
|
|
if (runData[node.name].length) {
|
|
return runData[node.name].length - 1;
|
|
}
|
|
|
|
return 0;
|
|
},
|
|
inputRun(): number {
|
|
if (this.isLinkingEnabled && this.maxOutputRun === this.maxInputRun) {
|
|
return this.outputRun;
|
|
}
|
|
if (this.runInputIndex === -1) {
|
|
return this.maxInputRun;
|
|
}
|
|
|
|
return Math.min(this.runInputIndex, this.maxInputRun);
|
|
},
|
|
canLinkRuns(): boolean {
|
|
return this.maxOutputRun > 0 && this.maxOutputRun === this.maxInputRun;
|
|
},
|
|
linked(): boolean {
|
|
return this.isLinkingEnabled && this.canLinkRuns;
|
|
},
|
|
inputPanelMargin(): number {
|
|
return this.isTriggerNode ? 0 : 80;
|
|
},
|
|
featureRequestUrl(): string {
|
|
if (!this.activeNodeType) {
|
|
return '';
|
|
}
|
|
return `${BASE_NODE_SURVEY_URL}${this.activeNodeType.name}`;
|
|
},
|
|
outputPanelEditMode(): { enabled: boolean; value: string } {
|
|
return this.ndvStore.outputPanelEditMode;
|
|
},
|
|
isWorkflowRunning(): boolean {
|
|
return this.uiStore.isActionActive('workflowRunning');
|
|
},
|
|
isExecutionWaitingForWebhook(): boolean {
|
|
return this.workflowsStore.executionWaitingForWebhook;
|
|
},
|
|
blockUi(): boolean {
|
|
return this.isWorkflowRunning || this.isExecutionWaitingForWebhook;
|
|
},
|
|
foreignCredentials(): string[] {
|
|
const credentials = (this.activeNode || {}).credentials;
|
|
const usedCredentials = this.workflowsStore.usedCredentials;
|
|
|
|
const foreignCredentials: string[] = [];
|
|
if (
|
|
credentials &&
|
|
this.settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)
|
|
) {
|
|
Object.values(credentials).forEach((credential) => {
|
|
if (
|
|
credential.id &&
|
|
usedCredentials[credential.id] &&
|
|
!usedCredentials[credential.id].currentUserHasAccess
|
|
) {
|
|
foreignCredentials.push(credential.id);
|
|
}
|
|
});
|
|
}
|
|
|
|
return foreignCredentials;
|
|
},
|
|
hasForeignCredential(): boolean {
|
|
return this.foreignCredentials.length > 0;
|
|
},
|
|
},
|
|
watch: {
|
|
activeNode(node: INodeUi | null, oldNode: INodeUi | null) {
|
|
if (node && node.name !== oldNode?.name && !this.isActiveStickyNode) {
|
|
this.runInputIndex = -1;
|
|
this.runOutputIndex = -1;
|
|
this.isLinkingEnabled = true;
|
|
this.selectedInput = undefined;
|
|
this.triggerWaitingWarningEnabled = false;
|
|
this.avgOutputRowHeight = 0;
|
|
this.avgInputRowHeight = 0;
|
|
|
|
setTimeout(() => this.ndvStore.setNDVSessionId(), 0);
|
|
void this.$externalHooks().run('dataDisplay.nodeTypeChanged', {
|
|
nodeSubtitle: this.getNodeSubtitle(node, this.activeNodeType, this.getCurrentWorkflow()),
|
|
});
|
|
|
|
setTimeout(() => {
|
|
if (this.activeNode) {
|
|
const outgoingConnections = this.workflowsStore.outgoingConnectionsByNodeName(
|
|
this.activeNode.name,
|
|
) as INodeConnections;
|
|
|
|
this.$telemetry.track('User opened node modal', {
|
|
node_type: this.activeNodeType ? this.activeNodeType.name : '',
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
session_id: this.sessionId,
|
|
is_editable: !this.hasForeignCredential,
|
|
parameters_pane_position: this.mainPanelPosition,
|
|
input_first_connector_runs: this.maxInputRun,
|
|
output_first_connector_runs: this.maxOutputRun,
|
|
selected_view_inputs: this.isTriggerNode
|
|
? 'trigger'
|
|
: this.ndvStore.inputPanelDisplayMode,
|
|
selected_view_outputs: this.ndvStore.outputPanelDisplayMode,
|
|
input_connectors: this.parentNodes.length,
|
|
output_connectors:
|
|
outgoingConnections && outgoingConnections.main && outgoingConnections.main.length,
|
|
input_displayed_run_index: this.inputRun,
|
|
output_displayed_run_index: this.outputRun,
|
|
data_pinning_tooltip_presented: this.pinDataDiscoveryTooltipVisible,
|
|
input_displayed_row_height_avg: this.avgInputRowHeight,
|
|
output_displayed_row_height_avg: this.avgOutputRowHeight,
|
|
});
|
|
}
|
|
}, 2000); // wait for RunData to mount and present pindata discovery tooltip
|
|
}
|
|
if (window.top && !this.isActiveStickyNode) {
|
|
window.top.postMessage(JSON.stringify({ command: node ? 'openNDV' : 'closeNDV' }), '*');
|
|
}
|
|
},
|
|
maxOutputRun() {
|
|
this.runOutputIndex = -1;
|
|
},
|
|
maxInputRun() {
|
|
this.runInputIndex = -1;
|
|
},
|
|
inputNodeName(nodeName: string | undefined) {
|
|
setTimeout(() => {
|
|
this.ndvStore.setInputNodeName(nodeName);
|
|
}, 0);
|
|
},
|
|
inputRun() {
|
|
setTimeout(() => {
|
|
this.ndvStore.setInputRunIndex(this.inputRun);
|
|
}, 0);
|
|
},
|
|
},
|
|
methods: {
|
|
setIsTooltipVisible({ isTooltipVisible }: { isTooltipVisible: boolean }) {
|
|
this.pinDataDiscoveryTooltipVisible = isTooltipVisible;
|
|
},
|
|
onKeyDown(e: KeyboardEvent) {
|
|
if (e.key === 's' && this.isCtrlKeyPressed(e)) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
|
|
if (this.readOnly) return;
|
|
|
|
this.$emit('saveKeyboardShortcut', e);
|
|
}
|
|
},
|
|
onInputItemHover(e: { itemIndex: number; outputIndex: number } | null) {
|
|
if (e === null || !this.inputNodeName || !this.isPairedItemHoveringEnabled) {
|
|
this.ndvStore.setHoveringItem(null);
|
|
return;
|
|
}
|
|
|
|
const item: TargetItem = {
|
|
nodeName: this.inputNodeName,
|
|
runIndex: this.inputRun,
|
|
outputIndex: e.outputIndex,
|
|
itemIndex: e.itemIndex,
|
|
};
|
|
this.ndvStore.setHoveringItem(item);
|
|
},
|
|
onOutputItemHover(e: { itemIndex: number; outputIndex: number } | null) {
|
|
if (e === null || !this.activeNode || !this.isPairedItemHoveringEnabled) {
|
|
this.ndvStore.setHoveringItem(null);
|
|
return;
|
|
}
|
|
|
|
const item: TargetItem = {
|
|
nodeName: this.activeNode.name,
|
|
runIndex: this.outputRun,
|
|
outputIndex: e.outputIndex,
|
|
itemIndex: e.itemIndex,
|
|
};
|
|
this.ndvStore.setHoveringItem(item);
|
|
},
|
|
onInputTableMounted(e: { avgRowHeight: number }) {
|
|
this.avgInputRowHeight = e.avgRowHeight;
|
|
},
|
|
onOutputTableMounted(e: { avgRowHeight: number }) {
|
|
this.avgOutputRowHeight = e.avgRowHeight;
|
|
},
|
|
onWorkflowActivate() {
|
|
this.ndvStore.activeNodeName = null;
|
|
setTimeout(() => {
|
|
void this.activateCurrentWorkflow('ndv');
|
|
}, 1000);
|
|
},
|
|
onFeatureRequestClick() {
|
|
window.open(this.featureRequestUrl, '_blank');
|
|
if (this.activeNode) {
|
|
this.$telemetry.track('User clicked ndv link', {
|
|
node_type: this.activeNode.type,
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
session_id: this.sessionId,
|
|
pane: NodeConnectionType.Main,
|
|
type: 'i-wish-this-node-would',
|
|
});
|
|
}
|
|
},
|
|
onPanelsInit(e: { position: number }) {
|
|
this.mainPanelPosition = e.position;
|
|
},
|
|
onDragStart(e: { position: number }) {
|
|
this.isDragging = true;
|
|
this.mainPanelPosition = e.position;
|
|
},
|
|
onDragEnd(e: { windowWidth: number; position: number }) {
|
|
this.isDragging = false;
|
|
this.$telemetry.track('User moved parameters pane', {
|
|
window_width: e.windowWidth,
|
|
start_position: this.mainPanelPosition,
|
|
end_position: e.position,
|
|
node_type: this.activeNodeType ? this.activeNodeType.name : '',
|
|
session_id: this.sessionId,
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
});
|
|
this.mainPanelPosition = e.position;
|
|
},
|
|
onLinkRunToInput() {
|
|
this.runOutputIndex = this.runInputIndex;
|
|
this.isLinkingEnabled = true;
|
|
this.trackLinking('input');
|
|
},
|
|
onLinkRunToOutput() {
|
|
this.isLinkingEnabled = true;
|
|
this.trackLinking('output');
|
|
},
|
|
onUnlinkRun(pane: string) {
|
|
this.runInputIndex = this.runOutputIndex;
|
|
this.isLinkingEnabled = false;
|
|
this.trackLinking(pane);
|
|
},
|
|
trackLinking(pane: string) {
|
|
this.$telemetry.track('User changed ndv run linking', {
|
|
node_type: this.activeNodeType ? this.activeNodeType.name : '',
|
|
session_id: this.sessionId,
|
|
linked: this.linked,
|
|
pane,
|
|
});
|
|
},
|
|
onNodeExecute() {
|
|
setTimeout(() => {
|
|
if (!this.activeNode || !this.workflowRunning) {
|
|
return;
|
|
}
|
|
this.triggerWaitingWarningEnabled = true;
|
|
}, 1000);
|
|
},
|
|
openSettings() {
|
|
this.settingsEventBus.emit('openSettings');
|
|
},
|
|
valueChanged(parameterData: IUpdateInformation) {
|
|
this.$emit('valueChanged', parameterData);
|
|
},
|
|
nodeTypeSelected(nodeTypeName: string) {
|
|
this.$emit('nodeTypeSelected', nodeTypeName);
|
|
},
|
|
async close() {
|
|
if (this.isDragging) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
typeof this.activeNodeType?.outputs === 'string' ||
|
|
typeof this.activeNodeType?.inputs === 'string' ||
|
|
this.redrawRequired
|
|
) {
|
|
// TODO: We should keep track of if it actually changed and only do if required
|
|
// Whenever a node with custom inputs and outputs gets closed redraw it in case
|
|
// they changed
|
|
const nodeName = this.activeNode.name;
|
|
setTimeout(() => {
|
|
this.$emit('redrawNode', nodeName);
|
|
}, 1);
|
|
}
|
|
|
|
if (this.outputPanelEditMode.enabled) {
|
|
const shouldPinDataBeforeClosing = await this.confirm(
|
|
'',
|
|
this.$locale.baseText('ndv.pinData.beforeClosing.title'),
|
|
{
|
|
confirmButtonText: this.$locale.baseText('ndv.pinData.beforeClosing.confirm'),
|
|
cancelButtonText: this.$locale.baseText('ndv.pinData.beforeClosing.cancel'),
|
|
},
|
|
);
|
|
|
|
if (shouldPinDataBeforeClosing === MODAL_CONFIRM) {
|
|
const { value } = this.outputPanelEditMode;
|
|
|
|
if (this.activeNode) {
|
|
try {
|
|
this.setPinData(this.activeNode, jsonParse(value), 'on-ndv-close-modal');
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.ndvStore.setOutputPanelEditModeEnabled(false);
|
|
}
|
|
|
|
await this.$externalHooks().run('dataDisplay.nodeEditingFinished');
|
|
this.$telemetry.track('User closed node modal', {
|
|
node_type: this.activeNodeType ? this.activeNodeType.name : '',
|
|
session_id: this.sessionId,
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
});
|
|
this.triggerWaitingWarningEnabled = false;
|
|
this.ndvStore.activeNodeName = null;
|
|
this.ndvStore.resetNDVSessionId();
|
|
},
|
|
onRunOutputIndexChange(run: number) {
|
|
this.runOutputIndex = run;
|
|
this.trackRunChange(run, 'output');
|
|
},
|
|
onRunInputIndexChange(run: number) {
|
|
this.runInputIndex = run;
|
|
if (this.linked) {
|
|
this.runOutputIndex = run;
|
|
}
|
|
this.trackRunChange(run, 'input');
|
|
},
|
|
trackRunChange(run: number, pane: string) {
|
|
this.$telemetry.track('User changed ndv run dropdown', {
|
|
session_id: this.sessionId,
|
|
run_index: run,
|
|
node_type: this.activeNodeType ? this.activeNodeType.name : '',
|
|
pane,
|
|
});
|
|
},
|
|
onInputNodeChange(value: string, index: number) {
|
|
this.runInputIndex = -1;
|
|
this.isLinkingEnabled = true;
|
|
this.selectedInput = value;
|
|
|
|
this.$telemetry.track('User changed ndv input dropdown', {
|
|
node_type: this.activeNode ? this.activeNode.type : '',
|
|
session_id: this.sessionId,
|
|
workflow_id: this.workflowsStore.workflowId,
|
|
selection_value: index,
|
|
input_node_type: this.inputNode ? this.inputNode.type : '',
|
|
});
|
|
},
|
|
onStopExecution() {
|
|
this.$emit('stopExecution');
|
|
},
|
|
activateInputPane() {
|
|
this.isInputPaneActive = true;
|
|
this.isOutputPaneActive = false;
|
|
},
|
|
activateOutputPane() {
|
|
this.isInputPaneActive = false;
|
|
this.isOutputPaneActive = true;
|
|
},
|
|
onSearch(search: string) {
|
|
this.isPairedItemHoveringEnabled = !search;
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.ndv-wrapper {
|
|
overflow: visible;
|
|
margin-top: 0;
|
|
}
|
|
|
|
.data-display-wrapper {
|
|
height: calc(100% - var(--spacing-2xl));
|
|
margin-top: var(--spacing-xl) !important;
|
|
width: 100%;
|
|
background: none;
|
|
border: none;
|
|
|
|
.el-dialog__header {
|
|
padding: 0 !important;
|
|
}
|
|
|
|
.el-dialog__body {
|
|
padding: 0 !important;
|
|
height: 100%;
|
|
min-height: 400px;
|
|
overflow: visible;
|
|
border-radius: 8px;
|
|
}
|
|
}
|
|
|
|
.data-display {
|
|
height: 100%;
|
|
width: 100%;
|
|
display: flex;
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss" module>
|
|
$main-panel-width: 360px;
|
|
|
|
.modalBackground {
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
|
|
.triggerWarning {
|
|
max-width: 180px;
|
|
}
|
|
|
|
.backToCanvas {
|
|
position: fixed;
|
|
top: var(--spacing-xs);
|
|
left: var(--spacing-l);
|
|
|
|
span {
|
|
color: var(--color-ndv-back-font);
|
|
}
|
|
|
|
&:hover {
|
|
cursor: pointer;
|
|
}
|
|
|
|
> * {
|
|
margin-right: var(--spacing-3xs);
|
|
}
|
|
}
|
|
|
|
@media (min-width: $breakpoint-lg) {
|
|
.backToCanvas {
|
|
top: var(--spacing-xs);
|
|
left: var(--spacing-m);
|
|
}
|
|
}
|
|
|
|
.featureRequest {
|
|
position: absolute;
|
|
bottom: var(--spacing-4xs);
|
|
left: calc(100% + var(--spacing-s));
|
|
color: var(--color-feature-request-font);
|
|
font-size: var(--font-size-2xs);
|
|
white-space: nowrap;
|
|
|
|
* {
|
|
margin-right: var(--spacing-3xs);
|
|
}
|
|
}
|
|
</style>
|