n8n/packages/editor-ui/src/components/NodeDetailsView.vue
Alex Grozav 885dba6f12
refactor: Migrate externalHooks mixin to composables (no-changelog) (#7930)
## Summary
Provide details about your pull request and what it adds, fixes, or
changes. Photos and videos are recommended.

As part of NodeView refactor, this PR migrates all externalHooks calls
to `useExternalHooks` composable.

#### How to test the change:
1. Run using env `export N8N_DEPLOYMENT_TYPE=cloud` 
2. Hooks should still run as expected


## Issues fixed
Include links to Github issue or Community forum post or **Linear
ticket**:
> Important in order to close automatically and provide context to
reviewers

https://linear.app/n8n/issue/N8N-6349/externalhooks


## Review / Merge checklist
- [x] PR title and summary are descriptive. **Remember, the title
automatically goes into the changelog. Use `(no-changelog)` otherwise.**
([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md))
- [x] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up
ticket created.
- [x] Tests included.
> A bug is not considered fixed, unless a test is added to prevent it
from happening again. A feature is not complete without tests.
  >
> *(internal)* You can use Slack commands to trigger [e2e
tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227)
or [deploy test
instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce)
or [deploy early access version on
Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e).
2023-12-06 17:28:09 +02:00

844 lines
24 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"
:data-has-output-connection="hasOutputConnection"
>
<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"
:key="activeNode.name"
@switchSelectedNode="onSwitchSelectedNode"
@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 { 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/composables/useDeviceSupport';
import { useMessage } from '@/composables/useMessage';
import { useExternalHooks } from '@/composables/useExternalHooks';
export default defineComponent({
name: 'NodeDetailsView',
mixins: [nodeHelpers, workflowHelpers, workflowActivate, pinData],
components: {
NodeSettings,
InputPanel,
OutputPanel,
NDVDraggablePanels,
TriggerPanel,
},
props: {
readOnly: {
type: Boolean,
},
renaming: {
type: Boolean,
},
isProductionExecutionPreview: {
type: Boolean,
default: false,
},
},
setup(props, ctx) {
const externalHooks = useExternalHooks();
return {
externalHooks,
...useDeviceSupport(),
...useMessage(),
// eslint-disable-next-line @typescript-eslint/no-misused-promises
...workflowActivate.setup?.(props, ctx),
};
},
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();
},
hasOutputConnection() {
if (!this.activeNode) return false;
const outgoingConnections = this.workflowsStore.outgoingConnectionsByNodeName(
this.activeNode.name,
) as INodeConnections;
// Check if there's at-least one output connection
return (Object.values(outgoingConnections)?.[0]?.[0] ?? []).length > 0;
},
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 onSwitchSelectedNode(nodeTypeName: string) {
this.$emit('switchSelectedNode', nodeTypeName);
},
async close() {
if (this.isDragging) {
return;
}
if (
this.activeNode &&
(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 && this.activeNode) {
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;
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">
// Hide notice(.ndv-connection-hint-notice) warning when node has output connection
[data-has-output-connection='true'] .ndv-connection-hint-notice {
display: none;
}
.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>