fix(editor): Fix canvas moving check (#17856)

This commit is contained in:
Suguru Inoue
2025-07-31 12:28:46 +02:00
committed by GitHub
parent 34991d6f58
commit ddc4c0b7d9
5 changed files with 27 additions and 38 deletions

View File

@@ -142,6 +142,7 @@ export function createCanvasProvide({
connectingHandle: ref(connectingHandle),
viewport: ref(viewport),
isExperimentalNdvActive: computed(() => false),
isPaneMoving: ref(false),
} satisfies CanvasInjectionData,
};
}

View File

@@ -196,15 +196,18 @@ const classes = computed(() => ({
const panningKeyCode = ref<string[] | true>(isMobileDevice ? true : [' ', controlKeyCode]);
const panningMouseButton = ref<number[] | true>(isMobileDevice ? true : [1]);
const selectionKeyCode = ref<string | true | null>(isMobileDevice ? 'Shift' : true);
const isInPanningMode = ref(false);
function switchToPanningMode() {
selectionKeyCode.value = null;
panningMouseButton.value = [0, 1];
isInPanningMode.value = true;
}
function switchToSelectionMode() {
selectionKeyCode.value = true;
panningMouseButton.value = [1];
isInPanningMode.value = false;
}
onKeyDown(panningKeyCode.value, switchToPanningMode, {
@@ -651,9 +654,14 @@ function setReadonly(value: boolean) {
elementsSelectable.value = true;
}
function onPaneMoveStart() {
function onPaneMove({ event }: { event: unknown }) {
// The event object is either D3ZoomEvent or WheelEvent.
// Here I'm ignoring D3ZoomEvent because it's not necessarily followed by a moveEnd event.
// This can be simplified once https://github.com/bcakmakoglu/vue-flow/issues/1908 is resolved
if (isInPanningMode.value || event instanceof WheelEvent) {
isPaneMoving.value = true;
}
}
function onPaneMoveEnd() {
isPaneMoving.value = false;
@@ -894,6 +902,7 @@ provide(CanvasKey, {
initialized,
viewport,
isExperimentalNdvActive,
isPaneMoving,
});
</script>
@@ -923,7 +932,7 @@ provide(CanvasKey, {
@connect-end="onConnectEnd"
@pane-click="onClickPane"
@pane-context-menu="onOpenContextMenu"
@move-start="onPaneMoveStart"
@move="onPaneMove"
@move-end="onPaneMoveEnd"
@node-drag-stop="onNodeDragStop"
@node-click="onNodeClick"

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import InputPanel from '@/components/InputPanel.vue';
import { useCanvas } from '@/composables/useCanvas';
import type { INodeUi } from '@/Interface';
import { useNDVStore } from '@/stores/ndv.store';
import { N8nText } from '@n8n/design-system';
@@ -7,7 +8,7 @@ import { useVueFlow } from '@vue-flow/core';
import { useActiveElement } from '@vueuse/core';
import { ElPopover } from 'element-plus';
import type { Workflow } from 'n8n-workflow';
import { onBeforeUnmount, ref, useTemplateRef, watch } from 'vue';
import { ref, useTemplateRef, watch } from 'vue';
const { node, container } = defineProps<{
workflow: Workflow;
@@ -18,23 +19,12 @@ const { node, container } = defineProps<{
const ndvStore = useNDVStore();
const vf = useVueFlow();
const { isPaneMoving, viewport } = useCanvas();
const activeElement = useActiveElement();
const inputPanelRef = useTemplateRef('inputPanel');
const shouldShowInputPanel = ref(false);
const moveStartListener = vf.onMoveStart(() => {
shouldShowInputPanel.value = false;
});
const moveEndListener = vf.onMoveEnd(() => {
shouldShowInputPanel.value = getShouldShowInputPanel();
});
const viewportChangeListener = vf.onViewportChange(() => {
shouldShowInputPanel.value = false;
});
function getShouldShowInputPanel() {
const active = activeElement.value;
@@ -59,10 +49,12 @@ watch([activeElement, vf.getSelectedNodes], ([active, selected]) => {
}
});
onBeforeUnmount(() => {
moveStartListener.off();
moveEndListener.off();
viewportChangeListener.off();
watch(isPaneMoving, (moving) => {
shouldShowInputPanel.value = !moving && getShouldShowInputPanel();
});
watch(viewport, () => {
shouldShowInputPanel.value = false;
});
</script>

View File

@@ -8,13 +8,14 @@ import type { ExpressionLocalResolveContext } from '@/types/expressions';
import { N8nText } from '@n8n/design-system';
import { useVueFlow } from '@vue-flow/core';
import { watchOnce } from '@vueuse/core';
import { computed, onBeforeUnmount, provide, ref, useTemplateRef } from 'vue';
import { computed, provide, ref, useTemplateRef } from 'vue';
import { useExperimentalNdvStore } from '../experimentalNdv.store';
import ExperimentalCanvasNodeSettings from './ExperimentalCanvasNodeSettings.vue';
import { useI18n } from '@n8n/i18n';
import NodeIcon from '@/components/NodeIcon.vue';
import { getNodeSubTitleText } from '@/components/canvas/experimental/experimentalNdv.utils';
import ExperimentalEmbeddedNdvActions from '@/components/canvas/experimental/components/ExperimentalEmbeddedNdvActions.vue';
import { useCanvas } from '@/composables/useCanvas';
const { nodeId, isReadOnly, isConfigurable } = defineProps<{
nodeId: string;
@@ -36,22 +37,7 @@ const nodeType = computed(() => {
return null;
});
const vf = useVueFlow();
const isMoving = ref(false);
const moveStartListener = vf.onMoveStart(() => {
isMoving.value = true;
});
const moveEndListener = vf.onMoveEnd(() => {
isMoving.value = false;
});
onBeforeUnmount(() => {
moveStartListener.off();
moveEndListener.off();
});
const { isPaneMoving } = useCanvas();
const isVisible = computed(() =>
vf.isNodeIntersecting(
{ id: nodeId },
@@ -150,7 +136,7 @@ watchOnce(isVisible, (visible) => {
'--zoom': `${1 / experimentalNdvStore.maxCanvasZoom}`,
'--node-width-scaler': isConfigurable ? 1 : 1.5,
'--max-height-on-focus': `${(vf.dimensions.value.height * 0.8) / experimentalNdvStore.maxCanvasZoom}px`,
pointerEvents: isMoving ? 'none' : 'auto', // Don't interrupt canvas panning
pointerEvents: isPaneMoving ? 'none' : 'auto', // Don't interrupt canvas panning
}"
>
<template v-if="!node || !isOnceVisible" />

View File

@@ -164,6 +164,7 @@ export interface CanvasInjectionData {
connectingHandle: Ref<ConnectStartEvent | undefined>;
viewport: Ref<ViewportTransform>;
isExperimentalNdvActive: ComputedRef<boolean>;
isPaneMoving: Ref<boolean>;
}
export type CanvasNodeEventBusEvents = {