fix(editor): Prevent cursor from jumping in the focus panel (no-changelog) (#17976)

This commit is contained in:
Daria
2025-08-05 10:22:41 +03:00
committed by GitHub
parent d0443dce11
commit 0fffb807ee

View File

@@ -24,7 +24,6 @@ import {
isResourceLocatorValue, isResourceLocatorValue,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { useEnvironmentsStore } from '@/stores/environments.ee.store'; import { useEnvironmentsStore } from '@/stores/environments.ee.store';
import { useDebounce } from '@/composables/useDebounce';
import { htmlEditorEventBus } from '@/event-bus'; import { htmlEditorEventBus } from '@/event-bus';
import { hasFocusOnInput, isFocusableEl } from '@/utils/typesUtils'; import { hasFocusOnInput, isFocusableEl } from '@/utils/typesUtils';
import type { ResizeData, TargetNodeParameterContext } from '@/Interface'; import type { ResizeData, TargetNodeParameterContext } from '@/Interface';
@@ -58,7 +57,6 @@ const telemetry = useTelemetry();
const nodeSettingsParameters = useNodeSettingsParameters(); const nodeSettingsParameters = useNodeSettingsParameters();
const environmentsStore = useEnvironmentsStore(); const environmentsStore = useEnvironmentsStore();
const deviceSupport = useDeviceSupport(); const deviceSupport = useDeviceSupport();
const { debounce } = useDebounce();
const styles = useStyles(); const styles = useStyles();
const focusedNodeParameter = computed(() => focusPanelStore.focusedNodeParameters[0]); const focusedNodeParameter = computed(() => focusPanelStore.focusedNodeParameters[0]);
@@ -68,6 +66,8 @@ const resolvedParameter = computed(() =>
: undefined, : undefined,
); );
const inputValue = ref<string>('');
const focusPanelActive = computed(() => focusPanelStore.focusPanelActive); const focusPanelActive = computed(() => focusPanelStore.focusPanelActive);
const focusPanelWidth = computed(() => focusPanelStore.focusPanelWidth); const focusPanelWidth = computed(() => focusPanelStore.focusPanelWidth);
@@ -290,7 +290,10 @@ function onExecute() {
); );
} }
const valueChangedDebounced = debounce(valueChanged, { debounceTime: 0 }); function onInputChange(val: string) {
inputValue.value = val;
valueChanged(val);
}
// Wait for editor to mount before focusing // Wait for editor to mount before focusing
function focusWithDelay() { function focusWithDelay() {
@@ -333,6 +336,19 @@ watch(
{ immediate: true }, { immediate: true },
); );
watch(
() => resolvedParameter.value,
(newValue) => {
if (newValue) {
const value = newValue.value;
if (typeof value === 'string' && value !== inputValue.value) {
inputValue.value = value;
}
}
},
{ immediate: true },
);
function onResize(event: ResizeData) { function onResize(event: ResizeData) {
focusPanelStore.updateWidth(event.width); focusPanelStore.updateWidth(event.width);
} }
@@ -412,86 +428,86 @@ const onResizeThrottle = useThrottleFn(onResize, 10);
<ExpressionEditorModalInput <ExpressionEditorModalInput
v-else-if="expressionModeEnabled" v-else-if="expressionModeEnabled"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:class="$style.editor" :class="$style.editor"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:path="resolvedParameter.parameterPath" :path="resolvedParameter.parameterPath"
data-test-id="expression-modal-input" data-test-id="expression-modal-input"
:target-node-parameter-context="targetNodeParameterContext" :target-node-parameter-context="targetNodeParameterContext"
@change="valueChangedDebounced($event.value)" @change="onInputChange($event.value)"
/> />
<template v-else-if="['json', 'string'].includes(resolvedParameter.parameter.type)"> <template v-else-if="['json', 'string'].includes(resolvedParameter.parameter.type)">
<CodeNodeEditor <CodeNodeEditor
v-if="editorType === 'codeNodeEditor'" v-if="editorType === 'codeNodeEditor'"
:id="resolvedParameter.parameterPath" :id="resolvedParameter.parameterPath"
ref="inputField" ref="inputField"
v-model="inputValue"
:class="$style.heightFull" :class="$style.heightFull"
:mode="codeEditorMode" :mode="codeEditorMode"
:model-value="resolvedParameter.value"
:default-value="resolvedParameter.parameter.default" :default-value="resolvedParameter.parameter.default"
:language="editorLanguage" :language="editorLanguage"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:target-node-parameter-context="targetNodeParameterContext" :target-node-parameter-context="targetNodeParameterContext"
fill-parent fill-parent
:disable-ask-ai="true" :disable-ask-ai="true"
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<HtmlEditor <HtmlEditor
v-else-if="editorType === 'htmlEditor'" v-else-if="editorType === 'htmlEditor'"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:rows="editorRows" :rows="editorRows"
:disable-expression-coloring="!isHtmlNode" :disable-expression-coloring="!isHtmlNode"
:disable-expression-completions="!isHtmlNode" :disable-expression-completions="!isHtmlNode"
fullscreen fullscreen
:target-node-parameter-context="targetNodeParameterContext" :target-node-parameter-context="targetNodeParameterContext"
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<CssEditor <CssEditor
v-else-if="editorType === 'cssEditor'" v-else-if="editorType === 'cssEditor'"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:rows="editorRows" :rows="editorRows"
fullscreen fullscreen
:target-node-parameter-context="targetNodeParameterContext" :target-node-parameter-context="targetNodeParameterContext"
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<SqlEditor <SqlEditor
v-else-if="editorType === 'sqlEditor'" v-else-if="editorType === 'sqlEditor'"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:dialect="getTypeOption('sqlDialect')" :dialect="getTypeOption('sqlDialect')"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:rows="editorRows" :rows="editorRows"
fullscreen fullscreen
:target-node-parameter-context="targetNodeParameterContext" :target-node-parameter-context="targetNodeParameterContext"
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<JsEditor <JsEditor
v-else-if="editorType === 'jsEditor'" v-else-if="editorType === 'jsEditor'"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:rows="editorRows" :rows="editorRows"
:posthog-capture="shouldCaptureForPosthog" :posthog-capture="shouldCaptureForPosthog"
fill-parent fill-parent
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<JsonEditor <JsonEditor
v-else-if="resolvedParameter.parameter.type === 'json'" v-else-if="resolvedParameter.parameter.type === 'json'"
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:rows="editorRows" :rows="editorRows"
fullscreen fullscreen
fill-parent fill-parent
@update:model-value="valueChangedDebounced" /> @update:model-value="onInputChange" />
<N8nInput <N8nInput
v-else v-else
ref="inputField" ref="inputField"
:model-value="resolvedParameter.value" v-model="inputValue"
:class="$style.editor" :class="$style.editor"
:readonly="isReadOnly" :readonly="isReadOnly"
type="textarea" type="textarea"
resize="none" resize="none"
@update:model-value="valueChangedDebounced" @update:model-value="onInputChange"
></N8nInput ></N8nInput
></template> ></template>
</div> </div>