From 5e2e205394adf76faf02aee2d4f21df71848e1d4 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Mon, 4 Nov 2024 14:36:54 +0200 Subject: [PATCH] feat(editor): Update panning and selection keybindings on new canvas (#11534) --- packages/editor-ui/package.json | 4 +- .../src/components/canvas/Canvas.spec.ts | 16 ---- .../src/components/canvas/Canvas.vue | 90 ++++--------------- pnpm-lock.yaml | 73 ++++++++------- 4 files changed, 60 insertions(+), 123 deletions(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 880ac59e3d..4243dddc9a 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -39,9 +39,9 @@ "@n8n/codemirror-lang-sql": "^1.0.2", "@n8n/permissions": "workspace:*", "@sentry/vue": "catalog:frontend", - "@vue-flow/background": "^1.3.0", + "@vue-flow/background": "^1.3.1", "@vue-flow/controls": "^1.1.2", - "@vue-flow/core": "^1.41.2", + "@vue-flow/core": "^1.41.4", "@vue-flow/minimap": "^1.5.0", "@vue-flow/node-resizer": "^1.4.0", "@vueuse/components": "^10.11.0", diff --git a/packages/editor-ui/src/components/canvas/Canvas.spec.ts b/packages/editor-ui/src/components/canvas/Canvas.spec.ts index aa23b7064a..8e53425532 100644 --- a/packages/editor-ui/src/components/canvas/Canvas.spec.ts +++ b/packages/editor-ui/src/components/canvas/Canvas.spec.ts @@ -220,20 +220,4 @@ describe('Canvas', () => { expect(container.querySelector('#diagonalHatch')).toBeInTheDocument(); }); }); - - describe('pane', () => { - describe('onPaneMouseDown', () => { - it('should enable panning when middle mouse button is pressed', async () => { - const { getByTestId } = renderComponent(); - const canvas = getByTestId('canvas'); - const pane = canvas.querySelector('.vue-flow__pane'); - - if (!pane) throw new Error('VueFlow pane not in the document'); - - await fireEvent.mouseDown(canvas, { button: 1, view: window }); - - expect(canvas).toHaveClass('draggable'); - }); - }); - }); }); diff --git a/packages/editor-ui/src/components/canvas/Canvas.vue b/packages/editor-ui/src/components/canvas/Canvas.vue index 554900f24b..1fa52ac756 100644 --- a/packages/editor-ui/src/components/canvas/Canvas.vue +++ b/packages/editor-ui/src/components/canvas/Canvas.vue @@ -18,19 +18,9 @@ import { Background } from '@vue-flow/background'; import { MiniMap } from '@vue-flow/minimap'; import Node from './elements/nodes/CanvasNode.vue'; import Edge from './elements/edges/CanvasEdge.vue'; -import { - computed, - nextTick, - onMounted, - onUnmounted, - provide, - ref, - toRef, - useCssModule, - watch, -} from 'vue'; +import { computed, onMounted, onUnmounted, provide, ref, toRef, useCssModule, watch } from 'vue'; import type { EventBus } from 'n8n-design-system'; -import { createEventBus } from 'n8n-design-system'; +import { createEventBus, useDeviceSupport } from 'n8n-design-system'; import { useContextMenu, type ContextMenuAction } from '@/composables/useContextMenu'; import { useKeybindings } from '@/composables/useKeybindings'; import ContextMenu from '@/components/ContextMenu/ContextMenu.vue'; @@ -42,7 +32,6 @@ import { CanvasKey } from '@/constants'; import { onKeyDown, onKeyUp, useDebounceFn } from '@vueuse/core'; import CanvasArrowHeadMarker from './elements/edges/CanvasArrowHeadMarker.vue'; import CanvasBackgroundStripedPattern from './elements/CanvasBackgroundStripedPattern.vue'; -import { isMiddleMouseButton } from '@/utils/eventUtils'; const $style = useCssModule(); @@ -106,8 +95,9 @@ const props = withDefaults( }, ); +const { controlKeyCode } = useDeviceSupport(); + const { - vueFlowRef, getSelectedNodes: selectedNodes, addSelectedNodes, removeSelectedNodes, @@ -130,7 +120,6 @@ const isPaneReady = ref(false); const classes = computed(() => ({ [$style.canvas]: true, [$style.ready]: isPaneReady.value, - [$style.draggable]: isPanningEnabled.value, })); /** @@ -143,16 +132,16 @@ const disableKeyBindings = computed(() => !props.keyBindings); * @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#whitespace_keys */ -const panningKeyCode = ' '; -const isPanningEnabled = ref(false); +const panningKeyCode = ref([' ', controlKeyCode]); +const panningMouseButton = ref([1]); const selectionKeyCode = ref(true); -onKeyDown(panningKeyCode, () => { - setPanningEnabled(true); +onKeyDown(panningKeyCode.value, () => { + selectionKeyCode.value = null; }); -onKeyUp(panningKeyCode, () => { - setPanningEnabled(false); +onKeyUp(panningKeyCode.value, () => { + selectionKeyCode.value = true; }); const keyMap = computed(() => ({ @@ -184,29 +173,6 @@ const keyMap = computed(() => ({ useKeybindings(keyMap, { disabled: disableKeyBindings }); -function setPanningEnabled(value: boolean) { - if (value) { - isPanningEnabled.value = true; - selectionKeyCode.value = null; - } else { - isPanningEnabled.value = false; - selectionKeyCode.value = true; - } -} - -/** - * When the window is focused, the selection key code is lost. - * We trigger a value refresh to ensure that the selection key code is set correctly again. - * - * @issue https://linear.app/n8n/issue/N8N-7843/selection-keycode-gets-unset-when-changing-tabs - */ -function resetSelectionKeyCode() { - selectionKeyCode.value = null; - void nextTick(() => { - selectionKeyCode.value = true; - }); -} - /** * Nodes */ @@ -392,28 +358,12 @@ function setReadonly(value: boolean) { elementsSelectable.value = true; } -function onPaneMouseDown(event: MouseEvent) { - if (isMiddleMouseButton(event)) { - setPanningEnabled(true); - - // Re-emit the event to start panning after setting the panning state to true - // This workaround is necessary because the Vue Flow library does not provide a way to - // start panning programmatically - void nextTick(() => - vueFlowRef.value - ?.querySelector('.vue-flow__pane') - ?.dispatchEvent(new MouseEvent('mousedown', event)), - ); - } -} - function onPaneMoveStart() { isPaneMoving.value = true; } function onPaneMoveEnd() { isPaneMoving.value = false; - setPanningEnabled(false); } /** @@ -522,13 +472,11 @@ function onMinimapMouseLeave() { onMounted(() => { props.eventBus.on('fitView', onFitView); props.eventBus.on('nodes:select', onSelectNodes); - window.addEventListener('focus', resetSelectionKeyCode); }); onUnmounted(() => { props.eventBus.off('fitView', onFitView); props.eventBus.off('nodes:select', onSelectNodes); - window.removeEventListener('focus', resetSelectionKeyCode); }); onPaneReady(async () => { @@ -560,6 +508,7 @@ provide(CanvasKey, { :apply-changes="false" :connection-line-options="{ markerEnd: MarkerType.ArrowClosed }" :connection-radius="60" + :pan-on-drag="panningMouseButton" pan-on-scroll snap-to-grid :snap-grid="[GRID_SIZE, GRID_SIZE]" @@ -578,7 +527,6 @@ provide(CanvasKey, { @nodes-change="onNodesChange" @move-start="onPaneMoveStart" @move-end="onPaneMoveEnd" - @mousedown="onPaneMouseDown" >