feat(editor): Open Focus Panel on canvas action button click (no-changelog) (#16698)

This commit is contained in:
Daria
2025-06-25 14:05:37 +03:00
committed by GitHub
parent 3835cb31ea
commit 781b680f2a
5 changed files with 43 additions and 11 deletions

View File

@@ -1454,6 +1454,7 @@
"nodeSettings.outputCleared.message": "Order of parameters changed, outgoing connections were cleared", "nodeSettings.outputCleared.message": "Order of parameters changed, outgoing connections were cleared",
"nodeSettings.nodeVersion": "{node} node version {version}", "nodeSettings.nodeVersion": "{node} node version {version}",
"nodeView.addNode": "Add node", "nodeView.addNode": "Add node",
"nodeView.openFocusPanel": "Open focus panel",
"nodeView.openNodesPanel": "Open nodes panel", "nodeView.openNodesPanel": "Open nodes panel",
"nodeView.addATriggerNodeFirst": "Add a <a data-action='showNodeCreator'>Trigger Node</a> first", "nodeView.addATriggerNodeFirst": "Add a <a data-action='showNodeCreator'>Trigger Node</a> first",
"nodeView.addOrEnableTriggerNode": "<a data-action='showNodeCreator'>Add</a> or enable a Trigger node to execute the workflow", "nodeView.addOrEnableTriggerNode": "<a data-action='showNodeCreator'>Add</a> or enable a Trigger node to execute the workflow",

View File

@@ -1,14 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
/* eslint-disable vue/no-multiple-template-root */ /* eslint-disable vue/no-multiple-template-root */
import { defineAsyncComponent, onBeforeUnmount, onMounted, ref } from 'vue'; import { computed, defineAsyncComponent, onBeforeUnmount, onMounted, ref } from 'vue';
import { getMidCanvasPosition } from '@/utils/nodeViewUtils'; import { getMidCanvasPosition } from '@/utils/nodeViewUtils';
import { import {
DEFAULT_STICKY_HEIGHT, DEFAULT_STICKY_HEIGHT,
DEFAULT_STICKY_WIDTH, DEFAULT_STICKY_WIDTH,
FOCUS_PANEL_EXPERIMENT,
NODE_CREATOR_OPEN_SOURCES, NODE_CREATOR_OPEN_SOURCES,
STICKY_NODE_TYPE, STICKY_NODE_TYPE,
} from '@/constants'; } from '@/constants';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useFocusPanelStore } from '@/stores/focusPanel.store';
import { usePostHog } from '@/stores/posthog.store';
import type { import type {
AddedNodesAndConnections, AddedNodesAndConnections,
NodeTypeSelectedPayload, NodeTypeSelectedPayload,
@@ -39,6 +42,8 @@ const emit = defineEmits<{
}>(); }>();
const uiStore = useUIStore(); const uiStore = useUIStore();
const focusPanelStore = useFocusPanelStore();
const posthogStore = usePostHog();
const i18n = useI18n(); const i18n = useI18n();
const { getAddedNodesAndConnections } = useActions(); const { getAddedNodesAndConnections } = useActions();
@@ -46,6 +51,9 @@ const { getAddedNodesAndConnections } = useActions();
const wrapperRef = ref<HTMLElement | undefined>(); const wrapperRef = ref<HTMLElement | undefined>();
const wrapperBoundingRect = ref<DOMRect | undefined>(); const wrapperBoundingRect = ref<DOMRect | undefined>();
const isStickyNotesButtonVisible = ref(true); const isStickyNotesButtonVisible = ref(true);
const isOpenFocusPanelButtonVisible = computed(() => {
return posthogStore.getVariant(FOCUS_PANEL_EXPERIMENT.name) === FOCUS_PANEL_EXPERIMENT.variant;
});
const onMouseMove = useThrottleFn((event: MouseEvent) => { const onMouseMove = useThrottleFn((event: MouseEvent) => {
if (wrapperBoundingRect.value) { if (wrapperBoundingRect.value) {
@@ -111,7 +119,7 @@ onBeforeUnmount(() => {
<template> <template>
<div v-if="!createNodeActive" :class="$style.nodeButtonsWrapper"> <div v-if="!createNodeActive" :class="$style.nodeButtonsWrapper">
<div ref="wrapperRef" :class="$style.nodeCreatorButton" data-test-id="node-creator-plus-button"> <div ref="wrapperRef" :class="$style.nodeCreatorButton">
<KeyboardShortcutTooltip <KeyboardShortcutTooltip
:label="i18n.baseText('nodeView.openNodesPanel')" :label="i18n.baseText('nodeView.openNodesPanel')"
:shortcut="{ keys: ['Tab'] }" :shortcut="{ keys: ['Tab'] }"
@@ -121,7 +129,7 @@ onBeforeUnmount(() => {
size="large" size="large"
icon="plus" icon="plus"
type="tertiary" type="tertiary"
:class="$style.nodeCreatorPlus" data-test-id="node-creator-plus-button"
@click="openNodeCreator" @click="openNodeCreator"
/> />
</KeyboardShortcutTooltip> </KeyboardShortcutTooltip>
@@ -138,6 +146,19 @@ onBeforeUnmount(() => {
<n8n-icon-button type="tertiary" :icon="['far', 'note-sticky']" /> <n8n-icon-button type="tertiary" :icon="['far', 'note-sticky']" />
</KeyboardShortcutTooltip> </KeyboardShortcutTooltip>
</div> </div>
<KeyboardShortcutTooltip
v-if="isOpenFocusPanelButtonVisible"
:label="i18n.baseText('nodeView.openFocusPanel')"
:shortcut="{ keys: ['f'], shiftKey: true }"
placement="left"
>
<n8n-icon-button
type="tertiary"
size="large"
icon="list"
@click="focusPanelStore.toggleFocusPanel"
/>
</KeyboardShortcutTooltip>
</div> </div>
</div> </div>
<Suspense> <Suspense>
@@ -169,10 +190,6 @@ onBeforeUnmount(() => {
pointer-events: all; pointer-events: all;
} }
.noEvents {
pointer-events: none;
}
.nodeCreatorButton { .nodeCreatorButton {
position: absolute; position: absolute;
text-align: center; text-align: center;
@@ -180,8 +197,4 @@ onBeforeUnmount(() => {
right: var(--spacing-s); right: var(--spacing-s);
pointer-events: all !important; pointer-events: all !important;
} }
.nodeCreatorPlus {
width: 36px;
height: 36px;
}
</style> </style>

View File

@@ -102,6 +102,7 @@ const emit = defineEmits<{
'create:workflow': []; 'create:workflow': [];
'drag-and-drop': [position: XYPosition, event: DragEvent]; 'drag-and-drop': [position: XYPosition, event: DragEvent];
'tidy-up': [CanvasLayoutEvent]; 'tidy-up': [CanvasLayoutEvent];
'toggle:focus-panel': [];
'viewport:change': [viewport: ViewportTransform, dimensions: Dimensions]; 'viewport:change': [viewport: ViewportTransform, dimensions: Dimensions];
'selection:end': [position: XYPosition]; 'selection:end': [position: XYPosition];
'open:sub-workflow': [nodeId: string]; 'open:sub-workflow': [nodeId: string];
@@ -315,6 +316,7 @@ const keyMap = computed(() => {
f2: emitWithLastSelectedNode((id) => emit('update:node:name', id)), f2: emitWithLastSelectedNode((id) => emit('update:node:name', id)),
tab: () => emit('create:node', 'tab'), tab: () => emit('create:node', 'tab'),
shift_s: () => emit('create:sticky'), shift_s: () => emit('create:sticky'),
shift_f: () => emit('toggle:focus-panel'),
ctrl_alt_n: () => emit('create:workflow'), ctrl_alt_n: () => emit('create:workflow'),
ctrl_enter: () => emit('run:workflow'), ctrl_enter: () => emit('run:workflow'),
ctrl_s: () => emit('save:workflow'), ctrl_s: () => emit('save:workflow'),

View File

@@ -27,10 +27,15 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
focusPanelActive.value = false; focusPanelActive.value = false;
}; };
const toggleFocusPanel = () => {
focusPanelActive.value = !focusPanelActive.value;
};
return { return {
focusPanelActive, focusPanelActive,
focusedNodeParameters, focusedNodeParameters,
setFocusedNodeParameter, setFocusedNodeParameter,
closeFocusPanel, closeFocusPanel,
toggleFocusPanel,
}; };
}); });

View File

@@ -136,6 +136,7 @@ import { needsAgentInput } from '@/utils/nodes/nodeTransforms';
import { useLogsStore } from '@/stores/logs.store'; import { useLogsStore } from '@/stores/logs.store';
import { canvasEventBus } from '@/event-bus/canvas'; import { canvasEventBus } from '@/event-bus/canvas';
import CanvasChatButton from '@/components/canvas/elements/buttons/CanvasChatButton.vue'; import CanvasChatButton from '@/components/canvas/elements/buttons/CanvasChatButton.vue';
import { useFocusPanelStore } from '@/stores/focusPanel.store';
defineOptions({ defineOptions({
name: 'NodeView', name: 'NodeView',
@@ -186,6 +187,7 @@ const usersStore = useUsersStore();
const tagsStore = useTagsStore(); const tagsStore = useTagsStore();
const pushConnectionStore = usePushConnectionStore(); const pushConnectionStore = usePushConnectionStore();
const ndvStore = useNDVStore(); const ndvStore = useNDVStore();
const focusPanelStore = useFocusPanelStore();
const templatesStore = useTemplatesStore(); const templatesStore = useTemplatesStore();
const builderStore = useBuilderStore(); const builderStore = useBuilderStore();
const foldersStore = useFoldersStore(); const foldersStore = useFoldersStore();
@@ -1207,6 +1209,14 @@ function onToggleNodeCreator(options: ToggleNodeCreatorOptions) {
} }
} }
function onToggleFocusPanel() {
if (!isFocusPanelFeatureEnabled.value) {
return;
}
focusPanelStore.toggleFocusPanel();
}
function closeNodeCreator() { function closeNodeCreator() {
if (nodeCreatorStore.isCreateNodeActive) { if (nodeCreatorStore.isCreateNodeActive) {
nodeCreatorStore.isCreateNodeActive = false; nodeCreatorStore.isCreateNodeActive = false;
@@ -2039,6 +2049,7 @@ onBeforeUnmount(() => {
@selection:end="onSelectionEnd" @selection:end="onSelectionEnd"
@drag-and-drop="onDragAndDrop" @drag-and-drop="onDragAndDrop"
@tidy-up="onTidyUp" @tidy-up="onTidyUp"
@toggle:focus-panel="onToggleFocusPanel"
@extract-workflow="onExtractWorkflow" @extract-workflow="onExtractWorkflow"
@start-chat="startChat()" @start-chat="startChat()"
> >